n2words 1.24.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +183 -156
  3. package/dist/languages/am-Latn.js +3 -0
  4. package/dist/languages/am-Latn.js.map +1 -0
  5. package/dist/languages/am.js +3 -0
  6. package/dist/languages/am.js.map +1 -0
  7. package/dist/languages/ar.js +3 -2
  8. package/dist/languages/ar.js.map +1 -1
  9. package/dist/languages/az.js +3 -2
  10. package/dist/languages/az.js.map +1 -1
  11. package/dist/languages/bn.js +3 -2
  12. package/dist/languages/bn.js.map +1 -1
  13. package/dist/languages/cs.js +3 -2
  14. package/dist/languages/cs.js.map +1 -1
  15. package/dist/languages/da.js +3 -2
  16. package/dist/languages/da.js.map +1 -1
  17. package/dist/languages/de.js +3 -2
  18. package/dist/languages/de.js.map +1 -1
  19. package/dist/languages/el.js +3 -2
  20. package/dist/languages/el.js.map +1 -1
  21. package/dist/languages/en.js +3 -2
  22. package/dist/languages/en.js.map +1 -1
  23. package/dist/languages/es.js +3 -2
  24. package/dist/languages/es.js.map +1 -1
  25. package/dist/languages/fa.js +3 -2
  26. package/dist/languages/fa.js.map +1 -1
  27. package/dist/languages/fi.js +3 -0
  28. package/dist/languages/fi.js.map +1 -0
  29. package/dist/languages/fil.js +3 -2
  30. package/dist/languages/fil.js.map +1 -1
  31. package/dist/languages/fr-BE.js +3 -2
  32. package/dist/languages/fr-BE.js.map +1 -1
  33. package/dist/languages/fr.js +3 -2
  34. package/dist/languages/fr.js.map +1 -1
  35. package/dist/languages/gu.js +3 -2
  36. package/dist/languages/gu.js.map +1 -1
  37. package/dist/languages/ha.js +3 -0
  38. package/dist/languages/ha.js.map +1 -0
  39. package/dist/languages/hbo.js +3 -0
  40. package/dist/languages/hbo.js.map +1 -0
  41. package/dist/languages/he.js +3 -2
  42. package/dist/languages/he.js.map +1 -1
  43. package/dist/languages/hi.js +3 -2
  44. package/dist/languages/hi.js.map +1 -1
  45. package/dist/languages/hr.js +3 -2
  46. package/dist/languages/hr.js.map +1 -1
  47. package/dist/languages/hu.js +3 -2
  48. package/dist/languages/hu.js.map +1 -1
  49. package/dist/languages/id.js +3 -2
  50. package/dist/languages/id.js.map +1 -1
  51. package/dist/languages/it.js +3 -2
  52. package/dist/languages/it.js.map +1 -1
  53. package/dist/languages/ja.js +3 -2
  54. package/dist/languages/ja.js.map +1 -1
  55. package/dist/languages/kn.js +3 -2
  56. package/dist/languages/kn.js.map +1 -1
  57. package/dist/languages/ko.js +3 -2
  58. package/dist/languages/ko.js.map +1 -1
  59. package/dist/languages/lt.js +3 -2
  60. package/dist/languages/lt.js.map +1 -1
  61. package/dist/languages/lv.js +3 -2
  62. package/dist/languages/lv.js.map +1 -1
  63. package/dist/languages/mr.js +3 -2
  64. package/dist/languages/mr.js.map +1 -1
  65. package/dist/languages/ms.js +3 -2
  66. package/dist/languages/ms.js.map +1 -1
  67. package/dist/languages/nb.js +3 -2
  68. package/dist/languages/nb.js.map +1 -1
  69. package/dist/languages/nl.js +3 -2
  70. package/dist/languages/nl.js.map +1 -1
  71. package/dist/languages/pa.js +3 -0
  72. package/dist/languages/pa.js.map +1 -0
  73. package/dist/languages/pl.js +3 -2
  74. package/dist/languages/pl.js.map +1 -1
  75. package/dist/languages/pt.js +3 -2
  76. package/dist/languages/pt.js.map +1 -1
  77. package/dist/languages/ro.js +3 -2
  78. package/dist/languages/ro.js.map +1 -1
  79. package/dist/languages/ru.js +3 -2
  80. package/dist/languages/ru.js.map +1 -1
  81. package/dist/languages/sr-Cyrl.js +3 -0
  82. package/dist/languages/sr-Cyrl.js.map +1 -0
  83. package/dist/languages/sr-Latn.js +3 -2
  84. package/dist/languages/sr-Latn.js.map +1 -1
  85. package/dist/languages/sv.js +3 -2
  86. package/dist/languages/sv.js.map +1 -1
  87. package/dist/languages/sw.js +3 -2
  88. package/dist/languages/sw.js.map +1 -1
  89. package/dist/languages/ta.js +3 -2
  90. package/dist/languages/ta.js.map +1 -1
  91. package/dist/languages/te.js +3 -2
  92. package/dist/languages/te.js.map +1 -1
  93. package/dist/languages/th.js +3 -2
  94. package/dist/languages/th.js.map +1 -1
  95. package/dist/languages/tr.js +3 -2
  96. package/dist/languages/tr.js.map +1 -1
  97. package/dist/languages/uk.js +3 -2
  98. package/dist/languages/uk.js.map +1 -1
  99. package/dist/languages/ur.js +3 -2
  100. package/dist/languages/ur.js.map +1 -1
  101. package/dist/languages/vi.js +3 -2
  102. package/dist/languages/vi.js.map +1 -1
  103. package/dist/languages/zh-Hans.js +3 -2
  104. package/dist/languages/zh-Hans.js.map +1 -1
  105. package/dist/languages/zh-Hant.js +3 -0
  106. package/dist/languages/zh-Hant.js.map +1 -0
  107. package/dist/n2words.js +3 -2
  108. package/dist/n2words.js.map +1 -1
  109. package/lib/languages/am-Latn.d.ts +7 -0
  110. package/lib/languages/am-Latn.js +164 -0
  111. package/lib/languages/am.d.ts +7 -0
  112. package/lib/languages/am.js +164 -0
  113. package/lib/languages/ar.d.ts +17 -0
  114. package/lib/languages/ar.js +171 -209
  115. package/lib/languages/az.d.ts +7 -0
  116. package/lib/languages/az.js +167 -49
  117. package/lib/languages/bn.d.ts +7 -0
  118. package/lib/languages/bn.js +142 -123
  119. package/lib/languages/cs.d.ts +18 -0
  120. package/lib/languages/cs.js +303 -176
  121. package/lib/languages/da.d.ts +14 -0
  122. package/lib/languages/da.js +267 -139
  123. package/lib/languages/de.d.ts +17 -0
  124. package/lib/languages/de.js +310 -113
  125. package/lib/languages/el.d.ts +14 -0
  126. package/lib/languages/el.js +225 -98
  127. package/lib/languages/en.d.ts +17 -0
  128. package/lib/languages/en.js +235 -102
  129. package/lib/languages/es.d.ts +21 -0
  130. package/lib/languages/es.js +307 -125
  131. package/lib/languages/fa.d.ts +7 -0
  132. package/lib/languages/fa.js +115 -108
  133. package/lib/languages/fi.d.ts +14 -0
  134. package/lib/languages/fi.js +245 -0
  135. package/lib/languages/fil.d.ts +7 -0
  136. package/lib/languages/fil.js +199 -139
  137. package/lib/languages/fr-BE.d.ts +11 -0
  138. package/lib/languages/fr-BE.js +287 -48
  139. package/lib/languages/fr.d.ts +21 -0
  140. package/lib/languages/fr.js +343 -119
  141. package/lib/languages/gu.d.ts +7 -0
  142. package/lib/languages/gu.js +125 -144
  143. package/lib/languages/ha.d.ts +7 -0
  144. package/lib/languages/ha.js +230 -0
  145. package/lib/languages/hbo.d.ts +13 -0
  146. package/lib/languages/hbo.js +300 -0
  147. package/lib/languages/he.d.ts +13 -0
  148. package/lib/languages/he.js +230 -283
  149. package/lib/languages/hi.d.ts +7 -0
  150. package/lib/languages/hi.js +142 -123
  151. package/lib/languages/hr.d.ts +11 -0
  152. package/lib/languages/hr.js +190 -129
  153. package/lib/languages/hu.d.ts +7 -0
  154. package/lib/languages/hu.js +194 -133
  155. package/lib/languages/id.d.ts +7 -0
  156. package/lib/languages/id.js +167 -140
  157. package/lib/languages/it.d.ts +19 -0
  158. package/lib/languages/it.js +337 -108
  159. package/lib/languages/ja.d.ts +17 -0
  160. package/lib/languages/ja.js +224 -155
  161. package/lib/languages/kn.d.ts +7 -0
  162. package/lib/languages/kn.js +128 -62
  163. package/lib/languages/ko.d.ts +14 -0
  164. package/lib/languages/ko.js +250 -70
  165. package/lib/languages/lt.d.ts +18 -0
  166. package/lib/languages/lt.js +287 -148
  167. package/lib/languages/lv.d.ts +18 -0
  168. package/lib/languages/lv.js +291 -123
  169. package/lib/languages/mr.d.ts +7 -0
  170. package/lib/languages/mr.js +125 -144
  171. package/lib/languages/ms.d.ts +7 -0
  172. package/lib/languages/ms.js +171 -112
  173. package/lib/languages/nb.d.ts +14 -0
  174. package/lib/languages/nb.js +275 -100
  175. package/lib/languages/nl.d.ts +26 -0
  176. package/lib/languages/nl.js +307 -174
  177. package/lib/languages/pa.d.ts +7 -0
  178. package/lib/languages/pa.js +163 -0
  179. package/lib/languages/pl.d.ts +22 -0
  180. package/lib/languages/pl.js +299 -158
  181. package/lib/languages/pt.d.ts +17 -0
  182. package/lib/languages/pt.js +279 -120
  183. package/lib/languages/ro.d.ts +18 -0
  184. package/lib/languages/ro.js +214 -337
  185. package/lib/languages/ru.d.ts +11 -0
  186. package/lib/languages/ru.js +219 -95
  187. package/lib/languages/sr-Cyrl.d.ts +11 -0
  188. package/lib/languages/sr-Cyrl.js +215 -0
  189. package/lib/languages/sr-Latn.d.ts +11 -0
  190. package/lib/languages/sr-Latn.js +190 -132
  191. package/lib/languages/sv.d.ts +14 -0
  192. package/lib/languages/sv.js +280 -103
  193. package/lib/languages/sw.d.ts +7 -0
  194. package/lib/languages/sw.js +135 -103
  195. package/lib/languages/ta.d.ts +7 -0
  196. package/lib/languages/ta.js +133 -205
  197. package/lib/languages/te.d.ts +7 -0
  198. package/lib/languages/te.js +148 -213
  199. package/lib/languages/th.d.ts +7 -0
  200. package/lib/languages/th.js +139 -101
  201. package/lib/languages/tr.d.ts +18 -0
  202. package/lib/languages/tr.js +246 -66
  203. package/lib/languages/uk.d.ts +11 -0
  204. package/lib/languages/uk.js +197 -101
  205. package/lib/languages/ur.d.ts +7 -0
  206. package/lib/languages/ur.js +160 -123
  207. package/lib/languages/vi.d.ts +17 -0
  208. package/lib/languages/vi.js +287 -164
  209. package/lib/languages/zh-Hans.d.ts +11 -0
  210. package/lib/languages/zh-Hans.js +159 -142
  211. package/lib/languages/zh-Hant.d.ts +11 -0
  212. package/lib/languages/zh-Hant.js +202 -0
  213. package/lib/n2words.d.ts +53 -0
  214. package/lib/n2words.js +91 -227
  215. package/lib/utils/is-plain-object.d.ts +13 -0
  216. package/lib/utils/is-plain-object.js +17 -0
  217. package/lib/utils/parse-numeric.d.ts +17 -0
  218. package/lib/utils/parse-numeric.js +108 -0
  219. package/lib/utils/validate-options.d.ts +8 -0
  220. package/lib/utils/validate-options.js +16 -0
  221. package/package.json +118 -67
  222. package/dist/languages/pa-Guru.js +0 -2
  223. package/dist/languages/pa-Guru.js.map +0 -1
  224. package/lib/classes/abstract-language.js +0 -261
  225. package/lib/classes/greedy-scale-language.js +0 -195
  226. package/lib/classes/slavic-language.js +0 -251
  227. package/lib/classes/south-asian-language.js +0 -161
  228. package/lib/classes/turkic-language.js +0 -63
  229. package/lib/languages/pa-Guru.js +0 -126
  230. package/typings/classes/abstract-language.d.ts +0 -144
  231. package/typings/classes/greedy-scale-language.d.ts +0 -148
  232. package/typings/classes/slavic-language.d.ts +0 -145
  233. package/typings/classes/south-asian-language.d.ts +0 -101
  234. package/typings/classes/turkic-language.d.ts +0 -42
  235. package/typings/languages/ar.d.ts +0 -93
  236. package/typings/languages/az.d.ts +0 -25
  237. package/typings/languages/bn.d.ts +0 -1
  238. package/typings/languages/cs.d.ts +0 -120
  239. package/typings/languages/da.d.ts +0 -53
  240. package/typings/languages/de.d.ts +0 -26
  241. package/typings/languages/el.d.ts +0 -11
  242. package/typings/languages/en.d.ts +0 -30
  243. package/typings/languages/es.d.ts +0 -43
  244. package/typings/languages/fa.d.ts +0 -81
  245. package/typings/languages/fil.d.ts +0 -12
  246. package/typings/languages/fr-BE.d.ts +0 -41
  247. package/typings/languages/fr.d.ts +0 -43
  248. package/typings/languages/gu.d.ts +0 -12
  249. package/typings/languages/he.d.ts +0 -197
  250. package/typings/languages/hi.d.ts +0 -1
  251. package/typings/languages/hr.d.ts +0 -110
  252. package/typings/languages/hu.d.ts +0 -37
  253. package/typings/languages/id.d.ts +0 -69
  254. package/typings/languages/it.d.ts +0 -51
  255. package/typings/languages/ja.d.ts +0 -58
  256. package/typings/languages/kn.d.ts +0 -11
  257. package/typings/languages/ko.d.ts +0 -25
  258. package/typings/languages/lt.d.ts +0 -110
  259. package/typings/languages/lv.d.ts +0 -99
  260. package/typings/languages/mr.d.ts +0 -12
  261. package/typings/languages/ms.d.ts +0 -37
  262. package/typings/languages/nb.d.ts +0 -27
  263. package/typings/languages/nl.d.ts +0 -65
  264. package/typings/languages/pa-Guru.d.ts +0 -1
  265. package/typings/languages/pl.d.ts +0 -116
  266. package/typings/languages/pt.d.ts +0 -39
  267. package/typings/languages/ro.d.ts +0 -229
  268. package/typings/languages/ru.d.ts +0 -108
  269. package/typings/languages/sr-Latn.d.ts +0 -98
  270. package/typings/languages/sv.d.ts +0 -30
  271. package/typings/languages/sw.d.ts +0 -1
  272. package/typings/languages/ta.d.ts +0 -1
  273. package/typings/languages/te.d.ts +0 -1
  274. package/typings/languages/th.d.ts +0 -1
  275. package/typings/languages/tr.d.ts +0 -46
  276. package/typings/languages/uk.d.ts +0 -117
  277. package/typings/languages/ur.d.ts +0 -1
  278. package/typings/languages/vi.d.ts +0 -116
  279. package/typings/languages/zh-Hans.d.ts +0 -57
  280. package/typings/n2words.d.ts +0 -177
