n2words 2.0.0 → 3.1.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 (335) hide show
  1. package/CHANGELOG.md +64 -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/ka.js +3 -0
  56. package/dist/languages/ka.js.map +1 -0
  57. package/dist/languages/kn.js +3 -0
  58. package/dist/languages/kn.js.map +1 -0
  59. package/dist/languages/ko.js +3 -0
  60. package/dist/languages/ko.js.map +1 -0
  61. package/dist/languages/lt.js +3 -0
  62. package/dist/languages/lt.js.map +1 -0
  63. package/dist/languages/lv.js +3 -0
  64. package/dist/languages/lv.js.map +1 -0
  65. package/dist/languages/mr.js +3 -0
  66. package/dist/languages/mr.js.map +1 -0
  67. package/dist/languages/ms.js +3 -0
  68. package/dist/languages/ms.js.map +1 -0
  69. package/dist/languages/nb.js +3 -0
  70. package/dist/languages/nb.js.map +1 -0
  71. package/dist/languages/nl.js +3 -0
  72. package/dist/languages/nl.js.map +1 -0
  73. package/dist/languages/pa.js +3 -0
  74. package/dist/languages/pa.js.map +1 -0
  75. package/dist/languages/pl.js +3 -0
  76. package/dist/languages/pl.js.map +1 -0
  77. package/dist/languages/pt.js +3 -0
  78. package/dist/languages/pt.js.map +1 -0
  79. package/dist/languages/ro.js +3 -0
  80. package/dist/languages/ro.js.map +1 -0
  81. package/dist/languages/ru.js +3 -0
  82. package/dist/languages/ru.js.map +1 -0
  83. package/dist/languages/sr-Cyrl.js +3 -0
  84. package/dist/languages/sr-Cyrl.js.map +1 -0
  85. package/dist/languages/sr-Latn.js +3 -0
  86. package/dist/languages/sr-Latn.js.map +1 -0
  87. package/dist/languages/sv.js +3 -0
  88. package/dist/languages/sv.js.map +1 -0
  89. package/dist/languages/sw.js +3 -0
  90. package/dist/languages/sw.js.map +1 -0
  91. package/dist/languages/ta.js +3 -0
  92. package/dist/languages/ta.js.map +1 -0
  93. package/dist/languages/te.js +3 -0
  94. package/dist/languages/te.js.map +1 -0
  95. package/dist/languages/th.js +3 -0
  96. package/dist/languages/th.js.map +1 -0
  97. package/dist/languages/tr.js +3 -0
  98. package/dist/languages/tr.js.map +1 -0
  99. package/dist/languages/uk.js +3 -0
  100. package/dist/languages/uk.js.map +1 -0
  101. package/dist/languages/ur.js +3 -0
  102. package/dist/languages/ur.js.map +1 -0
  103. package/dist/languages/vi.js +3 -0
  104. package/dist/languages/vi.js.map +1 -0
  105. package/dist/languages/yo.js +3 -0
  106. package/dist/languages/yo.js.map +1 -0
  107. package/dist/languages/zh-Hans.js +3 -0
  108. package/dist/languages/zh-Hans.js.map +1 -0
  109. package/dist/languages/zh-Hant.js +3 -0
  110. package/dist/languages/zh-Hant.js.map +1 -0
  111. package/dist/n2words.js +2 -2
  112. package/dist/n2words.js.map +1 -1
  113. package/lib/languages/am-Latn.d.ts +7 -0
  114. package/lib/languages/am-Latn.js +159 -0
  115. package/lib/languages/am.d.ts +7 -0
  116. package/lib/languages/am.js +159 -0
  117. package/lib/languages/ar.d.ts +14 -27
  118. package/lib/languages/ar.js +175 -129
  119. package/lib/languages/az.d.ts +4 -9
  120. package/lib/languages/az.js +166 -37
  121. package/lib/languages/bn.d.ts +4 -8
  122. package/lib/languages/bn.js +159 -124
  123. package/lib/languages/cs.d.ts +15 -85
  124. package/lib/languages/cs.js +293 -114
  125. package/lib/languages/da.d.ts +11 -12
  126. package/lib/languages/da.js +269 -101
  127. package/lib/languages/de.d.ts +14 -11
  128. package/lib/languages/de.js +305 -86
  129. package/lib/languages/el.d.ts +11 -11
  130. package/lib/languages/el.js +224 -78
  131. package/lib/languages/en.d.ts +14 -13
  132. package/lib/languages/en.js +228 -72
  133. package/lib/languages/es.d.ts +18 -12
  134. package/lib/languages/es.js +297 -103
  135. package/lib/languages/fa.d.ts +4 -44
  136. package/lib/languages/fa.js +112 -122
  137. package/lib/languages/fi.d.ts +14 -0
  138. package/lib/languages/fi.js +238 -0
  139. package/lib/languages/fil.d.ts +4 -13
  140. package/lib/languages/fil.js +196 -106
  141. package/lib/languages/fr-BE.d.ts +8 -8
  142. package/lib/languages/fr-BE.js +285 -19
  143. package/lib/languages/fr.d.ts +18 -12
  144. package/lib/languages/fr.js +339 -89
  145. package/lib/languages/gu.d.ts +4 -8
  146. package/lib/languages/gu.js +143 -125
  147. package/lib/languages/ha.d.ts +7 -0
  148. package/lib/languages/ha.js +225 -0
  149. package/lib/languages/hbo.d.ts +10 -110
  150. package/lib/languages/hbo.js +245 -214
  151. package/lib/languages/he.d.ts +10 -77
  152. package/lib/languages/he.js +231 -172
  153. package/lib/languages/hi.d.ts +4 -8
  154. package/lib/languages/hi.js +163 -124
  155. package/lib/languages/hr.d.ts +8 -77
  156. package/lib/languages/hr.js +200 -89
  157. package/lib/languages/hu.d.ts +4 -19
  158. package/lib/languages/hu.js +198 -119
  159. package/lib/languages/id.d.ts +4 -34
  160. package/lib/languages/id.js +166 -129
  161. package/lib/languages/it.d.ts +16 -34
  162. package/lib/languages/it.js +307 -97
  163. package/lib/languages/ja.d.ts +14 -14
  164. package/lib/languages/ja.js +221 -111
  165. package/lib/languages/ka.d.ts +17 -0
  166. package/lib/languages/ka.js +291 -0
  167. package/lib/languages/kn.d.ts +4 -8
  168. package/lib/languages/kn.js +143 -35
  169. package/lib/languages/ko.d.ts +11 -11
  170. package/lib/languages/ko.js +250 -49
  171. package/lib/languages/lt.d.ts +15 -67
  172. package/lib/languages/lt.js +287 -122
  173. package/lib/languages/lv.d.ts +15 -67
  174. package/lib/languages/lv.js +288 -106
  175. package/lib/languages/mr.d.ts +4 -8
  176. package/lib/languages/mr.js +143 -125
  177. package/lib/languages/ms.d.ts +4 -28
  178. package/lib/languages/ms.js +166 -116
  179. package/lib/languages/nb.d.ts +11 -9
  180. package/lib/languages/nb.js +272 -87
  181. package/lib/languages/nl.d.ts +23 -13
  182. package/lib/languages/nl.js +299 -133
  183. package/lib/languages/pa.d.ts +4 -8
  184. package/lib/languages/pa.js +151 -124
  185. package/lib/languages/pl.d.ts +19 -77
  186. package/lib/languages/pl.js +294 -87
  187. package/lib/languages/pt.d.ts +14 -26
  188. package/lib/languages/pt.js +272 -92
  189. package/lib/languages/ro.d.ts +15 -155
  190. package/lib/languages/ro.js +219 -235
  191. package/lib/languages/ru.d.ts +8 -82
  192. package/lib/languages/ru.js +239 -90
  193. package/lib/languages/sr-Cyrl.d.ts +8 -77
  194. package/lib/languages/sr-Cyrl.js +197 -89
  195. package/lib/languages/sr-Latn.d.ts +8 -77
  196. package/lib/languages/sr-Latn.js +197 -89
  197. package/lib/languages/sv.d.ts +11 -11
  198. package/lib/languages/sv.js +278 -74
  199. package/lib/languages/sw.d.ts +4 -36
  200. package/lib/languages/sw.js +133 -106
  201. package/lib/languages/ta.d.ts +4 -17
  202. package/lib/languages/ta.js +143 -202
  203. package/lib/languages/te.d.ts +4 -19
  204. package/lib/languages/te.js +133 -196
  205. package/lib/languages/th.d.ts +4 -14
  206. package/lib/languages/th.js +135 -91
  207. package/lib/languages/tr.d.ts +15 -9
  208. package/lib/languages/tr.js +245 -49
  209. package/lib/languages/uk.d.ts +8 -82
  210. package/lib/languages/uk.js +206 -78
  211. package/lib/languages/ur.d.ts +4 -8
  212. package/lib/languages/ur.js +151 -124
  213. package/lib/languages/vi.d.ts +14 -69
  214. package/lib/languages/vi.js +278 -129
  215. package/lib/languages/yo.d.ts +7 -0
  216. package/lib/languages/yo.js +303 -0
  217. package/lib/languages/zh-Hans.d.ts +8 -18
  218. package/lib/languages/zh-Hans.js +163 -92
  219. package/lib/languages/zh-Hant.d.ts +8 -18
  220. package/lib/languages/zh-Hant.js +181 -90
  221. package/lib/n2words.d.ts +55 -209
  222. package/lib/n2words.js +115 -530
  223. package/lib/utils/is-plain-object.d.ts +13 -0
  224. package/lib/utils/is-plain-object.js +17 -0
  225. package/lib/utils/parse-numeric.d.ts +17 -0
  226. package/lib/utils/parse-numeric.js +108 -0
  227. package/lib/utils/validate-options.d.ts +8 -0
  228. package/lib/utils/validate-options.js +16 -0
  229. package/package.json +26 -14
  230. package/dist/ArabicConverter.js +0 -3
  231. package/dist/ArabicConverter.js.map +0 -1
  232. package/dist/AzerbaijaniConverter.js +0 -3
  233. package/dist/AzerbaijaniConverter.js.map +0 -1
  234. package/dist/BanglaConverter.js +0 -3
  235. package/dist/BanglaConverter.js.map +0 -1
  236. package/dist/BiblicalHebrewConverter.js +0 -3
  237. package/dist/BiblicalHebrewConverter.js.map +0 -1
  238. package/dist/CroatianConverter.js +0 -3
  239. package/dist/CroatianConverter.js.map +0 -1
  240. package/dist/CzechConverter.js +0 -3
  241. package/dist/CzechConverter.js.map +0 -1
  242. package/dist/DanishConverter.js +0 -3
  243. package/dist/DanishConverter.js.map +0 -1
  244. package/dist/DutchConverter.js +0 -3
  245. package/dist/DutchConverter.js.map +0 -1
  246. package/dist/EnglishConverter.js +0 -3
  247. package/dist/EnglishConverter.js.map +0 -1
  248. package/dist/FilipinoConverter.js +0 -3
  249. package/dist/FilipinoConverter.js.map +0 -1
  250. package/dist/FrenchBelgiumConverter.js +0 -3
  251. package/dist/FrenchBelgiumConverter.js.map +0 -1
  252. package/dist/FrenchConverter.js +0 -3
  253. package/dist/FrenchConverter.js.map +0 -1
  254. package/dist/GermanConverter.js +0 -3
  255. package/dist/GermanConverter.js.map +0 -1
  256. package/dist/GreekConverter.js +0 -3
  257. package/dist/GreekConverter.js.map +0 -1
  258. package/dist/GujaratiConverter.js +0 -3
  259. package/dist/GujaratiConverter.js.map +0 -1
  260. package/dist/HebrewConverter.js +0 -3
  261. package/dist/HebrewConverter.js.map +0 -1
  262. package/dist/HindiConverter.js +0 -3
  263. package/dist/HindiConverter.js.map +0 -1
  264. package/dist/HungarianConverter.js +0 -3
  265. package/dist/HungarianConverter.js.map +0 -1
  266. package/dist/IndonesianConverter.js +0 -3
  267. package/dist/IndonesianConverter.js.map +0 -1
  268. package/dist/ItalianConverter.js +0 -3
  269. package/dist/ItalianConverter.js.map +0 -1
  270. package/dist/JapaneseConverter.js +0 -3
  271. package/dist/JapaneseConverter.js.map +0 -1
  272. package/dist/KannadaConverter.js +0 -3
  273. package/dist/KannadaConverter.js.map +0 -1
  274. package/dist/KoreanConverter.js +0 -3
  275. package/dist/KoreanConverter.js.map +0 -1
  276. package/dist/LatvianConverter.js +0 -3
  277. package/dist/LatvianConverter.js.map +0 -1
  278. package/dist/LithuanianConverter.js +0 -3
  279. package/dist/LithuanianConverter.js.map +0 -1
  280. package/dist/MalayConverter.js +0 -3
  281. package/dist/MalayConverter.js.map +0 -1
  282. package/dist/MarathiConverter.js +0 -3
  283. package/dist/MarathiConverter.js.map +0 -1
  284. package/dist/NorwegianBokmalConverter.js +0 -3
  285. package/dist/NorwegianBokmalConverter.js.map +0 -1
  286. package/dist/PersianConverter.js +0 -3
  287. package/dist/PersianConverter.js.map +0 -1
  288. package/dist/PolishConverter.js +0 -3
  289. package/dist/PolishConverter.js.map +0 -1
  290. package/dist/PortugueseConverter.js +0 -3
  291. package/dist/PortugueseConverter.js.map +0 -1
  292. package/dist/PunjabiConverter.js +0 -3
  293. package/dist/PunjabiConverter.js.map +0 -1
  294. package/dist/RomanianConverter.js +0 -3
  295. package/dist/RomanianConverter.js.map +0 -1
  296. package/dist/RussianConverter.js +0 -3
  297. package/dist/RussianConverter.js.map +0 -1
  298. package/dist/SerbianCyrillicConverter.js +0 -3
  299. package/dist/SerbianCyrillicConverter.js.map +0 -1
  300. package/dist/SerbianLatinConverter.js +0 -3
  301. package/dist/SerbianLatinConverter.js.map +0 -1
  302. package/dist/SimplifiedChineseConverter.js +0 -3
  303. package/dist/SimplifiedChineseConverter.js.map +0 -1
  304. package/dist/SpanishConverter.js +0 -3
  305. package/dist/SpanishConverter.js.map +0 -1
  306. package/dist/SwahiliConverter.js +0 -3
  307. package/dist/SwahiliConverter.js.map +0 -1
  308. package/dist/SwedishConverter.js +0 -3
  309. package/dist/SwedishConverter.js.map +0 -1
  310. package/dist/TamilConverter.js +0 -3
  311. package/dist/TamilConverter.js.map +0 -1
  312. package/dist/TeluguConverter.js +0 -3
  313. package/dist/TeluguConverter.js.map +0 -1
  314. package/dist/ThaiConverter.js +0 -3
  315. package/dist/ThaiConverter.js.map +0 -1
  316. package/dist/TraditionalChineseConverter.js +0 -3
  317. package/dist/TraditionalChineseConverter.js.map +0 -1
  318. package/dist/TurkishConverter.js +0 -3
  319. package/dist/TurkishConverter.js.map +0 -1
  320. package/dist/UkrainianConverter.js +0 -3
  321. package/dist/UkrainianConverter.js.map +0 -1
  322. package/dist/UrduConverter.js +0 -3
  323. package/dist/UrduConverter.js.map +0 -1
  324. package/dist/VietnameseConverter.js +0 -3
  325. package/dist/VietnameseConverter.js.map +0 -1
  326. package/lib/classes/abstract-language.d.ts +0 -178
  327. package/lib/classes/abstract-language.js +0 -268
  328. package/lib/classes/greedy-scale-language.d.ts +0 -109
  329. package/lib/classes/greedy-scale-language.js +0 -201
  330. package/lib/classes/slavic-language.d.ts +0 -148
  331. package/lib/classes/slavic-language.js +0 -281
  332. package/lib/classes/south-asian-language.d.ts +0 -70
  333. package/lib/classes/south-asian-language.js +0 -154
  334. package/lib/classes/turkic-language.d.ts +0 -26
  335. package/lib/classes/turkic-language.js +0 -59
