n2words 5.0.0 → 5.1.1

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 +133 -40
  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 +33 -24
  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 +106 -43
  266. package/src/sr-Latn-RS.d.ts +35 -15
  267. package/src/sr-Latn-RS.js +106 -43
  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/en-KE.js CHANGED
@@ -20,7 +20,9 @@
20
20
  import { parseCardinalValue } from './utils/parse-cardinal.js'
21
21
  import { parseCurrencyValue } from './utils/parse-currency.js'
22
22
  import { parseOrdinalValue } from './utils/parse-ordinal.js'
23
- import { validateOptions } from './utils/validate-options.js'
23
+ import { checkMax } from './utils/check-max.js'
24
+ import { western } from './utils/scale.js'
25
+ import { resolveOptions } from './utils/resolve-options.js'
24
26
 
25
27
  // ============================================================================
26
28
  // Vocabulary (module-level constants)
@@ -35,9 +37,13 @@ const SCALES = [
35
37
  'quintillion', 'sextillion', 'septillion', 'octillion', 'nonillion',
36
38
  'decillion', 'undecillion', 'duodecillion', 'tredecillion', 'quattuordecillion',
37
39
  'quindecillion', 'sexdecillion', 'septendecillion', 'octodecillion', 'novemdecillion',
38
- 'vigintillion'
40
+ 'vigintillion',
39
41
  ]
40
42
 
43
+ export const cardinalMax = western(SCALES.length)
44
+ export const ordinalMax = western(SCALES.length)
45
+ export const currencyMax = western(SCALES.length)
46
+
41
47
  const HUNDRED = 'hundred'
42
48
  const ZERO = 'zero'
43
49
  const NEGATIVE = 'minus'
@@ -60,7 +66,11 @@ const CENTS = 'cents'
60
66
 
61
67
  const segmentResult = { word: '', hasHundred: false }
62
68
 
