n2words 4.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 +53 -0
  2. package/README.md +14 -12
  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 -0
  112. package/dist/pt-BR.umd.js +2 -0
  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 +53 -36
  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 +42 -0
  257. package/src/pt-BR.js +574 -0
  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/yo-NG.d.ts CHANGED
@@ -1,18 +1,18 @@
1
+ export const cardinalMax: null;
2
+ export const ordinalMax: null;
3
+ export const currencyMax: null;
1
4
  /**
2
5
  * Converts a numeric value to Yoruba words.
3
- *
4
6
  * @param {number | string | bigint} value - The numeric value to convert
5
7
  * @returns {string} The number in Yoruba words
6
8
  */
7
9
  export function toCardinal(value: number | string | bigint): string;
8
10
  /**
9
11
  * Converts a numeric value to Yoruba 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) // 'àkọ́kọ́'
18
18
  * toOrdinal(2) // 'ìkejì'
@@ -23,12 +23,10 @@ export function toOrdinal(value: number | string | bigint): string;
23
23
  * Converts a numeric value to Yoruba currency words (Nigerian Naira).
24
24
  *
25
25
  * Uses náírà (naira) and kọ́bọ̀ (kobo).
26
- *
27
26
  * @param {number | string | bigint} value - The currency amount to convert
28
27
  * @returns {string} The amount in Yoruba currency words
29
28
  * @throws {TypeError} If value is not a valid numeric type
30
29
  * @throws {Error} If value is not a valid number format
31
- *
32
30
  * @example
33
31
  * toCurrency(42) // 'èjì lé lógójì náírà'
34
32
  * toCurrency(1.50) // 'ọ̀kan náírà àti àádọ́ta kọ́bọ̀'
package/src/yo-NG.js CHANGED
@@ -19,6 +19,12 @@
19
19
  import { parseCardinalValue } from './utils/parse-cardinal.js'
20
20
  import { parseCurrencyValue } from './utils/parse-currency.js'
21
21
  import { parseOrdinalValue } from './utils/parse-ordinal.js'
22
+ import { UNBOUNDED } from './utils/scale.js'
23
+
24
+ // No fixed scale ceiling — the vigesimal speller composes every magnitude.
25
+ export const cardinalMax = UNBOUNDED
26
+ export const ordinalMax = UNBOUNDED
27
+ export const currencyMax = UNBOUNDED
22
28
 
23
29
  // ============================================================================
24
30
  // Vocabulary (module-level constants)
@@ -36,7 +42,7 @@ const ONES = [
36
42
  'èje', // 7
37
43
  'ẹ̀jọ', // 8
38
44
  'ẹ̀sán', // 9
39
- 'ẹ̀wá' // 10
45
+ 'ẹ̀wá', // 10
40
46
  ]
41
47
 
42
48
  // Numbers 11-14 (additive: X + 10, using "lá")
@@ -45,7 +51,7 @@ const TEENS_ADD = [
45
51
  'ọ̀kànlá', // 11 = 1 + 10
46
52
  'èjìlá', // 12 = 2 + 10
47
53
  'ẹ̀talá', // 13 = 3 + 10
48
- 'ẹ̀rinlá' // 14 = 4 + 10
54
+ 'ẹ̀rinlá', // 14 = 4 + 10
49
55
  ]
50
56
 
51
57
  // Numbers 15-19 (subtractive: 20 - X, using "dín")
@@ -54,12 +60,13 @@ const TEENS_SUB = [
54
60
  'ẹ̀rìndínlógún', // 16 = 20 - 4
55
61
  'ẹ̀tadínlógún', // 17 = 20 - 3
56
62
  'èjìdínlógún', // 18 = 20 - 2
57
- 'ọ̀kàndínlógún' // 19 = 20 - 1
63
+ 'ọ̀kàndínlógún', // 19 = 20 - 1
58
64
  ]
59
65
 
60
66
  // Decades (base-20 structure)
61
67
  // Even decades are multiples of 20
62
68
  // Odd decades subtract 10 from next even decade
69
+ /** @type {Record<number, string>} */
63
70
  const DECADES = {
64
71
  20: 'ogún', // 20 = 20 × 1
65
72
  30: 'ọgbọ̀n', // 30 = 20 + 10 (special word)
@@ -69,10 +76,11 @@ const DECADES = {
69
76
  70: 'àádọ́rin', // 70 = 80 - 10
70
77
  80: 'ogórin', // 80 = 20 × 4
71
78
  90: 'àádọ́rùn', // 90 = 100 - 10
72
- 100: 'ọgọ́rùn' // 100 = 20 × 5
79
+ 100: 'ọgọ́rùn', // 100 = 20 × 5
73
80
  }
74
81
 
75
82
  // Prefixes for adding to decades (lé lógún, lé lọgbọ̀n, etc.)
83
+ /** @type {Record<number, string>} */
76
84
  const DECADE_ADD_SUFFIX = {
77
85
  20: 'lógún',
78
86
  30: 'lọgbọ̀n',
@@ -82,10 +90,11 @@ const DECADE_ADD_SUFFIX = {
82
90
  70: 'láàádọ́rin',
83
91
  80: 'lógórin',
84
92
  90: 'láàádọ́rùn',
85
- 100: 'lọ́gọ́rùn'
93
+ 100: 'lọ́gọ́rùn',
86
94
  }
87
95
 
88
96
  // Prefixes for subtracting from decades (dín lógójì, etc.)
97
+ /** @type {Record<number, string>} */
89
98
  const DECADE_SUB_SUFFIX = {
90
99
  20: 'dínlógún',
91
100
  30: 'dínlọgbọ̀n',
@@ -95,7 +104,7 @@ const DECADE_SUB_SUFFIX = {
95
104
  70: 'dínláàádọ́rin',
96
105
  80: 'dínlógórin',
97
106
  90: 'dínláàádọ́rùn',
98
- 100: 'dínlọ́gọ́rùn'
107
+ 100: 'dínlọ́gọ́rùn',
99
108
  }
100
109
 
101
110
  // Scale words
@@ -135,8 +144,10 @@ const KOBO = 'kọ́bọ̀'
135
144
 
136
145
  /**
137
146
  * Builds word for numbers 0-99
147
+ * @param {number} n - Integer in the range 0-99
148
+ * @returns {string} Yoruba words for the number
138
149
  */
139
- function buildUnder100 (n) {
150
+ function buildUnder100(n) {
140
151
  if (n === 0) return ''
141
152
  if (n <= 10) return ONES[n]
142
153
 
@@ -169,8 +180,10 @@ function buildUnder100 (n) {
169
180
 
170
181
  /**
171
182
  * Converts hundreds (100-999)
183
+ * @param {number} n - Integer in the range 0-999
184
+ * @returns {string} Yoruba words for the number
172
185
  */
173
- function convertHundreds (n) {
186
+ function convertHundreds(n) {
174
187
  if (n < 100) return buildUnder100(n)
175
188
 
176
189
  const hundreds = Math.trunc(n / 100)
@@ -189,11 +202,14 @@ function convertHundreds (n) {
189
202
  // Build hundreds
190
203
  if (hundreds === 1) {
191
204
  result = HUNDRED
192
- } else if (hundreds === 2) {
205
+ }
206
+ else if (hundreds === 2) {
193
207
  result = TWO_HUNDRED
194
- } else if (hundreds === 4) {
208
+ }
209
+ else if (hundreds === 4) {
195
210
  result = FOUR_HUNDRED
196
- } else {
211
+ }
212
+ else {
197
213
  // Other hundreds: X ọgọ́rùn
198
214
  result = ONES[hundreds] + ' ' + HUNDRED
199
215
  }
@@ -207,11 +223,10 @@ function convertHundreds (n) {
207
223
 
208
224
  /**
209
225
  * Converts a non-negative integer to Yoruba words.
210
- *
211
226
  * @param {bigint} n - Non-negative integer to convert
212
227
  * @returns {string} Yoruba words
213
228
  */
214
- function integerToWords (n) {
229
+ function integerToWords(n) {
215
230
  if (n === 0n) return ZERO
216
231
 
217
232
  // Fast path: numbers < 100
@@ -234,7 +249,8 @@ function integerToWords (n) {
234
249
  remaining = remaining % 1_000_000n
235
250
  if (millions === 1n) {
236
251
  parts.push(MILLION + ' kan')
237
- } else {
252
+ }
253
+ else {
238
254
  parts.push(MILLION + ' ' + integerToWords(millions))
239
255
  }
240
256
  }
@@ -246,13 +262,17 @@ function integerToWords (n) {
246
262
 
247
263
  if (thousands === 1n) {
248
264
  parts.push(THOUSAND + ' kan')
249
- } else if (thousands === 10n) {
265
+ }
266
+ else if (thousands === 10n) {
250
267
  parts.push(TEN_THOUSAND)
251
- } else if (thousands === 20n) {
268
+ }
269
+ else if (thousands === 20n) {
252
270
  parts.push(TWENTY_THOUSAND)
253
- } else if (thousands < 100n) {
271
+ }
272
+ else if (thousands < 100n) {
254
273
  parts.push(THOUSAND + ' ' + buildUnder100(Number(thousands)))
255
- } else {
274
+ }
275
+ else {
256
276
  parts.push(THOUSAND + ' ' + convertHundreds(Number(thousands)))
257
277
  }
258
278
  }
@@ -262,13 +282,16 @@ function integerToWords (n) {
262
282
  if (remaining < 100n) {
263
283
  if (parts.length > 0) {
264
284
  parts.push(AND + ' ' + buildUnder100(Number(remaining)))
265
- } else {
285
+ }
286
+ else {
266
287
  parts.push(buildUnder100(Number(remaining)))
267
288
  }
268
- } else {
289
+ }
290
+ else {
269
291
  if (parts.length > 0) {
270
292
  parts.push(AND + ' ' + convertHundreds(Number(remaining)))
271
- } else {
293
+ }
294
+ else {
272
295
  parts.push(convertHundreds(Number(remaining)))
273
296
  }
274
297
  }
@@ -279,11 +302,10 @@ function integerToWords (n) {
279
302
 
280
303
  /**
281
304
  * Converts decimal digits to Yoruba words.
282
- *
283
305
  * @param {string} decimalPart - Decimal digits
284
306
  * @returns {string} Yoruba words for decimal
285
307
  */
286
- function decimalPartToWords (decimalPart) {
308
+ function decimalPartToWords(decimalPart) {
287
309
  const parts = []
288
310
 
289
311
  for (const digit of decimalPart) {
@@ -296,11 +318,10 @@ function decimalPartToWords (decimalPart) {
296
318
 
297
319
  /**
298
320
  * Converts a numeric value to Yoruba words.
299
- *
300
321
  * @param {number | string | bigint} value - The numeric value to convert
301
322
  * @returns {string} The number in Yoruba words
302
323
  */
303
- function toCardinal (value) {
324
+ function toCardinal(value) {
304
325
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
305
326
 
306
327
  let result = ''
@@ -326,11 +347,10 @@ function toCardinal (value) {
326
347
  * Converts a non-negative integer to Yoruba ordinal words.
327
348
  *
328
349
  * Yoruba ordinals: àkọ́kọ́ (1st), ìkejì (2nd), ìkẹta (3rd), ìkẹrin (4th), etc.
329
- *
330
350
  * @param {bigint} n - Positive integer to convert
331
351
  * @returns {string} Yoruba ordinal words
332
352
  */
333
- function integerToOrdinal (n) {
353
+ function integerToOrdinal(n) {
334
354
  // Special forms
335
355
  if (n === 1n) return ORDINAL_FIRST
336
356
  if (n === 2n) return ORDINAL_SECOND
@@ -341,18 +361,16 @@ function integerToOrdinal (n) {
341
361
 
342
362
  /**
343
363
  * Converts a numeric value to Yoruba ordinal words.
344
- *
345
364
  * @param {number | string | bigint} value - The numeric value to convert (positive integer)
346
365
  * @returns {string} The number as ordinal words
347
366
  * @throws {TypeError} If value is not a valid numeric type
348
367
  * @throws {RangeError} If value is negative, zero, or has a decimal part
349
- *
350
368
  * @example
351
369
  * toOrdinal(1) // 'àkọ́kọ́'
352
370
  * toOrdinal(2) // 'ìkejì'
353
371
  * toOrdinal(3) // 'ìkẹẹ̀ta'
354
372
  */
355
- function toOrdinal (value) {
373
+ function toOrdinal(value) {
356
374
  const integerPart = parseOrdinalValue(value)
357
375
  return integerToOrdinal(integerPart)
358
376
  }
@@ -365,18 +383,16 @@ function toOrdinal (value) {
365
383
  * Converts a numeric value to Yoruba currency words (Nigerian Naira).
366
384
  *
367
385
  * Uses náírà (naira) and kọ́bọ̀ (kobo).
368
- *
369
386
  * @param {number | string | bigint} value - The currency amount to convert
370
387
  * @returns {string} The amount in Yoruba currency words
371
388
  * @throws {TypeError} If value is not a valid numeric type
372
389
  * @throws {Error} If value is not a valid number format
373
- *
374
390
  * @example
375
391
  * toCurrency(42) // 'èjì lé lógójì náírà'
376
392
  * toCurrency(1.50) // 'ọ̀kan náírà àti àádọ́ta kọ́bọ̀'
377
393
  * toCurrency(-5) // 'àìní àrùn náírà'
378
394
  */
379
- function toCurrency (value) {
395
+ function toCurrency(value) {
380
396
  const { isNegative, dollars: naira, cents: kobo } = parseCurrencyValue(value)
381
397
 
382
398
  let result = ''
@@ -1,48 +1,73 @@
1
+ export const cardinalMax: bigint;
2
+ export const ordinalMax: bigint;
3
+ export const currencyMax: bigint;
4
+ /**
5
+ * @typedef {object} CardinalOptions
6
+ * @property {boolean} [formal] - Use formal/financial numerals
7
+ */
8
+ /** @type {Required<CardinalOptions>} */
9
+ export const cardinalDefaults: Required<CardinalOptions>;
10
+ /**
11
+ * @typedef {object} OrdinalOptions
12
+ * @property {boolean} [formal] - Use formal/financial numerals
13
+ */
14
+ /** @type {Required<OrdinalOptions>} */
15
+ export const ordinalDefaults: Required<OrdinalOptions>;
16
+ /**
17
+ * @typedef {object} CurrencyOptions
18
+ * @property {boolean} [formal] - Use formal/financial numerals
19
+ */
20
+ /** @type {Required<CurrencyOptions>} */
21
+ export const currencyDefaults: Required<CurrencyOptions>;
22
+ export type CardinalOptions = {
23
+ /**
24
+ * - Use formal/financial numerals
25
+ */
26
+ formal?: boolean | undefined;
27
+ };
28
+ export type OrdinalOptions = {
29
+ /**
30
+ * - Use formal/financial numerals
31
+ */
32
+ formal?: boolean | undefined;
33
+ };
34
+ export type CurrencyOptions = {
35
+ /**
36
+ * - Use formal/financial numerals
37
+ */
38
+ formal?: boolean | undefined;
39
+ };
1
40
  /**
2
41
  * Converts a numeric value to Simplified Chinese words.
3
- *
4
42
  * @param {number | string | bigint} value - The numeric value to convert
5
- * @param {Object} [options] - Optional configuration
6
- * @param {boolean} [options.formal=true] - Use formal/financial numerals
43
+ * @param {CardinalOptions} [options] - Optional configuration
7
44
  * @returns {string} The number in Simplified Chinese words
8
45
  */
9
- export function toCardinal(value: number | string | bigint, options?: {
10
- formal?: boolean | undefined;
11
- }): string;
46
+ export function toCardinal(value: number | string | bigint, options?: CardinalOptions): string;
12
47
  /**
13
48
  * Converts a numeric value to Simplified Chinese ordinal words.
14
- *
15
49
  * @param {number | string | bigint} value - The numeric value to convert (positive integer)
16
- * @param {Object} [options] - Optional configuration
17
- * @param {boolean} [options.formal=true] - Use formal/financial numerals
50
+ * @param {OrdinalOptions} [options] - Optional configuration
18
51
  * @returns {string} The number as ordinal words
19
52
  * @throws {TypeError} If value is not a valid numeric type
20
53
  * @throws {RangeError} If value is negative, zero, or has a decimal part
21
- *
22
54
  * @example
23
55
  * toOrdinal(1) // '第壹'
24
56
  * toOrdinal(1, { formal: false }) // '第一'
25
57
  * toOrdinal(10) // '第壹拾'
26
58
  */
27
- export function toOrdinal(value: number | string | bigint, options?: {
28
- formal?: boolean | undefined;
29
- }): string;
59
+ export function toOrdinal(value: number | string | bigint, options?: OrdinalOptions): string;
30
60
  /**
31
61
  * Converts a numeric value to Simplified Chinese currency words (Yuan/Renminbi).
32
- *
33
62
  * @param {number | string | bigint} value - The currency amount to convert
34
- * @param {Object} [options] - Optional configuration
35
- * @param {boolean} [options.formal=true] - Use formal/financial numerals
63
+ * @param {CurrencyOptions} [options] - Optional configuration
36
64
  * @returns {string} The amount in Simplified Chinese currency words
37
65
  * @throws {TypeError} If value is not a valid numeric type
38
66
  * @throws {Error} If value is not a valid number format
39
- *
40
67
  * @example
41
68
  * toCurrency(42.50) // '肆拾贰圆伍角整'
42
69
  * toCurrency(1) // '壹圆整'
43
70
  * toCurrency(0.05) // '伍分'
44
71
  * toCurrency(42.50, { formal: false }) // '四十二元五角整'
45
72
  */
46
- export function toCurrency(value: number | string | bigint, options?: {
47
- formal?: boolean | undefined;
48
- }): string;
73
+ export function toCurrency(value: number | string | bigint, options?: CurrencyOptions): string;
package/src/zh-Hans-CN.js CHANGED
@@ -13,7 +13,9 @@
13
13
  import { parseCardinalValue } from './utils/parse-cardinal.js'
14
14
  import { parseCurrencyValue } from './utils/parse-currency.js'
15
15
  import { parseOrdinalValue } from './utils/parse-ordinal.js'
16
- import { validateOptions } from './utils/validate-options.js'
16
+ import { checkMax } from './utils/check-max.js'
17
+ import { bounded } from './utils/scale.js'
18
+ import { resolveOptions } from './utils/resolve-options.js'
17
19
 
18
20
  // ============================================================================
19
21
  // Vocabulary
@@ -35,6 +37,17 @@ const THOUSAND_FORMAL = '仟'
35
37
  const WAN_WORD = '万' // 10,000
36
38
  const YI_WORD = '亿' // 100,000,000
37
39
 
40
+ // Supported magnitude ceiling (checked at the public entry points). Numbers
41
+ // >= 亿 (10^8) split into n / 10^8, which is then spelled by convertBelowYi —
42
+ // itself only valid below 亿. So the ceiling is 亿² = 10^16. Ordinal (第 +
43
+ // cardinal) and currency build on the cardinal, so they share it. Decimals are
44
+ // spelled digit-by-digit, so they have no ceiling.
45
+ // No scale table — bounded by the convertBelowYi recursion (亿² = 10^16),
46
+ // declared via the escape hatch and verified by the gate. The three forms share it.
47
+ export const cardinalMax = bounded(16)
48
+ export const ordinalMax = bounded(16)
49
+ export const currencyMax = bounded(16)
50
+
38
51
  const ZERO = '零'
39
52
  const NEGATIVE = '负'
40
53
  const DECIMAL_SEP = '点'
@@ -69,8 +82,14 @@ const ZHENG = '整'
69
82
 
70
83
  /**
71
84
  * Convert number below 万 (10,000) to words using direct string concatenation.
85
+ * @param {bigint} value The integer (0-9999) to convert
86
+ * @param {string[]} ones Digit words for 0-9
87
+ * @param {string} ten Word for the tens place
88
+ * @param {string} hundred Word for the hundreds place
89
+ * @param {string} thousand Word for the thousands place
90
+ * @returns {string} The number in words
72
91
  */
73
- function convertBelowWan (value, ones, ten, hundred, thousand) {
92
+ function convertBelowWan(value, ones, ten, hundred, thousand) {
74
93
  if (value === 0n) return ''
75
94
 
76
95
  let result = ''
@@ -91,7 +110,8 @@ function convertBelowWan (value, ones, ten, hundred, thousand) {
91
110
  if (needsZero) result += ZERO
92
111
  result += ones[Number(hundredsVal)] + hundred
93
112
  needsZero = hundredsRemainder > 0n && hundredsRemainder < 10n
94
- } else if (thousandsVal > 0n && hundredsRemainder > 0n) {
113
+ }
114
+ else if (thousandsVal > 0n && hundredsRemainder > 0n) {
95
115
  needsZero = true
96
116
  }
97
117
 
@@ -102,7 +122,8 @@ function convertBelowWan (value, ones, ten, hundred, thousand) {
102
122
  if (needsZero) result += ZERO
103
123
  result += ones[Number(tensVal)] + ten
104
124
  needsZero = false
105
- } else if ((hundredsVal > 0n || thousandsVal > 0n) && onesVal > 0n) {
125
+ }
126
+ else if ((hundredsVal > 0n || thousandsVal > 0n) && onesVal > 0n) {
106
127
  needsZero = true
107
128
  }
108
129
 
@@ -117,8 +138,14 @@ function convertBelowWan (value, ones, ten, hundred, thousand) {
117
138
 
118
139
  /**
119
140
  * Convert number below 亿 (100 million) to words.
141
+ * @param {bigint} value The integer to convert
142
+ * @param {string[]} ones Digit words for 0-9
143
+ * @param {string} ten Word for the tens place
144
+ * @param {string} hundred Word for the hundreds place
145
+ * @param {string} thousand Word for the thousands place
146
+ * @returns {string} The number in words
120
147
  */
121
- function convertBelowYi (value, ones, ten, hundred, thousand) {
148
+ function convertBelowYi(value, ones, ten, hundred, thousand) {
122
149
  if (value === 0n) return ''
123
150
 
124
151
  if (value >= 10_000n) {
@@ -139,7 +166,13 @@ function convertBelowYi (value, ones, ten, hundred, thousand) {
139
166
  return convertBelowWan(value, ones, ten, hundred, thousand)
140
167
  }
141
168
 
142
- function integerToWords (n, formal = true) {
169
+ /**
170
+ * Convert an integer to Simplified Chinese words.
171
+ * @param {bigint} n The integer to convert
172
+ * @param {boolean} [formal] Use formal/financial numerals
173
+ * @returns {string} The integer in Simplified Chinese words
174
+ */
175
+ function integerToWords(n, formal = true) {
143
176
  if (n === 0n) return ZERO
144
177
 
145
178
  const ones = formal ? ONES_FORMAL : ONES_COMMON
@@ -167,8 +200,11 @@ function integerToWords (n, formal = true) {
167
200
 
168
201
  /**
169
202
  * Convert decimal digits to words using direct concatenation.
203
+ * @param {string} decimalString The decimal digits to convert
204
+ * @param {string[]} ones Digit words for 0-9
205
+ * @returns {string} The decimal digits in words
170
206
  */
171
- function decimalDigitsToWords (decimalString, ones) {
207
+ function decimalDigitsToWords(decimalString, ones) {
172
208
  let result = ''
173
209
  for (let i = 0; i < decimalString.length; i++) {
174
210
  result += ones[Number(decimalString[i])]
@@ -176,20 +212,26 @@ function decimalDigitsToWords (decimalString, ones) {
176
212
  return result
177
213
  }
178
214
 
215
+ /**
216
+ * @typedef {object} CardinalOptions
217
+ * @property {boolean} [formal] - Use formal/financial numerals
218
+ */
219
+
220
+ /** @type {Required<CardinalOptions>} */
221
+ export const cardinalDefaults = { formal: true }
222
+
179
223
  /**
180
224
  * Converts a numeric value to Simplified Chinese words.
181
- *
182
225
  * @param {number | string | bigint} value - The numeric value to convert
183
- * @param {Object} [options] - Optional configuration
184
- * @param {boolean} [options.formal=true] - Use formal/financial numerals
226
+ * @param {CardinalOptions} [options] - Optional configuration
185
227
  * @returns {string} The number in Simplified Chinese words
186
228
  */
187
- function toCardinal (value, options) {
188
- options = validateOptions(options)
229
+ function toCardinal(value, options) {
189
230
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
231
+ checkMax(integerPart, cardinalMax)
190
232
 
191
233
  // Apply option defaults
192
- const { formal = true } = options
234
+ const { formal } = resolveOptions(options, cardinalDefaults)
193
235
 
194
236
  let result = isNegative ? NEGATIVE : ''
195
237
 
@@ -211,34 +253,38 @@ function toCardinal (value, options) {
211
253
  * Converts a positive integer to Simplified Chinese ordinal words.
212
254
  *
213
255
  * Chinese ordinals: 第 prefix + cardinal number.
214
- *
215
256
  * @param {bigint} n - Positive integer to convert
216
257
  * @param {boolean} formal - Use formal numerals
217
258
  * @returns {string} Simplified Chinese ordinal words
218
259
  */
219
- function integerToOrdinal (n, formal) {
260
+ function integerToOrdinal(n, formal) {
220
261
  return ORDINAL_PREFIX + integerToWords(n, formal)
221
262
  }
222
263
 
264
+ /**
265
+ * @typedef {object} OrdinalOptions
266
+ * @property {boolean} [formal] - Use formal/financial numerals
267
+ */
268
+
269
+ /** @type {Required<OrdinalOptions>} */
270
+ export const ordinalDefaults = { formal: true }
271
+
223
272
  /**
224
273
  * Converts a numeric value to Simplified Chinese ordinal words.
225
- *
226
274
  * @param {number | string | bigint} value - The numeric value to convert (positive integer)
227
- * @param {Object} [options] - Optional configuration
228
- * @param {boolean} [options.formal=true] - Use formal/financial numerals
275
+ * @param {OrdinalOptions} [options] - Optional configuration
229
276
  * @returns {string} The number as ordinal words
230
277
  * @throws {TypeError} If value is not a valid numeric type
231
278
  * @throws {RangeError} If value is negative, zero, or has a decimal part
232
- *
233
279
  * @example
234
280
  * toOrdinal(1) // '第壹'
235
281
  * toOrdinal(1, { formal: false }) // '第一'
236
282
  * toOrdinal(10) // '第壹拾'
237
283
  */
238
- function toOrdinal (value, options) {
239
- options = validateOptions(options)
284
+ function toOrdinal(value, options) {
240
285
  const integerPart = parseOrdinalValue(value)
241
- const { formal = true } = options
286
+ checkMax(integerPart, ordinalMax)
287
+ const { formal } = resolveOptions(options, ordinalDefaults)
242
288
  return integerToOrdinal(integerPart, formal)
243
289
  }
244
290
 
@@ -246,26 +292,31 @@ function toOrdinal (value, options) {
246
292
  // CURRENCY: toCurrency(value, options?)
247
293
  // ============================================================================
248
294
 
295
+ /**
296
+ * @typedef {object} CurrencyOptions
297
+ * @property {boolean} [formal] - Use formal/financial numerals
298
+ */
299
+
300
+ /** @type {Required<CurrencyOptions>} */
301
+ export const currencyDefaults = { formal: true }
302
+
249
303
  /**
250
304
  * Converts a numeric value to Simplified Chinese currency words (Yuan/Renminbi).
251
- *
252
305
  * @param {number | string | bigint} value - The currency amount to convert
253
- * @param {Object} [options] - Optional configuration
254
- * @param {boolean} [options.formal=true] - Use formal/financial numerals
306
+ * @param {CurrencyOptions} [options] - Optional configuration
255
307
  * @returns {string} The amount in Simplified Chinese currency words
256
308
  * @throws {TypeError} If value is not a valid numeric type
257
309
  * @throws {Error} If value is not a valid number format
258
- *
259
310
  * @example
260
311
  * toCurrency(42.50) // '肆拾贰圆伍角整'
261
312
  * toCurrency(1) // '壹圆整'
262
313
  * toCurrency(0.05) // '伍分'
263
314
  * toCurrency(42.50, { formal: false }) // '四十二元五角整'
264
315
  */
265
- function toCurrency (value, options) {
266
- options = validateOptions(options)
316
+ function toCurrency(value, options) {
267
317
  const { isNegative, dollars: yuan, cents } = parseCurrencyValue(value)
268
- const { formal = true } = options
318
+ checkMax(yuan, currencyMax)
319
+ const { formal } = resolveOptions(options, currencyDefaults)
269
320
 
270
321
  const ones = formal ? ONES_FORMAL : ONES_COMMON
271
322
  const yuanWord = formal ? YUAN_FORMAL : YUAN_COMMON
@@ -286,7 +337,8 @@ function toCurrency (value, options) {
286
337
  // Jiao part (1/10)
287
338
  if (jiao > 0n) {
288
339
  result += ones[Number(jiao)] + JIAO
289
- } else if (yuan > 0n && fen > 0n) {
340
+ }
341
+ else if (yuan > 0n && fen > 0n) {
290
342
  // Need zero placeholder between yuan and fen
291
343
  result += ZERO
292
344
  }
@@ -294,7 +346,8 @@ function toCurrency (value, options) {
294
346
  // Fen part (1/100)
295
347
  if (fen > 0n) {
296
348
  result += ones[Number(fen)] + FEN
297
- } else if (yuan > 0n || jiao > 0n) {
349
+ }
350
+ else if (yuan > 0n || jiao > 0n) {
298
351
  // Add 整 (zheng) to indicate whole amount
299
352
  result += ZHENG
300
353
  }