n2words 5.0.0 → 5.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 (309) hide show
  1. package/CHANGELOG.md +128 -42
  2. package/README.md +6 -4
  3. package/dist/am-ET.js +2 -2
  4. package/dist/am-ET.umd.js +2 -2
  5. package/dist/am-Latn-ET.js +2 -2
  6. package/dist/am-Latn-ET.umd.js +2 -2
  7. package/dist/ar-SA.js +2 -2
  8. package/dist/ar-SA.umd.js +2 -2
  9. package/dist/az-AZ.js +2 -2
  10. package/dist/az-AZ.umd.js +2 -2
  11. package/dist/bn-BD.js +2 -2
  12. package/dist/bn-BD.umd.js +2 -2
  13. package/dist/cs-CZ.js +2 -2
  14. package/dist/cs-CZ.umd.js +2 -2
  15. package/dist/da-DK.js +2 -2
  16. package/dist/da-DK.umd.js +2 -2
  17. package/dist/de-DE.js +2 -2
  18. package/dist/de-DE.umd.js +2 -2
  19. package/dist/el-GR.js +2 -2
  20. package/dist/el-GR.umd.js +2 -2
  21. package/dist/en-AU.js +2 -2
  22. package/dist/en-AU.umd.js +2 -2
  23. package/dist/en-BD.js +2 -2
  24. package/dist/en-BD.umd.js +2 -2
  25. package/dist/en-CA.js +2 -2
  26. package/dist/en-CA.umd.js +2 -2
  27. package/dist/en-GB.js +2 -2
  28. package/dist/en-GB.umd.js +2 -2
  29. package/dist/en-GH.js +2 -2
  30. package/dist/en-GH.umd.js +2 -2
  31. package/dist/en-IE.js +2 -2
  32. package/dist/en-IE.umd.js +2 -2
  33. package/dist/en-IN.js +2 -2
  34. package/dist/en-IN.umd.js +2 -2
  35. package/dist/en-KE.js +2 -2
  36. package/dist/en-KE.umd.js +2 -2
  37. package/dist/en-MY.js +2 -2
  38. package/dist/en-MY.umd.js +2 -2
  39. package/dist/en-NG.js +2 -2
  40. package/dist/en-NG.umd.js +2 -2
  41. package/dist/en-NZ.js +2 -2
  42. package/dist/en-NZ.umd.js +2 -2
  43. package/dist/en-PH.js +2 -2
  44. package/dist/en-PH.umd.js +2 -2
  45. package/dist/en-PK.js +2 -2
  46. package/dist/en-PK.umd.js +2 -2
  47. package/dist/en-SG.js +2 -2
  48. package/dist/en-SG.umd.js +2 -2
  49. package/dist/en-US.js +2 -2
  50. package/dist/en-US.umd.js +2 -2
  51. package/dist/en-ZA.js +2 -2
  52. package/dist/en-ZA.umd.js +2 -2
  53. package/dist/es-ES.js +2 -2
  54. package/dist/es-ES.umd.js +2 -2
  55. package/dist/es-MX.js +2 -2
  56. package/dist/es-MX.umd.js +2 -2
  57. package/dist/es-US.js +2 -2
  58. package/dist/es-US.umd.js +2 -2
  59. package/dist/fa-IR.js +2 -2
  60. package/dist/fa-IR.umd.js +2 -2
  61. package/dist/fi-FI.js +2 -2
  62. package/dist/fi-FI.umd.js +2 -2
  63. package/dist/fil-PH.js +2 -2
  64. package/dist/fil-PH.umd.js +2 -2
  65. package/dist/fr-BE.js +2 -2
  66. package/dist/fr-BE.umd.js +2 -2
  67. package/dist/fr-FR.js +2 -2
  68. package/dist/fr-FR.umd.js +2 -2
  69. package/dist/gu-IN.js +2 -2
  70. package/dist/gu-IN.umd.js +2 -2
  71. package/dist/ha-NG.js +2 -2
  72. package/dist/ha-NG.umd.js +2 -2
  73. package/dist/hbo-IL.js +2 -2
  74. package/dist/hbo-IL.umd.js +2 -2
  75. package/dist/he-IL.js +2 -2
  76. package/dist/he-IL.umd.js +2 -2
  77. package/dist/hi-IN.js +2 -2
  78. package/dist/hi-IN.umd.js +2 -2
  79. package/dist/hr-HR.js +2 -2
  80. package/dist/hr-HR.umd.js +2 -2
  81. package/dist/hu-HU.js +2 -2
  82. package/dist/hu-HU.umd.js +2 -2
  83. package/dist/id-ID.js +2 -2
  84. package/dist/id-ID.umd.js +2 -2
  85. package/dist/it-IT.js +2 -2
  86. package/dist/it-IT.umd.js +2 -2
  87. package/dist/ja-JP.js +2 -2
  88. package/dist/ja-JP.umd.js +2 -2
  89. package/dist/ka-GE.js +2 -2
  90. package/dist/ka-GE.umd.js +2 -2
  91. package/dist/kn-IN.js +2 -2
  92. package/dist/kn-IN.umd.js +2 -2
  93. package/dist/ko-KR.js +2 -2
  94. package/dist/ko-KR.umd.js +2 -2
  95. package/dist/lt-LT.js +2 -2
  96. package/dist/lt-LT.umd.js +2 -2
  97. package/dist/lv-LV.js +2 -2
  98. package/dist/lv-LV.umd.js +2 -2
  99. package/dist/mr-IN.js +2 -2
  100. package/dist/mr-IN.umd.js +2 -2
  101. package/dist/ms-MY.js +2 -2
  102. package/dist/ms-MY.umd.js +2 -2
  103. package/dist/nb-NO.js +2 -2
  104. package/dist/nb-NO.umd.js +2 -2
  105. package/dist/nl-NL.js +2 -2
  106. package/dist/nl-NL.umd.js +2 -2
  107. package/dist/pa-IN.js +2 -2
  108. package/dist/pa-IN.umd.js +2 -2
  109. package/dist/pl-PL.js +2 -2
  110. package/dist/pl-PL.umd.js +2 -2
  111. package/dist/pt-BR.js +2 -2
  112. package/dist/pt-BR.umd.js +2 -2
  113. package/dist/pt-PT.js +2 -2
  114. package/dist/pt-PT.umd.js +2 -2
  115. package/dist/ro-RO.js +2 -2
  116. package/dist/ro-RO.umd.js +2 -2
  117. package/dist/ru-RU.js +2 -2
  118. package/dist/ru-RU.umd.js +2 -2
  119. package/dist/sr-Cyrl-RS.js +2 -2
  120. package/dist/sr-Cyrl-RS.umd.js +2 -2
  121. package/dist/sr-Latn-RS.js +2 -2
  122. package/dist/sr-Latn-RS.umd.js +2 -2
  123. package/dist/sv-SE.js +2 -2
  124. package/dist/sv-SE.umd.js +2 -2
  125. package/dist/sw-KE.js +2 -2
  126. package/dist/sw-KE.umd.js +2 -2
  127. package/dist/ta-IN.js +2 -2
  128. package/dist/ta-IN.umd.js +2 -2
  129. package/dist/te-IN.js +2 -2
  130. package/dist/te-IN.umd.js +2 -2
  131. package/dist/th-TH.js +2 -2
  132. package/dist/th-TH.umd.js +2 -2
  133. package/dist/tr-TR.js +2 -2
  134. package/dist/tr-TR.umd.js +2 -2
  135. package/dist/uk-UA.js +2 -2
  136. package/dist/uk-UA.umd.js +2 -2
  137. package/dist/ur-PK.js +2 -2
  138. package/dist/ur-PK.umd.js +2 -2
  139. package/dist/vi-VN.js +2 -2
  140. package/dist/vi-VN.umd.js +2 -2
  141. package/dist/yo-NG.js +2 -2
  142. package/dist/yo-NG.umd.js +2 -2
  143. package/dist/zh-Hans-CN.js +2 -2
  144. package/dist/zh-Hans-CN.umd.js +2 -2
  145. package/dist/zh-Hant-TW.js +2 -2
  146. package/dist/zh-Hant-TW.umd.js +2 -2
  147. package/package.json +31 -22
  148. package/src/am-ET.d.ts +3 -5
  149. package/src/am-ET.js +41 -16
  150. package/src/am-Latn-ET.d.ts +3 -5
  151. package/src/am-Latn-ET.js +45 -16
  152. package/src/ar-SA.d.ts +44 -18
  153. package/src/ar-SA.js +93 -40
  154. package/src/az-AZ.d.ts +3 -5
  155. package/src/az-AZ.js +58 -20
  156. package/src/bn-BD.d.ts +3 -5
  157. package/src/bn-BD.js +32 -16
  158. package/src/cs-CZ.d.ts +3 -6
  159. package/src/cs-CZ.js +66 -42
  160. package/src/da-DK.d.ts +3 -6
  161. package/src/da-DK.js +53 -48
  162. package/src/de-DE.d.ts +17 -11
  163. package/src/de-DE.js +88 -57
  164. package/src/el-GR.d.ts +3 -6
  165. package/src/el-GR.js +45 -32
  166. package/src/en-AU.d.ts +17 -11
  167. package/src/en-AU.js +56 -41
  168. package/src/en-BD.d.ts +17 -11
  169. package/src/en-BD.js +60 -41
  170. package/src/en-CA.d.ts +36 -18
  171. package/src/en-CA.js +67 -46
  172. package/src/en-GB.d.ts +17 -11
  173. package/src/en-GB.js +56 -41
  174. package/src/en-GH.d.ts +32 -3
  175. package/src/en-GH.js +104 -26
  176. package/src/en-IE.d.ts +17 -11
  177. package/src/en-IE.js +56 -41
  178. package/src/en-IN.d.ts +17 -11
  179. package/src/en-IN.js +60 -41
  180. package/src/en-KE.d.ts +28 -3
  181. package/src/en-KE.js +93 -26
  182. package/src/en-MY.d.ts +26 -3
  183. package/src/en-MY.js +91 -26
  184. package/src/en-NG.d.ts +17 -11
  185. package/src/en-NG.js +56 -41
  186. package/src/en-NZ.d.ts +32 -3
  187. package/src/en-NZ.js +85 -31
  188. package/src/en-PH.d.ts +32 -3
  189. package/src/en-PH.js +97 -26
  190. package/src/en-PK.d.ts +17 -11
  191. package/src/en-PK.js +60 -41
  192. package/src/en-SG.d.ts +28 -3
  193. package/src/en-SG.js +93 -26
  194. package/src/en-US.d.ts +36 -18
  195. package/src/en-US.js +70 -47
  196. package/src/en-ZA.d.ts +17 -11
  197. package/src/en-ZA.js +56 -41
  198. package/src/es-ES.d.ts +53 -21
  199. package/src/es-ES.js +104 -56
  200. package/src/es-MX.d.ts +53 -21
  201. package/src/es-MX.js +104 -56
  202. package/src/es-US.d.ts +53 -21
  203. package/src/es-US.js +92 -51
  204. package/src/fa-IR.d.ts +3 -5
  205. package/src/fa-IR.js +28 -13
  206. package/src/fi-FI.d.ts +3 -6
  207. package/src/fi-FI.js +47 -29
  208. package/src/fil-PH.d.ts +3 -5
  209. package/src/fil-PH.js +61 -28
  210. package/src/fr-BE.d.ts +31 -15
  211. package/src/fr-BE.js +128 -57
  212. package/src/fr-FR.d.ts +31 -16
  213. package/src/fr-FR.js +97 -60
  214. package/src/gu-IN.d.ts +3 -5
  215. package/src/gu-IN.js +31 -16
  216. package/src/ha-NG.d.ts +3 -5
  217. package/src/ha-NG.js +55 -27
  218. package/src/hbo-IL.d.ts +26 -12
  219. package/src/hbo-IL.js +92 -51
  220. package/src/he-IL.d.ts +17 -10
  221. package/src/he-IL.js +92 -50
  222. package/src/hi-IN.d.ts +3 -5
  223. package/src/hi-IN.js +30 -17
  224. package/src/hr-HR.d.ts +21 -10
  225. package/src/hr-HR.js +89 -33
  226. package/src/hu-HU.d.ts +3 -5
  227. package/src/hu-HU.js +57 -23
  228. package/src/id-ID.d.ts +3 -5
  229. package/src/id-ID.js +56 -23
  230. package/src/it-IT.d.ts +17 -11
  231. package/src/it-IT.js +74 -43
  232. package/src/ja-JP.d.ts +3 -6
  233. package/src/ja-JP.js +39 -26
  234. package/src/ka-GE.d.ts +3 -6
  235. package/src/ka-GE.js +38 -26
  236. package/src/kn-IN.d.ts +3 -5
  237. package/src/kn-IN.js +31 -16
  238. package/src/ko-KR.d.ts +3 -6
  239. package/src/ko-KR.js +34 -26
  240. package/src/lt-LT.d.ts +21 -11
  241. package/src/lt-LT.js +64 -42
  242. package/src/lv-LV.d.ts +21 -11
  243. package/src/lv-LV.js +79 -51
  244. package/src/mr-IN.d.ts +3 -5
  245. package/src/mr-IN.js +31 -16
  246. package/src/ms-MY.d.ts +3 -5
  247. package/src/ms-MY.js +58 -24
  248. package/src/nb-NO.d.ts +3 -6
  249. package/src/nb-NO.js +54 -34
  250. package/src/nl-NL.d.ts +41 -20
  251. package/src/nl-NL.js +111 -69
  252. package/src/pa-IN.d.ts +3 -5
  253. package/src/pa-IN.js +32 -16
  254. package/src/pl-PL.d.ts +21 -11
  255. package/src/pl-PL.js +69 -45
  256. package/src/pt-BR.d.ts +22 -11
  257. package/src/pt-BR.js +93 -53
  258. package/src/pt-PT.d.ts +17 -11
  259. package/src/pt-PT.js +80 -48
  260. package/src/ro-RO.d.ts +21 -11
  261. package/src/ro-RO.js +77 -39
  262. package/src/ru-RU.d.ts +35 -15
  263. package/src/ru-RU.js +100 -38
  264. package/src/sr-Cyrl-RS.d.ts +35 -15
  265. package/src/sr-Cyrl-RS.js +100 -38
  266. package/src/sr-Latn-RS.d.ts +35 -15
  267. package/src/sr-Latn-RS.js +100 -38
  268. package/src/sv-SE.d.ts +3 -6
  269. package/src/sv-SE.js +53 -34
  270. package/src/sw-KE.d.ts +3 -5
  271. package/src/sw-KE.js +50 -20
  272. package/src/ta-IN.d.ts +3 -5
  273. package/src/ta-IN.js +29 -17
  274. package/src/te-IN.d.ts +3 -5
  275. package/src/te-IN.js +31 -16
  276. package/src/th-TH.d.ts +3 -5
  277. package/src/th-TH.js +42 -19
  278. package/src/tr-TR.d.ts +17 -11
  279. package/src/tr-TR.js +63 -37
  280. package/src/uk-UA.d.ts +21 -10
  281. package/src/uk-UA.js +89 -33
  282. package/src/ur-PK.d.ts +3 -5
  283. package/src/ur-PK.js +32 -16
  284. package/src/utils/check-max.d.ts +26 -0
  285. package/src/utils/check-max.js +33 -0
  286. package/src/utils/expand-scientific.d.ts +0 -4
  287. package/src/utils/expand-scientific.js +7 -9
  288. package/src/utils/is-plain-object.d.ts +3 -4
  289. package/src/utils/is-plain-object.js +3 -4
  290. package/src/utils/parse-cardinal.d.ts +1 -2
  291. package/src/utils/parse-cardinal.js +12 -9
  292. package/src/utils/parse-currency.d.ts +1 -2
  293. package/src/utils/parse-currency.js +9 -11
  294. package/src/utils/parse-ordinal.d.ts +0 -1
  295. package/src/utils/parse-ordinal.js +9 -10
  296. package/src/utils/resolve-options.d.ts +17 -0
  297. package/src/utils/resolve-options.js +56 -0
  298. package/src/utils/scale.d.ts +49 -0
  299. package/src/utils/scale.js +65 -0
  300. package/src/vi-VN.d.ts +3 -6
  301. package/src/vi-VN.js +41 -28
  302. package/src/yo-NG.d.ts +3 -5
  303. package/src/yo-NG.js +49 -33
  304. package/src/zh-Hans-CN.d.ts +45 -20
  305. package/src/zh-Hans-CN.js +84 -31
  306. package/src/zh-Hant-TW.d.ts +45 -20
  307. package/src/zh-Hant-TW.js +85 -34
  308. package/src/utils/validate-options.d.ts +0 -8
  309. package/src/utils/validate-options.js +0 -16
