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,174 +1,201 @@
1
- import AbstractLanguage from '../classes/abstract-language.js'
2
-
3
1
  /**
4
- * Indonesian language converter.
5
- *
6
- * Converts numbers to Indonesian words following Indonesian conventions:
7
- * - Simple base-10 structure
8
- * - "Se-" prefix for one (e.g., "seratus" = one hundred, "seribu" = one thousand)
9
- * - Space-separated number components
10
- * - Straightforward grouping by thousands
2
+ * Indonesian language converter - Functional Implementation
11
3
  *
12
- * Key Features:
13
- * - Base number mapping (base) for single digits 0-9
14
- * - Magnitude scale (thousands) mapping powers of 10 to Indonesian words
15
- * - Group-based algorithm:
16
- * 1. Split number into groups of 3 digits
17
- * 2. For each group, convert ones/tens/hundreds using base and naming rules
18
- * 3. Apply "se-" prefix for 1 (seratus, seribu, sejuta)
19
- * 4. Combine with magnitude words
20
- * 5. Join all parts with spaces
21
- * - Regular patterns (puluh for tens, ratus for hundreds, ribu for thousands)
22
- * - Clear grouping: ribu (10³), juta (10⁶), miliar (10⁹), triliun (10¹²)
4
+ * Self-contained converter with precomputed lookup tables.
23
5
  *
24
- * Features:
25
- * - "Se-" prefix usage for singular units
26
- * - Support for very large numbers (up to decillions)
6
+ * Key features:
7
+ * - "Se-" prefix for 100 (seratus) and 1000 (seribu)
8
+ * - Regular patterns (puluh for tens, ratus for hundreds)
9
+ * - Teens with "belas" suffix
10
+ * - Indonesian uses "satu juta" (not "sejuta") for millions+
27
11
  */
28
- export class Indonesian extends AbstractLanguage {
29
- negativeWord = 'min'
30
- decimalSeparatorWord = 'koma'
31
- zeroWord = 'nol'
32
- base = {
33
- 0: [],
34
- 1: ['satu'],
35
- 2: ['dua'],
36
- 3: ['tiga'],
37
- 4: ['empat'],
38
- 5: ['lima'],
39
- 6: ['enam'],
40
- 7: ['tujuh'],
41
- 8: ['delapan'],
42
- 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
+ }
43
51
  }
44
52
 
45
- thousands = {
46
- 3: 'ribu', // 10^3
47
- 6: 'juta', // 10^6
48
- 9: 'miliar', // 10^9
49
- 12: 'triliun',
50
- 15: 'kuadriliun',
51
- 18: 'kuantiliun',
52
- 21: 'sekstiliun',
53
- 24: 'septiliun',
54
- 27: 'oktiliun',
55
- 30: 'noniliun',
56
- 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])
57
66
  }
58
67
 
59
- splitBy3 (number) {
60
- // Split to groups of 3 numbers: 1234567 -> [['1'], ['234'], ['567']]
61
- const blocks = []
62
- const stringNumber = number.toString()
63
- const length = stringNumber.length
64
- let firstBlock
68
+ return parts.join(' ')
69
+ }
65
70
 
