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/fr-BE.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 { longScale } from './utils/scale.js'
18
+ import { resolveOptions } from './utils/resolve-options.js'
17
19
 
18
20
  // ============================================================================
19
21
  // Vocabulary
@@ -27,6 +29,15 @@ const TENS = ['', '', 'vingt', 'trente', 'quarante', 'cinquante', 'soixante', 's
27
29
  const SCALES = ['million', 'billion', 'trillion', 'quadrillion']
28
30
  const SCALES_ARD = ['milliard', 'billiard', 'trilliard', 'quadrilliard']
29
31
 
32
+ // Supported magnitude ceiling (checked at the public entry points). Long scale:
33
+ // each SCALES entry has a base and an "-ard" form spanning two segment groups,
34
+ // so with the units group that's 2 * SCALES.length + 2 groups of 3 digits.
35
+ // Cardinals must stay below 10^30; ordinals and currency build on the cardinal,
36
+ // so they share the same ceiling.
37
+ export const cardinalMax = longScale(SCALES.length)
38
+ export const ordinalMax = longScale(SCALES.length)
39
+ export const currencyMax = longScale(SCALES.length)
40
+
30
41
  const THOUSAND = 'mille'
31
42
  const HUNDRED = 'cent'
32
43
  const ZERO = 'zéro'
@@ -56,7 +67,12 @@ const CENTIMES = 'centimes'
56
67
  // Segment Building
57
68
  // ============================================================================
58
69
 
59
- function buildSegment (n) {
70
+ /**
71
+ * Builds the words for a 0-999 segment.
72
+ * @param {number} n - Segment value (0-999)
73
+ * @returns {{word: string, endsWithCents: boolean, endsWithVingts: boolean}} Segment words and flags
74
+ */
75
+ function buildSegment(n) {
60
76
  if (n === 0) return { word: '', endsWithCents: false, endsWithVingts: false }
61
77
 
62
78
  const tensOnes = n % 100
@@ -70,11 +86,13 @@ function buildSegment (n) {
70
86
  if (hundreds > 0) {
71
87
  if (hundreds === 1) {
72
88
  parts.push(HUNDRED)
73
- } else {
89
+ }
90
+ else {
74
91
  if (tensOnes === 0) {
75
92
  parts.push(ONES[hundreds] + ' ' + HUNDRED + 's')
76
93
  endsWithCents = true
77
- } else {
94
+ }
95
+ else {
78
96
  parts.push(ONES[hundreds] + ' ' + HUNDRED)
79
97
  }
80
98
  }
@@ -83,49 +101,63 @@ function buildSegment (n) {
83
101
  // Tens and ones - Belgian pattern
84
102
  if (tensOnes === 0) {
85
103
  // Just hundreds
86
- } else if (tensOnes < 10) {
104
+ }
105
+ else if (tensOnes < 10) {
87
106
  parts.push(ONES[tensOnes])
88
- } else if (tensOnes < 17) {
107
+ }
108
+ else if (tensOnes < 17) {
89
109
  parts.push(TEENS[tensOnes - 10])
90
- } else if (tensOnes < 20) {
110
+ }
111
+ else if (tensOnes < 20) {
91
112
  parts.push(TEENS[tensOnes - 10])
92
- } else if (tensOnes < 70) {
113
+ }
114
+ else if (tensOnes < 70) {
93
115
  // 20-69: standard pattern
94
116
  const t = Math.trunc(tensOnes / 10)
95
117
  const o = tensOnes % 10
96
118
  if (o === 0) {
97
119
  parts.push(TENS[t])
98
- } else if (o === 1) {
120
+ }
121
+ else if (o === 1) {
99
122
  parts.push(TENS[t] + ' et ' + ONES[1])
100
- } else {
123
+ }
124
+ else {
101
125
  parts.push(TENS[t] + '-' + ONES[o])
102
126
  }
103
- } else if (tensOnes < 80) {
127
+ }
128
+ else if (tensOnes < 80) {
104
129
  // 70-79: septante pattern (Belgian)
105
130
  const o = tensOnes % 10
106
131
  if (o === 0) {
107
132
  parts.push('septante')
108
- } else if (o === 1) {
133
+ }
134
+ else if (o === 1) {
109
135
  parts.push('septante et ' + ONES[1])
110
- } else {
136
+ }
137
+ else {
111
138
  parts.push('septante-' + ONES[o])
112
139
  }
113
- } else if (tensOnes === 80) {
140
+ }
141
+ else if (tensOnes === 80) {
114
142
  // 80: quatre-vingts (same as standard)
115
143
  parts.push('quatre-vingts')
116
144
  endsWithVingts = true
117
- } else if (tensOnes < 90) {
145
+ }
146
+ else if (tensOnes < 90) {
118
147
  // 81-89: quatre-vingt-X (same as standard)
119
148
  const remainder = tensOnes - 80
120
149
  parts.push('quatre-vingt-' + ONES[remainder])
121
- } else {
150
+ }
151
+ else {
122
152
  // 90-99: nonante pattern (Belgian)
123
153
  const o = tensOnes % 10
124
154
  if (o === 0) {
125
155
  parts.push('nonante')
126
- } else if (o === 1) {
156
+ }
157
+ else if (o === 1) {
127
158
  parts.push('nonante et ' + ONES[1])
128
- } else {
159
+ }
160
+ else {
129
161
  parts.push('nonante-' + ONES[o])
130
162
  }
131
163
  }
@@ -137,7 +169,13 @@ function buildSegment (n) {
137
169
  // Helper Functions
138
170
  // ============================================================================
139
171
 
140
- function getScaleWord (scaleIndex, segment) {
172
+ /**
173
+ * Returns the scale word (mille, million, milliard, ...) for a scale index.
174
+ * @param {number} scaleIndex - Scale group index
175
+ * @param {bigint} segment - Segment value used to decide pluralization
176
+ * @returns {string} The scale word
177
+ */
178
+ function getScaleWord(scaleIndex, segment) {
141
179
  if (scaleIndex === 1) return THOUSAND
142
180
 
143
181
  if (scaleIndex % 2 === 0) {
@@ -145,7 +183,8 @@ function getScaleWord (scaleIndex, segment) {
145
183
  const baseWord = SCALES[arrayIndex]
146
184
  if (!baseWord) return ''
147
185
  return segment > 1n ? baseWord + 's' : baseWord
148
- } else {
186
+ }
187
+ else {
149
188
  const arrayIndex = ((scaleIndex - 1) / 2) - 1
150
189
  const ardWord = SCALES_ARD[arrayIndex]
151
190
  if (!ardWord) return THOUSAND
@@ -157,7 +196,13 @@ function getScaleWord (scaleIndex, segment) {
157
196
  // Conversion Functions
158
197
  // ============================================================================
159
198
 
160
- function integerToWords (n, withHyphen = false) {
199
+ /**
200
+ * Converts a non-negative integer to Belgian French cardinal words.
201
+ * @param {bigint} n - Non-negative integer
202
+ * @param {boolean} [withHyphen] - Use hyphens between words
203
+ * @returns {string} The integer in Belgian French words
204
+ */
205
+ function integerToWords(n, withHyphen = false) {
161
206
  if (n === 0n) return ZERO
162
207
 
163
208
  if (n < 1000n) {
@@ -172,7 +217,8 @@ function integerToWords (n, withHyphen = false) {
172
217
  let result
173
218
  if (thousands === 1) {
174
219
  result = THOUSAND
175
- } else {
220
+ }
221
+ else {
176
222
  const { word: thousandsWord, endsWithCents, endsWithVingts } = buildSegment(thousands)
177
223
  let adjustedWord = thousandsWord
178
224
  if (endsWithCents || endsWithVingts) {
@@ -196,7 +242,13 @@ function integerToWords (n, withHyphen = false) {
196
242
  return buildLargeNumberWords(n, withHyphen)
197
243
  }
198
244
 
199
- function buildLargeNumberWords (n, withHyphen) {
245
+ /**
246
+ * Builds words for large integers (>= 1,000,000) using scale groups.
247
+ * @param {bigint} n - Integer value (>= 1,000,000)
248
+ * @param {boolean} withHyphen - Use hyphens between words
249
+ * @returns {string} The integer in Belgian French words
250
+ */
251
+ function buildLargeNumberWords(n, withHyphen) {
200
252
  const numStr = n.toString()
201
253
  const len = numStr.length
202
254
 
@@ -226,10 +278,12 @@ function buildLargeNumberWords (n, withHyphen) {
226
278
 
227
279
  if (scaleIndex === 0) {
228
280
  parts.push(segWords)
229
- } else if (scaleIndex === 1) {
281
+ }
282
+ else if (scaleIndex === 1) {
230
283
  if (segment === 1) {
231
284
  parts.push(THOUSAND)
232
- } else {
285
+ }
286
+ else {
233
287
  let adjustedWord = segWords
234
288
  if (endsWithCents || endsWithVingts) {
235
289
  adjustedWord = segWords.slice(0, -1)
@@ -237,7 +291,8 @@ function buildLargeNumberWords (n, withHyphen) {
237
291
  parts.push(adjustedWord)
238
292
  parts.push(scaleWord)
239
293
  }
240
- } else {
294
+ }
295
+ else {
241
296
  parts.push(segWords)
242
297
  parts.push(scaleWord)
243
298
  }
@@ -256,7 +311,13 @@ function buildLargeNumberWords (n, withHyphen) {
256
311
  return result
257
312
  }
258
313
 
259
- function decimalPartToWords (decimalPart, withHyphen) {
314
+ /**
315
+ * Converts the decimal part digits to words (leading zeros spoken as zéro).
316
+ * @param {string} decimalPart - Decimal digits as a string
317
+ * @param {boolean} withHyphen - Use hyphens between words
318
+ * @returns {string} The decimal part in Belgian French words
319
+ */
320
+ function decimalPartToWords(decimalPart, withHyphen) {
260
321
  let result = ''
261
322
  const sep = withHyphen ? '-' : ' '
262
323
 
@@ -276,20 +337,28 @@ function decimalPartToWords (decimalPart, withHyphen) {
276
337
  return result
277
338
  }
278
339
 
340
+ /**
341
+ * @typedef {object} CardinalOptions
342
+ * @property {boolean} [withHyphenSeparator] - Use hyphens between words
343
+ */
344
+
345
+ /** @type {Required<CardinalOptions>} */
346
+ export const cardinalDefaults = { withHyphenSeparator: false }
347
+
279
348
  /**
280
349
  * Converts a numeric value to Belgian French words.
281
- *
282
350
  * @param {number | string | bigint} value - The numeric value to convert
283
- * @param {Object} [options] - Optional configuration
284
- * @param {boolean} [options.withHyphenSeparator=false] - Use hyphens between words
351
+ * @param {CardinalOptions} [options] - Optional configuration
285
352
  * @returns {string} The number in Belgian French words
286
353
  */
287
- function toCardinal (value, options) {
288
- options = validateOptions(options)
354
+ function toCardinal(value, options) {
289
355
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
356
+ // Both the integer part and the decimal's significant digits are spelled via
357
+ // the scale builder, so both must clear the ceiling.
358
+ checkMax(integerPart, cardinalMax, decimalPart)
290
359
 
291
360
  // Apply option defaults
292
- const { withHyphenSeparator = false } = options
361
+ const { withHyphenSeparator } = resolveOptions(options, cardinalDefaults)
293
362
 
294
363
  let result = ''
295
364
  const sep = withHyphenSeparator ? '-' : ' '
@@ -318,11 +387,10 @@ function toCardinal (value, options) {
318
387
  * - Drop final -e before adding -ième (quatre → quatrième)
319
388
  * - cinq → cinquième (add -u- before -ième)
320
389
  * - neuf → neuvième (f → v before -ième)
321
- *
322
390
  * @param {string} cardinalWord - Cardinal word to convert
323
391
  * @returns {string} Ordinal form
324
392
  */
325
- function cardinalToOrdinal (cardinalWord) {
393
+ function cardinalToOrdinal(cardinalWord) {
326
394
  // Handle special endings
327
395
  if (cardinalWord.endsWith('cinq')) {
328
396
  // cinq → cinquième (add 'u')
@@ -336,16 +404,16 @@ function cardinalToOrdinal (cardinalWord) {
336
404
 
337
405
  // Drop plural -s from cents/vingts/millions/etc. (quatre-vingts → quatre-vingtième)
338
406
  // Note: "trois", "six" also end in s but that's not a plural
339
- if (cardinalWord.endsWith('cents') ||
340
- cardinalWord.endsWith('vingts') ||
341
- cardinalWord.endsWith('millions') ||
342
- cardinalWord.endsWith('milliards') ||
343
- cardinalWord.endsWith('billions') ||
344
- cardinalWord.endsWith('billiards') ||
345
- cardinalWord.endsWith('trillions') ||
346
- cardinalWord.endsWith('trilliards') ||
347
- cardinalWord.endsWith('quadrillions') ||
348
- cardinalWord.endsWith('quadrilliards')) {
407
+ if (cardinalWord.endsWith('cents')
408
+ || cardinalWord.endsWith('vingts')
409
+ || cardinalWord.endsWith('millions')
410
+ || cardinalWord.endsWith('milliards')
411
+ || cardinalWord.endsWith('billions')
412
+ || cardinalWord.endsWith('billiards')
413
+ || cardinalWord.endsWith('trillions')
414
+ || cardinalWord.endsWith('trilliards')
415
+ || cardinalWord.endsWith('quadrillions')
416
+ || cardinalWord.endsWith('quadrilliards')) {
349
417
  return cardinalWord.slice(0, -1) + ORDINAL_SUFFIX
350
418
  }
351
419
 
@@ -360,11 +428,10 @@ function cardinalToOrdinal (cardinalWord) {
360
428
 
361
429
  /**
362
430
  * Converts a positive integer to Belgian French ordinal words.
363
- *
364
431
  * @param {bigint} n - Positive integer
365
432
  * @returns {string} Belgian French ordinal words
366
433
  */
367
- function integerToOrdinal (n) {
434
+ function integerToOrdinal(n) {
368
435
  // Special case: 1 → premier
369
436
  if (n === 1n) {
370
437
  return PREMIER
@@ -380,20 +447,19 @@ function integerToOrdinal (n) {
380
447
  *
381
448
  * Belgian French ordinals: premier (1st), then cardinal + ième.
382
449
  * Special rules: quatre→quatrième, cinq→cinquième, neuf→neuvième.
383
- *
384
450
  * @param {number | string | bigint} value - The numeric value to convert (positive integer)
385
451
  * @returns {string} The number as ordinal words
386
452
  * @throws {TypeError} If value is not a valid numeric type
387
453
  * @throws {RangeError} If value is negative, zero, or has a decimal part
388
- *
389
454
  * @example
390
455
  * toOrdinal(1) // 'premier'
391
456
  * toOrdinal(2) // 'deuxième'
392
457
  * toOrdinal(70) // 'septantième'
393
458
  * toOrdinal(90) // 'nonantième'
394
459
  */
395
- function toOrdinal (value) {
460
+ function toOrdinal(value) {
396
461
  const integerPart = parseOrdinalValue(value)
462
+ checkMax(integerPart, ordinalMax)
397
463
  return integerToOrdinal(integerPart)
398
464
  }
399
465
 
@@ -401,16 +467,21 @@ function toOrdinal (value) {
401
467
  // CURRENCY: toCurrency(value, options?)
402
468
  // ============================================================================
403
469
 
470
+ /**
471
+ * @typedef {object} CurrencyOptions
472
+ * @property {boolean} [and] - Use "et" between euros and centimes
473
+ */
474
+
475
+ /** @type {Required<CurrencyOptions>} */
476
+ export const currencyDefaults = { and: true }
477
+
404
478
  /**
405
479
  * Converts a numeric value to Belgian French currency words (Euro).
406
- *
407
480
  * @param {number | string | bigint} value - The currency amount to convert
408
- * @param {Object} [options] - Optional configuration
409
- * @param {boolean} [options.and=true] - Use "et" between euros and centimes
481
+ * @param {CurrencyOptions} [options] - Optional configuration
410
482
  * @returns {string} The amount in Belgian French currency words
411
483
  * @throws {TypeError} If value is not a valid numeric type
412
484
  * @throws {Error} If value is not a valid number format
413
- *
414
485
  * @example
415
486
  * toCurrency(42.50) // 'quarante-deux euros et cinquante centimes'
416
487
  * toCurrency(1) // 'un euro'
@@ -418,10 +489,10 @@ function toOrdinal (value) {
418
489
  * toCurrency(0.01) // 'un centime'
419
490
  * toCurrency(42.50, { and: false }) // 'quarante-deux euros cinquante centimes'
420
491
  */
421
- function toCurrency (value, options) {
422
- options = validateOptions(options)
492
+ function toCurrency(value, options) {
423
493
  const { isNegative, dollars: euros, cents: centimes } = parseCurrencyValue(value)
424
- const { and: useAnd = true } = options
494
+ checkMax(euros, currencyMax)
495
+ const { and: useAnd } = resolveOptions(options, currencyDefaults)
425
496
 
426
497
  // Build result
427
498
  let result = ''
package/src/fr-FR.d.ts CHANGED
@@ -1,35 +1,55 @@
1
+ export const cardinalMax: bigint;
2
+ export const ordinalMax: bigint;
3
+ export const currencyMax: bigint;
4
+ /**
5
+ * @typedef {object} CardinalOptions
6
+ * @property {boolean} [withHyphenSeparator] - Use hyphens between all words
7
+ */
8
+ /** @type {Required<CardinalOptions>} */
9
+ export const cardinalDefaults: Required<CardinalOptions>;
10
+ /**
11
+ * @typedef {object} CurrencyOptions
12
+ * @property {boolean} [and] - Use "et" between euros and centimes
13
+ */
14
+ /** @type {Required<CurrencyOptions>} */
15
+ export const currencyDefaults: Required<CurrencyOptions>;
16
+ export type CardinalOptions = {
17
+ /**
18
+ * - Use hyphens between all words
19
+ */
20
+ withHyphenSeparator?: boolean | undefined;
21
+ };
22
+ export type CurrencyOptions = {
23
+ /**
24
+ * - Use "et" between euros and centimes
25
+ */
26
+ and?: boolean | undefined;
27
+ };
1
28
  /**
2
29
  * Converts a numeric value to French words.
3
30
  *
4
31
  * This is the main public API. It accepts any valid numeric input
5
32
  * (number, string, or bigint) and handles parsing internally.
6
- *
7
33
  * @param {number | string | bigint} value - The numeric value to convert
8
- * @param {Object} [options] - Optional configuration
9
- * @param {boolean} [options.withHyphenSeparator=false] - Use hyphens between all words
34
+ * @param {CardinalOptions} [options] - Optional configuration
10
35
  * @returns {string} The number in French words
11
36
  * @throws {TypeError} If value is not a valid numeric type
12
37
  * @throws {Error} If value is not a valid number format
13
- *
14
38
  * @example
15
39
  * toCardinal(21) // 'vingt et un'
16
40
  * toCardinal(80) // 'quatre-vingts'
17
41
  * toCardinal(1000000) // 'un million'
18
42
  */
19
- export function toCardinal(value: number | string | bigint, options?: {
20
- withHyphenSeparator?: boolean | undefined;
21
- }): string;
43
+ export function toCardinal(value: number | string | bigint, options?: CardinalOptions): string;
22
44
  /**
23
45
  * Converts a numeric value to French ordinal words.
24
46
  *
25
47
  * French ordinals: premier (1st), then cardinal + ième.
26
48
  * Special rules: quatre→quatrième, cinq→cinquième, neuf→neuvième.
27
- *
28
49
  * @param {number | string | bigint} value - The numeric value to convert (positive integer)
29
50
  * @returns {string} The number as ordinal words
30
51
  * @throws {TypeError} If value is not a valid numeric type
31
52
  * @throws {RangeError} If value is negative, zero, or has a decimal part
32
- *
33
53
  * @example
34
54
  * toOrdinal(1) // 'premier'
35
55
  * toOrdinal(2) // 'deuxième'
@@ -43,14 +63,11 @@ export function toCardinal(value: number | string | bigint, options?: {
43
63
  export function toOrdinal(value: number | string | bigint): string;
44
64
  /**
45
65
  * Converts a numeric value to French currency words (Euro).
46
- *
47
66
  * @param {number | string | bigint} value - The currency amount to convert
48
- * @param {Object} [options] - Optional configuration
49
- * @param {boolean} [options.and=true] - Use "et" between euros and centimes
67
+ * @param {CurrencyOptions} [options] - Optional configuration
50
68
  * @returns {string} The amount in French currency words
51
69
  * @throws {TypeError} If value is not a valid numeric type
52
70
  * @throws {Error} If value is not a valid number format
53
- *
54
71
  * @example
55
72
  * toCurrency(42.50) // 'quarante-deux euros et cinquante centimes'
56
73
  * toCurrency(1) // 'un euro'
@@ -58,6 +75,4 @@ export function toOrdinal(value: number | string | bigint): string;
58
75
  * toCurrency(0.01) // 'un centime'
59
76
  * toCurrency(42.50, { and: false }) // 'quarante-deux euros cinquante centimes'
60
77
  */
61
- export function toCurrency(value: number | string | bigint, options?: {
62
- and?: boolean | undefined;
63
- }): string;
78
+ export function toCurrency(value: number | string | bigint, options?: CurrencyOptions): string;