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,157 +1,215 @@
1
- import SlavicLanguage from '../classes/slavic-language.js'
2
-
3
- /**
4
- * @typedef {Object} SlavicOptions
5
- * @property {boolean} [feminine=false] Use feminine forms for numbers.
6
- */
7
-
8
1
  /**
9
- * Serbian language converter.
2
+ * Serbian Latin language converter - Functional Implementation
10
3
  *
11
- * Implements Serbian number words using the Slavic language pattern:
12
- * - Serbian number words (jedan/jedna, dva/dve, tri, četiri...)
13
- * - Gender-aware forms (masculine/feminine)
14
- * - Slavic three-form pluralization (hiljada/hiljade/hiljada)
15
- * - Latin script representation
4
+ * Self-contained converter using shared Slavic utilities.
16
5
  *
17
- * Key Features:
18
- * - Three-form pluralization system shared across Slavic languages
19
- * * Form 1 (singular): 1 (e.g., "hiljada")
20
- * * Form 2 (few): 2-4, 22-24, 32-34... excluding teens (e.g., "hiljade")
21
- * * Form 3 (many): all other numbers (e.g., "hiljada")
22
- * - Chunk-based decomposition (splits into groups of 3 digits: ones, thousands, millions, etc.)
23
- * - Large number handling via SCALE[] array with indexed [singular, few, many] forms
24
- * - Gender-specific number forms for 1 and 2 (masculine/feminine dual forms)
25
- *
26
- * Features:
27
- * - Dual gender forms for 1 and 2 (jedan/jedna, dva/dve)
28
- * - Similar structure to Croatian with different orthography
29
- * - Latin script (Serbian can use both Latin and Cyrillic)
30
- *
31
- * Inherits from SlavicLanguage for complex pluralization algorithms.
6
+ * Key features:
7
+ * - Three-form pluralization (one/few/many)
8
+ * - Gender: thousands are feminine, millions+ are masculine
9
+ * - Irregular hundreds (dvesta, trista, etc.)
10
+ * - Long scale naming with -ard forms
11
+ * - Latin script
32
12
  */
