n2words 2.0.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 (327) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +86 -188
  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 -0
  8. package/dist/languages/ar.js.map +1 -0
  9. package/dist/languages/az.js +3 -0
  10. package/dist/languages/az.js.map +1 -0
  11. package/dist/languages/bn.js +3 -0
  12. package/dist/languages/bn.js.map +1 -0
  13. package/dist/languages/cs.js +3 -0
  14. package/dist/languages/cs.js.map +1 -0
  15. package/dist/languages/da.js +3 -0
  16. package/dist/languages/da.js.map +1 -0
  17. package/dist/languages/de.js +3 -0
  18. package/dist/languages/de.js.map +1 -0
  19. package/dist/languages/el.js +3 -0
  20. package/dist/languages/el.js.map +1 -0
  21. package/dist/languages/en.js +3 -0
  22. package/dist/languages/en.js.map +1 -0
  23. package/dist/languages/es.js +3 -0
  24. package/dist/languages/es.js.map +1 -0
  25. package/dist/languages/fa.js +3 -0
  26. package/dist/languages/fa.js.map +1 -0
  27. package/dist/languages/fi.js +3 -0
  28. package/dist/languages/fi.js.map +1 -0
  29. package/dist/languages/fil.js +3 -0
  30. package/dist/languages/fil.js.map +1 -0
  31. package/dist/languages/fr-BE.js +3 -0
  32. package/dist/languages/fr-BE.js.map +1 -0
  33. package/dist/languages/fr.js +3 -0
  34. package/dist/languages/fr.js.map +1 -0
  35. package/dist/languages/gu.js +3 -0
  36. package/dist/languages/gu.js.map +1 -0
  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 -0
  42. package/dist/languages/he.js.map +1 -0
  43. package/dist/languages/hi.js +3 -0
  44. package/dist/languages/hi.js.map +1 -0
  45. package/dist/languages/hr.js +3 -0
  46. package/dist/languages/hr.js.map +1 -0
  47. package/dist/languages/hu.js +3 -0
  48. package/dist/languages/hu.js.map +1 -0
  49. package/dist/languages/id.js +3 -0
  50. package/dist/languages/id.js.map +1 -0
  51. package/dist/languages/it.js +3 -0
  52. package/dist/languages/it.js.map +1 -0
  53. package/dist/languages/ja.js +3 -0
  54. package/dist/languages/ja.js.map +1 -0
  55. package/dist/languages/kn.js +3 -0
  56. package/dist/languages/kn.js.map +1 -0
  57. package/dist/languages/ko.js +3 -0
  58. package/dist/languages/ko.js.map +1 -0
  59. package/dist/languages/lt.js +3 -0
  60. package/dist/languages/lt.js.map +1 -0
  61. package/dist/languages/lv.js +3 -0
  62. package/dist/languages/lv.js.map +1 -0
  63. package/dist/languages/mr.js +3 -0
  64. package/dist/languages/mr.js.map +1 -0
  65. package/dist/languages/ms.js +3 -0
  66. package/dist/languages/ms.js.map +1 -0
  67. package/dist/languages/nb.js +3 -0
  68. package/dist/languages/nb.js.map +1 -0
  69. package/dist/languages/nl.js +3 -0
  70. package/dist/languages/nl.js.map +1 -0
  71. package/dist/languages/pa.js +3 -0
  72. package/dist/languages/pa.js.map +1 -0
  73. package/dist/languages/pl.js +3 -0
  74. package/dist/languages/pl.js.map +1 -0
  75. package/dist/languages/pt.js +3 -0
  76. package/dist/languages/pt.js.map +1 -0
  77. package/dist/languages/ro.js +3 -0
  78. package/dist/languages/ro.js.map +1 -0
  79. package/dist/languages/ru.js +3 -0
  80. package/dist/languages/ru.js.map +1 -0
  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 -0
  84. package/dist/languages/sr-Latn.js.map +1 -0
  85. package/dist/languages/sv.js +3 -0
  86. package/dist/languages/sv.js.map +1 -0
  87. package/dist/languages/sw.js +3 -0
  88. package/dist/languages/sw.js.map +1 -0
  89. package/dist/languages/ta.js +3 -0
  90. package/dist/languages/ta.js.map +1 -0
  91. package/dist/languages/te.js +3 -0
  92. package/dist/languages/te.js.map +1 -0
  93. package/dist/languages/th.js +3 -0
  94. package/dist/languages/th.js.map +1 -0
  95. package/dist/languages/tr.js +3 -0
  96. package/dist/languages/tr.js.map +1 -0
  97. package/dist/languages/uk.js +3 -0
  98. package/dist/languages/uk.js.map +1 -0
  99. package/dist/languages/ur.js +3 -0
  100. package/dist/languages/ur.js.map +1 -0
  101. package/dist/languages/vi.js +3 -0
  102. package/dist/languages/vi.js.map +1 -0
  103. package/dist/languages/zh-Hans.js +3 -0
  104. package/dist/languages/zh-Hans.js.map +1 -0
  105. package/dist/languages/zh-Hant.js +3 -0
  106. package/dist/languages/zh-Hant.js.map +1 -0
  107. package/dist/n2words.js +2 -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 +14 -27
  114. package/lib/languages/ar.js +175 -129
  115. package/lib/languages/az.d.ts +4 -9
  116. package/lib/languages/az.js +171 -37
  117. package/lib/languages/bn.d.ts +4 -8
  118. package/lib/languages/bn.js +138 -124
  119. package/lib/languages/cs.d.ts +15 -85
  120. package/lib/languages/cs.js +310 -114
  121. package/lib/languages/da.d.ts +11 -12
  122. package/lib/languages/da.js +276 -101
  123. package/lib/languages/de.d.ts +14 -11
  124. package/lib/languages/de.js +317 -86
  125. package/lib/languages/el.d.ts +11 -11
  126. package/lib/languages/el.js +231 -78
  127. package/lib/languages/en.d.ts +14 -13
  128. package/lib/languages/en.js +242 -72
  129. package/lib/languages/es.d.ts +18 -12
  130. package/lib/languages/es.js +317 -103
  131. package/lib/languages/fa.d.ts +4 -44
  132. package/lib/languages/fa.js +112 -122
  133. package/lib/languages/fi.d.ts +14 -0
  134. package/lib/languages/fi.js +245 -0
  135. package/lib/languages/fil.d.ts +4 -13
  136. package/lib/languages/fil.js +207 -106
  137. package/lib/languages/fr-BE.d.ts +8 -8
  138. package/lib/languages/fr-BE.js +294 -19
  139. package/lib/languages/fr.d.ts +18 -12
  140. package/lib/languages/fr.js +352 -89
  141. package/lib/languages/gu.d.ts +4 -8
  142. package/lib/languages/gu.js +130 -125
  143. package/lib/languages/ha.d.ts +7 -0
  144. package/lib/languages/ha.js +230 -0
  145. package/lib/languages/hbo.d.ts +10 -110
  146. package/lib/languages/hbo.js +263 -214
  147. package/lib/languages/he.d.ts +10 -77
  148. package/lib/languages/he.js +242 -172
  149. package/lib/languages/hi.d.ts +4 -8
  150. package/lib/languages/hi.js +138 -124
  151. package/lib/languages/hr.d.ts +8 -77
  152. package/lib/languages/hr.js +194 -89
  153. package/lib/languages/hu.d.ts +4 -19
  154. package/lib/languages/hu.js +198 -119
  155. package/lib/languages/id.d.ts +4 -34
  156. package/lib/languages/id.js +171 -129
  157. package/lib/languages/it.d.ts +16 -34
  158. package/lib/languages/it.js +339 -94
  159. package/lib/languages/ja.d.ts +14 -14
  160. package/lib/languages/ja.js +233 -111
  161. package/lib/languages/kn.d.ts +4 -8
  162. package/lib/languages/kn.js +130 -35
  163. package/lib/languages/ko.d.ts +11 -11
  164. package/lib/languages/ko.js +257 -49
  165. package/lib/languages/lt.d.ts +15 -67
  166. package/lib/languages/lt.js +296 -122
  167. package/lib/languages/lv.d.ts +15 -67
  168. package/lib/languages/lv.js +297 -106
  169. package/lib/languages/mr.d.ts +4 -8
  170. package/lib/languages/mr.js +130 -125
  171. package/lib/languages/ms.d.ts +4 -28
  172. package/lib/languages/ms.js +171 -116
  173. package/lib/languages/nb.d.ts +11 -9
  174. package/lib/languages/nb.js +282 -87
  175. package/lib/languages/nl.d.ts +23 -13
  176. package/lib/languages/nl.js +317 -133
  177. package/lib/languages/pa.d.ts +4 -8
  178. package/lib/languages/pa.js +156 -124
  179. package/lib/languages/pl.d.ts +19 -77
  180. package/lib/languages/pl.js +307 -87
  181. package/lib/languages/pt.d.ts +14 -26
  182. package/lib/languages/pt.js +286 -92
  183. package/lib/languages/ro.d.ts +15 -155
  184. package/lib/languages/ro.js +219 -235
  185. package/lib/languages/ru.d.ts +8 -82
  186. package/lib/languages/ru.js +222 -78
  187. package/lib/languages/sr-Cyrl.d.ts +8 -77
  188. package/lib/languages/sr-Cyrl.js +191 -89
  189. package/lib/languages/sr-Latn.d.ts +8 -77
  190. package/lib/languages/sr-Latn.js +191 -89
  191. package/lib/languages/sv.d.ts +11 -11
  192. package/lib/languages/sv.js +288 -74
  193. package/lib/languages/sw.d.ts +4 -36
  194. package/lib/languages/sw.js +133 -106
  195. package/lib/languages/ta.d.ts +4 -17
  196. package/lib/languages/ta.js +129 -201
  197. package/lib/languages/te.d.ts +4 -19
  198. package/lib/languages/te.js +141 -196
  199. package/lib/languages/th.d.ts +4 -14
  200. package/lib/languages/th.js +135 -91
  201. package/lib/languages/tr.d.ts +15 -9
  202. package/lib/languages/tr.js +256 -49
  203. package/lib/languages/uk.d.ts +8 -82
  204. package/lib/languages/uk.js +200 -78
  205. package/lib/languages/ur.d.ts +4 -8
  206. package/lib/languages/ur.js +156 -124
  207. package/lib/languages/vi.d.ts +14 -69
  208. package/lib/languages/vi.js +294 -125
  209. package/lib/languages/zh-Hans.d.ts +8 -18
  210. package/lib/languages/zh-Hans.js +163 -92
  211. package/lib/languages/zh-Hant.d.ts +8 -18
  212. package/lib/languages/zh-Hant.js +181 -90
  213. package/lib/n2words.d.ts +53 -209
  214. package/lib/n2words.js +111 -530
  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 +26 -14
  222. package/dist/ArabicConverter.js +0 -3
  223. package/dist/ArabicConverter.js.map +0 -1
  224. package/dist/AzerbaijaniConverter.js +0 -3
  225. package/dist/AzerbaijaniConverter.js.map +0 -1
  226. package/dist/BanglaConverter.js +0 -3
  227. package/dist/BanglaConverter.js.map +0 -1
  228. package/dist/BiblicalHebrewConverter.js +0 -3
  229. package/dist/BiblicalHebrewConverter.js.map +0 -1
  230. package/dist/CroatianConverter.js +0 -3
  231. package/dist/CroatianConverter.js.map +0 -1
  232. package/dist/CzechConverter.js +0 -3
  233. package/dist/CzechConverter.js.map +0 -1
  234. package/dist/DanishConverter.js +0 -3
  235. package/dist/DanishConverter.js.map +0 -1
  236. package/dist/DutchConverter.js +0 -3
  237. package/dist/DutchConverter.js.map +0 -1
  238. package/dist/EnglishConverter.js +0 -3
  239. package/dist/EnglishConverter.js.map +0 -1
  240. package/dist/FilipinoConverter.js +0 -3
  241. package/dist/FilipinoConverter.js.map +0 -1
  242. package/dist/FrenchBelgiumConverter.js +0 -3
  243. package/dist/FrenchBelgiumConverter.js.map +0 -1
  244. package/dist/FrenchConverter.js +0 -3
  245. package/dist/FrenchConverter.js.map +0 -1
  246. package/dist/GermanConverter.js +0 -3
  247. package/dist/GermanConverter.js.map +0 -1
  248. package/dist/GreekConverter.js +0 -3
  249. package/dist/GreekConverter.js.map +0 -1
  250. package/dist/GujaratiConverter.js +0 -3
  251. package/dist/GujaratiConverter.js.map +0 -1
  252. package/dist/HebrewConverter.js +0 -3
  253. package/dist/HebrewConverter.js.map +0 -1
  254. package/dist/HindiConverter.js +0 -3
  255. package/dist/HindiConverter.js.map +0 -1
  256. package/dist/HungarianConverter.js +0 -3
  257. package/dist/HungarianConverter.js.map +0 -1
  258. package/dist/IndonesianConverter.js +0 -3
  259. package/dist/IndonesianConverter.js.map +0 -1
  260. package/dist/ItalianConverter.js +0 -3
  261. package/dist/ItalianConverter.js.map +0 -1
  262. package/dist/JapaneseConverter.js +0 -3
  263. package/dist/JapaneseConverter.js.map +0 -1
  264. package/dist/KannadaConverter.js +0 -3
  265. package/dist/KannadaConverter.js.map +0 -1
  266. package/dist/KoreanConverter.js +0 -3
  267. package/dist/KoreanConverter.js.map +0 -1
  268. package/dist/LatvianConverter.js +0 -3
  269. package/dist/LatvianConverter.js.map +0 -1
  270. package/dist/LithuanianConverter.js +0 -3
  271. package/dist/LithuanianConverter.js.map +0 -1
  272. package/dist/MalayConverter.js +0 -3
  273. package/dist/MalayConverter.js.map +0 -1
  274. package/dist/MarathiConverter.js +0 -3
  275. package/dist/MarathiConverter.js.map +0 -1
  276. package/dist/NorwegianBokmalConverter.js +0 -3
  277. package/dist/NorwegianBokmalConverter.js.map +0 -1
  278. package/dist/PersianConverter.js +0 -3
  279. package/dist/PersianConverter.js.map +0 -1
  280. package/dist/PolishConverter.js +0 -3
  281. package/dist/PolishConverter.js.map +0 -1
  282. package/dist/PortugueseConverter.js +0 -3
  283. package/dist/PortugueseConverter.js.map +0 -1
  284. package/dist/PunjabiConverter.js +0 -3
  285. package/dist/PunjabiConverter.js.map +0 -1
  286. package/dist/RomanianConverter.js +0 -3
  287. package/dist/RomanianConverter.js.map +0 -1
  288. package/dist/RussianConverter.js +0 -3
  289. package/dist/RussianConverter.js.map +0 -1
  290. package/dist/SerbianCyrillicConverter.js +0 -3
  291. package/dist/SerbianCyrillicConverter.js.map +0 -1
  292. package/dist/SerbianLatinConverter.js +0 -3
  293. package/dist/SerbianLatinConverter.js.map +0 -1
  294. package/dist/SimplifiedChineseConverter.js +0 -3
  295. package/dist/SimplifiedChineseConverter.js.map +0 -1
  296. package/dist/SpanishConverter.js +0 -3
  297. package/dist/SpanishConverter.js.map +0 -1
  298. package/dist/SwahiliConverter.js +0 -3
  299. package/dist/SwahiliConverter.js.map +0 -1
  300. package/dist/SwedishConverter.js +0 -3
  301. package/dist/SwedishConverter.js.map +0 -1
  302. package/dist/TamilConverter.js +0 -3
  303. package/dist/TamilConverter.js.map +0 -1
  304. package/dist/TeluguConverter.js +0 -3
  305. package/dist/TeluguConverter.js.map +0 -1
  306. package/dist/ThaiConverter.js +0 -3
  307. package/dist/ThaiConverter.js.map +0 -1
  308. package/dist/TraditionalChineseConverter.js +0 -3
  309. package/dist/TraditionalChineseConverter.js.map +0 -1
  310. package/dist/TurkishConverter.js +0 -3
  311. package/dist/TurkishConverter.js.map +0 -1
  312. package/dist/UkrainianConverter.js +0 -3
  313. package/dist/UkrainianConverter.js.map +0 -1
  314. package/dist/UrduConverter.js +0 -3
  315. package/dist/UrduConverter.js.map +0 -1
  316. package/dist/VietnameseConverter.js +0 -3
  317. package/dist/VietnameseConverter.js.map +0 -1
  318. package/lib/classes/abstract-language.d.ts +0 -178
  319. package/lib/classes/abstract-language.js +0 -268
  320. package/lib/classes/greedy-scale-language.d.ts +0 -109
  321. package/lib/classes/greedy-scale-language.js +0 -201
  322. package/lib/classes/slavic-language.d.ts +0 -148
  323. package/lib/classes/slavic-language.js +0 -281
  324. package/lib/classes/south-asian-language.d.ts +0 -70
  325. package/lib/classes/south-asian-language.js +0 -154
  326. package/lib/classes/turkic-language.d.ts +0 -26
  327. package/lib/classes/turkic-language.js +0 -59