@@ -1,159 +1,196 @@
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 module with its own input validation, ready for subpath exports.
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
+ // Segment Building
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
+ // ============================================================================
72
+ // Conversion Functions
73
+ // ============================================================================
63
74
 
64
- if (firstBlockLength > 0) {
65
- firstBlock = [stringNumber.slice(0, firstBlockLength)]
66
- blocks.push(firstBlock)
67
- }
75
+ function integerToWords (n) {
76
+ if (n === 0n) return ZERO
68
77
 
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
78
+ if (n < 1000n) {
79
+ return buildSegment(Number(n))
75
80
  }
76
81
 
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])
82
+ if (n < 1_000_000n) {
83
+ const thousands = Number(n / 1000n)
84
+ const remainder = Number(n % 1000n)
85
+
86
+ let result
87
+ if (thousands === 1) {
88
+ result = 'se' + THOUSAND_WORD
86
89
  } else {
87
- spelling = [...this.getHundreds(firstBlock[0][0]), ...this.getTens(firstBlock[0].slice(1, 3))]
90
+ result = buildSegment(thousands) + ' ' + THOUSAND_WORD
88
91
  }
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]
92
+
93
+ if (remainder > 0) {
94
+ result += ' ' + buildSegment(remainder)
95
95
  }
96
- return wordBlocks
96
+
97
+ return result
97
98
  }