33
- export class Serbian extends SlavicLanguage {
34
- negativeWord = 'minus'
35
- decimalSeparatorWord = 'zapeta'
36
- zeroWord = 'nula'
37
- ones = {
38
- 1: ['jedan', 'jedna'],
39
- 2: ['dva', 'dve'],
40
- 3: ['tri', 'tri'],
41
- 4: ['četiri', 'četiri'],
42
- 5: ['pet', 'pet'],
43
- 6: ['šest', 'šest'],
44
- 7: ['sedam', 'sedam'],
45
- 8: ['osam', 'osam'],
46
- 9: ['devet', 'devet']
13
+
14
+ import { parseNumericValue } from '../utils/parse-numeric.js'
15
+ import { validateOptions } from '../utils/validate-options.js'
16
+
17
+ // ============================================================================
18
+ // Slavic Utilities (inlined for performance)
19
+ // ============================================================================
20
+
21
+ function pluralize (n, forms) {
22
+ const num = typeof n === 'bigint' ? Number(n) : n
23
+ const lastDigit = num % 10
24
+ const lastTwoDigits = num % 100
25
+
26
+ if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
27
+ return forms[2]
28
+ }
29
+
30
+ if (lastDigit === 1) return forms[0]
31
+ if (lastDigit >= 2 && lastDigit <= 4) return forms[1]
32
+ return forms[2]
33
+ }
34
+
35
+ function buildAllSegments (onesMasc, onesFem, teens, tens, hundreds) {
36
+ const masc = new Array(1000)
37
+ const fem = new Array(1000)
38
+
39
+ for (let i = 0; i < 1000; i++) {
40
+ masc[i] = buildSegment(i, onesMasc, teens, tens, hundreds)
41
+ fem[i] = buildSegment(i, onesFem, teens, tens, hundreds)
47
42
  }
48
43
 
49
- tens = {
50
- 0: 'deset',
51
- 1: 'jedanaest',
52
- 2: 'dvanaest',
53
- 3: 'trinaest',
54
- 4: 'četrnaest',
55
- 5: 'petnaest',
56
- 6: 'šesnaest',
57
- 7: 'sedamnaest',
58
- 8: 'osamnaest',
59
- 9: 'devetnaest'
44
+ return { masc, fem }
45
+ }
46
+
47
+ function buildSegment (n, ones, teens, tens, hundreds) {
48
+ if (n === 0) return ''
49
+
50
+ const onesDigit = n % 10
51
+ const tensDigit = Math.floor(n / 10) % 10
52
+ const hundredsDigit = Math.floor(n / 100)
53
+
54
+ const parts = []
55
+
56
+ if (hundredsDigit > 0) {
57
+ parts.push(hundreds[hundredsDigit])
60
58
  }
61
59
 
62
- twenties = {
63
- 2: 'dvadeset',
64
- 3: 'trideset',
65
- 4: 'četrdeset',
66
- 5: 'pedeset',
67
- 6: 'šezdeset',
68
- 7: 'sedamdeset',
69
- 8: 'osamdeset',
70
- 9: 'devedeset'
60
+ if (tensDigit > 1) {
61
+ parts.push(tens[tensDigit])
71
62
  }
72
63
 
73
- hundreds = {
74
- 1: 'sto',
75
- 2: 'dvesta',
76
- 3: 'trista',
77
- 4: 'četiristo',
78
- 5: 'petsto',
79
- 6: 'šesto',
80
- 7: 'sedamsto',
81
- 8: 'osamsto',
82
- 9: 'devetsto'
64
+ if (tensDigit === 1) {
65
+ parts.push(teens[onesDigit])
66
+ } else if (onesDigit > 0) {
67
+ parts.push(ones[onesDigit])
83
68
  }
84
69
 
85
- SCALE = [
86
- ['', '', '', false],
87
- ['hiljada', 'hiljade', 'hiljada', true], // 10 ^ 3
88
- ['milion', 'miliona', 'miliona', false], // 10 ^ 6
89
- ['milijarda', 'milijarde', 'milijarda', false], // 10 ^ 9
90
- ['bilion', 'biliona', 'biliona', false], // 10 ^ 12
91
- ['bilijarda', 'bilijarde', 'bilijarda', false], // 10 ^ 15
92
- ['trilion', 'triliona', 'triliona', false], // 10 ^ 18
93
- ['trilijarda', 'trilijarde', 'trilijarda', false], // 10 ^ 21
94
- ['kvadrilion', 'kvadriliona', 'kvadriliona', false], // 10 ^ 24
95
- ['kvadrilijarda', 'kvadrilijarde', 'kvadrilijarda', false], // 10 ^ 27
96
- ['kvintilion', 'kvintiliona', 'kvintiliona', false] // 10 ^ 30
97
- ]
98
-
99
- pluralize (n, forms) {
100
- const lastDigit = n % 10n
101
- const lastTwoDigits = n % 100n
102
-
103
- if ((lastTwoDigits < 10n || lastTwoDigits > 20n) && lastDigit === 1n) {
104
- return forms[0]
105
- }
70
+ return parts.join(' ')
71
+ }
106
72
 
107
- if ((lastTwoDigits < 10n || lastTwoDigits > 20n) && lastDigit > 1n && lastDigit < 5n) {
108
- return forms[1]
109
- }
73
+ // ============================================================================
74
+ // Vocabulary
75
+ // ============================================================================
110
76
 
111
- return forms[2]
77
+ const ONES_MASC = ['', 'jedan', 'dva', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']
78
+ const ONES_FEM = ['', 'jedna', 'dve', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']
79
+ const TEENS = ['deset', 'jedanaest', 'dvanaest', 'trinaest', 'četrnaest', 'petnaest', 'šesnaest', 'sedamnaest', 'osamnaest', 'devetnaest']
80
+ const TENS = ['', '', 'dvadeset', 'trideset', 'četrdeset', 'pedeset', 'šezdeset', 'sedamdeset', 'osamdeset', 'devedeset']
81
+ const HUNDREDS = ['', 'sto', 'dvesta', 'trista', 'četiristo', 'petsto', 'šesto', 'sedamsto', 'osamsto', 'devetsto']
82
+
83
+ const ZERO = 'nula'
84
+ const NEGATIVE = 'minus'
85
+ const DECIMAL_SEP = 'zapeta'
86
+
87
+ // Scale words: [singular, few, many]
88
+ const SCALE_FORMS = [
89
+ ['hiljada', 'hiljade', 'hiljada'],
90
+ ['milion', 'miliona', 'miliona'],
91
+ ['milijarda', 'milijarde', 'milijarda'],
92
+ ['bilion', 'biliona', 'biliona'],
93
+ ['bilijarda', 'bilijarde', 'bilijarda'],
94
+ ['trilion', 'triliona', 'triliona'],
95
+ ['trilijarda', 'trilijarde', 'trilijarda'],
96
+ ['kvadrilion', 'kvadriliona', 'kvadriliona'],
97
+ ['kvadrilijarda', 'kvadrilijarde', 'kvadrilijarda']
98
+ ]
99
+
100
+ // ============================================================================
101
+ // Precomputed Lookup Tables
102
+ // ============================================================================
103
+
104
+ const { masc: SEGMENTS_MASC, fem: SEGMENTS_FEM } = buildAllSegments(ONES_MASC, ONES_FEM, TEENS, TENS, HUNDREDS)
105
+
106
+ // ============================================================================
107
+ // Conversion Functions
108
+ // ============================================================================
109
+
110
+ function integerToWords (n, options = {}) {
111
+ if (n === 0n) return ZERO
112
+
113
+ if (n < 1000n) {
114
+ const segments = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC
115
+ return segments[Number(n)]
112
116
  }
113
117
 
114
- convertWholePart (number) {
115
- if (number === 0n) {
116
- return this.zeroWord
117
- }
118
- const words = []
119
- const chunks = this.splitByX(number.toString(), 3)
120
- let index = chunks.length
121
- for (const x of chunks) {
122
- index = index - 1
123
- const [n1, n2, n3] = this.getDigits(x)
124
- if (n3 > 0) {
125
- words.push(this.hundreds[n3])
126
- }
127
- if (n2 > 1) {
128
- words.push(this.twenties[n2])
129
- }
130
- if (n2 === 1n) {
131
- words.push(this.tens[n1])
132
- } else if (n1 > 0) {
133
- const isFeminine = (this.feminine || this.SCALE[index][3])
134
- const genderIndex = isFeminine ? 1 : 0
135
- words.push(this.ones[n1][genderIndex])
136
- }
137
- if ((index > 0) && (x !== 0n)) {
138
- words.push(this.pluralize(x, this.SCALE[index]))
118
+ return buildLargeNumberWords(n, options)
119
+ }
120
+
121
+ function buildLargeNumberWords (n, options) {
122
+ const numStr = n.toString()
123
+ const len = numStr.length
124
+
125
+ const segments = []
126
+ const segmentSize = 3
127
+
128
+ const remainderLen = len % segmentSize
129
+ let pos = 0
130
+ if (remainderLen > 0) {
131
+ segments.push(Number(numStr.slice(0, remainderLen)))
132
+ pos = remainderLen
133
+ }
134
+ while (pos < len) {
135
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
136
+ pos += segmentSize
137
+ }
138
+
139
+ const parts = []
140
+ let scaleIndex = segments.length - 1
141
+
142
+ for (let i = 0; i < segments.length; i++) {
143
+ const segment = segments[i]
144
+
145
+ if (segment !== 0) {
146
+ if (scaleIndex === 0) {
147
+ const segmentWords = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC
148
+ parts.push(segmentWords[segment])
149
+ } else {
150
+ const scaleForms = SCALE_FORMS[scaleIndex - 1]
151
+ const scaleWord = pluralize(segment, scaleForms)
152
+ // Thousands (scaleIndex=1) are feminine, others masculine
153
+ const isFeminine = scaleIndex === 1
154
+ const segmentWords = isFeminine ? SEGMENTS_FEM : SEGMENTS_MASC
155
+ parts.push(segmentWords[segment] + ' ' + scaleWord)
139
156
  }
140
157
  }
141
- return words.join(' ')
158
+
159
+ scaleIndex--
142
160
  }
161
+
162
+ return parts.join(' ')
163
+ }
164
+
165
+ function decimalPartToWords (decimalPart, options) {
166
+ let result = ''
167
+ let i = 0
168
+
169
+ while (i < decimalPart.length && decimalPart[i] === '0') {
170
+ if (result) result += ' '
171
+ result += ZERO
172
+ i++
173
+ }
174
+
175
+ const remainder = decimalPart.slice(i)
176
+ if (remainder) {
177
+ if (result) result += ' '
178
+ result += integerToWords(BigInt(remainder), options)
179
+ }
180
+
181
+ return result
143
182
  }
144
183
 
145
184
  /**
146
- * Converts a number to Serbian cardinal (written) form.
185
+ * Converts a numeric value to Serbian (Latin) words.
147
186
  *
148
- * @param {number|string|bigint} value The number to convert.
149
- * @param {Object} [options={}] Configuration options.
150
- * @param {boolean} [options.feminine=false] Use feminine forms for numbers.
151
- * @returns {string} The number expressed in Serbian words.
152
- * @throws {TypeError} If value is NaN or invalid type.
153
- * @throws {Error} If value is an invalid number string.
187
+ * @param {number | string | bigint} value - The numeric value to convert
188
+ * @param {Object} [options] - Optional configuration
189
+ * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
190
+ * @returns {string} The number in Serbian Latin words
154
191
  */
155
- export default function convertToWords (value, options = {}) {
156
- return new Serbian(options).convertToWords(value)
192
+ function toWords (value, options) {
193
+ options = validateOptions(options)
194
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
195
+
196
+ let result = ''
197
+
198
+ if (isNegative) {
199
+ result = NEGATIVE + ' '
200
+ }
201
+
202
+ result += integerToWords(integerPart, options)
203
+
204
+ if (decimalPart) {
205
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)
206
+ }
207
+
208
+ return result
157
209
  }
210
+
211
+ // ============================================================================
212
+ // Exports
213
+ // ============================================================================
214
+
215
+ export { toWords }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Converts a numeric value to Swedish words.
3
+ *
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @returns {string} The number in Swedish words
6
+ * @throws {TypeError} If value is not a valid numeric type
7
+ * @throws {Error} If value is not a valid number format
8
+ *
9
+ * @example
10
+ * toWords(42) // 'fyrtio-två'
11
+ * toWords(101) // 'hundra och ett'
12
+ * toWords(1000000) // 'en miljon'
13
+ */
14
+ export function toWords(value: number | string | bigint): string;