@@ -1,159 +1,201 @@
1
- import { AbstractLanguage } from '../classes/abstract-language.js'
2
-
3
1
  /**
4
- * Indonesian language converter.
2
+ * Indonesian language converter - Functional Implementation
3
+ *
4
+ * Self-contained converter with precomputed lookup tables.
5
5
  *
6
- * Supports:
7
- * - "Se-" prefix for one (seratus, seribu, sejuta)
6
+ * Key features:
7
+ * - "Se-" prefix for 100 (seratus) and 1000 (seribu)
8
8
  * - Regular patterns (puluh for tens, ratus for hundreds)
9
- * - Space-separated number components
9
+ * - Teens with "belas" suffix
10
+ * - Indonesian uses "satu juta" (not "sejuta") for millions+
10
11
  */
11
- export class Indonesian extends AbstractLanguage {
12
- negativeWord = 'min'
13
- decimalSeparatorWord = 'koma'
14
- zeroWord = 'nol'
15
-
16
- /**
17
- * Word forms for digits 0-9.
18
- * @type {Object.<number, string[]>}
19
- */
20
- digitWords = {
21
- 0: [],
22
- 1: ['satu'],
23
- 2: ['dua'],
24
- 3: ['tiga'],
25
- 4: ['empat'],
26
- 5: ['lima'],
27
- 6: ['enam'],
28
- 7: ['tujuh'],
29
- 8: ['delapan'],
30
- 9: ['sembilan']
12
+
13
+ import { parseNumericValue } from '../utils/parse-numeric.js'
14
+
15
+ // ============================================================================
16
+ // Vocabulary
17
+ // ============================================================================
18
+
19
+ const ONES = ['', 'satu', 'dua', 'tiga', 'empat', 'lima', 'enam', 'tujuh', 'delapan', 'sembilan']
20
+ const TEENS = ['sepuluh', 'sebelas', 'dua belas', 'tiga belas', 'empat belas', 'lima belas', 'enam belas', 'tujuh belas', 'delapan belas', 'sembilan belas']
21
+ const TENS = ['', '', 'dua puluh', 'tiga puluh', 'empat puluh', 'lima puluh', 'enam puluh', 'tujuh puluh', 'delapan puluh', 'sembilan puluh']
22
+
23
+ const HUNDRED_WORD = 'ratus'
24
+ const THOUSAND_WORD = 'ribu'
25
+ const SCALE_WORDS = ['juta', 'miliar', 'triliun', 'kuadriliun', 'kuantiliun', 'sekstiliun', 'septiliun', 'oktiliun', 'noniliun', 'desiliun']
26
+
27
+ const ZERO = 'nol'
28
+ const NEGATIVE = 'min'
29
+ const DECIMAL_SEP = 'koma'
30
+
31
+ // ============================================================================
32
+ // Precomputed Lookup Tables
33
+ // ============================================================================
34
+
35
+ function buildSegment (n) {
36
+ if (n === 0) return ''
37
+
38
+ const onesDigit = n % 10
39
+ const tensDigit = Math.floor(n / 10) % 10
40
+ const hundredsDigit = Math.floor(n / 100)
41
+
42
+ const parts = []
43
+
44
+ // Hundreds: seratus (100) or N ratus (200-900)
45
+ if (hundredsDigit > 0) {
46
+ if (hundredsDigit === 1) {
47
+ parts.push('se' + HUNDRED_WORD)
48
+ } else {
49
+ parts.push(ONES[hundredsDigit] + ' ' + HUNDRED_WORD)
50
+ }
31
51
  }
32
52
 
33
- /**
34
- * Scale magnitude words keyed by exponent (10^n).
35
- * @type {Object.<number, string>}
36
- */
37
- scaleWords = {
38
- 3: 'ribu', // 10^3
39
- 6: 'juta', // 10^6
40
- 9: 'miliar', // 10^9
41
- 12: 'triliun',
42
- 15: 'kuadriliun',
43
- 18: 'kuantiliun',
44
- 21: 'sekstiliun',
45
- 24: 'septiliun',
46
- 27: 'oktiliun',
47
- 30: 'noniliun',
48
- 33: 'desiliun'
53
+ // Tens and ones
54
+ const tensOnes = n % 100
55
+
56
+ if (tensOnes === 0) {
57
+ // Just hundreds
58
+ } else if (tensOnes < 10) {
59
+ parts.push(ONES[tensOnes])
60
+ } else if (tensOnes < 20) {
61
+ parts.push(TEENS[tensOnes - 10])
62
+ } else if (onesDigit === 0) {
63
+ parts.push(TENS[tensDigit])
64
+ } else {
65
+ parts.push(TENS[tensDigit] + ' ' + ONES[onesDigit])
49
66
  }
50
67
 
51
- /** Splits number into groups of 3 digits. */
52
- splitBy3 (number) {
53
- // Split to groups of 3 numbers: 1234567 -> [['1'], ['234'], ['567']]
54
- const blocks = []
55
- const stringNumber = number.toString()
56
- const length = stringNumber.length
57
- let firstBlock
68
+ return parts.join(' ')
69
+ }
58
70
 
59
- if (length < 3) {
60
- blocks.push([stringNumber])
61
- } else {
62
- const firstBlockLength = length % 3
71
+ const SEGMENTS = new Array(1000)
72
+ for (let i = 0; i < 1000; i++) {
73
+ SEGMENTS[i] = buildSegment(i)
74
+ }
63
75
 
64
- if (firstBlockLength > 0) {
65
- firstBlock = [stringNumber.slice(0, firstBlockLength)]
66
- blocks.push(firstBlock)
67
- }
76
+ // ============================================================================
77
+ // Conversion Functions
78
+ // ============================================================================
68
79
 
69
- for (let index = firstBlockLength; index < length; index += 3) {
70
- const nextBlock = [stringNumber.slice(index, index + 3)]
71
- blocks.push(nextBlock)
72
- }
73
- }
74
- return blocks
80
+ function integerToWords (n) {
81
+ if (n === 0n) return ZERO
82
+
83
+ if (n < 1000n) {
84
+ return SEGMENTS[Number(n)]
75
85
  }
76
86
 
77
- /** Converts digit blocks to Indonesian words. */
78
- spell (blocks) {
79
- let wordBlocks = []
80
- let spelling
81
- const firstBlock = blocks[0]
82
- if (firstBlock[0].length === 1) {
83
- spelling = firstBlock[0] === '0' ? ['nol'] : this.digitWords[Math.trunc(firstBlock[0])]
84
- } else if (firstBlock[0].length === 2) {
85
- spelling = this.getTens(firstBlock[0])
87
+ if (n < 1_000_000n) {
88
+ const thousands = Number(n / 1000n)
89
+ const remainder = Number(n % 1000n)
90
+
91
+ let result
92
+ if (thousands === 1) {
93
+ result = 'se' + THOUSAND_WORD
86
94
  } else {
87
- spelling = [...this.getHundreds(firstBlock[0][0]), ...this.getTens(firstBlock[0].slice(1, 3))]
95
+ result = SEGMENTS[thousands] + ' ' + THOUSAND_WORD
88
96
  }
89
- wordBlocks = [...wordBlocks, [firstBlock[0], spelling]]
90
- for (let index = 1; index < blocks.length; index++) {
91
- let block = blocks[index]
92
- spelling = [...this.getHundreds(block[0][0]), ...this.getTens(block[0].slice(1, 3))]
93
- block = [...block, spelling]
94
- wordBlocks = [...wordBlocks, block]
97
+
98
+ if (remainder > 0) {
99
+ result += ' ' + SEGMENTS[remainder]
95
100
  }
96
- return wordBlocks
101
+
102
+ return result
97
103
  }
98
104
 
99
- /** Converts hundreds digit with "seratus" or "ratus" suffix. */
100
- getHundreds (number) {
101
- if (number === '1') {
102
- return ['seratus']
103
- } else if (number === '0') {
104
- return []
105
- } else {
106
- return [...this.digitWords[Math.trunc(number)], 'ratus']
107
- }
105
+ return buildLargeNumberWords(n)
106
+ }
107
+
108
+ function buildLargeNumberWords (n) {
109
+ const numStr = n.toString()
110
+ const len = numStr.length
111
+
112
+ const segments = []
113
+ const segmentSize = 3
114
+
115
+ const remainderLen = len % segmentSize
116
+ let pos = 0
117
+ if (remainderLen > 0) {
118
+ segments.push(Number(numStr.slice(0, remainderLen)))
119
+ pos = remainderLen
120
+ }
121
+ while (pos < len) {
122
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
123
+ pos += segmentSize
108
124
  }
109
125
 
110
- /** Converts tens and units digits with "puluh" or "belas" suffix. */
111
- getTens (number) {
112
- if (number[0] === '1') {
113
- if (number[1] === '0') {
114
- return ['sepuluh']
115
- } else if (number[1] === '1') {
116
- return ['sebelas']
126
+ const parts = []
127
+ let scaleIndex = segments.length - 1
128
+
129
+ for (let i = 0; i < segments.length; i++) {
130
+ const segment = segments[i]
131
+
132
+ if (segment !== 0) {
133
+ if (scaleIndex === 0) {
134
+ parts.push(SEGMENTS[segment])
135
+ } else if (scaleIndex === 1) {
136
+ if (segment === 1) {
137
+ parts.push('se' + THOUSAND_WORD)
138
+ } else {
139
+ parts.push(SEGMENTS[segment] + ' ' + THOUSAND_WORD)
140
+ }
141
+ } else {
142
+ // Indonesian: "satu juta" not "sejuta"
143
+ const scaleWord = SCALE_WORDS[scaleIndex - 2]
144
+ parts.push(SEGMENTS[segment] + ' ' + scaleWord)
117
145
  }
118
- return [...this.digitWords[Math.trunc(number[1])], 'belas']
119
146
  }
120
147
 
121
- if (number[0] === '0') {
122
- return this.digitWords[Math.trunc(number[1])]
123
- }
148
+ scaleIndex--
149
+ }
150
+
151
+ return parts.join(' ')
152
+ }
153
+
154
+ function decimalPartToWords (decimalPart) {
155
+ let result = ''
124
156
 
125
- return [...this.digitWords[Math.trunc(number[0])], 'puluh', ...this.digitWords[Math.trunc(number[1])]]
157
+ let i = 0
158
+ while (i < decimalPart.length && decimalPart[i] === '0') {
159
+ if (result) result += ' '
160
+ result += ZERO
161
+ i++
126
162
  }
127
163
 
128
- /** Joins word blocks with magnitude scale words. */
129
- join (wordBlocks) {
130
- let wordList = []
131
- const length = wordBlocks.length - 1
132
- const firstBlock = [wordBlocks[0]]
133
- let start = 0
134
- if (length === 1 && firstBlock[0][0] === '1') {
135
- wordList.push('seribu')
136
- start = 1
137
- }
138
- for (let index = start; index < length + 1; index++) {
139
- wordList = [...wordList, ...wordBlocks[index][1]]
140
- if (wordBlocks[index][1].length === 0) {
141
- continue
142
- }
143
- if (index === length) {
144
- break
145
- }
146
- wordList = [...wordList, this.scaleWords[(length - index) * 3]]
147
- }
148
- return wordList.join(' ')
164
+ const remainder = decimalPart.slice(i)
165
+ if (remainder) {
166
+ if (result) result += ' '
167
+ result += integerToWords(BigInt(remainder))
168
+ }
169
+
170
+ return result
171
+ }
172
+
173
+ /**
174
+ * Converts a numeric value to Indonesian words.
175
+ *
176
+ * @param {number | string | bigint} value - The numeric value to convert
177
+ * @returns {string} The number in Indonesian words
178
+ */
179
+ function toWords (value) {
180
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
181
+
182
+ let result = ''
183
+
184
+ if (isNegative) {
185
+ result = NEGATIVE + ' '
149
186
  }
150
187
 
151
- /** Converts integer part using Indonesian group-based conversion. */
152
- integerToWords (integerPart) {
153
- return this.join(
154
- this.spell(
155
- this.splitBy3(integerPart)
156
- )
157
- ).trim()
188
+ result += integerToWords(integerPart)
189
+
190
+ if (decimalPart) {
191
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
158
192
  }
193
+
194
+ return result
159
195
  }
196
+
197
+ // ============================================================================
198
+ // Exports
199
+ // ============================================================================
200
+
201
+ export { toWords }
@@ -1,37 +1,19 @@
1
1
  /**
2
- * Italian language converter.
2
+ * Converts a numeric value to Italian words.
3
3
  *
4
- * Supports:
5
- * - Phonetic contractions (vowel elision: "ventotto" not "ventiotto")
6
- * - Accentuation rules for "tre" in compounds ("ventitré")
7
- * - Custom word construction for irregular patterns
4
+ * This is the main public API. It accepts any valid numeric input
5
+ * (number, string, or bigint) and handles parsing internally.
6
+ *
7
+ * @param {number | string | bigint} value - The numeric value to convert
8
+ * @returns {string} The number in Italian words
9
+ * @throws {TypeError} If value is not a valid numeric type
10
+ * @throws {Error} If value is not a valid number format
11
+ *
12
+ * @example
13
+ * toWords(28) // 'ventotto'
14
+ * toWords(23) // 'ventitré'
15
+ * toWords(1000) // 'mille'
16
+ * toWords(2000) // 'duemila'
17
+ * toWords(1000000) // 'un milione'
8
18
  */
9
- export class Italian extends AbstractLanguage {
10
- onesWords: string[];
11
- tensWords: {
12
- 2: string;
13
- 3: string;
14
- 4: string;
15
- 6: string;
16
- };
17
- exponentPrefixes: string[];
18
- /** Adds accent to final "tre" in compound words (ventitré). */
19
- accentuate(string: any): any;
20
- /** Omits word if it represents zero. */
21
- omitIfZero(numberToString: any): any;
22
- /** Removes duplicate vowels for phonetic contractions. */
23
- phoneticContraction(string: any): any;
24
- /** Converts tens (20-99) with phonetic contractions. */
25
- tensToCardinal(number: any): any;
26
- /** Converts hundreds (100-999) with "cento" composition. */
27
- hundredsToCardinal(number: any): any;
28
- /** Converts thousands (1000-999999) with "mille/mila" composition. */
29
- thousandsToCardinal(number: any): string;
30
- /** Converts exponent length to Italian scale word (milione, miliardo, etc.). */
31
- exponentLengthToString(exponentLength: any): string;
32
- /** Converts large numbers (millions and above) with exponent-based naming. */
33
- bigNumberToCardinal(number: any): string;
34
- /** Converts integer part using Italian custom algorithm with accentuation. */
35
- integerToWords(integerPart: any): any;
36
- }
37
- import { AbstractLanguage } from '../classes/abstract-language.js';
19
+ export function toWords(value: number | string | bigint): string;