66
- if (length < 3) {
67
- blocks.push([stringNumber])
68
- } else {
69
- const firstBlockLength = length % 3
71
+ const SEGMENTS = new Array(1000)
72
+ for (let i = 0; i < 1000; i++) {
73
+ SEGMENTS[i] = buildSegment(i)
74
+ }
70
75
 
71
- if (firstBlockLength > 0) {
72
- firstBlock = [stringNumber.slice(0, firstBlockLength)]
73
- blocks.push(firstBlock)
74
- }
76
+ // ============================================================================
77
+ // Conversion Functions
78
+ // ============================================================================
75
79
 
76
- for (let index = firstBlockLength; index < length; index += 3) {
77
- const nextBlock = [stringNumber.slice(index, index + 3)]
78
- blocks.push(nextBlock)
79
- }
80
- }
81
- return blocks
80
+ function integerToWords (n) {
81
+ if (n === 0n) return ZERO
82
+
83
+ if (n < 1000n) {
84
+ return SEGMENTS[Number(n)]
82
85
  }
83
86
 
84
- spell (blocks) {
85
- let wordBlocks = []
86
- let spelling
87
- const firstBlock = blocks[0]
88
- if (firstBlock[0].length === 1) {
89
- spelling = firstBlock[0] === '0' ? ['nol'] : this.base[Math.trunc(firstBlock[0])]
90
- } else if (firstBlock[0].length === 2) {
91
- 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
92
94
  } else {
93
- spelling = [...this.getHundreds(firstBlock[0][0]), ...this.getTens(firstBlock[0].slice(1, 3))]
94
- }
95
- wordBlocks = [...wordBlocks, [firstBlock[0], spelling]]
96
- for (let index = 1; index < blocks.length; index++) {
97
- let block = blocks[index]
98
- spelling = [...this.getHundreds(block[0][0]), ...this.getTens(block[0].slice(1, 3))]
99
- block = [...block, spelling]
100
- wordBlocks = [...wordBlocks, block]
95
+ result = SEGMENTS[thousands] + ' ' + THOUSAND_WORD
101
96
  }
102
- return wordBlocks
103
- }
104
97
 
105
- getHundreds (number) {
106
- if (number === '1') {
107
- return ['seratus']
108
- } else if (number === '0') {
109
- return []
110
- } else {
111
- return [...this.base[Math.trunc(number)], 'ratus']
98
+ if (remainder > 0) {
99
+ result += ' ' + SEGMENTS[remainder]
112
100
  }
101
+
102
+ return result
113
103
  }
114
104
 
115
- getTens (number) {
116
- if (number[0] === '1') {
117
- if (number[1] === '0') {
118
- return ['sepuluh']
119
- } else if (number[1] === '1') {
120
- return ['sebelas']
121
- }
122
- return [...this.base[Math.trunc(number[1])], 'belas']
123
- }
105
+ return buildLargeNumberWords(n)
106
+ }
124
107
 
125
- if (number[0] === '0') {
126
- return this.base[Math.trunc(number[1])]
127
- }
108
+ function buildLargeNumberWords (n) {
109
+ const numStr = n.toString()
110
+ const len = numStr.length
128
111
 
129
- return [...this.base[Math.trunc(number[0])], 'puluh', ...this.base[Math.trunc(number[1])]]
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
130
124
  }
131
125
 
132
- join (wordBlocks) {
133
- let wordList = []
134
- const length = wordBlocks.length - 1
135
- const firstBlock = [wordBlocks[0]]
136
- let start = 0
137
- if (length === 1 && firstBlock[0][0] === '1') {
138
- wordList.push('seribu')
139
- start = 1
140
- }
141
- for (let index = start; index < length + 1; index++) {
142
- wordList = [...wordList, ...wordBlocks[index][1]]
143
- if (wordBlocks[index][1].length === 0) {
144
- continue
145
- }
146
- if (index === length) {
147
- break
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)
148
145
  }
149
- wordList = [...wordList, this.thousands[(length - index) * 3]]
150
146
  }
151
- return wordList.join(' ')
147
+
148
+ scaleIndex--
149
+ }
150
+
151
+ return parts.join(' ')
152
+ }
153
+
154
+ function decimalPartToWords (decimalPart) {
155
+ let result = ''
156
+
157
+ let i = 0
158
+ while (i < decimalPart.length && decimalPart[i] === '0') {
159
+ if (result) result += ' '
160
+ result += ZERO
161
+ i++
152
162
  }
153
163
 
154
- convertWholePart (number) {
155
- return this.join(
156
- this.spell(
157
- this.splitBy3(number)
158
- )
159
- ).trim()
164
+ const remainder = decimalPart.slice(i)
165
+ if (remainder) {
166
+ if (result) result += ' '
167
+ result += integerToWords(BigInt(remainder))
160
168
  }
169
+
170
+ return result
161
171
  }
162
172
 
163
173
  /**
164
- * Converts a number to Indonesian cardinal (written) form.
174
+ * Converts a numeric value to Indonesian words.
165
175
  *
166
- * @param {number|string|bigint} value The number to convert.
167
- * @param {Object} [options={}] Configuration options.
168
- * @returns {string} The number expressed in Indonesian words.
169
- * @throws {TypeError} If value is NaN or invalid type.
170
- * @throws {Error} If value is an invalid number string.
176
+ * @param {number | string | bigint} value - The numeric value to convert
177
+ * @returns {string} The number in Indonesian words
171
178
  */
172
- export default function convertToWords (value, options = {}) {
173
- return new Indonesian(options).convertToWords(value)
179
+ function toWords (value) {
180
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
181
+
182
+ let result = ''
183
+
184
+ if (isNegative) {
185
+ result = NEGATIVE + ' '
186
+ }
187
+
188
+ result += integerToWords(integerPart)
189
+
190
+ if (decimalPart) {
191
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
192
+ }
193
+
194
+ return result
174
195
  }
196
+
197
+ // ============================================================================
198
+ // Exports
199
+ // ============================================================================
200
+
201
+ export { toWords }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Converts a numeric value to Italian words.
3
+ *
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'
18
+ */
19
+ export function toWords(value: number | string | bigint): string;