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/en-CA.d.ts CHANGED
@@ -1,17 +1,45 @@
1
+ export const cardinalMax: bigint;
2
+ export const ordinalMax: bigint;
3
+ export const currencyMax: bigint;
4
+ /**
5
+ * @typedef {object} CardinalOptions
6
+ * @property {boolean} [hundredPairing] - Use hundred-pairing for 1100-9999 (e.g., "fifteen hundred" instead of "one thousand five hundred")
7
+ * @property {boolean} [and] - Use "and" after hundreds and before final small numbers (default: true, Canadian/British style)
8
+ */
9
+ /** @type {Required<CardinalOptions>} */
10
+ export const cardinalDefaults: Required<CardinalOptions>;
11
+ /**
12
+ * @typedef {object} CurrencyOptions
13
+ * @property {boolean} [and] - Use "and" between dollars and cents (e.g., "one dollar and fifty cents")
14
+ */
15
+ /** @type {Required<CurrencyOptions>} */
16
+ export const currencyDefaults: Required<CurrencyOptions>;
17
+ export type CardinalOptions = {
18
+ /**
19
+ * - Use hundred-pairing for 1100-9999 (e.g., "fifteen hundred" instead of "one thousand five hundred")
20
+ */
21
+ hundredPairing?: boolean | undefined;
22
+ /**
23
+ * - Use "and" after hundreds and before final small numbers (default: true, Canadian/British style)
24
+ */
25
+ and?: boolean | undefined;
26
+ };
27
+ export type CurrencyOptions = {
28
+ /**
29
+ * - Use "and" between dollars and cents (e.g., "one dollar and fifty cents")
30
+ */
31
+ and?: boolean | undefined;
32
+ };
1
33
  /**
2
34
  * Converts a numeric value to Canadian English words.
3
35
  *
4
36
  * This is the main public API. It accepts any valid numeric input
5
37
  * (number, string, or bigint) and handles parsing internally.
6
- *
7
38
  * @param {number | string | bigint} value - The numeric value to convert
8
- * @param {Object} [options] - Optional configuration
9
- * @param {boolean} [options.hundredPairing=false] - Use hundred-pairing for 1100-9999 (e.g., "fifteen hundred" instead of "one thousand five hundred")
10
- * @param {boolean} [options.and=true] - Use "and" after hundreds and before final small numbers (default: true, Canadian/British style)
39
+ * @param {CardinalOptions} [options] - Optional configuration
11
40
  * @returns {string} The number in Canadian English words
12
41
  * @throws {TypeError} If value is not a valid numeric type
13
42
  * @throws {Error} If value is not a valid number format
14
- *
15
43
  * @example
16
44
  * toCardinal(42) // 'forty-two'
17
45
  * toCardinal(101) // 'one hundred and one'
@@ -19,18 +47,13 @@
19
47
  * toCardinal(1500) // 'one thousand five hundred'
20
48
  * toCardinal(1500, { hundredPairing: true }) // 'fifteen hundred'
21
49
  */
22
- export function toCardinal(value: number | string | bigint, options?: {
23
- hundredPairing?: boolean | undefined;
24
- and?: boolean | undefined;
25
- }): string;
50
+ export function toCardinal(value: number | string | bigint, options?: CardinalOptions): string;
26
51
  /**
27
52
  * Converts a numeric value to Canadian English ordinal words.
28
- *
29
53
  * @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
30
54
  * @returns {string} The number as ordinal words (e.g., "first", "forty-second")
31
55
  * @throws {TypeError} If value is not a valid numeric type
32
56
  * @throws {RangeError} If value is negative, zero, or has a decimal part
33
- *
34
57
  * @example
35
58
  * toOrdinal(1) // 'first'
36
59
  * toOrdinal(2) // 'second'
@@ -44,20 +67,15 @@ export function toCardinal(value: number | string | bigint, options?: {
44
67
  export function toOrdinal(value: number | string | bigint): string;
45
68
  /**
46
69
  * Converts a numeric value to Canadian English currency words.
47
- *
48
70
  * @param {number | string | bigint} value - The currency amount to convert
49
- * @param {Object} [options] - Optional configuration
50
- * @param {boolean} [options.and=true] - Use "and" between dollars and cents (e.g., "one dollar and fifty cents")
71
+ * @param {CurrencyOptions} [options] - Optional configuration
51
72
  * @returns {string} The amount in Canadian English currency words
52
73
  * @throws {TypeError} If value is not a valid numeric type
53
74
  * @throws {Error} If value is not a valid number format
54
- *
55
75
  * @example
56
76
  * toCurrency(42.50) // 'forty-two dollars and fifty cents'
57
77
  * toCurrency(1) // 'one dollar'
58
78
  * toCurrency(0.99) // 'ninety-nine cents'
59
79
  * toCurrency(42.50, { and: false }) // 'forty-two dollars fifty cents'
60
80
  */