@@ -1,171 +1,310 @@
1
- import SlavicLanguage from '../classes/slavic-language.js'
1
+ /**
2
+ * Lithuanian language converter - Functional Implementation
3
+ *
4
+ * A performance-optimized number-to-words converter using precomputed lookup tables.
5
+ *
6
+ * Key features:
7
+ * - Three-form pluralization (singular/plural/genitive)
8
+ * - Gender agreement (masculine/feminine for numbers < 1000)
9
+ * - Two-form hundreds (šimtas/šimtai)
10
+ * - Long scale naming
11
+ */
12
+
13
+ import { parseNumericValue } from '../utils/parse-numeric.js'
14
+ import { validateOptions } from '../utils/validate-options.js'
15
+
16
+ // ============================================================================
17
+ // Vocabulary (module-level constants)
18
+ // ============================================================================
19
+
20
+ const ONES_MASC = ['', 'vienas', 'du', 'trys', 'keturi', 'penki', 'šeši', 'septyni', 'aštuoni', 'devyni']
21
+ const ONES_FEM = ['', 'viena', 'dvi', 'trys', 'keturios', 'penkios', 'šešios', 'septynios', 'aštuonios', 'devynios']
22
+
23
+ const TEENS = ['dešimt', 'vienuolika', 'dvylika', 'trylika', 'keturiolika', 'penkiolika', 'šešiolika', 'septyniolika', 'aštuoniolika', 'devyniolika']
24
+ const TENS = ['', '', 'dvidešimt', 'trisdešimt', 'keturiasdešimt', 'penkiasdešimt', 'šešiasdešimt', 'septyniasdešimt', 'aštuoniasdešimt', 'devyniasdešimt']
25
+
26
+ // Hundreds: šimtas (singular), šimtai (plural)
27
+ const HUNDRED_SINGULAR = 'šimtas'
28
+ const HUNDRED_PLURAL = 'šimtai'
29
+
30
+ const ZERO = 'nulis'
31
+ const NEGATIVE = 'minus'
32
+ const DECIMAL_SEP = 'kablelis'
33
+
34
+ // Scale words: [singular, plural, genitive]
35
+ const SCALE_FORMS = [
36
+ ['tūkstantis', 'tūkstančiai', 'tūkstančių'],
37
+ ['milijonas', 'milijonai', 'milijonų'],
38
+ ['milijardas', 'milijardai', 'milijardų'],
39
+ ['trilijonas', 'trilijonai', 'trilijonų'],
40
+ ['kvadrilijonas', 'kvadrilijonai', 'kvadrilijonų'],
41
+ ['kvintilijonas', 'kvintilijonai', 'kvintilijonų'],
42
+ ['sikstilijonas', 'sikstilijonai', 'sikstilijonų'],
43
+ ['septilijonas', 'septilijonai', 'septilijonų'],
44
+ ['oktilijonas', 'oktilijonai', 'oktilijonų']
45
+ ]
46
+
47
+ // ============================================================================
48
+ // Precomputed Lookup Tables (built once at module load)
49
+ // ============================================================================
2
50
 
