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/en-ZA.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 rand and cents (e.g., "one rand and fifty cents")
7
+ */
8
+ /** @type {Required<CurrencyOptions>} */
9
+ export const currencyDefaults: Required<CurrencyOptions>;
10
+ export type CurrencyOptions = {
11
+ /**
12
+ * - Use "and" between rand and cents (e.g., "one rand and fifty cents")
13
+ */
14
+ and?: boolean | undefined;
15
+ };
1
16
  /**
2
17
  * Converts a numeric value to South African 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 South African 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 South African 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 rand and cents (e.g., "one rand and fifty cents")
51
+ * @param {CurrencyOptions} [options] - Optional configuration
43
52
  * @returns {string} The amount in South African 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 rand and fifty cents'
49
57
  * toCurrency(1) // 'one rand'
@@ -51,6 +59,4 @@ export function toOrdinal(value: number | string | bigint): string;
51
59
  * toCurrency(0.01) // 'one cent'
52
60
  * toCurrency(42.50, { and: false }) // 'forty-two rand fifty cents'
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;
package/src/en-ZA.js CHANGED
@@ -22,7 +22,9 @@
22
22
  import { parseCardinalValue } from './utils/parse-cardinal.js'
23
23
  import { parseCurrencyValue } from './utils/parse-currency.js'
24
24
  import { parseOrdinalValue } from './utils/parse-ordinal.js'
25
- import { validateOptions } from './utils/validate-options.js'
25
+ import { checkMax } from './utils/check-max.js'
26
+ import { western } from './utils/scale.js'
27
+ import { resolveOptions } from './utils/resolve-options.js'
26
28
 
27
29
  // ============================================================================
28
30
  // Vocabulary (module-level constants)
@@ -37,9 +39,13 @@ const SCALES = [
37
39
  'quintillion', 'sextillion', 'septillion', 'octillion', 'nonillion',
38
40
  'decillion', 'undecillion', 'duodecillion', 'tredecillion', 'quattuordecillion',
39
41
  'quindecillion', 'sexdecillion', 'septendecillion', 'octodecillion', 'novemdecillion',
40
- 'vigintillion'
42
+ 'vigintillion',
41
43
  ]
42
44
 
45
+ export const cardinalMax = western(SCALES.length)
46
+ export const ordinalMax = western(SCALES.length)
47
+ export const currencyMax = western(SCALES.length)
48
+
43
49
  const HUNDRED = 'hundred'
44
50
  const ZERO = 'zero'
45
51
  const NEGATIVE = 'minus'
@@ -64,11 +70,10 @@ const segmentResult = { word: '', hasHundred: false }
64
70
 
65
71
  /**
66
72
  * Builds words for a 0-999 segment.
67
- *
68
73
  * @param {number} n - Number 0-999
69
- * @returns {{ word: string, hasHundred: boolean }}
74
+ * @returns {{ word: string, hasHundred: boolean }} The segment words and whether it contains a hundreds place
70
75
  */