61
- export function toCurrency(value: number | string | bigint, options?: {
62
- and?: boolean | undefined;
63
- }): string;
81
+ export function toCurrency(value: number | string | bigint, options?: CurrencyOptions): string;
package/src/en-CA.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
@@ -37,8 +39,12 @@ 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
  ]
44
+
45
+ export const cardinalMax = western(SCALES.length)
46
+ export const ordinalMax = western(SCALES.length)
47
+ export const currencyMax = western(SCALES.length)
42
48
  const HUNDRED = 'hundred'
43
49
  const ZERO = 'zero'
44
50
  const NEGATIVE = 'minus'
@@ -64,12 +70,11 @@ 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
74
  * @param {boolean} useAnd - Whether to use "and" after hundreds
70
- * @returns {{ word: string, hasHundred: boolean }}
75
+ * @returns {{ word: string, hasHundred: boolean }} The segment words and whether a hundreds place is present
71
76
  */
72
- function buildSegment (n, useAnd) {
77
+ function buildSegment(n, useAnd) {
73
78
  if (n === 0) {
74
79
  segmentResult.word = ''
75
80
  segmentResult.hasHundred = false
@@ -84,9 +89,11 @@ function buildSegment (n, useAnd) {
84
89
  let tensOnes = ''
85
90
  if (tens === 1) {
86
91
  tensOnes = TEENS[ones]
87
- } else if (tens >= 2) {
92
+ }
93
+ else if (tens >= 2) {
88
94
  tensOnes = ones > 0 ? TENS[tens] + '-' + ONES[ones] : TENS[tens]
89
- } else if (ones > 0) {
95
+ }
96
+ else if (ones > 0) {
90
97
  tensOnes = ONES[ones]
91
98
  }
92
99
 
@@ -95,11 +102,13 @@ function buildSegment (n, useAnd) {
95
102
  if (tensOnes) {
96
103
  const connector = useAnd ? ' and ' : ' '
97
104
  segmentResult.word = ONES[hundreds] + ' ' + HUNDRED + connector + tensOnes
98
- } else {
105
+ }
106
+ else {
99
107
  segmentResult.word = ONES[hundreds] + ' ' + HUNDRED
100
108
  }
101
109
  segmentResult.hasHundred = true
102
- } else {
110
+ }
111
+ else {
103
112
  segmentResult.word = tensOnes
104
113
  segmentResult.hasHundred = false
105
114
  }
@@ -113,13 +122,12 @@ function buildSegment (n, useAnd) {
113
122
 
114
123
  /**
115
124
  * Converts a non-negative integer to English words.
116
- *
117
125
  * @param {bigint} n - Non-negative integer to convert
118
126
  * @param {boolean} hundredPairing - Use hundred-pairing for 1100-9999
119
127
  * @param {boolean} useAnd - Use "and" after hundreds and before final segment
120
128
  * @returns {string} English words
121
129
  */
122
- function integerToWords (n, hundredPairing, useAnd) {
130
+ function integerToWords(n, hundredPairing, useAnd) {
123
131
  if (n === 0n) return ZERO
124
132
 
125
133
  // Fast path: numbers < 1000
@@ -175,12 +183,11 @@ function integerToWords (n, hundredPairing, useAnd) {
175
183
  /**
176
184
  * Builds words for numbers >= 1,000,000.
177
185
  * Uses BigInt division for faster segment extraction.
178
- *
179
186
  * @param {bigint} n - Number >= 1,000,000
180
187
  * @param {boolean} useAnd - Use "and" after hundreds and before final segment
181
188
  * @returns {string} English words
182
189
  */
183
- function buildLargeNumberWords (n, useAnd) {
190
+ function buildLargeNumberWords(n, useAnd) {
184
191
  // Extract segments using BigInt division
185
192
  // Segments are stored least-significant first (index 0 = ones, 1 = thousands, etc.)
186
193
  const segments = []
@@ -225,7 +232,8 @@ function buildLargeNumberWords (n, useAnd) {
225
232
  if (i > 0) {
226
233
  result += ' ' + SCALES[i - 1]
227
234
  prevWasScale = true
228
- } else {
235
+ }
236
+ else {
229
237
  prevWasScale = false
230
238
  }
231
239
  }
@@ -235,12 +243,11 @@ function buildLargeNumberWords (n, useAnd) {
235
243
 
236
244
  /**
237
245
  * Converts decimal digits to English words.
238
- *
239
246
  * @param {string} decimalPart - Decimal digits (without the point)
240
247
  * @param {boolean} useAnd - Use "and" in number conversion
241
248
  * @returns {string} English words for decimal part
242
249
  */
243
- function decimalPartToWords (decimalPart, useAnd) {
250
+ function decimalPartToWords(decimalPart, useAnd) {
244
251
  let result = ''
245
252
 
246
253
  // Handle leading zeros
@@ -261,20 +268,25 @@ function decimalPartToWords (decimalPart, useAnd) {
261
268
  return result
262
269
  }
263
270
 
271
+ /**
272
+ * @typedef {object} CardinalOptions
273
+ * @property {boolean} [hundredPairing] - Use hundred-pairing for 1100-9999 (e.g., "fifteen hundred" instead of "one thousand five hundred")
274
+ * @property {boolean} [and] - Use "and" after hundreds and before final small numbers (default: true, Canadian/British style)
275
+ */
276
+
277
+ /** @type {Required<CardinalOptions>} */
278
+ export const cardinalDefaults = { hundredPairing: false, and: true }
279
+
264
280
  /**
265
281
  * Converts a numeric value to Canadian English words.
266
282
  *
267
283
  * This is the main public API. It accepts any valid numeric input
268
284
  * (number, string, or bigint) and handles parsing internally.
269
- *
270
285
  * @param {number | string | bigint} value - The numeric value to convert
271
- * @param {Object} [options] - Optional configuration
272
- * @param {boolean} [options.hundredPairing=false] - Use hundred-pairing for 1100-9999 (e.g., "fifteen hundred" instead of "one thousand five hundred")
273
- * @param {boolean} [options.and=true] - Use "and" after hundreds and before final small numbers (default: true, Canadian/British style)
286
+ * @param {CardinalOptions} [options] - Optional configuration
274
287
  * @returns {string} The number in Canadian English words
275
288
  * @throws {TypeError} If value is not a valid numeric type
276
289
  * @throws {Error} If value is not a valid number format
277
- *
278
290
  * @example
279
291
  * toCardinal(42) // 'forty-two'
280
292
  * toCardinal(101) // 'one hundred and one'
@@ -282,12 +294,14 @@ function decimalPartToWords (decimalPart, useAnd) {
282
294
  * toCardinal(1500) // 'one thousand five hundred'
283
295
  * toCardinal(1500, { hundredPairing: true }) // 'fifteen hundred'
284
296
  */
285
- function toCardinal (value, options) {
286
- options = validateOptions(options)
297
+ function toCardinal(value, options) {
287
298
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
299
+ // Both the integer part and the decimal's significant digits are spelled via
300
+ // the scale builder, so both must clear the ceiling.
301
+ checkMax(integerPart, cardinalMax, decimalPart)
288
302
 
289
303
  // Extract options with defaults (Canadian English uses "and" like British English)
290
- const { hundredPairing = false, and: useAnd = true } = options
304
+ const { hundredPairing, and: useAnd } = resolveOptions(options, cardinalDefaults)
291
305
 
292
306
  let result = ''
293
307
 
@@ -311,11 +325,10 @@ function toCardinal (value, options) {
311
325
  /**
312
326
  * Builds ordinal words for a 0-999 segment (final segment only).
313
327
  * Returns ordinal form: "first", "twenty-third", "one hundred forty-fifth"
314
- *
315
328
  * @param {number} n - Number 0-999
316
329
  * @returns {string} Ordinal words for this segment
317
330
  */
318
- function buildOrdinalSegment (n) {
331
+ function buildOrdinalSegment(n) {
319
332
  const ones = n % 10
320
333
  const tens = Math.trunc(n / 10) % 10
321
334
  const hundreds = Math.trunc(n / 100)
@@ -325,15 +338,18 @@ function buildOrdinalSegment (n) {
325
338
  if (tens === 1) {
326
339
  // Teens: 10-19 → "tenth" through "nineteenth"
327
340
  tensOnesOrdinal = ORDINAL_TEENS[ones]
328
- } else if (tens >= 2) {
341
+ }
342
+ else if (tens >= 2) {
329
343
  if (ones > 0) {
330
344
  // Compound: "twenty-first", "thirty-second", etc.
331
345
  tensOnesOrdinal = TENS[tens] + '-' + ORDINAL_ONES[ones]
332
- } else {
346
+ }
347
+ else {
333
348
  // Round tens: "twentieth", "thirtieth", etc.
334
349
  tensOnesOrdinal = ORDINAL_TENS[tens]
335
350
  }
336
- } else if (ones > 0) {
351
+ }
352
+ else if (ones > 0) {
337
353
  // Single digit: "first", "second", etc.
338
354
  tensOnesOrdinal = ORDINAL_ONES[ones]
339
355
  }
@@ -343,7 +359,8 @@ function buildOrdinalSegment (n) {
343
359
  if (tensOnesOrdinal) {
344
360
  // "one hundred twenty-first"
345
361
  return ONES[hundreds] + ' ' + HUNDRED + ' ' + tensOnesOrdinal
346
- } else {
362
+ }
363
+ else {
347
364
  // "one hundredth", "two hundredth", etc.
348
365
  return ONES[hundreds] + ' hundredth'
349
366
  }
@@ -355,11 +372,10 @@ function buildOrdinalSegment (n) {
355
372
  /**
356
373
  * Converts a positive integer to ordinal words.
357
374
  * Generates ordinals directly without string manipulation.
358
- *
359
375
  * @param {bigint} n - Positive integer to convert
360
376
  * @returns {string} Ordinal English words
361
377
  */
362
- function integerToOrdinal (n) {
378
+ function integerToOrdinal(n) {
363
379
  // Fast path: numbers < 1000
364
380
  if (n < 1000n) {
365
381
  return buildOrdinalSegment(Number(n))
@@ -387,11 +403,10 @@ function integerToOrdinal (n) {
387
403
  /**
388
404
  * Builds ordinal words for numbers >= 1,000,000.
389
405
  * All segments except the final one are cardinal; final segment is ordinal.
390
- *
391
406
  * @param {bigint} n - Number >= 1,000,000
392
407
  * @returns {string} Ordinal English words
393
408
  */
394
- function buildLargeOrdinal (n) {
409
+ function buildLargeOrdinal(n) {
395
410
  // Extract segments (least-significant first)
396
411
  const segments = []
397
412
  let temp = n
@@ -425,11 +440,13 @@ function buildLargeOrdinal (n) {
425
440
  if (i === 0) {
426
441
  // Units position: use ordinal segment
427
442
  result += buildOrdinalSegment(segment)
428
- } else {
443
+ }
444
+ else {
429
445
  // Scale position with no remainder below: "one millionth"
430
446
  result += buildSegment(segment, false).word + ' ' + SCALES[i - 1] + 'th'
431
447
  }
432
- } else {
448
+ }
449
+ else {
433
450
  // Non-final segments are cardinal
434
451
  result += buildSegment(segment, false).word
435
452
  if (i > 0) {
@@ -443,12 +460,10 @@ function buildLargeOrdinal (n) {
443
460
 
444
461
  /**
445
462
  * Converts a numeric value to Canadian English ordinal words.
446
- *
447
463
  * @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
448
464
  * @returns {string} The number as ordinal words (e.g., "first", "forty-second")
449
465
  * @throws {TypeError} If value is not a valid numeric type
450
466
  * @throws {RangeError} If value is negative, zero, or has a decimal part
451
- *
452
467
  * @example
453
468
  * toOrdinal(1) // 'first'
454
469
  * toOrdinal(2) // 'second'
@@ -459,8 +474,9 @@ function buildLargeOrdinal (n) {
459
474
  * toOrdinal(101) // 'one hundred first'
460
475
  * toOrdinal(1000) // 'one thousandth'
461
476
  */
462
- function toOrdinal (value) {
477
+ function toOrdinal(value) {
463
478
  const integerPart = parseOrdinalValue(value)
479
+ checkMax(integerPart, ordinalMax)
464
480
  return integerToOrdinal(integerPart)
465
481
  }
466
482
 
@@ -468,26 +484,31 @@ function toOrdinal (value) {
468
484
  // CURRENCY: toCurrency(value, options?)
469
485
  // ============================================================================
470
486
 
487
+ /**
488
+ * @typedef {object} CurrencyOptions
489
+ * @property {boolean} [and] - Use "and" between dollars and cents (e.g., "one dollar and fifty cents")
490
+ */
491
+
492
+ /** @type {Required<CurrencyOptions>} */
493
+ export const currencyDefaults = { and: true }
494
+
471
495
  /**
472
496
  * Converts a numeric value to Canadian English currency words.
473
- *
474
497
  * @param {number | string | bigint} value - The currency amount to convert
475
- * @param {Object} [options] - Optional configuration
476
- * @param {boolean} [options.and=true] - Use "and" between dollars and cents (e.g., "one dollar and fifty cents")
498
+ * @param {CurrencyOptions} [options] - Optional configuration
477
499
  * @returns {string} The amount in Canadian English currency words
478
500
  * @throws {TypeError} If value is not a valid numeric type
479
501
  * @throws {Error} If value is not a valid number format
480
- *
481
502
  * @example
482
503
  * toCurrency(42.50) // 'forty-two dollars and fifty cents'
483
504
  * toCurrency(1) // 'one dollar'
484
505
  * toCurrency(0.99) // 'ninety-nine cents'
485
506
  * toCurrency(42.50, { and: false }) // 'forty-two dollars fifty cents'
486
507
  */
487
- function toCurrency (value, options) {
488
- options = validateOptions(options)
508
+ function toCurrency(value, options) {
489
509
  const { isNegative, dollars, cents } = parseCurrencyValue(value)
490
- const { and: useAnd = true } = options
510
+ checkMax(dollars, currencyMax)
511
+ const { and: useAnd } = resolveOptions(options, currencyDefaults)
491
512
 
492
513
  // Build result
493
514
  let result = ''
package/src/en-GB.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 pounds and pence (e.g., "one pound and fifty pence")
7
+ */
8
+ /** @type {Required<CurrencyOptions>} */
9
+ export const currencyDefaults: Required<CurrencyOptions>;
10
+ export type CurrencyOptions = {
11
+ /**
12
+ * - Use "and" between pounds and pence (e.g., "one pound and fifty pence")
13
+ */
14
+ and?: boolean | undefined;
15
+ };
1
16
  /**
2
17
  * Converts a numeric value to 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 British 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 British 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 pounds and pence (e.g., "one pound and fifty pence")
51
+ * @param {CurrencyOptions} [options] - Optional configuration
43
52
  * @returns {string} The amount in British 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 pounds and fifty pence'
49
57
  * toCurrency(1) // 'one pound'
@@ -51,6 +59,4 @@ export function toOrdinal(value: number | string | bigint): string;
51
59
  * toCurrency(0.01) // 'one penny'
52
60
  * toCurrency(42.50, { and: false }) // 'forty-two pounds fifty pence'
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;