package/src/he-IL.js CHANGED
@@ -14,7 +14,9 @@
14
14
  import { parseCardinalValue } from './utils/parse-cardinal.js'
15
15
  import { parseCurrencyValue } from './utils/parse-currency.js'
16
16
  import { parseOrdinalValue } from './utils/parse-ordinal.js'
17
- import { validateOptions } from './utils/validate-options.js'
17
+ import { checkMax } from './utils/check-max.js'
18
+ import { bounded, western } from './utils/scale.js'
19
+ import { resolveOptions } from './utils/resolve-options.js'
18
20
 
19
21
  // ============================================================================
20
22
  // Vocabulary (arrays for indexed access - faster than object property lookup)
@@ -33,6 +35,15 @@ const THOUSANDS_SPECIAL = ['', 'אלף', 'אלפיים', 'שלשת אלפים',
33
35
  const SCALE = ['', 'אלף', 'מיליון', 'מיליארד', 'טריליון', 'קוודרליון', 'קווינטיליון']
34
36
  const SCALE_PLURAL = ['', 'אלפים', 'מיליונים', 'מיליארדים', 'טריליונים', 'קוודרליונים', 'קווינטיליונים']
35
37
 
38
+ // Supported magnitude ceilings (checked at the public entry points). Cardinal
39
+ // SCALE reaches index SCALE.length-1 (10^18), so cardinals/currency must stay
40
+ // below 10^(SCALE.length * 3) = 10^21. Ordinals are bounded lower: past 10^6
41
+ // the millions multiplier is built with buildScaleSegment (0-999), so n must
42
+ // stay below 10^9. Decimals are read digit-by-digit (no ceiling).
43
+ export const cardinalMax = western(SCALE.length - 1)
44
+ export const ordinalMax = bounded(9)
45
+ export const currencyMax = western(SCALE.length - 1)
46
+
36
47
  const ZERO = 'אפס'
37
48
  const NEGATIVE = 'מינוס'
38
49
  const DECIMAL_SEP = 'נקודה'
@@ -59,8 +70,11 @@ const AGORA_PLURAL = 'אגורות'
59
70
  /**
60
71
  * Builds segment word for scale segments (thousands, millions, etc.).
61
72
  * "ו" is added before tens and ones when following hundreds.
73
+ * @param {number} n - Segment value (0-999)
74
+ * @param {string} andWord - Conjunction word
75
+ * @returns {string} Hebrew words for the segment
62
76
  */
63
- function buildScaleSegment (n, andWord) {
77
+ function buildScaleSegment(n, andWord) {
64
78
  if (n === 0) return ''
65
79
 
66
80
  const ones = n % 10
@@ -80,15 +94,18 @@ function buildScaleSegment (n, andWord) {
80
94
  const teenWord = TEENS[ones]
81
95
  if (result) {
82
96
  result += ' ' + andWord + teenWord
83
- } else {
97
+ }
98
+ else {
84
99
  result = teenWord
85
100
  }
86
- } else {
101
+ }
102
+ else {
87
103
  // Tens (20-90)
88
104
  if (tens >= 2) {
89
105
  if (result) {
90
106
  result += ' ' + andWord + TENS[tens]
91
- } else {
107
+ }
108
+ else {
92
109
  result = TENS[tens]
93
110
  }
94
111
  }
@@ -97,7 +114,8 @@ function buildScaleSegment (n, andWord) {
97
114
  if (ones > 0) {
98
115
  if (result) {
99
116
  result += ' ' + andWord + ONES[ones]
100
- } else {
117
+ }
118
+ else {
101
119
  result = ONES[ones]
102
120
  }
103
121
  }
@@ -109,8 +127,11 @@ function buildScaleSegment (n, andWord) {
109
127
  /**
110
128
  * Builds segment word for units segment (no scale word).
111
129
  * "ו" is only added before the final ones digit.
130
+ * @param {number} n - Segment value (0-999)
131
+ * @param {string} andWord - Conjunction word
132
+ * @returns {string} Hebrew words for the segment
112
133
  */
113
- function buildUnitsSegment (n, andWord) {
134
+ function buildUnitsSegment(n, andWord) {
114
135
  if (n === 0) return ''
115
136
 
116
137
  const ones = n % 10
@@ -129,14 +150,17 @@ function buildUnitsSegment (n, andWord) {
129
150
  // Teens (10-19)
130
151
  if (result) {
131
152
  result += ' ' + TEENS[ones]
132
- } else {
153
+ }
154
+ else {
133
155
  result = TEENS[ones]
134
156
  }
135
- } else {
157
+ }
158
+ else {
136
159
  if (tens >= 2) {
137
160
  if (result) {
138
161
  result += ' ' + TENS[tens]
139
- } else {
162
+ }
163
+ else {
140
164
  result = TENS[tens]
141
165
  }
142
166
  }
@@ -145,7 +169,8 @@ function buildUnitsSegment (n, andWord) {
145
169
  if (ones > 0) {
146
170
  if (result) {
147
171
  result += ' ' + andWord + ONES[ones]
148
- } else {
172
+ }
173
+ else {
149
174
  result = ONES[ones]
150
175
  }
151
176
  }
@@ -160,12 +185,11 @@ function buildUnitsSegment (n, andWord) {
160
185
 
161
186
  /**
162
187
  * Converts a non-negative integer to Hebrew words.
163
- *
164
188
  * @param {bigint} n - Non-negative integer to convert
165
189
  * @param {string} andWord - Conjunction word
166
190
  * @returns {string} Hebrew words
167
191
  */
168
- function integerToWords (n, andWord) {
192
+ function integerToWords(n, andWord) {
169
193
  if (n === 0n) return ZERO
170
194
 
171
195
  // Fast path: numbers < 1000
@@ -195,28 +219,34 @@ function integerToWords (n, andWord) {
195
219
  // Add "ו" before single-digit units when following scale words
196
220
  if (segment <= 9) {
197
221
  result += ' ' + andWord + segmentWord
198
- } else {
222
+ }
223
+ else {
199
224
  result += ' ' + segmentWord
200
225
  }
201
- } else {
226
+ }
227
+ else {
202
228
  result = segmentWord
203
229
  }
204
- } else if (i === 1) {
230
+ }
231
+ else if (i === 1) {
205
232
  // Thousands - special handling for 1-9
206
233
  if (segment <= 9) {
207
234
  if (result) result += ' '
208
235
  result += THOUSANDS_SPECIAL[segment]
209
- } else {
236
+ }
237
+ else {
210
238
  const segmentWord = buildScaleSegment(segment, andWord)
211
239
  if (result) result += ' '
212
240
  result += segmentWord + ' ' + SCALE[1]
213
241
  }
214
- } else {
242
+ }
243
+ else {
215
244
  // Millions and above
216
245
  if (segment === 1) {
217
246
  if (result) result += ' '
218
247
  result += SCALE[i]
219
- } else {
248
+ }
249
+ else {
220
250
  const segmentWord = buildScaleSegment(segment, andWord)
221
251
  if (result) result += ' '
222
252
  result += segmentWord + ' ' + SCALE_PLURAL[i]
@@ -229,11 +259,10 @@ function integerToWords (n, andWord) {
229
259
 
230
260
  /**
231
261
  * Converts decimal digits to Hebrew words (digit by digit).
232
- *
233
262
  * @param {string} decimalPart - Decimal digits (without the point)
234
263
  * @returns {string} Hebrew words for decimal part
235
264
  */
236
- function decimalPartToWords (decimalPart) {
265
+ function decimalPartToWords(decimalPart) {
237
266
  let result = ''
238
267
 
239
268
  for (let i = 0; i < decimalPart.length; i++) {
@@ -245,20 +274,26 @@ function decimalPartToWords (decimalPart) {
245
274
  return result
246
275
  }
247
276
 
277
+ /**
278
+ * @typedef {object} CardinalOptions
279
+ * @property {string} [andWord] - Custom conjunction word
280
+ */
281
+
282
+ /** @type {Required<CardinalOptions>} */
283
+ export const cardinalDefaults = { andWord: 'ו' }
284
+
248
285
  /**
249
286
  * Converts a numeric value to Modern Hebrew words.
250
- *
251
287
  * @param {number | string | bigint} value - The numeric value to convert
252
- * @param {Object} [options] - Optional configuration
253
- * @param {string} [options.andWord='ו'] - Custom conjunction word
288
+ * @param {CardinalOptions} [options] - Optional configuration
254
289
  * @returns {string} The number in Modern Hebrew words
255
290
  */
256
- function toCardinal (value, options) {
257
- options = validateOptions(options)
291
+ function toCardinal(value, options) {
258
292
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
293
+ checkMax(integerPart, cardinalMax)
259
294
 
260
295
  // Apply option defaults
261
- const { andWord = 'ו' } = options
296
+ const { andWord } = resolveOptions(options, cardinalDefaults)
262
297
 
263
298
  let result = ''
264
299
 
@@ -281,11 +316,10 @@ function toCardinal (value, options) {
281
316
 
282
317
  /**
283
318
  * Builds ordinal for tens and ones (0-99).
284
- *
285
319
  * @param {number} n - Number 0-99
286
320
  * @returns {string} Ordinal word
287
321
  */
288
- function buildOrdinalTensOnes (n) {
322
+ function buildOrdinalTensOnes(n) {
289
323
  if (n === 0) return ''
290
324
  if (n < 10) return ORDINAL_ONES[n]
291
325
  if (n < 20) return ORDINAL_TEENS[n - 10]
@@ -303,11 +337,10 @@ function buildOrdinalTensOnes (n) {
303
337
 
304
338
  /**
305
339
  * Converts a non-negative integer to Hebrew ordinal words.
306
- *
307
340
  * @param {bigint} n - Non-negative integer to convert
308
341
  * @returns {string} Hebrew ordinal words
309
342
  */
310
- function integerToOrdinal (n) {
343
+ function integerToOrdinal(n) {
311
344
  if (n === 0n) return ''
312
345
  if (n === 1n) return ORDINAL_ONES[1]
313
346
 
@@ -327,7 +360,8 @@ function integerToOrdinal (n) {
327
360
  return ORDINAL_HUNDRED
328
361
  }
329
362
  result = HUNDREDS[hundreds]
330
- } else {
363
+ }
364
+ else {
331
365
  if (remainder === 0) {
332
366
  return HUNDREDS[hundreds]
333
367
  }
@@ -353,7 +387,8 @@ function integerToOrdinal (n) {
353
387
  let result
354
388
  if (thousands <= 9) {
355
389
  result = THOUSANDS_SPECIAL[thousands]
356
- } else {
390
+ }
391
+ else {
357
392
  result = buildScaleSegment(thousands, 'ו') + ' ' + SCALE[1]
358
393
  }
359
394
 
@@ -385,7 +420,8 @@ function integerToOrdinal (n) {
385
420
  let result
386
421
  if (millions === 1) {
387
422
  result = SCALE[2]
388
- } else {
423
+ }
424
+ else {
389
425
  result = buildScaleSegment(millions, 'ו') + ' ' + SCALE_PLURAL[2]
390
426
  }
391
427
 
@@ -394,18 +430,17 @@ function integerToOrdinal (n) {
394
430
 
395
431
  /**
396
432
  * Converts a numeric value to Hebrew ordinal words.
397
- *
398
433
  * @param {number | string | bigint} value - The numeric value to convert
399
434
  * @returns {string} The ordinal in Hebrew words
400
435
  * @throws {TypeError} If value is not a valid numeric type
401
436
  * @throws {Error} If value is not a positive integer
402
- *
403
437
  * @example
404
438
  * toOrdinal(1) // 'ראשון'
405
439
  * toOrdinal(21) // 'עשרים וראשון'
406
440
  */
407
- function toOrdinal (value) {
441
+ function toOrdinal(value) {
408
442
  const n = parseOrdinalValue(value)
443
+ checkMax(n, ordinalMax)
409
444
  return integerToOrdinal(n)
410
445
  }
411
446
 
@@ -415,8 +450,10 @@ function toOrdinal (value) {
415
450
 
416
451
  /**
417
452
  * Builds segment word for currency (masculine forms).
453
+ * @param {number} n - Segment value (0-999)
454
+ * @returns {string} Hebrew words for the segment (masculine form)
418
455
  */
419
- function buildCurrencySegment (n) {
456
+ function buildCurrencySegment(n) {
420
457
  if (n === 0) return ''
421
458
 
422
459
  const ones = n % 10
@@ -436,15 +473,18 @@ function buildCurrencySegment (n) {
436
473
  const teenWord = TEENS[ones]
437
474
  if (result) {
438
475
  result += ' ' + teenWord
439
- } else {
476
+ }
477
+ else {
440
478
  result = teenWord
441
479
  }
442
- } else {
480
+ }
481
+ else {
443
482
  // Tens (20-90)
444
483
  if (tens >= 2) {
445
484
  if (result) {
446
485
  result += ' ' + TENS[tens]
447
- } else {
486
+ }
487
+ else {
448
488
  result = TENS[tens]
449
489
  }
450
490
  }
@@ -453,7 +493,8 @@ function buildCurrencySegment (n) {
453
493
  if (ones > 0) {
454
494
  if (result) {
455
495
  result += ' ו' + ONES_MASC[ones]
456
- } else {
496
+ }
497
+ else {
457
498
  result = ONES_MASC[ones]
458
499
  }
459
500
  }
@@ -464,11 +505,10 @@ function buildCurrencySegment (n) {
464
505
 
465
506
  /**
466
507
  * Converts a non-negative integer to Hebrew currency words (masculine).
467
- *
468
508
  * @param {bigint} n - Non-negative integer to convert
469
509
  * @returns {string} Hebrew words (masculine form)
470
510
  */
471
- function integerToCurrencyWords (n) {
511
+ function integerToCurrencyWords(n) {
472
512
  if (n === 0n) return ZERO
473
513
 
474
514
  // Fast path: numbers < 1000
@@ -484,18 +524,17 @@ function integerToCurrencyWords (n) {
484
524
 
485
525
  /**
486
526
  * Converts a numeric value to Hebrew New Israeli Shekel currency words.
487
- *
488
527
  * @param {number | string | bigint} value - The numeric value to convert
489
528
  * @returns {string} The currency in Hebrew words
490
529
  * @throws {TypeError} If value is not a valid numeric type
491
530
  * @throws {Error} If value is not a valid number format
492
- *
493
531
  * @example
494
532
  * toCurrency(1) // 'שקל אחד'
495
533
  * toCurrency(2.50) // 'שניים שקלים חמישים אגורות'
496
534
  */
497
- function toCurrency (value) {
535
+ function toCurrency(value) {
498
536
  const { isNegative, dollars, cents } = parseCurrencyValue(value)
537
+ checkMax(dollars, currencyMax)
499
538
 
500
539
  const parts = []
501
540
 
@@ -507,9 +546,11 @@ function toCurrency (value) {
507
546
  if (dollars > 0n || cents === 0n) {
508
547
  if (dollars === 1n) {
509
548
  parts.push(SHEKEL_SINGULAR + ' ' + ONES_MASC[1])
510
- } else if (dollars === 2n) {
549
+ }
550
+ else if (dollars === 2n) {
511
551
  parts.push(ONES_MASC[2] + ' ' + SHEKEL_PLURAL)
512
- } else {
552
+ }
553
+ else {
513
554
  const shekelWord = integerToCurrencyWords(dollars)
514
555
  const shekelForm = SHEKEL_PLURAL
515
556
  parts.push(shekelWord + ' ' + shekelForm)
@@ -521,7 +562,8 @@ function toCurrency (value) {
521
562
  const centNum = Number(cents)
522
563
  if (centNum === 1) {
523
564
  parts.push(AGORA_SINGULAR + ' ' + ONES[1])
524
- } else {
565
+ }
566
+ else {
525
567
  const centWord = integerToWords(cents, 'ו')
526
568
  parts.push(centWord + ' ' + AGORA_PLURAL)
527
569
  }
package/src/hi-IN.d.ts CHANGED
@@ -1,18 +1,18 @@
1
+ export const cardinalMax: bigint;
2
+ export const ordinalMax: bigint;
3
+ export const currencyMax: bigint;
1
4
  /**
2
5
  * Converts a numeric value to Hindi words.
3
- *
4
6
  * @param {number | string | bigint} value - The numeric value to convert
5
7
  * @returns {string} The number in Hindi words
6
8
  */
7
9
  export function toCardinal(value: number | string | bigint): string;
8
10
  /**
9
11
  * Converts a numeric value to Hindi ordinal words.
10
- *
11
12
  * @param {number | string | bigint} value - The numeric value to convert (positive integer)
12
13
  * @returns {string} The number as ordinal words
13
14
  * @throws {TypeError} If value is not a valid numeric type
14
15
  * @throws {RangeError} If value is negative, zero, or has a decimal part
15
- *
16
16
  * @example
17
17
  * toOrdinal(1) // 'पहला'
18
18
  * toOrdinal(2) // 'दूसरा'
@@ -22,12 +22,10 @@ export function toCardinal(value: number | string | bigint): string;
22
22
  export function toOrdinal(value: number | string | bigint): string;
23
23
  /**
24
24
  * Converts a numeric value to Hindi currency words (Indian Rupee).
25
- *
26
25
  * @param {number | string | bigint} value - The currency amount to convert
27
26
  * @returns {string} The amount in Hindi currency words
28
27
  * @throws {TypeError} If value is not a valid numeric type
29
28
  * @throws {Error} If value is not a valid number format
30
- *
31
29
  * @example
32
30
  * toCurrency(42.50) // 'बयालीस रुपये पचास पैसे'
33
31
  * toCurrency(1) // 'एक रुपया'
package/src/hi-IN.js CHANGED
@@ -14,6 +14,8 @@
14
14
  import { parseCardinalValue } from './utils/parse-cardinal.js'
15
15
  import { parseCurrencyValue } from './utils/parse-currency.js'
16
16
  import { parseOrdinalValue } from './utils/parse-ordinal.js'
17
+ import { checkMax } from './utils/check-max.js'
18
+ import { indian } from './utils/scale.js'
17
19
 
18
20
  // ============================================================================
19
21
  // Vocabulary
@@ -56,23 +58,29 @@ const BELOW_HUNDRED = [
56
58
  'साठ', 'इकसठ', 'बासठ', 'तिरसठ', 'चौंसठ', 'पैंसठ', 'छियासठ', 'सड़सठ', 'अड़सठ', 'उनहत्तर',
57
59
  'सत्तर', 'इकहत्तर', 'बहत्तर', 'तिहत्तर', 'चौहत्तर', 'पचहत्तर', 'छिहत्तर', 'सतहत्तर', 'अठहत्तर', 'उन्यासी',
58
60
  'अस्सी', 'इक्यासी', 'बयासी', 'तिरासी', 'चौरासी', 'पचासी', 'छियासी', 'सत्तासी', 'अट्ठासी', 'नवासी',
59
- 'नब्बे', 'इक्यानवे', 'बानवे', 'तिरानवे', 'चौरानवे', 'पचानवे', 'छियानवे', 'सत्तानवे', 'अट्ठानवे', 'निन्यानवे'
61
+ 'नब्बे', 'इक्यानवे', 'बानवे', 'तिरानवे', 'चौरानवे', 'पचानवे', 'छियानवे', 'सत्तानवे', 'अट्ठानवे', 'निन्यानवे',
60
62
  ]
61
63
 
62
64
  // Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.
63
65
  const SCALE_WORDS = ['', 'हज़ार', 'लाख', 'करोड़', 'अरब', 'खरब', 'नील', 'पद्म', 'शंख']
64
66
 
67
+ // 3-2-2 Indian grouping: a 3-digit base segment, then 2 digits per scale word
68
+ // (SCALE_WORDS[0] = '' is the units slot). Past the table the scale word is
69
+ // dropped, which collapses the magnitude — so cap there.
70
+ export const cardinalMax = indian(SCALE_WORDS.length)
71
+ export const ordinalMax = indian(SCALE_WORDS.length)
72
+ export const currencyMax = indian(SCALE_WORDS.length)
73
+
65
74
  // ============================================================================
66
75
  // Segment Building
67
76
  // ============================================================================
68
77
 
69
78
  /**
70
79
  * Builds words for a 0-999 segment.
71
- *
72
80
  * @param {number} n - Number 0-999
73
81
  * @returns {string} Hindi words for the segment
74
82
  */
75
- function buildSegment (n) {
83
+ function buildSegment(n) {
76
84
  if (n === 0) return ''
77
85
  if (n < 100) return BELOW_HUNDRED[n]
78
86
 
@@ -94,11 +102,10 @@ function buildSegment (n) {
94
102
  *
95
103
  * Uses BigInt modulo for segment extraction (faster than string slicing).
96
104
  * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.
97
- *
98
105
  * @param {bigint} n - Non-negative integer to convert
99
106
  * @returns {string} Hindi words
100
107
  */
101
- function integerToWords (n) {
108
+ function integerToWords(n) {
102
109
  if (n === 0n) return ZERO
103
110
 
104
111
  // Fast path: numbers < 1000
@@ -130,7 +137,8 @@ function integerToWords (n) {
130
137
  if (i === 0) {
131
138
  // First segment (units place) can be 0-999
132
139
  words.push(buildSegment(segment))
133
- } else {
140
+ }
141
+ else {
134
142
  // Other segments are 0-99
135
143
  words.push(BELOW_HUNDRED[segment])
136
144
  }
@@ -144,7 +152,12 @@ function integerToWords (n) {
144
152
  return words.join(' ')
145
153
  }
146
154
 
147
- function decimalPartToWords (decimalPart) {
155
+ /**
156
+ * Converts the decimal-part digit string to Hindi words.
157
+ * @param {string} decimalPart - The digits after the decimal separator
158
+ * @returns {string} Hindi words for the decimal part
159
+ */
160
+ function decimalPartToWords(decimalPart) {
148
161
  let result = ''
149
162
  let i = 0
150
163
 
@@ -165,12 +178,14 @@ function decimalPartToWords (decimalPart) {
165
178
 
166
179
  /**
167
180
  * Converts a numeric value to Hindi words.
168
- *
169
181
  * @param {number | string | bigint} value - The numeric value to convert
170
182
  * @returns {string} The number in Hindi words
171
183
  */
172
- function toCardinal (value) {
184
+ function toCardinal(value) {
173
185
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
186
+ // Both the integer part and the decimal's significant digits are spelled via
187
+ // the scale builder, so both must clear the ceiling.
188
+ checkMax(integerPart, cardinalMax, decimalPart)
174
189
 
175
190
  let result = ''
176
191
 
@@ -195,11 +210,10 @@ function toCardinal (value) {
195
210
  * Converts a positive integer to Hindi ordinal words.
196
211
  *
197
212
  * Hindi ordinals: First 6 are irregular, then add -वाँ suffix.
198
- *
199
213
  * @param {bigint} n - Positive integer to convert
200
214
  * @returns {string} Hindi ordinal words
201
215
  */
202
- function integerToOrdinal (n) {
216
+ function integerToOrdinal(n) {
203
217
  // Special ordinals for 1-6
204
218
  if (n >= 1n && n <= 6n) {
205
219
  return ORDINAL_SPECIAL[Number(n)]
@@ -212,20 +226,20 @@ function integerToOrdinal (n) {
212
226
 
213
227
  /**
214
228
  * Converts a numeric value to Hindi ordinal words.
215
- *
216
229
  * @param {number | string | bigint} value - The numeric value to convert (positive integer)
217
230
  * @returns {string} The number as ordinal words
218
231
  * @throws {TypeError} If value is not a valid numeric type
219
232
  * @throws {RangeError} If value is negative, zero, or has a decimal part
220
- *
221
233
  * @example
222
234
  * toOrdinal(1) // 'पहला'
223
235
  * toOrdinal(2) // 'दूसरा'
224
236
  * toOrdinal(3) // 'तीसरा'
225
237
  * toOrdinal(10) // 'दसवाँ'
226
238
  */
227
- function toOrdinal (value) {
239
+ function toOrdinal(value) {
228
240
  const integerPart = parseOrdinalValue(value)
241
+ // Ordinals build on the cardinal speller, so they share its ceiling.
242
+ checkMax(integerPart, ordinalMax)
229
243
  return integerToOrdinal(integerPart)
230
244
  }
231
245
 
@@ -235,19 +249,18 @@ function toOrdinal (value) {
235
249
 
236
250
  /**
237
251
  * Converts a numeric value to Hindi currency words (Indian Rupee).
238
- *
239
252
  * @param {number | string | bigint} value - The currency amount to convert
240
253
  * @returns {string} The amount in Hindi currency words
241
254
  * @throws {TypeError} If value is not a valid numeric type
242
255
  * @throws {Error} If value is not a valid number format
243
- *
244
256
  * @example
245
257
  * toCurrency(42.50) // 'बयालीस रुपये पचास पैसे'
246
258
  * toCurrency(1) // 'एक रुपया'
247
259
  * toCurrency(0.01) // 'एक पैसा'
248
260
  */
249
- function toCurrency (value) {
261
+ function toCurrency(value) {
250
262
  const { isNegative, dollars: rupees, cents: paise } = parseCurrencyValue(value)
263
+ checkMax(rupees, currencyMax)
251
264
 
252
265
  // Build result
253
266
  let result = ''
package/src/hr-HR.d.ts CHANGED
@@ -1,22 +1,35 @@
1
+ export const cardinalMax: bigint;
2
+ export const ordinalMax: bigint;
3
+ export const currencyMax: bigint;
4
+ /**
5
+ * @typedef {object} CardinalOptions
6
+ * @property {('masculine'|'feminine')} [gender] - Grammatical gender
7
+ */
8
+ /** @type {Required<CardinalOptions>} */
9
+ export const cardinalDefaults: Required<CardinalOptions>;
10
+ /** @type {{ gender: ReadonlyArray<Required<CardinalOptions>['gender']> }} */
11
+ export const cardinalValues: {
12
+ gender: ReadonlyArray<Required<CardinalOptions>["gender"]>;
13
+ };
14
+ export type CardinalOptions = {
15
+ /**
16
+ * - Grammatical gender
17
+ */
18
+ gender?: "feminine" | "masculine" | undefined;
19
+ };
1
20
  /**
2
21
  * Converts a numeric value to Croatian words.
3
- *
4
22
  * @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
23
+ * @param {CardinalOptions} [options] - Optional configuration
7
24
  * @returns {string} The number in Croatian words
8
25
  */
9
- export function toCardinal(value: number | string | bigint, options?: {
10
- gender?: "masculine" | "feminine" | undefined;
11
- }): string;
26
+ export function toCardinal(value: number | string | bigint, options?: CardinalOptions): string;
12
27
  /**
13
28
  * Converts a numeric value to Croatian ordinal words (masculine nominative).
14
- *
15
29
  * @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
16
30
  * @returns {string} The number as ordinal words
17
31
  * @throws {TypeError} If value is not a valid numeric type
18
32
  * @throws {RangeError} If value is negative, zero, or has a decimal part
19
- *
20
33
  * @example
21
34
  * toOrdinal(1) // 'prvi'
22
35
  * toOrdinal(2) // 'drugi'
@@ -27,12 +40,10 @@ export function toCardinal(value: number | string | bigint, options?: {
27
40
  export function toOrdinal(value: number | string | bigint): string;
28
41
  /**
29
42
  * Converts a numeric value to Croatian currency words (Euro).
30
- *
31
43
  * @param {number | string | bigint} value - The currency amount to convert
32
44
  * @returns {string} The amount in Croatian currency words
33
45
  * @throws {TypeError} If value is not a valid numeric type
34
46
  * @throws {Error} If value is not a valid number format
35
- *
36
47
  * @example
37
48
  * toCurrency(42) // 'četrdeset dva eura'
38
49
  * toCurrency(1) // 'jedan euro'