71
- function buildSegment (n) {
76
+ function buildSegment(n) {
72
77
  if (n === 0) {
73
78
  segmentResult.word = ''
74
79
  segmentResult.hasHundred = false
@@ -83,9 +88,11 @@ function buildSegment (n) {
83
88
  let tensOnes = ''
84
89
  if (tens === 1) {
85
90
  tensOnes = TEENS[ones]
86
- } else if (tens >= 2) {
91
+ }
92
+ else if (tens >= 2) {
87
93
  tensOnes = ones > 0 ? TENS[tens] + '-' + ONES[ones] : TENS[tens]
88
- } else if (ones > 0) {
94
+ }
95
+ else if (ones > 0) {
89
96
  tensOnes = ONES[ones]
90
97
  }
91
98
 
@@ -93,11 +100,13 @@ function buildSegment (n) {
93
100
  if (hundreds > 0) {
94
101
  if (tensOnes) {
95
102
  segmentResult.word = ONES[hundreds] + ' ' + HUNDRED + ' and ' + tensOnes
96
- } else {
103
+ }
104
+ else {
97
105
  segmentResult.word = ONES[hundreds] + ' ' + HUNDRED
98
106
  }
99
107
  segmentResult.hasHundred = true
100
- } else {
108
+ }
109
+ else {
101
110
  segmentResult.word = tensOnes
102
111
  segmentResult.hasHundred = false
103
112
  }
@@ -111,11 +120,10 @@ function buildSegment (n) {
111
120
 
112
121
  /**
113
122
  * Converts a non-negative integer to English words.
114
- *
115
123
  * @param {bigint} n - Non-negative integer to convert
116
124
  * @returns {string} English words
117
125
  */
118
- function integerToWords (n) {
126
+ function integerToWords(n) {
119
127
  if (n === 0n) return ZERO
120
128
 
121
129
  // Fast path: numbers < 1000
@@ -146,11 +154,10 @@ function integerToWords (n) {
146
154
  /**
147
155
  * Builds words for numbers >= 1,000,000.
148
156
  * Uses BigInt division for faster segment extraction.
149
- *
150
157
  * @param {bigint} n - Number >= 1,000,000
151
158
  * @returns {string} English words
152
159
  */
153
- function buildLargeNumberWords (n) {
160
+ function buildLargeNumberWords(n) {
154
161
  // Extract segments using BigInt division
155
162
  // Segments are stored least-significant first (index 0 = ones, 1 = thousands, etc.)
156
163
  const segments = []
@@ -193,7 +200,8 @@ function buildLargeNumberWords (n) {
193
200
  if (i > 0) {
194
201
  result += ' ' + SCALES[i - 1]
195
202
  prevWasScale = true
196
- } else {
203
+ }
204
+ else {
197
205
  prevWasScale = false
198
206
  }
199
207
  }
@@ -203,11 +211,10 @@ function buildLargeNumberWords (n) {
203
211
 
204
212
  /**
205
213
  * Converts decimal digits to English words.
206
- *
207
214
  * @param {string} decimalPart - Decimal digits (without the point)
208
215
  * @returns {string} English words for decimal part
209
216
  */
210
- function decimalPartToWords (decimalPart) {
217
+ function decimalPartToWords(decimalPart) {
211
218
  let result = ''
212
219
 
213
220
  // Handle leading zeros
@@ -233,19 +240,20 @@ function decimalPartToWords (decimalPart) {
233
240
  *
234
241
  * This is the main public API. It accepts any valid numeric input
235
242
  * (number, string, or bigint) and handles parsing internally.
236
- *
237
243
  * @param {number | string | bigint} value - The numeric value to convert
238
244
  * @returns {string} The number in English words
239
245
  * @throws {TypeError} If value is not a valid numeric type
240
246
  * @throws {Error} If value is not a valid number format
241
- *
242
247
  * @example
243
248
  * toCardinal(42) // 'forty-two'
244
249
  * toCardinal(-3.14) // 'minus three point fourteen'
245
250
  * toCardinal('1000000') // 'one million'
246
251
  */
247
- function toCardinal (value) {
252
+ function toCardinal(value) {
248
253
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
254
+ // Both the integer part and the decimal's significant digits are spelled via
255
+ // the scale builder, so both must clear the ceiling.
256
+ checkMax(integerPart, cardinalMax, decimalPart)
249
257
 
250
258
  let result = ''
251
259
 
@@ -269,11 +277,10 @@ function toCardinal (value) {
269
277
  /**
270
278
  * Builds ordinal words for a 0-999 segment (final segment only).
271
279
  * Returns ordinal form: "first", "twenty-third", "one hundred forty-fifth"
272
- *
273
280
  * @param {number} n - Number 0-999
274
281
  * @returns {string} Ordinal words for this segment
275
282
  */
276
- function buildOrdinalSegment (n) {
283
+ function buildOrdinalSegment(n) {
277
284
  const ones = n % 10
278
285
  const tens = Math.trunc(n / 10) % 10
279
286
  const hundreds = Math.trunc(n / 100)
@@ -283,15 +290,18 @@ function buildOrdinalSegment (n) {
283
290
  if (tens === 1) {
284
291
  // Teens: 10-19 → "tenth" through "nineteenth"
285
292
  tensOnesOrdinal = ORDINAL_TEENS[ones]
286
- } else if (tens >= 2) {
293
+ }
294
+ else if (tens >= 2) {
287
295
  if (ones > 0) {
288
296
  // Compound: "twenty-first", "thirty-second", etc.
289
297
  tensOnesOrdinal = TENS[tens] + '-' + ORDINAL_ONES[ones]
290
- } else {
298
+ }
299
+ else {
291
300
  // Round tens: "twentieth", "thirtieth", etc.
292
301
  tensOnesOrdinal = ORDINAL_TENS[tens]
293
302
  }
294
- } else if (ones > 0) {
303
+ }
304
+ else if (ones > 0) {
295
305
  // Single digit: "first", "second", etc.
296
306
  tensOnesOrdinal = ORDINAL_ONES[ones]
297
307
  }
@@ -301,7 +311,8 @@ function buildOrdinalSegment (n) {
301
311
  if (tensOnesOrdinal) {
302
312
  // "one hundred twenty-first"
303
313
  return ONES[hundreds] + ' ' + HUNDRED + ' ' + tensOnesOrdinal
304
- } else {
314
+ }
315
+ else {
305
316
  // "one hundredth", "two hundredth", etc.
306
317
  return ONES[hundreds] + ' hundredth'
307
318
  }
@@ -313,11 +324,10 @@ function buildOrdinalSegment (n) {
313
324
  /**
314
325
  * Converts a positive integer to ordinal words.
315
326
  * Generates ordinals directly without string manipulation.
316
- *
317
327
  * @param {bigint} n - Positive integer to convert
318
328
  * @returns {string} Ordinal English words
319
329
  */
320
- function integerToOrdinal (n) {
330
+ function integerToOrdinal(n) {
321
331
  // Fast path: numbers < 1000
322
332
  if (n < 1000n) {
323
333
  return buildOrdinalSegment(Number(n))
@@ -345,11 +355,10 @@ function integerToOrdinal (n) {
345
355
  /**
346
356
  * Builds ordinal words for numbers >= 1,000,000.
347
357
  * All segments except the final one are cardinal; final segment is ordinal.
348
- *
349
358
  * @param {bigint} n - Number >= 1,000,000
350
359
  * @returns {string} Ordinal English words
351
360
  */
352
- function buildLargeOrdinal (n) {
361
+ function buildLargeOrdinal(n) {
353
362
  // Extract segments (least-significant first)
354
363
  const segments = []
355
364
  let temp = n
@@ -383,11 +392,13 @@ function buildLargeOrdinal (n) {
383
392
  if (i === 0) {
384
393
  // Units position: use ordinal segment
385
394
  result += buildOrdinalSegment(segment)
386
- } else {
395
+ }
396
+ else {
387
397
  // Scale position with no remainder below: "one millionth"
388
398
  result += buildSegment(segment).word + ' ' + SCALES[i - 1] + 'th'
389
399
  }
390
- } else {
400
+ }
401
+ else {
391
402
  // Non-final segments are cardinal
392
403
  result += buildSegment(segment).word
393
404
  if (i > 0) {
@@ -401,12 +412,10 @@ function buildLargeOrdinal (n) {
401
412
 
402
413
  /**
403
414
  * Converts a numeric value to South African English ordinal words.
404
- *
405
415
  * @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
406
416
  * @returns {string} The number as ordinal words (e.g., "first", "forty-second")
407
417
  * @throws {TypeError} If value is not a valid numeric type
408
418
  * @throws {RangeError} If value is negative, zero, or has a decimal part
409
- *
410
419
  * @example
411
420
  * toOrdinal(1) // 'first'
412
421
  * toOrdinal(2) // 'second'
@@ -417,8 +426,9 @@ function buildLargeOrdinal (n) {
417
426
  * toOrdinal(101) // 'one hundred first'
418
427
  * toOrdinal(1000) // 'one thousandth'
419
428
  */
420
- function toOrdinal (value) {
429
+ function toOrdinal(value) {
421
430
  const integerPart = parseOrdinalValue(value)
431
+ checkMax(integerPart, ordinalMax)
422
432
  return integerToOrdinal(integerPart)
423
433
  }
424
434
 
@@ -426,16 +436,21 @@ function toOrdinal (value) {
426
436
  // CURRENCY: toCurrency(value, options?)
427
437
  // ============================================================================
428
438
 
439
+ /**
440
+ * @typedef {object} CurrencyOptions
441
+ * @property {boolean} [and] - Use "and" between rand and cents (e.g., "one rand and fifty cents")
442
+ */
443
+
444
+ /** @type {Required<CurrencyOptions>} */
445
+ export const currencyDefaults = { and: true }
446
+
429
447
  /**
430
448
  * Converts a numeric value to South African English currency words.
431
- *
432
449
  * @param {number | string | bigint} value - The currency amount to convert
433
- * @param {Object} [options] - Optional configuration
434
- * @param {boolean} [options.and=true] - Use "and" between rand and cents (e.g., "one rand and fifty cents")
450
+ * @param {CurrencyOptions} [options] - Optional configuration
435
451
  * @returns {string} The amount in South African English currency words
436
452
  * @throws {TypeError} If value is not a valid numeric type
437
453
  * @throws {Error} If value is not a valid number format
438
- *
439
454
  * @example
440
455
  * toCurrency(42.50) // 'forty-two rand and fifty cents'
441
456
  * toCurrency(1) // 'one rand'
@@ -443,10 +458,10 @@ function toOrdinal (value) {
443
458
  * toCurrency(0.01) // 'one cent'
444
459
  * toCurrency(42.50, { and: false }) // 'forty-two rand fifty cents'
445
460
  */
446
- function toCurrency (value, options) {
447
- options = validateOptions(options)
461
+ function toCurrency(value, options) {
448
462
  const { isNegative, dollars: rand, cents } = parseCurrencyValue(value)
449
- const { and: useAnd = true } = options
463
+ checkMax(rand, currencyMax)
464
+ const { and: useAnd } = resolveOptions(options, currencyDefaults)
450
465
 
451
466
  // Build result
452
467
  let result = ''
package/src/es-ES.d.ts CHANGED
@@ -1,65 +1,97 @@
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
+ /**
15
+ * @typedef {object} OrdinalOptions
16
+ * @property {('masculine'|'feminine')} [gender] - Grammatical gender
17
+ */
18
+ /** @type {Required<OrdinalOptions>} */
19
+ export const ordinalDefaults: Required<OrdinalOptions>;
20
+ /** @type {{ gender: ReadonlyArray<Required<OrdinalOptions>['gender']> }} */
21
+ export const ordinalValues: {
22
+ gender: ReadonlyArray<Required<OrdinalOptions>["gender"]>;
23
+ };
24
+ /**
25
+ * @typedef {object} CurrencyOptions
26
+ * @property {boolean} [and] - Use "con" between euros and cents
27
+ */
28
+ /** @type {Required<CurrencyOptions>} */
29
+ export const currencyDefaults: Required<CurrencyOptions>;
30
+ export type CardinalOptions = {
31
+ /**
32
+ * - Grammatical gender
33
+ */
34
+ gender?: "feminine" | "masculine" | undefined;
35
+ };
36
+ export type OrdinalOptions = {
37
+ /**
38
+ * - Grammatical gender
39
+ */
40
+ gender?: "feminine" | "masculine" | undefined;
41
+ };
42
+ export type CurrencyOptions = {
43
+ /**
44
+ * - Use "con" between euros and cents
45
+ */
46
+ and?: boolean | undefined;
47
+ };
1
48
  /**
2
49
  * Converts a numeric value to Spanish words.
3
50
  *
4
51
  * This is the main public API. It accepts any valid numeric input
5
52
  * (number, string, or bigint) and handles parsing internally.
6
- *
7
53
  * @param {number | string | bigint} value - The numeric value to convert
8
- * @param {Object} [options] - Optional configuration
9
- * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
54
+ * @param {CardinalOptions} [options] - Optional configuration
10
55
  * @returns {string} The number in Spanish words
11
56
  * @throws {TypeError} If value is not a valid numeric type
12
57
  * @throws {Error} If value is not a valid number format
13
- *
14
58
  * @example
15
59
  * toCardinal(21) // 'veintiuno'
16
60
  * toCardinal(21, {gender: 'feminine'}) // 'veintiuna'
17
61
  * toCardinal(1000000) // 'un millón'
18
62
  */
19
- export function toCardinal(value: number | string | bigint, options?: {
20
- gender?: "masculine" | "feminine" | undefined;
21
- }): string;
63
+ export function toCardinal(value: number | string | bigint, options?: CardinalOptions): string;
22
64
  /**
23
65
  * Converts a numeric value to Spanish ordinal words.
24
66
  *
25
67
  * Spanish ordinals agree in gender with the noun they modify.
26
68
  * Only positive integers are valid for ordinals.
27
- *
28
69
  * @param {number | string | bigint} value - The positive integer to convert
29
- * @param {Object} [options] - Optional configuration
30
- * @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
70
+ * @param {OrdinalOptions} [options] - Optional configuration
31
71
  * @returns {string} The number in Spanish ordinal words
32
72
  * @throws {TypeError} If value is not a valid numeric type
33
73
  * @throws {Error} If value is not a positive integer
34
- *
35
74
  * @example
36
75
  * toOrdinal(1) // 'primero'
37
76
  * toOrdinal(1, { gender: 'feminine' }) // 'primera'
38
77
  * toOrdinal(21) // 'vigésimo primero'
39
78
  * toOrdinal(100) // 'centésimo'
40
79
  */
41
- export function toOrdinal(value: number | string | bigint, options?: {
42
- gender?: "masculine" | "feminine" | undefined;
43
- }): string;
80
+ export function toOrdinal(value: number | string | bigint, options?: OrdinalOptions): string;
44
81
  /**
45
82
  * Converts a numeric value to Spanish Euro currency words.
46
83
  *
47
84
  * Spanish currency uses masculine gender for euros (el euro)
48
85
  * and masculine for céntimos (el céntimo).
49
- *
50
86
  * @param {number | string | bigint} value - The currency amount to convert
51
- * @param {Object} [options] - Optional configuration
52
- * @param {boolean} [options.and=true] - Use "con" between euros and cents
87
+ * @param {CurrencyOptions} [options] - Optional configuration
53
88
  * @returns {string} The amount in Spanish currency words
54
89
  * @throws {TypeError} If value is not a valid numeric type
55
90
  * @throws {Error} If value is not a valid number format
56
- *
57
91
  * @example
58
92
  * toCurrency(42.50) // 'cuarenta y dos euros con cincuenta céntimos'
59
93
  * toCurrency(1) // 'un euro'
60
94
  * toCurrency(0.99) // 'noventa y nueve céntimos'
61
95
  * toCurrency(42.50, { and: false }) // 'cuarenta y dos euros cincuenta céntimos'
62
96
  */
63
- export function toCurrency(value: number | string | bigint, options?: {
64
- and?: boolean | undefined;
65
- }): string;
97
+ export function toCurrency(value: number | string | bigint, options?: CurrencyOptions): string;