98
99
 
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
- }
100
+ return buildLargeNumberWords(n)
101
+ }
102
+
103
+ function buildLargeNumberWords (n) {
104
+ const numStr = n.toString()
105
+ const len = numStr.length
106
+
107
+ const segments = []
108
+ const segmentSize = 3
109
+
110
+ const remainderLen = len % segmentSize
111
+ let pos = 0
112
+ if (remainderLen > 0) {
113
+ segments.push(Number(numStr.slice(0, remainderLen)))
114
+ pos = remainderLen
108
115
  }
116
+ while (pos < len) {
117
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
118
+ pos += segmentSize
119
+ }
120
+
121
+ const parts = []
122
+ let scaleIndex = segments.length - 1
109
123
 
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']
124
+ for (let i = 0; i < segments.length; i++) {
125
+ const segment = segments[i]
126
+
127
+ if (segment !== 0) {
128
+ if (scaleIndex === 0) {
129
+ parts.push(buildSegment(segment))
130
+ } else if (scaleIndex === 1) {
131
+ if (segment === 1) {
132
+ parts.push('se' + THOUSAND_WORD)
133
+ } else {
134
+ parts.push(buildSegment(segment) + ' ' + THOUSAND_WORD)
135
+ }
136
+ } else {
137
+ // Indonesian: "satu juta" not "sejuta"
138
+ const scaleWord = SCALE_WORDS[scaleIndex - 2]
139
+ parts.push(buildSegment(segment) + ' ' + scaleWord)
117
140
  }
118
- return [...this.digitWords[Math.trunc(number[1])], 'belas']
119
141
  }
120
142
 
121
- if (number[0] === '0') {
122
- return this.digitWords[Math.trunc(number[1])]
123
- }
143
+ scaleIndex--
144
+ }
124
145
 
125
- return [...this.digitWords[Math.trunc(number[0])], 'puluh', ...this.digitWords[Math.trunc(number[1])]]
146
+ return parts.join(' ')
147
+ }
148
+
149
+ function decimalPartToWords (decimalPart) {
150
+ let result = ''
151
+
152
+ let i = 0
153
+ while (i < decimalPart.length && decimalPart[i] === '0') {
154
+ if (result) result += ' '
155
+ result += ZERO
156
+ i++
126
157
  }
127
158
 
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(' ')
159
+ const remainder = decimalPart.slice(i)
160
+ if (remainder) {
161
+ if (result) result += ' '
162
+ result += integerToWords(BigInt(remainder))
149
163
  }
150
164
 
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()
165
+ return result
166
+ }
167
+
168
+ /**
169
+ * Converts a numeric value to Indonesian words.
170
+ *
171
+ * @param {number | string | bigint} value - The numeric value to convert
172
+ * @returns {string} The number in Indonesian words
173
+ */
174
+ function toWords (value) {
175
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
176
+
177
+ let result = ''
178
+
179
+ if (isNegative) {
180
+ result = NEGATIVE + ' '
158
181
  }
182
+
183
+ result += integerToWords(integerPart)
184
+
185
+ if (decimalPart) {
186
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
187
+ }
188
+
189
+ return result
159
190
  }
191
+
192
+ // ============================================================================
193
+ // Exports
194
+ // ============================================================================
195
+
196
+ 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;