3
51
  /**
4
- * @typedef {Object} SlavicOptions
5
- * @property {boolean} [feminine=false] Use feminine forms for numbers.
52
+ * Builds segment word for 0-999 (masculine form).
6
53
  */
54
+ function buildSegment (n) {
55
+ if (n === 0) return ''
56
+
57
+ const ones = n % 10
58
+ const tens = Math.floor(n / 10) % 10
59
+ const hundreds = Math.floor(n / 100)
60
+
61
+ const parts = []
62
+
63
+ // Hundreds - Lithuanian always includes the numeral
64
+ if (hundreds > 0) {
65
+ parts.push(ONES_MASC[hundreds])
66
+ parts.push(hundreds === 1 ? HUNDRED_SINGULAR : HUNDRED_PLURAL)
67
+ }
68
+
69
+ // Tens
70
+ if (tens > 1) {
71
+ parts.push(TENS[tens])
72
+ }
73
+
74
+ // Teens or ones
75
+ if (tens === 1) {
76
+ parts.push(TEENS[ones])
77
+ } else if (ones > 0) {
78
+ parts.push(ONES_MASC[ones])
79
+ }
80
+
81
+ return parts.join(' ')
82
+ }
7
83
 
8
84
  /**
9
- * Lithuanian language converter.
10
- *
11
- * Implements Lithuanian number words using the Slavic language pattern:
12
- * - Lithuanian number words (vienas/viena, du/dvi, trys, keturi...)
13
- * - Gender-aware forms (masculine/feminine)
14
- * - Baltic three-form pluralization (tūkstantis/tūkstančiai/tūkstančių)
15
- * - Lithuanian-specific declension patterns
16
- *
17
- * Key Features:
18
- * - Three-form pluralization system shared across Slavic/Baltic languages
19
- * * Form 1 (singular): 1 (e.g., "tūkstantis")
20
- * * Form 2 (few): 2-4, 22-24, 32-34... excluding teens (e.g., "tūkstančiai")
21
- * * Form 3 (many): all other numbers (e.g., "tūkstančių")
22
- * - Chunk-based decomposition (splits into groups of 3 digits: ones, thousands, millions, etc.)
23
- * - Large number handling via thousands[] array with indexed [singular, few, many] forms
24
- * - Gender-specific number forms for 1 and 2 (masculine/feminine dual forms)
85
+ * Builds segment word for 0-999 (feminine form - only differs in ones).
86
+ */
87
+ function buildSegmentFeminine (n) {
88
+ if (n === 0) return ''
89
+
90
+ const ones = n % 10
91
+ const tens = Math.floor(n / 10) % 10
92
+ const hundreds = Math.floor(n / 100)
93
+
94
+ const parts = []
95
+
96
+ // Hundreds - always masculine
97
+ if (hundreds > 0) {
98
+ parts.push(ONES_MASC[hundreds])
99
+ parts.push(hundreds === 1 ? HUNDRED_SINGULAR : HUNDRED_PLURAL)
100
+ }
101
+
102
+ // Tens
103
+ if (tens > 1) {
104
+ parts.push(TENS[tens])
105
+ }
106
+
107
+ // Teens or ones - feminine for ones only
108
+ if (tens === 1) {
109
+ parts.push(TEENS[ones])
110
+ } else if (ones > 0) {
111
+ parts.push(ONES_FEM[ones])
112
+ }
113
+
114
+ return parts.join(' ')
115
+ }
116
+
117
+ // Precompute all 1000 segment words (0-999)
118
+ const SEGMENTS_MASC = new Array(1000)
119
+ const SEGMENTS_FEM = new Array(1000)
120
+
121
+ for (let i = 0; i < 1000; i++) {
122
+ SEGMENTS_MASC[i] = buildSegment(i)
123
+ SEGMENTS_FEM[i] = buildSegmentFeminine(i)
124
+ }
125
+
126
+ // ============================================================================
127
+ // Pluralization
128
+ // ============================================================================
129
+
130
+ /**
131
+ * Lithuanian pluralization rules.
132
+ * - Singular: ends in 1 (except 11)
133
+ * - Plural: ends in 2-9 (except 12-19)
134
+ * - Genitive: 0, 10-19, or ends in 0
25
135
  *
26
- * Features:
27
- * - Dual gender forms (vienas/viena, du/dvi, keturi/keturios)
28
- * - Complex declension patterns for large numbers
29
- * - Baltic language characteristics
136
+ * @param {number} n - The segment value
137
+ * @param {string[]} forms - [singular, plural, genitive]
138
+ * @returns {string} The appropriate form
139
+ */
140
+ function pluralize (n, forms) {
141
+ if (n === 0) return forms[2]
142
+
143
+ const lastDigit = n % 10
144
+ const lastTwoDigits = n % 100
145
+
146
+ // 10-19 always use genitive
147
+ if (lastTwoDigits >= 10 && lastTwoDigits <= 19) {
148
+ return forms[2]
149
+ }
150
+
151
+ // Ends in 0 → genitive
152
+ if (lastDigit === 0) {
153
+ return forms[2]
154
+ }
155
+
156
+ // Ends in 1 → singular
157
+ if (lastDigit === 1) {
158
+ return forms[0]
159
+ }
160
+
161
+ // Ends in 2-9 → plural
162
+ return forms[1]
163
+ }
164
+
165
+ // ============================================================================
166
+ // Conversion Functions
167
+ // ============================================================================
168
+
169
+ /**
170
+ * Converts a non-negative integer to Lithuanian words.
30
171
  *
31
- * Inherits from SlavicLanguage as Lithuanian uses similar pluralization.
172
+ * @param {bigint} n - Non-negative integer to convert
173
+ * @param {Object} options - Conversion options
174
+ * @returns {string} Lithuanian words
32
175
  */
