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,126 +1,145 @@
1
- import SouthAsianLanguage from '../classes/south-asian-language.js'
2
-
3
- class Hindi extends SouthAsianLanguage {
4
- negativeWord = 'माइनस'
5
- decimalSeparatorWord = 'दशमलव'
6
- zeroWord = 'शून्य'
7
- hundredWord = 'सौ'
8
- belowHundred = [
9
- 'शून्य',
10
- 'एक',
11
- 'दो',
12
- 'तीन',
13
- 'चार',
14
- 'पाँच',
15
- 'छह',
16
- 'सात',
17
- 'आठ',
18
- 'नौ',
19
- 'दस',
20
- 'ग्यारह',
21
- 'बारह',
22
- 'तेरह',
23
- 'चौदह',
24
- 'पंद्रह',
25
- 'सोलह',
26
- 'सत्रह',
27
- 'अठारह',
28
- 'उन्नीस',
29
- 'बीस',
30
- 'इक्कीस',
31
- 'बाईस',
32
- 'तेईस',
33
- 'चौबीस',
34
- 'पच्चीस',
35
- 'छब्बीस',
36
- 'सत्ताईस',
37
- 'अट्ठाईस',
38
- 'उनतीस',
39
- 'तीस',
40
- 'इकतीस',
41
- 'बत्तीस',
42
- 'तैंतीस',
43
- 'चौंतीस',
44
- 'पैंतीस',
45
- 'छत्तीस',
46
- 'सैंतीस',
47
- 'अड़तीस',
48
- 'उनतालीस',
49
- 'चालीस',
50
- 'इकतालीस',
51
- 'बयालीस',
52
- 'तेतालीस',
53
- 'चवालीस',
54
- 'पैंतालीस',
55
- 'छियालीस',
56
- 'सैंतालीस',
57
- 'अड़तालीस',
58
- 'उनचास',
59
- 'पचास',
60
- 'इक्यावन',
61
- 'बावन',
62
- 'तिरपन',
63
- 'चौवन',
64
- 'पचपन',
65
- 'छप्पन',
66
- 'सत्तावन',
67
- 'अट्ठावन',
68
- 'उनसठ',
69
- 'साठ',
70
- 'इकसठ',
71
- 'बासठ',
72
- 'तिरसठ',
73
- 'चौंसठ',
74
- 'पैंसठ',
75
- 'छियासठ',
76
- 'सड़सठ',
77
- 'अड़सठ',
78
- 'उनहत्तर',
79
- 'सत्तर',
80
- 'इकहत्तर',
81
- 'बहत्तर',
82
- 'तिहत्तर',
83
- 'चौहत्तर',
84
- 'पचहत्तर',
85
- 'छिहत्तर',
86
- 'सतहत्तर',
87
- 'अठहत्तर',
88
- 'उन्यासी',
89
- 'अस्सी',
90
- 'इक्यासी',
91
- 'बयासी',
92
- 'तिरासी',
93
- 'चौरासी',
94
- 'पचासी',
95
- 'छियासी',
96
- 'सत्तासी',
97
- 'अट्ठासी',
98
- 'नवासी',
99
- 'नब्बे',
100
- 'इक्यानवे',
101
- 'बानवे',
102
- 'तिरानवे',
103
- 'चौरानवे',
104
- 'पचानवे',
105
- 'छियानवे',
106
- 'सत्तानवे',
107
- 'अट्ठानवे',
108
- 'निन्यानवे'
109
- ]
110
-
111
- scaleWords = [
112
- '',
113
- 'हज़ार',
114
- 'लाख',
115
- 'करोड़',
116
- 'अरब',
117
- 'खरब',
118
- 'नील',
119
- 'पद्म',
120
- 'शंख'
121
- ]
1
+ /**
2
+ * Hindi language converter - Functional Implementation
3
+ *
4
+ * Self-contained converter for South Asian numbering.
5
+ *
6
+ * Key features:
7
+ * - Indian numbering system (हज़ार, लाख, करोड़)
8
+ * - Devanagari script
9
+ * - 3-2-2 grouping pattern (last 3 digits, then groups of 2)
10
+ * - Complete word forms for 0-99
11
+ */
12
+
13
+ import { parseNumericValue } from '../utils/parse-numeric.js'
14
+
15
+ // ============================================================================
16
+ // Vocabulary
17
+ // ============================================================================
18
+
19
+ const ZERO = 'शून्य'
20
+ const NEGATIVE = 'माइनस'
21
+ const DECIMAL_SEP = 'दशमलव'
22
+ const HUNDRED = 'सौ'
23
+
24
+ const BELOW_HUNDRED = [
25
+ 'शून्य', 'एक', 'दो', 'तीन', 'चार', 'पाँच', 'छह', 'सात', 'आठ', 'नौ',
26
+ 'दस', 'ग्यारह', 'बारह', 'तेरह', 'चौदह', 'पंद्रह', 'सोलह', 'सत्रह', 'अठारह', 'उन्नीस',
27
+ 'बीस', 'इक्कीस', 'बाईस', 'तेईस', 'चौबीस', 'पच्चीस', 'छब्बीस', 'सत्ताईस', 'अट्ठाईस', 'उनतीस',
28
+ 'तीस', 'इकतीस', 'बत्तीस', 'तैंतीस', 'चौंतीस', 'पैंतीस', 'छत्तीस', 'सैंतीस', 'अड़तीस', 'उनतालीस',
29
+ 'चालीस', 'इकतालीस', 'बयालीस', 'तेतालीस', 'चवालीस', 'पैंतालीस', 'छियालीस', 'सैंतालीस', 'अड़तालीस', 'उनचास',
30
+ 'पचास', 'इक्यावन', 'बावन', 'तिरपन', 'चौवन', 'पचपन', 'छप्पन', 'सत्तावन', 'अट्ठावन', 'उनसठ',
31
+ 'साठ', 'इकसठ', 'बासठ', 'तिरसठ', 'चौंसठ', 'पैंसठ', 'छियासठ', 'सड़सठ', 'अड़सठ', 'उनहत्तर',
32
+ 'सत्तर', 'इकहत्तर', 'बहत्तर', 'तिहत्तर', 'चौहत्तर', 'पचहत्तर', 'छिहत्तर', 'सतहत्तर', 'अठहत्तर', 'उन्यासी',
33
+ 'अस्सी', 'इक्यासी', 'बयासी', 'तिरासी', 'चौरासी', 'पचासी', 'छियासी', 'सत्तासी', 'अट्ठासी', 'नवासी',
34
+ 'नब्बे', 'इक्यानवे', 'बानवे', 'तिरानवे', 'चौरानवे', 'पचानवे', 'छियानवे', 'सत्तानवे', 'अट्ठानवे', 'निन्यानवे'
35
+ ]
36
+
37
+ // Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.
38
+ const SCALE_WORDS = ['', 'हज़ार', 'लाख', 'करोड़', 'अरब', 'खरब', 'नील', 'पद्म', 'शंख']
39
+
40
+ // ============================================================================
41
+ // Segment Splitting (inlined for performance)
42
+ // ============================================================================
43
+
44
+ function groupByThreeThenTwos (n) {
45
+ const numStr = n.toString()
46
+ if (numStr.length <= 3) return [Number(numStr)]
47
+
48
+ const segments = []
49
+ segments.unshift(Number(numStr.slice(-3)))
50
+
51
+ let remaining = numStr.slice(0, -3)
52
+ while (remaining.length > 0) {
53
+ segments.unshift(Number(remaining.slice(-2)))
54
+ remaining = remaining.slice(0, -2)
55
+ }
56
+
57
+ return segments
122
58
  }
123
59
 
124
- export default function convertToWords (value, options = {}) {
125
- return new Hindi(options).convertToWords(value)
60
+ function segmentToWords (n) {
61
+ if (n === 0) return ''
62
+ if (n < 100) return BELOW_HUNDRED[n]
63
+
64
+ const hundreds = Math.trunc(n / 100)
65
+ const remainder = n % 100
66
+
67
+ if (remainder === 0) {
68
+ return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED
69
+ }
70
+ return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]
126
71
  }
72
+
73
+ // ============================================================================
74
+ // Conversion Functions
75
+ // ============================================================================
76
+
77
+ function integerToWords (n) {
78
+ if (n === 0n) return ZERO
79
+
80
+ const segments = groupByThreeThenTwos(n)
81
+ const segmentCount = segments.length
82
+ const words = []
83
+
84
+ for (let i = 0; i < segmentCount; i++) {
85
+ const segmentValue = segments[i]
86
+ if (segmentValue === 0) continue
87
+
88
+ const scaleIndex = segmentCount - i - 1
89
+ words.push(segmentToWords(segmentValue))
90
+ if (scaleIndex > 0 && SCALE_WORDS[scaleIndex]) {
91
+ words.push(SCALE_WORDS[scaleIndex])
92
+ }
93
+ }
94
+
95
+ return words.join(' ').trim()
96
+ }
97
+
98
+ function decimalPartToWords (decimalPart) {
99
+ let result = ''
100
+ let i = 0
101
+
102
+ while (i < decimalPart.length && decimalPart[i] === '0') {
103
+ if (result) result += ' '
104
+ result += ZERO
105
+ i++
106
+ }
107
+
108
+ const remainder = decimalPart.slice(i)
109
+ if (remainder) {
110
+ if (result) result += ' '
111
+ result += integerToWords(BigInt(remainder))
112
+ }
113
+
114
+ return result
115
+ }
116
+
117
+ /**
118
+ * Converts a numeric value to Hindi words.
119
+ *
120
+ * @param {number | string | bigint} value - The numeric value to convert
121
+ * @returns {string} The number in Hindi words
122
+ */
123
+ function toWords (value) {
124
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
125
+
126
+ let result = ''
127
+
128
+ if (isNegative) {
129
+ result = NEGATIVE + ' '
130
+ }
131
+
132
+ result += integerToWords(integerPart)
133
+
134
+ if (decimalPart) {
135
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
136
+ }
137
+
138
+ return result
139
+ }
140
+
141
+ // ============================================================================
142
+ // Exports
143
+ // ============================================================================
144
+
145
+ export { toWords }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Converts a numeric value to Croatian words.
3
+ *
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @param {Object} [options] - Optional configuration
6
+ * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
7
+ * @returns {string} The number in Croatian words
8
+ */
9
+ export function toWords(value: number | string | bigint, options?: {
10
+ gender?: "masculine" | "feminine" | undefined;
11
+ }): string;
@@ -1,157 +1,218 @@
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
- * Croatian language converter.
2
+ * Croatian language converter - Functional Implementation
10
3
  *
11
- * Implements Croatian number words using the Slavic language pattern:
12
- * - Croatian number words (jedan/jedna, dva/dvije, tri, četiri...)
13
- * - Gender-aware forms (masculine/feminine)
14
- * - Slavic three-form pluralization (tisuća/tisuće/tisuća)
15
- * - Croatian-specific declension endings
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., "tisuća")
20
- * * Form 2 (few): 2-4, 22-24, 32-34... excluding teens (e.g., "tisuće")
21
- * * Form 3 (many): all other numbers (e.g., "tisuća")
22
- * - Chunk-based decomposition (splits into groups of 3 digits: ones, thousands, millions, etc.)
23
- * - Large number handling via thousands[] array with indexed [singular, few, many] forms
24
- * - Gender-specific number forms for 1 and 2 (masculine/feminine dual forms)
25
- *
26
- * Features:
27
- * - Dual gender forms for 1 and 2 (jedan/jedna, dva/dvije)
28
- * - Latin script orthography
29
- * - Similar structure to Serbian
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 (dvjesto, tristo, etc.)
10
+ * - Long scale naming with -ard forms
32
11
  */