63
- function buildSegment (n) {
69
+ /**
70
+ * @param {number} n The 0-999 group value to convert
71
+ * @returns {{word: string, hasHundred: boolean}} The segment words and whether it includes a hundreds place
72
+ */
73
+ function buildSegment(n) {
64
74
  if (n === 0) {
65
75
  segmentResult.word = ''
66
76
  segmentResult.hasHundred = false
@@ -74,20 +84,24 @@ function buildSegment (n) {
74
84
  let tensOnes = ''
75
85
  if (tens === 1) {
76
86
  tensOnes = TEENS[ones]
77
- } else if (tens >= 2) {
87
+ }
88
+ else if (tens >= 2) {
78
89
  tensOnes = ones > 0 ? TENS[tens] + '-' + ONES[ones] : TENS[tens]
79
- } else if (ones > 0) {
90
+ }
91
+ else if (ones > 0) {
80
92
  tensOnes = ONES[ones]
81
93
  }
82
94
 
83
95
  if (hundreds > 0) {
84
96
  if (tensOnes) {
85
97
  segmentResult.word = ONES[hundreds] + ' ' + HUNDRED + ' and ' + tensOnes
86
- } else {
98
+ }
99
+ else {
87
100
  segmentResult.word = ONES[hundreds] + ' ' + HUNDRED
88
101
  }
89
102
  segmentResult.hasHundred = true
90
- } else {
103
+ }
104
+ else {
91
105
  segmentResult.word = tensOnes
92
106
  segmentResult.hasHundred = false
93
107
  }
@@ -99,7 +113,11 @@ function buildSegment (n) {
99
113
  // Conversion Functions
100
114
  // ============================================================================
101
115
 
102
- function integerToWords (n) {
116
+ /**
117
+ * @param {bigint} n The non-negative integer to convert
118
+ * @returns {string} The integer in English words
119
+ */
120
+ function integerToWords(n) {
103
121
  if (n === 0n) return ZERO
104
122
 
105
123
  if (n < 1000n) {
@@ -124,7 +142,11 @@ function integerToWords (n) {
124
142
  return buildLargeNumberWords(n)
125
143
  }
126
144
 
127
- function buildLargeNumberWords (n) {
145
+ /**
146
+ * @param {bigint} n The integer of one million or greater to convert
147
+ * @returns {string} The integer in English words
148
+ */
149
+ function buildLargeNumberWords(n) {
128
150
  const segments = []
129
151
  let temp = n
130
152
  while (temp > 0n) {
@@ -160,7 +182,8 @@ function buildLargeNumberWords (n) {
160
182
  if (i > 0) {
161
183
  result += ' ' + SCALES[i - 1]
162
184
  prevWasScale = true
163
- } else {
185
+ }
186
+ else {
164
187
  prevWasScale = false
165
188
  }
166
189
  }
@@ -168,7 +191,11 @@ function buildLargeNumberWords (n) {
168
191
  return result
169
192
  }
170
193
 
171
- function decimalPartToWords (decimalPart) {
194
+ /**
195
+ * @param {string} decimalPart The fractional digits as a string
196
+ * @returns {string} The decimal digits in English words
197
+ */
198
+ function decimalPartToWords(decimalPart) {
172
199
  let result = ''
173
200
 
174
201
  let i = 0
@@ -189,14 +216,16 @@ function decimalPartToWords (decimalPart) {
189
216
 
190
217
  /**
191
218
  * Converts a numeric value to Kenyan English words.
192
- *
193
219
  * @param {number | string | bigint} value - The numeric value to convert
194
220
  * @returns {string} The number in English words
195
221
  * @throws {TypeError} If value is not a valid numeric type
196
222
  * @throws {Error} If value is not a valid number format
197
223
  */
198
- function toCardinal (value) {
224
+ function toCardinal(value) {
199
225
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
226
+ // Both the integer part and the decimal's significant digits are spelled via
227
+ // the scale builder, so both must clear the ceiling.
228
+ checkMax(integerPart, cardinalMax, decimalPart)
200
229
 
201
230
  let result = ''
202
231
 
@@ -217,7 +246,11 @@ function toCardinal (value) {
217
246
  // ORDINAL
218
247
  // ============================================================================
219
248
 
220
- function buildOrdinalSegment (n) {
249
+ /**
250
+ * @param {number} n The 0-999 group value to convert
251
+ * @returns {string} The segment as ordinal English words
252
+ */
253
+ function buildOrdinalSegment(n) {
221
254
  const ones = n % 10
222
255
  const tens = Math.trunc(n / 10) % 10
223
256
  const hundreds = Math.trunc(n / 100)
@@ -225,20 +258,24 @@ function buildOrdinalSegment (n) {
225
258
  let tensOnesOrdinal = ''
226
259
  if (tens === 1) {
227
260
  tensOnesOrdinal = ORDINAL_TEENS[ones]
228
- } else if (tens >= 2) {
261
+ }
262
+ else if (tens >= 2) {
229
263
  if (ones > 0) {
230
264
  tensOnesOrdinal = TENS[tens] + '-' + ORDINAL_ONES[ones]
231
- } else {
265
+ }
266
+ else {
232
267
  tensOnesOrdinal = ORDINAL_TENS[tens]
233
268
  }
234
- } else if (ones > 0) {
269
+ }
270
+ else if (ones > 0) {
235
271
  tensOnesOrdinal = ORDINAL_ONES[ones]
236
272
  }
237
273
 
238
274
  if (hundreds > 0) {
239
275
  if (tensOnesOrdinal) {
240
276
  return ONES[hundreds] + ' ' + HUNDRED + ' ' + tensOnesOrdinal
241
- } else {
277
+ }
278
+ else {
242
279
  return ONES[hundreds] + ' hundredth'
243
280
  }
244
281
  }
@@ -246,7 +283,11 @@ function buildOrdinalSegment (n) {
246
283
  return tensOnesOrdinal
247
284
  }
248
285
 
249
- function integerToOrdinal (n) {
286
+ /**
287
+ * @param {bigint} n The non-negative integer to convert
288
+ * @returns {string} The integer as ordinal English words
289
+ */
290
+ function integerToOrdinal(n) {
250
291
  if (n < 1000n) {
251
292
  return buildOrdinalSegment(Number(n))
252
293
  }
@@ -266,7 +307,11 @@ function integerToOrdinal (n) {
266
307
  return buildLargeOrdinal(n)
267
308
  }
268
309
 
269
- function buildLargeOrdinal (n) {
310
+ /**
311
+ * @param {bigint} n The integer of one million or greater to convert
312
+ * @returns {string} The integer as ordinal English words
313
+ */
314
+ function buildLargeOrdinal(n) {
270
315
  const segments = []
271
316
  let temp = n
272
317
  while (temp > 0n) {
@@ -295,10 +340,12 @@ function buildLargeOrdinal (n) {
295
340
  if (isLowestSegment) {
296
341
  if (i === 0) {
297
342
  result += buildOrdinalSegment(segment)
298
- } else {
343
+ }
344
+ else {
299
345
  result += buildSegment(segment).word + ' ' + SCALES[i - 1] + 'th'
300
346
  }
301
- } else {
347
+ }
348
+ else {
302
349
  result += buildSegment(segment).word
303
350
  if (i > 0) {
304
351
  result += ' ' + SCALES[i - 1]
@@ -309,8 +356,14 @@ function buildLargeOrdinal (n) {
309
356
  return result
310
357
  }
311
358
 
312
- function toOrdinal (value) {
359
+ /**
360
+ * Converts a numeric value to Kenyan English ordinal words.
361
+ * @param {number | string | bigint} value - The numeric value to convert
362
+ * @returns {string} The ordinal in English words
363
+ */
364
+ function toOrdinal(value) {
313
365
  const integerPart = parseOrdinalValue(value)
366
+ checkMax(integerPart, ordinalMax)
314
367
  return integerToOrdinal(integerPart)
315
368
  }
316
369
 
@@ -318,10 +371,24 @@ function toOrdinal (value) {
318
371
  // CURRENCY
319
372
  // ============================================================================
320
373
 
321
- function toCurrency (value, options) {
322
- options = validateOptions(options)
374
+ /**
375
+ * @typedef {object} CurrencyOptions
376
+ * @property {boolean} [and] - Use "and" between shillings and cents
377
+ */
378
+
379
+ /** @type {Required<CurrencyOptions>} */
380
+ export const currencyDefaults = { and: true }
381
+
382
+ /**
383
+ * Converts a numeric value to Kenyan English currency words.
384
+ * @param {number | string | bigint} value - The numeric value to convert
385
+ * @param {CurrencyOptions} [options] - Optional configuration
386
+ * @returns {string} The currency in English words
387
+ */
388
+ function toCurrency(value, options) {
323
389
  const { isNegative, dollars: shillings, cents } = parseCurrencyValue(value)
324
- const { and: useAnd = true } = options
390
+ checkMax(shillings, currencyMax)
391
+ const { and: useAnd } = resolveOptions(options, currencyDefaults)
325
392
 
326
393
  let result = ''
327
394
  if (isNegative) result = NEGATIVE + ' '
package/src/en-MY.d.ts CHANGED
@@ -1,11 +1,34 @@
1
+ export const cardinalMax: bigint;
2
+ export const ordinalMax: bigint;
3
+ export const currencyMax: bigint;
4
+ /**
5
+ * @typedef {object} CurrencyOptions
6
+ * @property {boolean} [and] - Use "and" between ringgit and sen
7
+ */
8
+ /** @type {Required<CurrencyOptions>} */
9
+ export const currencyDefaults: Required<CurrencyOptions>;
10
+ export type CurrencyOptions = {
11
+ /**
12
+ * - Use "and" between ringgit and sen
13
+ */
14
+ and?: boolean | undefined;
15
+ };
1
16
  /**
2
17
  * Converts a numeric value to Malaysian English words.
3
- *
4
18
  * @param {number | string | bigint} value - The numeric value to convert
5
19
  * @returns {string} The number in English words
6
20
  * @throws {TypeError} If value is not a valid numeric type
7
21
  * @throws {Error} If value is not a valid number format
8
22
  */
9
23
  export function toCardinal(value: number | string | bigint): string;
10
- export function toOrdinal(value: any): string;
11
- export function toCurrency(value: any, options: any): string;
24
+ /**
25
+ * @param {number | string | bigint} value The numeric value to convert.
26
+ * @returns {string} The number as an ordinal in English words.
27
+ */
28
+ export function toOrdinal(value: number | string | bigint): string;
29
+ /**
30
+ * @param {number | string | bigint} value The numeric value to convert.
31
+ * @param {CurrencyOptions} [options] - Optional configuration
32
+ * @returns {string} The amount in Malaysian ringgit and sen in English words.
33
+ */
34
+ export function toCurrency(value: number | string | bigint, options?: CurrencyOptions): string;
package/src/en-MY.js CHANGED
@@ -23,7 +23,9 @@
23
23
  import { parseCardinalValue } from './utils/parse-cardinal.js'
24
24
  import { parseCurrencyValue } from './utils/parse-currency.js'
25
25
  import { parseOrdinalValue } from './utils/parse-ordinal.js'
26
- import { validateOptions } from './utils/validate-options.js'
26
+ import { checkMax } from './utils/check-max.js'
27
+ import { western } from './utils/scale.js'
28
+ import { resolveOptions } from './utils/resolve-options.js'
27
29
 
28
30
  // ============================================================================
29
31
  // Vocabulary (module-level constants)
@@ -38,9 +40,13 @@ const SCALES = [
38
40
  'quintillion', 'sextillion', 'septillion', 'octillion', 'nonillion',
39
41
  'decillion', 'undecillion', 'duodecillion', 'tredecillion', 'quattuordecillion',
40
42
  'quindecillion', 'sexdecillion', 'septendecillion', 'octodecillion', 'novemdecillion',
41
- 'vigintillion'
43
+ 'vigintillion',
42
44
  ]
43
45
 
46
+ export const cardinalMax = western(SCALES.length)
47
+ export const ordinalMax = western(SCALES.length)
48
+ export const currencyMax = western(SCALES.length)
49
+
44
50
  const HUNDRED = 'hundred'
45
51
  const ZERO = 'zero'
46
52
  const NEGATIVE = 'minus'
@@ -61,7 +67,11 @@ const SEN = 'sen'
61
67
 
62
68
  const segmentResult = { word: '', hasHundred: false }
63
69
 
64
- function buildSegment (n) {
70
+ /**
71
+ * @param {number} n The 0-999 segment to convert.
72
+ * @returns {{word: string, hasHundred: boolean}} The segment in words and whether it includes a hundreds part.
73
+ */
74
+ function buildSegment(n) {
65
75
  if (n === 0) {
66
76
  segmentResult.word = ''
67
77
  segmentResult.hasHundred = false
@@ -75,20 +85,24 @@ function buildSegment (n) {
75
85
  let tensOnes = ''
76
86
  if (tens === 1) {
77
87
  tensOnes = TEENS[ones]
78
- } else if (tens >= 2) {
88
+ }
89
+ else if (tens >= 2) {
79
90
  tensOnes = ones > 0 ? TENS[tens] + '-' + ONES[ones] : TENS[tens]
80
- } else if (ones > 0) {
91
+ }
92
+ else if (ones > 0) {
81
93
  tensOnes = ONES[ones]
82
94
  }
83
95
 
84
96
  if (hundreds > 0) {
85
97
  if (tensOnes) {
86
98
  segmentResult.word = ONES[hundreds] + ' ' + HUNDRED + ' and ' + tensOnes
87
- } else {
99
+ }
100
+ else {
88
101
  segmentResult.word = ONES[hundreds] + ' ' + HUNDRED
89
102
  }
90
103
  segmentResult.hasHundred = true
91
- } else {
104
+ }
105
+ else {
92
106
  segmentResult.word = tensOnes
93
107
  segmentResult.hasHundred = false
94
108
  }
@@ -100,7 +114,11 @@ function buildSegment (n) {
100
114
  // Conversion Functions
101
115
  // ============================================================================
102
116
 
103
- function integerToWords (n) {
117
+ /**
118
+ * @param {bigint} n The non-negative integer to convert.
119
+ * @returns {string} The integer in English words.
120
+ */
121
+ function integerToWords(n) {
104
122
  if (n === 0n) return ZERO
105
123
 
106
124
  if (n < 1000n) {
@@ -125,7 +143,11 @@ function integerToWords (n) {
125
143
  return buildLargeNumberWords(n)
126
144
  }
127
145
 
128
- function buildLargeNumberWords (n) {
146
+ /**
147
+ * @param {bigint} n The integer of one million or greater to convert.
148
+ * @returns {string} The integer in English words.
149
+ */
150
+ function buildLargeNumberWords(n) {
129
151
  const segments = []
130
152
  let temp = n
131
153
  while (temp > 0n) {
@@ -161,7 +183,8 @@ function buildLargeNumberWords (n) {
161
183
  if (i > 0) {
162
184
  result += ' ' + SCALES[i - 1]
163
185
  prevWasScale = true
164
- } else {
186
+ }
187
+ else {
165
188
  prevWasScale = false
166
189
  }
167
190
  }
@@ -169,7 +192,11 @@ function buildLargeNumberWords (n) {
169
192
  return result
170
193
  }
171
194
 
172
- function decimalPartToWords (decimalPart) {
195
+ /**
196
+ * @param {string} decimalPart The fractional digits after the decimal point.
197
+ * @returns {string} The decimal digits in English words.
198
+ */
199
+ function decimalPartToWords(decimalPart) {
173
200
  let result = ''
174
201
 
175
202
  let i = 0
@@ -190,14 +217,16 @@ function decimalPartToWords (decimalPart) {
190
217
 
191
218
  /**
192
219
  * Converts a numeric value to Malaysian English words.
193
- *
194
220
  * @param {number | string | bigint} value - The numeric value to convert
195
221
  * @returns {string} The number in English words
196
222
  * @throws {TypeError} If value is not a valid numeric type
197
223
  * @throws {Error} If value is not a valid number format
198
224
  */
199
- function toCardinal (value) {
225
+ function toCardinal(value) {
200
226
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
227
+ // Both the integer part and the decimal's significant digits are spelled via
228
+ // the scale builder, so both must clear the ceiling.
229
+ checkMax(integerPart, cardinalMax, decimalPart)
201
230
 
202
231
  let result = ''
203
232
 
@@ -218,7 +247,11 @@ function toCardinal (value) {
218
247
  // ORDINAL
219
248
  // ============================================================================
220
249
 
221
- function buildOrdinalSegment (n) {
250
+ /**
251
+ * @param {number} n The 0-999 segment to convert.
252
+ * @returns {string} The segment as an ordinal in English words.
253
+ */
254
+ function buildOrdinalSegment(n) {
222
255
  const ones = n % 10
223
256
  const tens = Math.trunc(n / 10) % 10
224
257
  const hundreds = Math.trunc(n / 100)
@@ -226,20 +259,24 @@ function buildOrdinalSegment (n) {
226
259
  let tensOnesOrdinal = ''
227
260
  if (tens === 1) {
228
261
  tensOnesOrdinal = ORDINAL_TEENS[ones]
229
- } else if (tens >= 2) {
262
+ }
263
+ else if (tens >= 2) {
230
264
  if (ones > 0) {
231
265
  tensOnesOrdinal = TENS[tens] + '-' + ORDINAL_ONES[ones]
232
- } else {
266
+ }
267
+ else {
233
268
  tensOnesOrdinal = ORDINAL_TENS[tens]
234
269
  }
235
- } else if (ones > 0) {
270
+ }
271
+ else if (ones > 0) {
236
272
  tensOnesOrdinal = ORDINAL_ONES[ones]
237
273
  }
238
274
 
239
275
  if (hundreds > 0) {
240
276
  if (tensOnesOrdinal) {
241
277
  return ONES[hundreds] + ' ' + HUNDRED + ' ' + tensOnesOrdinal
242
- } else {
278
+ }
279
+ else {
243
280
  return ONES[hundreds] + ' hundredth'
244
281
  }
245
282
  }
@@ -247,7 +284,11 @@ function buildOrdinalSegment (n) {
247
284
  return tensOnesOrdinal
248
285
  }
249
286
 
250
- function integerToOrdinal (n) {
287
+ /**
288
+ * @param {bigint} n The non-negative integer to convert.
289
+ * @returns {string} The integer as an ordinal in English words.
290
+ */
291
+ function integerToOrdinal(n) {
251
292
  if (n < 1000n) {
252
293
  return buildOrdinalSegment(Number(n))
253
294
  }
@@ -267,7 +308,11 @@ function integerToOrdinal (n) {
267
308
  return buildLargeOrdinal(n)
268
309
  }
269
310
 
270
- function buildLargeOrdinal (n) {
311
+ /**
312
+ * @param {bigint} n The integer of one million or greater to convert.
313
+ * @returns {string} The integer as an ordinal in English words.
314
+ */
315
+ function buildLargeOrdinal(n) {
271
316
  const segments = []
272
317
  let temp = n
273
318
  while (temp > 0n) {
@@ -296,10 +341,12 @@ function buildLargeOrdinal (n) {
296
341
  if (isLowestSegment) {
297
342
  if (i === 0) {
298
343
  result += buildOrdinalSegment(segment)
299
- } else {
344
+ }
345
+ else {
300
346
  result += buildSegment(segment).word + ' ' + SCALES[i - 1] + 'th'
301
347
  }
302
- } else {
348
+ }
349
+ else {
303
350
  result += buildSegment(segment).word
304
351
  if (i > 0) {
305
352
  result += ' ' + SCALES[i - 1]
@@ -310,8 +357,13 @@ function buildLargeOrdinal (n) {
310
357
  return result
311
358
  }
312
359
 
313
- function toOrdinal (value) {
360
+ /**
361
+ * @param {number | string | bigint} value The numeric value to convert.
362
+ * @returns {string} The number as an ordinal in English words.
363
+ */
364
+ function toOrdinal(value) {
314
365
  const integerPart = parseOrdinalValue(value)
366
+ checkMax(integerPart, ordinalMax)
315
367
  return integerToOrdinal(integerPart)
316
368
  }
317
369
 
@@ -319,10 +371,23 @@ function toOrdinal (value) {
319
371
  // CURRENCY
320
372
  // ============================================================================
321
373
 
322
- function toCurrency (value, options) {
323
- options = validateOptions(options)
374
+ /**
375
+ * @typedef {object} CurrencyOptions
376
+ * @property {boolean} [and] - Use "and" between ringgit and sen
377
+ */
378
+
379
+ /** @type {Required<CurrencyOptions>} */
380
+ export const currencyDefaults = { and: true }
381
+
382
+ /**
383
+ * @param {number | string | bigint} value The numeric value to convert.
384
+ * @param {CurrencyOptions} [options] - Optional configuration
385
+ * @returns {string} The amount in Malaysian ringgit and sen in English words.
386
+ */
387
+ function toCurrency(value, options) {
324
388
  const { isNegative, dollars: ringgit, cents: sen } = parseCurrencyValue(value)
325
- const { and: useAnd = true } = options
389
+ checkMax(ringgit, currencyMax)
390
+ const { and: useAnd } = resolveOptions(options, currencyDefaults)
326
391
 
327
392
  let result = ''
328
393
  if (isNegative) result = NEGATIVE + ' '
package/src/en-NG.d.ts CHANGED
@@ -1,14 +1,27 @@
1
+ export const cardinalMax: bigint;
2
+ export const ordinalMax: bigint;
3
+ export const currencyMax: bigint;
4
+ /**
5
+ * @typedef {object} CurrencyOptions
6
+ * @property {boolean} [and] - Use "and" between naira and kobo (e.g., "one naira and fifty kobo")
7
+ */
8
+ /** @type {Required<CurrencyOptions>} */
9
+ export const currencyDefaults: Required<CurrencyOptions>;
10
+ export type CurrencyOptions = {
11
+ /**
12
+ * - Use "and" between naira and kobo (e.g., "one naira and fifty kobo")
13
+ */
14
+ and?: boolean | undefined;
15
+ };
1
16
  /**
2
17
  * Converts a numeric value to Nigerian English words.
3
18
  *
4
19
  * This is the main public API. It accepts any valid numeric input
5
20
  * (number, string, or bigint) and handles parsing internally.
6
- *
7
21
  * @param {number | string | bigint} value - The numeric value to convert
8
22
  * @returns {string} The number in English words
9
23
  * @throws {TypeError} If value is not a valid numeric type
10
24
  * @throws {Error} If value is not a valid number format
11
- *
12
25
  * @example
13
26
  * toCardinal(42) // 'forty-two'
14
27
  * toCardinal(-3.14) // 'minus three point fourteen'
@@ -17,12 +30,10 @@
17
30
  export function toCardinal(value: number | string | bigint): string;
18
31
  /**
19
32
  * Converts a numeric value to Nigerian English ordinal words.
20
- *
21
33
  * @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
22
34
  * @returns {string} The number as ordinal words (e.g., "first", "forty-second")
23
35
  * @throws {TypeError} If value is not a valid numeric type
24
36
  * @throws {RangeError} If value is negative, zero, or has a decimal part
25
- *
26
37
  * @example
27
38
  * toOrdinal(1) // 'first'
28
39
  * toOrdinal(2) // 'second'
@@ -36,14 +47,11 @@ export function toCardinal(value: number | string | bigint): string;
36
47
  export function toOrdinal(value: number | string | bigint): string;
37
48
  /**
38
49
  * Converts a numeric value to Nigerian English currency words.
39
- *
40
50
  * @param {number | string | bigint} value - The currency amount to convert
41
- * @param {Object} [options] - Optional configuration
42
- * @param {boolean} [options.and=true] - Use "and" between naira and kobo (e.g., "one naira and fifty kobo")
51
+ * @param {CurrencyOptions} [options] - Optional configuration
43
52
  * @returns {string} The amount in Nigerian English currency words
44
53
  * @throws {TypeError} If value is not a valid numeric type
45
54
  * @throws {Error} If value is not a valid number format
46
- *
47
55
  * @example
48
56
  * toCurrency(42.50) // 'forty-two naira and fifty kobo'
49
57
  * toCurrency(1) // 'one naira'
@@ -51,6 +59,4 @@ export function toOrdinal(value: number | string | bigint): string;
51
59
  * toCurrency(0.01) // 'one kobo'
52
60
  * toCurrency(42.50, { and: false }) // 'forty-two naira fifty kobo'
53
61
  */
54
- export function toCurrency(value: number | string | bigint, options?: {
55
- and?: boolean | undefined;
56
- }): string;
62
+ export function toCurrency(value: number | string | bigint, options?: CurrencyOptions): string;