33
- export class Lithuanian extends SlavicLanguage {
34
- negativeWord = 'minus'
35
- decimalSeparatorWord = 'kablelis'
36
- zeroWord = 'nulis'
37
- ones = {
38
- 1: 'vienas',
39
- 2: 'du',
40
- 3: 'trys',
41
- 4: 'keturi',
42
- 5: 'penki',
43
- 6: 'šeši',
44
- 7: 'septyni',
45
- 8: 'aštuoni',
46
- 9: 'devyni'
47
- }
48
-
49
- onesFeminine = {
50
- 1: 'viena',
51
- 2: 'dvi',
52
- 3: 'trys',
53
- 4: 'keturios',
54
- 5: 'penkios',
55
- 6: 'šešios',
56
- 7: 'septynios',
57
- 8: 'aštuonios',
58
- 9: 'devynios'
59
- }
60
-
61
- tens = {
62
- 0: 'dešimt',
63
- 1: 'vienuolika',
64
- 2: 'dvylika',
65
- 3: 'trylika',
66
- 4: 'keturiolika',
67
- 5: 'penkiolika',
68
- 6: 'šešiolika',
69
- 7: 'septyniolika',
70
- 8: 'aštuoniolika',
71
- 9: 'devyniolika'
72
- }
73
-
74
- twenties = {
75
- 2: 'dvidešimt',
76
- 3: 'trisdešimt',
77
- 4: 'keturiasdešimt',
78
- 5: 'penkiasdešimt',
79
- 6: 'šešiasdešimt',
80
- 7: 'septyniasdešimt',
81
- 8: 'aštuoniasdešimt',
82
- 9: 'devyniasdešimt'
83
- }
84
-
85
- hundreds = ['šimtas', 'šimtai']
86
-
87
- thousands = {
88
- 1: ['tūkstantis', 'tūkstančiai', 'tūkstančių'],
89
- 2: ['milijonas', 'milijonai', 'milijonų'],
90
- 3: ['milijardas', 'milijardai', 'milijardų'],
91
- 4: ['trilijonas', 'trilijonai', 'trilijonų'],
92
- 5: ['kvadrilijonas', 'kvadrilijonai', 'kvadrilijonų'],
93
- 6: ['kvintilijonas', 'kvintilijonai', 'kvintilijonų'],
94
- 7: ['sikstilijonas', 'sikstilijonai', 'sikstilijonų'],
95
- 8: ['septilijonas', 'septilijonai', 'septilijonų'],
96
- 9: ['oktilijonas', 'oktilijonai', 'oktilijonų'],
97
- 10: ['naintilijonas', 'naintilijonai', 'naintilijonų']
98
- }
99
-
100
- pluralize (n, forms) {
101
- if (n === 0n) {
102
- return forms[2]
103
- }
176
+ function integerToWords (n, options = {}) {
177
+ if (n === 0n) return ZERO
104
178
 
105
- const [n1, n2] = this.getDigits(n)
179
+ // Fast path: numbers < 1000 (direct lookup)
180
+ if (n < 1000n) {
181
+ const segments = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC
182
+ return segments[Number(n)]
183
+ }
106
184
 
107
- if (n2 === 1n || n1 === 0n) {
108
- return forms[2]
109
- }
185
+ // For numbers >= 1000, feminine only applies to final segment if < 1000
186
+ // But the fixture shows feminine NOT applying for n >= 1000
187
+ // So we use masculine for all segments when n >= 1000
188
+ return buildLargeNumberWords(n, options)
189
+ }
110
190
 
111
- if (n1 === 1n) {
112
- return forms[0]
113
- }
191
+ /**
192
+ * Builds words for numbers >= 1000.
193
+ *
194
+ * @param {bigint} n - Number >= 1000
195
+ * @param {Object} options - Conversion options
196
+ * @returns {string} Lithuanian words
197
+ */
198
+ function buildLargeNumberWords (n, options) {
199
+ const numStr = n.toString()
200
+ const len = numStr.length
114
201
 
115
- return forms[1]
202
+ // Build segments of 3 digits from right to left
203
+ const segments = []
204
+ const segmentSize = 3
205
+
206
+ const remainderLen = len % segmentSize
207
+ let pos = 0
208
+ if (remainderLen > 0) {
209
+ segments.push(Number(numStr.slice(0, remainderLen)))
210
+ pos = remainderLen
211
+ }
212
+ while (pos < len) {
213
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
214
+ pos += segmentSize
116
215
  }
117
216
 
118
- convertWholePart (number) {
119
- if (number === 0n) {
120
- return this.zeroWord
121
- }
122
- const words = []
123
- const chunks = this.splitByX(number.toString(), 3)
124
- let index = chunks.length
125
- for (const x of chunks) {
126
- index = index - 1
127
- if (x === 0n) {
128
- continue
129
- }
130
- const [n1, n2, n3] = this.getDigits(x)
131
- if (n3 > 0n) {
132
- words.push(this.ones[n3])
133
- if (n3 > 1n) {
134
- words.push(this.hundreds[1])
135
- } else {
136
- words.push(this.hundreds[0])
137
- }
138
- }
139
- if (n2 > 1n) {
140
- words.push(this.twenties[n2])
141
- }
142
- if (n2 === 1n) {
143
- words.push(this.tens[n1])
144
- } else if (n1 > 0n) {
145
- if ((index === 1 || (this.feminine && index === 0)) && number < 1000n) {
146
- words.push(this.onesFeminine[n1])
147
- } else {
148
- words.push(this.ones[n1])
149
- }
150
- }
151
- if (index > 0) {
152
- words.push(this.pluralize(x, this.thousands[index]))
217
+ // Convert segments to words
218
+ const parts = []
219
+ let scaleIndex = segments.length - 1
220
+
221
+ for (let i = 0; i < segments.length; i++) {
222
+ const segment = segments[i]
223
+
224
+ if (segment !== 0) {
225
+ const segmentWord = SEGMENTS_MASC[segment]
226
+
227
+ if (scaleIndex === 0) {
228
+ // Units segment - use masculine (feminine doesn't apply when n >= 1000)
229
+ parts.push(segmentWord)
230
+ } else {
231
+ // Segment with scale word
232
+ const scaleForms = SCALE_FORMS[scaleIndex - 1]
233
+ const scaleWord = pluralize(segment, scaleForms)
234
+ parts.push(segmentWord + ' ' + scaleWord)
153
235
  }
154
236
  }
155
- return words.join(' ')
237
+
238
+ scaleIndex--
156
239
  }
240
+
241
+ return parts.join(' ')
157
242
  }
158
243
 
159
244
  /**
160
- * Converts a number to Lithuanian cardinal (written) form.
245
+ * Converts decimal digits to Lithuanian words.
161
246
  *
162
- * @param {number|string|bigint} value The number to convert.
163
- * @param {Object} [options={}] Configuration options.
164
- * @param {boolean} [options.feminine=false] Use feminine forms for numbers.
165
- * @returns {string} The number expressed in Lithuanian words.
166
- * @throws {TypeError} If value is NaN or invalid type.
167
- * @throws {Error} If value is an invalid number string.
247
+ * @param {string} decimalPart - Decimal digits (without the point)
248
+ * @param {Object} options - Conversion options
249
+ * @returns {string} Lithuanian words for decimal part
168
250
  */
169
- export default function convertToWords (value, options = {}) {
170
- return new Lithuanian(options).convertToWords(value)
251
+ function decimalPartToWords (decimalPart, options) {
252
+ let result = ''
253
+
254
+ // Handle leading zeros
255
+ let i = 0
256
+ while (i < decimalPart.length && decimalPart[i] === '0') {
257
+ if (result) result += ' '
258
+ result += ZERO
259
+ i++
260
+ }
261
+
262
+ // Convert remainder as a single number
263
+ const remainder = decimalPart.slice(i)
264
+ if (remainder) {
265
+ if (result) result += ' '
266
+ result += integerToWords(BigInt(remainder), options)
267
+ }
268
+
269
+ return result
171
270
  }
271
+
272
+ /**
273
+ * Converts a numeric value to Lithuanian words.
274
+ *
275
+ * @param {number | string | bigint} value - The numeric value to convert
276
+ * @param {Object} [options] - Conversion options
277
+ * @param {string} [options.gender='masculine'] - Gender for numbers < 1000
278
+ * @returns {string} The number in Lithuanian words
279
+ * @throws {TypeError} If value is not a valid numeric type
280
+ * @throws {Error} If value is not a valid number format
281
+ *
282
+ * @example
283
+ * toWords(42) // 'keturiasdešimt du'
284
+ * toWords(1, { gender: 'feminine' }) // 'viena'
285
+ * toWords(1000000) // 'vienas milijonas'
286
+ */
287
+ function toWords (value, options) {
288
+ options = validateOptions(options)
289
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
290
+
291
+ let result = ''
292
+
293
+ if (isNegative) {
294
+ result = NEGATIVE + ' '
295
+ }
296
+
297
+ result += integerToWords(integerPart, options)
298
+
299
+ if (decimalPart) {
300
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)
301
+ }
302
+
303
+ return result
304
+ }
305
+
306
+ // ============================================================================
307
+ // Public API
308
+ // ============================================================================
309
+
310
+ export { toWords }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Converts a numeric value to Latvian words.
3
+ *
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @param {Object} [options] - Conversion options
6
+ * @param {string} [options.gender='masculine'] - Gender for numbers < 1000
7
+ * @returns {string} The number in Latvian words
8
+ * @throws {TypeError} If value is not a valid numeric type
9
+ * @throws {Error} If value is not a valid number format
10
+ *
11
+ * @example
12
+ * toWords(42) // 'četrdesmit divi'
13
+ * toWords(1, { gender: 'feminine' }) // 'viena'
14
+ * toWords(1000) // 'tūkstotis'
15
+ */
16
+ export function toWords(value: number | string | bigint, options?: {
17
+ gender?: string | undefined;
18
+ }): string;