33
- export class Croatian extends SlavicLanguage {
34
- negativeWord = 'minus'
35
- decimalSeparatorWord = 'zarez'
36
- zeroWord = 'nula'
37
- ones = {
38
- 1: ['jedan', 'jedna'],
39
- 2: ['dva', 'dvije'],
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']
12
+
13
+ import { parseNumericValue } from '../utils/parse-numeric.js'
14
+ import { validateOptions } from '../utils/validate-options.js'
15
+
16
+ // ============================================================================
17
+ // Slavic Utilities (inlined for performance)
18
+ // ============================================================================
19
+
20
+ function pluralize (n, forms) {
21
+ const num = typeof n === 'bigint' ? Number(n) : n
22
+ const lastDigit = num % 10
23
+ const lastTwoDigits = num % 100
24
+
25
+ if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
26
+ return forms[2]
47
27
  }
48
28
 
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'
29
+ if (lastDigit === 1) return forms[0]
30
+ if (lastDigit >= 2 && lastDigit <= 4) return forms[1]
31
+ return forms[2]
32
+ }
33
+
34
+ function buildAllSegments (onesMasc, onesFem, teens, tens, hundreds) {
35
+ const masc = new Array(1000)
36
+ const fem = new Array(1000)
37
+
38
+ for (let i = 0; i < 1000; i++) {
39
+ masc[i] = buildSegment(i, onesMasc, teens, tens, hundreds)
40
+ fem[i] = buildSegment(i, onesFem, teens, tens, hundreds)
60
41
  }
61
42
 
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'
43
+ return { masc, fem }
44
+ }
45
+
46
+ function buildSegment (n, ones, teens, tens, hundreds) {
47
+ if (n === 0) return ''
48
+
49
+ const onesDigit = n % 10
50
+ const tensDigit = Math.floor(n / 10) % 10
51
+ const hundredsDigit = Math.floor(n / 100)
52
+
53
+ const parts = []
54
+
55
+ if (hundredsDigit > 0) {
56
+ parts.push(hundreds[hundredsDigit])
71
57
  }
72
58
 
73
- hundreds = {
74
- 1: 'sto',
75
- 2: 'dvjesto',
76
- 3: 'tristo',
77
- 4: 'četiristo',
78
- 5: 'petsto',
79
- 6: 'šesto',
80
- 7: 'sedamsto',
81
- 8: 'osamsto',
82
- 9: 'devetsto'
59
+ if (tensDigit > 1) {
60
+ parts.push(tens[tensDigit])
83
61
  }
84
62
 
85
- SCALE = {
86
- 0: ['', '', '', false],
87
- 1: ['tisuća', 'tisuće', 'tisuća', true], // 10 ^ 3
88
- 2: ['milijun', 'milijuna', 'milijuna', false], // 10 ^ 6
89
- 3: ['milijarda', 'milijarde', 'milijarda', false], // 10 ^ 9
90
- 4: ['bilijun', 'bilijuna', 'bilijuna', false], // 10 ^ 12
91
- 5: ['bilijarda', 'bilijarde', 'bilijarda', false], // 10 ^ 15
92
- 6: ['trilijun', 'trilijuna', 'trilijuna', false], // 10 ^ 18
93
- 7: ['trilijarda', 'trilijarde', 'trilijarda', false], // 10 ^ 21
94
- 8: ['kvadrilijun', 'kvadrilijuna', 'kvadrilijuna', false], // 10 ^ 24
95
- 9: ['kvadrilijarda', 'kvadrilijarde', 'kvadrilijarda', false], // 10 ^ 27
96
- 10: ['kvintilijun', 'kvintilijuna', 'kvintilijuna', false] // 10 ^ 30
63
+ if (tensDigit === 1) {
64
+ parts.push(teens[onesDigit])
65
+ } else if (onesDigit > 0) {
66
+ parts.push(ones[onesDigit])
97
67
  }
98
68
 
99
- pluralize (n, forms) {
100
- const lastDigit = n % 10n
101
- const lastTwoDigits = n % 100n
69
+ return parts.join(' ')
70
+ }
71
+
72
+ // ============================================================================
73
+ // Vocabulary
74
+ // ============================================================================
102
75
 
103
- if ((lastTwoDigits < 10n || lastTwoDigits > 20n) && lastDigit === 1n) {
104
- return forms[0]
105
- }
76
+ const ONES_MASC = ['', 'jedan', 'dva', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']
77
+ const ONES_FEM = ['', 'jedna', 'dvije', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']
106
78
 
107
- if ((lastTwoDigits < 10n || lastTwoDigits > 20n) && lastDigit > 1n && lastDigit < 5n) {
108
- return forms[1]
109
- }
79
+ const TEENS = ['deset', 'jedanaest', 'dvanaest', 'trinaest', 'četrnaest', 'petnaest', 'šesnaest', 'sedamnaest', 'osamnaest', 'devetnaest']
80
+ const TENS = ['', '', 'dvadeset', 'trideset', 'četrdeset', 'pedeset', 'šezdeset', 'sedamdeset', 'osamdeset', 'devedeset']
110
81
 
111
- return forms[2]
82
+ // Croatian has irregular hundreds
83
+ const HUNDREDS = ['', 'sto', 'dvjesto', 'tristo', 'četiristo', 'petsto', 'šesto', 'sedamsto', 'osamsto', 'devetsto']
84
+
85
+ const ZERO = 'nula'
86
+ const NEGATIVE = 'minus'
87
+ const DECIMAL_SEP = 'zarez'
88
+
89
+ // Scale words: [singular, few, many]
90
+ // Thousands (index 0) are feminine, rest are masculine
91
+ const SCALE_FORMS = [
92
+ ['tisuća', 'tisuće', 'tisuća'],
93
+ ['milijun', 'milijuna', 'milijuna'],
94
+ ['milijarda', 'milijarde', 'milijarda'],
95
+ ['bilijun', 'bilijuna', 'bilijuna'],
96
+ ['bilijarda', 'bilijarde', 'bilijarda'],
97
+ ['trilijun', 'trilijuna', 'trilijuna'],
98
+ ['trilijarda', 'trilijarde', 'trilijarda'],
99
+ ['kvadrilijun', 'kvadrilijuna', 'kvadrilijuna'],
100
+ ['kvadrilijarda', 'kvadrilijarde', 'kvadrilijarda']
101
+ ]
102
+
103
+ // ============================================================================
104
+ // Precomputed Lookup Tables
105
+ // ============================================================================
106
+
107
+ const { masc: SEGMENTS_MASC, fem: SEGMENTS_FEM } = buildAllSegments(ONES_MASC, ONES_FEM, TEENS, TENS, HUNDREDS)
108
+
109
+ // ============================================================================
110
+ // Conversion Functions
111
+ // ============================================================================
112
+
113
+ function integerToWords (n, options = {}) {
114
+ if (n === 0n) return ZERO
115
+
116
+ if (n < 1000n) {
117
+ const segments = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC
118
+ return segments[Number(n)]
112
119
  }
113
120
 
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 > 0n) {
125
- words.push(this.hundreds[n3])
126
- }
127
- if (n2 > 1n) {
128
- words.push(this.twenties[n2])
129
- }
130
- if (n2 === 1n) {
131
- words.push(this.tens[n1])
132
- } else if (n1 > 0n) {
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]))
121
+ return buildLargeNumberWords(n, options)
122
+ }
123
+
124
+ function buildLargeNumberWords (n, options) {
125
+ const numStr = n.toString()
126
+ const len = numStr.length
127
+
128
+ const segments = []
129
+ const segmentSize = 3
130
+
131
+ const remainderLen = len % segmentSize
132
+ let pos = 0
133
+ if (remainderLen > 0) {
134
+ segments.push(Number(numStr.slice(0, remainderLen)))
135
+ pos = remainderLen
136
+ }
137
+ while (pos < len) {
138
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
139
+ pos += segmentSize
140
+ }
141
+
142
+ const parts = []
143
+ let scaleIndex = segments.length - 1
144
+
145
+ for (let i = 0; i < segments.length; i++) {
146
+ const segment = segments[i]
147
+
148
+ if (segment !== 0) {
149
+ if (scaleIndex === 0) {
150
+ const segmentWords = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC
151
+ parts.push(segmentWords[segment])
152
+ } else {
153
+ const scaleForms = SCALE_FORMS[scaleIndex - 1]
154
+ const scaleWord = pluralize(segment, scaleForms)
155
+ // Thousands (scaleIndex=1) are feminine, others masculine
156
+ const isFeminine = scaleIndex === 1
157
+ const segmentWords = isFeminine ? SEGMENTS_FEM : SEGMENTS_MASC
158
+ parts.push(segmentWords[segment] + ' ' + scaleWord)
139
159
  }
140
160
  }
141
- return words.join(' ')
161
+
162
+ scaleIndex--
142
163
  }
164
+
165
+ return parts.join(' ')
166
+ }
167
+
168
+ function decimalPartToWords (decimalPart, options) {
169
+ let result = ''
170
+ let i = 0
171
+
172
+ while (i < decimalPart.length && decimalPart[i] === '0') {
173
+ if (result) result += ' '
174
+ result += ZERO
175
+ i++
176
+ }
177
+
178
+ const remainder = decimalPart.slice(i)
179
+ if (remainder) {
180
+ if (result) result += ' '
181
+ result += integerToWords(BigInt(remainder), options)
182
+ }
183
+
184
+ return result
143
185
  }
144
186
 
145
187
  /**
146
- * Converts a number to Croatian cardinal (written) form.
188
+ * Converts a numeric value to Croatian words.
147
189
  *
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 Croatian words.
152
- * @throws {TypeError} If value is NaN or invalid type.
153
- * @throws {Error} If value is an invalid number string.
190
+ * @param {number | string | bigint} value - The numeric value to convert
191
+ * @param {Object} [options] - Optional configuration
192
+ * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
193
+ * @returns {string} The number in Croatian words
154
194
  */
155
- export default function convertToWords (value, options = {}) {
156
- return new Croatian(options).convertToWords(value)
195
+ function toWords (value, options) {
196
+ options = validateOptions(options)
197
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
198
+
199
+ let result = ''
200
+
201
+ if (isNegative) {
202
+ result = NEGATIVE + ' '
203
+ }
204
+
205
+ result += integerToWords(integerPart, options)
206
+
207
+ if (decimalPart) {
208
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)
209
+ }
210
+
211
+ return result
157
212
  }
213
+
214
+ // ============================================================================
215
+ // Exports
216
+ // ============================================================================
217
+
218
+ export { toWords }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Converts a numeric value to Hungarian words.
3
+ *
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @returns {string} The number in Hungarian words
6
+ */
7
+ export function toWords(value: number | string | bigint): string;