n2words 5.0.0 → 5.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/CHANGELOG.md +133 -40
  2. package/README.md +6 -4
  3. package/dist/am-ET.js +2 -2
  4. package/dist/am-ET.umd.js +2 -2
  5. package/dist/am-Latn-ET.js +2 -2
  6. package/dist/am-Latn-ET.umd.js +2 -2
  7. package/dist/ar-SA.js +2 -2
  8. package/dist/ar-SA.umd.js +2 -2
  9. package/dist/az-AZ.js +2 -2
  10. package/dist/az-AZ.umd.js +2 -2
  11. package/dist/bn-BD.js +2 -2
  12. package/dist/bn-BD.umd.js +2 -2
  13. package/dist/cs-CZ.js +2 -2
  14. package/dist/cs-CZ.umd.js +2 -2
  15. package/dist/da-DK.js +2 -2
  16. package/dist/da-DK.umd.js +2 -2
  17. package/dist/de-DE.js +2 -2
  18. package/dist/de-DE.umd.js +2 -2
  19. package/dist/el-GR.js +2 -2
  20. package/dist/el-GR.umd.js +2 -2
  21. package/dist/en-AU.js +2 -2
  22. package/dist/en-AU.umd.js +2 -2
  23. package/dist/en-BD.js +2 -2
  24. package/dist/en-BD.umd.js +2 -2
  25. package/dist/en-CA.js +2 -2
  26. package/dist/en-CA.umd.js +2 -2
  27. package/dist/en-GB.js +2 -2
  28. package/dist/en-GB.umd.js +2 -2
  29. package/dist/en-GH.js +2 -2
  30. package/dist/en-GH.umd.js +2 -2
  31. package/dist/en-IE.js +2 -2
  32. package/dist/en-IE.umd.js +2 -2
  33. package/dist/en-IN.js +2 -2
  34. package/dist/en-IN.umd.js +2 -2
  35. package/dist/en-KE.js +2 -2
  36. package/dist/en-KE.umd.js +2 -2
  37. package/dist/en-MY.js +2 -2
  38. package/dist/en-MY.umd.js +2 -2
  39. package/dist/en-NG.js +2 -2
  40. package/dist/en-NG.umd.js +2 -2
  41. package/dist/en-NZ.js +2 -2
  42. package/dist/en-NZ.umd.js +2 -2
  43. package/dist/en-PH.js +2 -2
  44. package/dist/en-PH.umd.js +2 -2
  45. package/dist/en-PK.js +2 -2
  46. package/dist/en-PK.umd.js +2 -2
  47. package/dist/en-SG.js +2 -2
  48. package/dist/en-SG.umd.js +2 -2
  49. package/dist/en-US.js +2 -2
  50. package/dist/en-US.umd.js +2 -2
  51. package/dist/en-ZA.js +2 -2
  52. package/dist/en-ZA.umd.js +2 -2
  53. package/dist/es-ES.js +2 -2
  54. package/dist/es-ES.umd.js +2 -2
  55. package/dist/es-MX.js +2 -2
  56. package/dist/es-MX.umd.js +2 -2
  57. package/dist/es-US.js +2 -2
  58. package/dist/es-US.umd.js +2 -2
  59. package/dist/fa-IR.js +2 -2
  60. package/dist/fa-IR.umd.js +2 -2
  61. package/dist/fi-FI.js +2 -2
  62. package/dist/fi-FI.umd.js +2 -2
  63. package/dist/fil-PH.js +2 -2
  64. package/dist/fil-PH.umd.js +2 -2
  65. package/dist/fr-BE.js +2 -2
  66. package/dist/fr-BE.umd.js +2 -2
  67. package/dist/fr-FR.js +2 -2
  68. package/dist/fr-FR.umd.js +2 -2
  69. package/dist/gu-IN.js +2 -2
  70. package/dist/gu-IN.umd.js +2 -2
  71. package/dist/ha-NG.js +2 -2
  72. package/dist/ha-NG.umd.js +2 -2
  73. package/dist/hbo-IL.js +2 -2
  74. package/dist/hbo-IL.umd.js +2 -2
  75. package/dist/he-IL.js +2 -2
  76. package/dist/he-IL.umd.js +2 -2
  77. package/dist/hi-IN.js +2 -2
  78. package/dist/hi-IN.umd.js +2 -2
  79. package/dist/hr-HR.js +2 -2
  80. package/dist/hr-HR.umd.js +2 -2
  81. package/dist/hu-HU.js +2 -2
  82. package/dist/hu-HU.umd.js +2 -2
  83. package/dist/id-ID.js +2 -2
  84. package/dist/id-ID.umd.js +2 -2
  85. package/dist/it-IT.js +2 -2
  86. package/dist/it-IT.umd.js +2 -2
  87. package/dist/ja-JP.js +2 -2
  88. package/dist/ja-JP.umd.js +2 -2
  89. package/dist/ka-GE.js +2 -2
  90. package/dist/ka-GE.umd.js +2 -2
  91. package/dist/kn-IN.js +2 -2
  92. package/dist/kn-IN.umd.js +2 -2
  93. package/dist/ko-KR.js +2 -2
  94. package/dist/ko-KR.umd.js +2 -2
  95. package/dist/lt-LT.js +2 -2
  96. package/dist/lt-LT.umd.js +2 -2
  97. package/dist/lv-LV.js +2 -2
  98. package/dist/lv-LV.umd.js +2 -2
  99. package/dist/mr-IN.js +2 -2
  100. package/dist/mr-IN.umd.js +2 -2
  101. package/dist/ms-MY.js +2 -2
  102. package/dist/ms-MY.umd.js +2 -2
  103. package/dist/nb-NO.js +2 -2
  104. package/dist/nb-NO.umd.js +2 -2
  105. package/dist/nl-NL.js +2 -2
  106. package/dist/nl-NL.umd.js +2 -2
  107. package/dist/pa-IN.js +2 -2
  108. package/dist/pa-IN.umd.js +2 -2
  109. package/dist/pl-PL.js +2 -2
  110. package/dist/pl-PL.umd.js +2 -2
  111. package/dist/pt-BR.js +2 -2
  112. package/dist/pt-BR.umd.js +2 -2
  113. package/dist/pt-PT.js +2 -2
  114. package/dist/pt-PT.umd.js +2 -2
  115. package/dist/ro-RO.js +2 -2
  116. package/dist/ro-RO.umd.js +2 -2
  117. package/dist/ru-RU.js +2 -2
  118. package/dist/ru-RU.umd.js +2 -2
  119. package/dist/sr-Cyrl-RS.js +2 -2
  120. package/dist/sr-Cyrl-RS.umd.js +2 -2
  121. package/dist/sr-Latn-RS.js +2 -2
  122. package/dist/sr-Latn-RS.umd.js +2 -2
  123. package/dist/sv-SE.js +2 -2
  124. package/dist/sv-SE.umd.js +2 -2
  125. package/dist/sw-KE.js +2 -2
  126. package/dist/sw-KE.umd.js +2 -2
  127. package/dist/ta-IN.js +2 -2
  128. package/dist/ta-IN.umd.js +2 -2
  129. package/dist/te-IN.js +2 -2
  130. package/dist/te-IN.umd.js +2 -2
  131. package/dist/th-TH.js +2 -2
  132. package/dist/th-TH.umd.js +2 -2
  133. package/dist/tr-TR.js +2 -2
  134. package/dist/tr-TR.umd.js +2 -2
  135. package/dist/uk-UA.js +2 -2
  136. package/dist/uk-UA.umd.js +2 -2
  137. package/dist/ur-PK.js +2 -2
  138. package/dist/ur-PK.umd.js +2 -2
  139. package/dist/vi-VN.js +2 -2
  140. package/dist/vi-VN.umd.js +2 -2
  141. package/dist/yo-NG.js +2 -2
  142. package/dist/yo-NG.umd.js +2 -2
  143. package/dist/zh-Hans-CN.js +2 -2
  144. package/dist/zh-Hans-CN.umd.js +2 -2
  145. package/dist/zh-Hant-TW.js +2 -2
  146. package/dist/zh-Hant-TW.umd.js +2 -2
  147. package/package.json +33 -24
  148. package/src/am-ET.d.ts +3 -5
  149. package/src/am-ET.js +41 -16
  150. package/src/am-Latn-ET.d.ts +3 -5
  151. package/src/am-Latn-ET.js +45 -16
  152. package/src/ar-SA.d.ts +44 -18
  153. package/src/ar-SA.js +93 -40
  154. package/src/az-AZ.d.ts +3 -5
  155. package/src/az-AZ.js +58 -20
  156. package/src/bn-BD.d.ts +3 -5
  157. package/src/bn-BD.js +32 -16
  158. package/src/cs-CZ.d.ts +3 -6
  159. package/src/cs-CZ.js +66 -42
  160. package/src/da-DK.d.ts +3 -6
  161. package/src/da-DK.js +53 -48
  162. package/src/de-DE.d.ts +17 -11
  163. package/src/de-DE.js +88 -57
  164. package/src/el-GR.d.ts +3 -6
  165. package/src/el-GR.js +45 -32
  166. package/src/en-AU.d.ts +17 -11
  167. package/src/en-AU.js +56 -41
  168. package/src/en-BD.d.ts +17 -11
  169. package/src/en-BD.js +60 -41
  170. package/src/en-CA.d.ts +36 -18
  171. package/src/en-CA.js +67 -46
  172. package/src/en-GB.d.ts +17 -11
  173. package/src/en-GB.js +56 -41
  174. package/src/en-GH.d.ts +32 -3
  175. package/src/en-GH.js +104 -26
  176. package/src/en-IE.d.ts +17 -11
  177. package/src/en-IE.js +56 -41
  178. package/src/en-IN.d.ts +17 -11
  179. package/src/en-IN.js +60 -41
  180. package/src/en-KE.d.ts +28 -3
  181. package/src/en-KE.js +93 -26
  182. package/src/en-MY.d.ts +26 -3
  183. package/src/en-MY.js +91 -26
  184. package/src/en-NG.d.ts +17 -11
  185. package/src/en-NG.js +56 -41
  186. package/src/en-NZ.d.ts +32 -3
  187. package/src/en-NZ.js +85 -31
  188. package/src/en-PH.d.ts +32 -3
  189. package/src/en-PH.js +97 -26
  190. package/src/en-PK.d.ts +17 -11
  191. package/src/en-PK.js +60 -41
  192. package/src/en-SG.d.ts +28 -3
  193. package/src/en-SG.js +93 -26
  194. package/src/en-US.d.ts +36 -18
  195. package/src/en-US.js +70 -47
  196. package/src/en-ZA.d.ts +17 -11
  197. package/src/en-ZA.js +56 -41
  198. package/src/es-ES.d.ts +53 -21
  199. package/src/es-ES.js +104 -56
  200. package/src/es-MX.d.ts +53 -21
  201. package/src/es-MX.js +104 -56
  202. package/src/es-US.d.ts +53 -21
  203. package/src/es-US.js +92 -51
  204. package/src/fa-IR.d.ts +3 -5
  205. package/src/fa-IR.js +28 -13
  206. package/src/fi-FI.d.ts +3 -6
  207. package/src/fi-FI.js +47 -29
  208. package/src/fil-PH.d.ts +3 -5
  209. package/src/fil-PH.js +61 -28
  210. package/src/fr-BE.d.ts +31 -15
  211. package/src/fr-BE.js +128 -57
  212. package/src/fr-FR.d.ts +31 -16
  213. package/src/fr-FR.js +97 -60
  214. package/src/gu-IN.d.ts +3 -5
  215. package/src/gu-IN.js +31 -16
  216. package/src/ha-NG.d.ts +3 -5
  217. package/src/ha-NG.js +55 -27
  218. package/src/hbo-IL.d.ts +26 -12
  219. package/src/hbo-IL.js +92 -51
  220. package/src/he-IL.d.ts +17 -10
  221. package/src/he-IL.js +92 -50
  222. package/src/hi-IN.d.ts +3 -5
  223. package/src/hi-IN.js +30 -17
  224. package/src/hr-HR.d.ts +21 -10
  225. package/src/hr-HR.js +89 -33
  226. package/src/hu-HU.d.ts +3 -5
  227. package/src/hu-HU.js +57 -23
  228. package/src/id-ID.d.ts +3 -5
  229. package/src/id-ID.js +56 -23
  230. package/src/it-IT.d.ts +17 -11
  231. package/src/it-IT.js +74 -43
  232. package/src/ja-JP.d.ts +3 -6
  233. package/src/ja-JP.js +39 -26
  234. package/src/ka-GE.d.ts +3 -6
  235. package/src/ka-GE.js +38 -26
  236. package/src/kn-IN.d.ts +3 -5
  237. package/src/kn-IN.js +31 -16
  238. package/src/ko-KR.d.ts +3 -6
  239. package/src/ko-KR.js +34 -26
  240. package/src/lt-LT.d.ts +21 -11
  241. package/src/lt-LT.js +64 -42
  242. package/src/lv-LV.d.ts +21 -11
  243. package/src/lv-LV.js +79 -51
  244. package/src/mr-IN.d.ts +3 -5
  245. package/src/mr-IN.js +31 -16
  246. package/src/ms-MY.d.ts +3 -5
  247. package/src/ms-MY.js +58 -24
  248. package/src/nb-NO.d.ts +3 -6
  249. package/src/nb-NO.js +54 -34
  250. package/src/nl-NL.d.ts +41 -20
  251. package/src/nl-NL.js +111 -69
  252. package/src/pa-IN.d.ts +3 -5
  253. package/src/pa-IN.js +32 -16
  254. package/src/pl-PL.d.ts +21 -11
  255. package/src/pl-PL.js +69 -45
  256. package/src/pt-BR.d.ts +22 -11
  257. package/src/pt-BR.js +93 -53
  258. package/src/pt-PT.d.ts +17 -11
  259. package/src/pt-PT.js +80 -48
  260. package/src/ro-RO.d.ts +21 -11
  261. package/src/ro-RO.js +77 -39
  262. package/src/ru-RU.d.ts +35 -15
  263. package/src/ru-RU.js +100 -38
  264. package/src/sr-Cyrl-RS.d.ts +35 -15
  265. package/src/sr-Cyrl-RS.js +106 -43
  266. package/src/sr-Latn-RS.d.ts +35 -15
  267. package/src/sr-Latn-RS.js +106 -43
  268. package/src/sv-SE.d.ts +3 -6
  269. package/src/sv-SE.js +53 -34
  270. package/src/sw-KE.d.ts +3 -5
  271. package/src/sw-KE.js +50 -20
  272. package/src/ta-IN.d.ts +3 -5
  273. package/src/ta-IN.js +29 -17
  274. package/src/te-IN.d.ts +3 -5
  275. package/src/te-IN.js +31 -16
  276. package/src/th-TH.d.ts +3 -5
  277. package/src/th-TH.js +42 -19
  278. package/src/tr-TR.d.ts +17 -11
  279. package/src/tr-TR.js +63 -37
  280. package/src/uk-UA.d.ts +21 -10
  281. package/src/uk-UA.js +89 -33
  282. package/src/ur-PK.d.ts +3 -5
  283. package/src/ur-PK.js +32 -16
  284. package/src/utils/check-max.d.ts +26 -0
  285. package/src/utils/check-max.js +33 -0
  286. package/src/utils/expand-scientific.d.ts +0 -4
  287. package/src/utils/expand-scientific.js +7 -9
  288. package/src/utils/is-plain-object.d.ts +3 -4
  289. package/src/utils/is-plain-object.js +3 -4
  290. package/src/utils/parse-cardinal.d.ts +1 -2
  291. package/src/utils/parse-cardinal.js +12 -9
  292. package/src/utils/parse-currency.d.ts +1 -2
  293. package/src/utils/parse-currency.js +9 -11
  294. package/src/utils/parse-ordinal.d.ts +0 -1
  295. package/src/utils/parse-ordinal.js +9 -10
  296. package/src/utils/resolve-options.d.ts +17 -0
  297. package/src/utils/resolve-options.js +56 -0
  298. package/src/utils/scale.d.ts +49 -0
  299. package/src/utils/scale.js +65 -0
  300. package/src/vi-VN.d.ts +3 -6
  301. package/src/vi-VN.js +41 -28
  302. package/src/yo-NG.d.ts +3 -5
  303. package/src/yo-NG.js +49 -33
  304. package/src/zh-Hans-CN.d.ts +45 -20
  305. package/src/zh-Hans-CN.js +84 -31
  306. package/src/zh-Hant-TW.d.ts +45 -20
  307. package/src/zh-Hant-TW.js +85 -34
  308. package/src/utils/validate-options.d.ts +0 -8
  309. package/src/utils/validate-options.js +0 -16
package/src/en-AU.d.ts CHANGED
@@ -1,11 +1,24 @@
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 dollars and cents
7
+ */
8
+ /** @type {Required<CurrencyOptions>} */
9
+ export const currencyDefaults: Required<CurrencyOptions>;
10
+ export type CurrencyOptions = {
11
+ /**
12
+ * - Use "and" between dollars and cents
13
+ */
14
+ and?: boolean | undefined;
15
+ };
1
16
  /**
2
17
  * Converts a numeric value to Australian English words.
3
- *
4
18
  * @param {number | string | bigint} value - The numeric value to convert
5
19
  * @returns {string} The number in English words
6
20
  * @throws {TypeError} If value is not a valid numeric type
7
21
  * @throws {Error} If value is not a valid number format
8
- *
9
22
  * @example
10
23
  * toCardinal(42) // 'forty-two'
11
24
  * toCardinal(101) // 'one hundred and one'
@@ -14,12 +27,10 @@
14
27
  export function toCardinal(value: number | string | bigint): string;
15
28
  /**
16
29
  * Converts a numeric value to Australian English ordinal words.
17
- *
18
30
  * @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
19
31
  * @returns {string} The number as ordinal words
20
32
  * @throws {TypeError} If value is not a valid numeric type
21
33
  * @throws {RangeError} If value is negative, zero, or has a decimal part
22
- *
23
34
  * @example
24
35
  * toOrdinal(1) // 'first'
25
36
  * toOrdinal(42) // 'forty-second'
@@ -28,20 +39,15 @@ export function toCardinal(value: number | string | bigint): string;
28
39
  export function toOrdinal(value: number | string | bigint): string;
29
40
  /**
30
41
  * Converts a numeric value to Australian English currency words.
31
- *
32
42
  * @param {number | string | bigint} value - The currency amount to convert
33
- * @param {Object} [options] - Optional configuration
34
- * @param {boolean} [options.and=true] - Use "and" between dollars and cents
43
+ * @param {CurrencyOptions} [options] - Optional configuration
35
44
  * @returns {string} The amount in Australian English currency words
36
45
  * @throws {TypeError} If value is not a valid numeric type
37
46
  * @throws {Error} If value is not a valid number format
38
- *
39
47
  * @example
40
48
  * toCurrency(42.50) // 'forty-two dollars and fifty cents'
41
49
  * toCurrency(1) // 'one dollar'
42
50
  * toCurrency(0.99) // 'ninety-nine cents'
43
51
  * toCurrency(42.50, { and: false }) // 'forty-two dollars fifty cents'
44
52
  */
45
- export function toCurrency(value: number | string | bigint, options?: {
46
- and?: boolean | undefined;
47
- }): string;
53
+ export function toCurrency(value: number | string | bigint, options?: CurrencyOptions): string;
package/src/en-AU.js CHANGED
@@ -12,7 +12,9 @@
12
12
  import { parseCardinalValue } from './utils/parse-cardinal.js'
13
13
  import { parseCurrencyValue } from './utils/parse-currency.js'
14
14
  import { parseOrdinalValue } from './utils/parse-ordinal.js'
15
- import { validateOptions } from './utils/validate-options.js'
15
+ import { checkMax } from './utils/check-max.js'
16
+ import { western } from './utils/scale.js'
17
+ import { resolveOptions } from './utils/resolve-options.js'
16
18
 
17
19
  // ============================================================================
18
20
  // Vocabulary (module-level constants)
@@ -27,9 +29,13 @@ const SCALES = [
27
29
  'quintillion', 'sextillion', 'septillion', 'octillion', 'nonillion',
28
30
  'decillion', 'undecillion', 'duodecillion', 'tredecillion', 'quattuordecillion',
29
31
  'quindecillion', 'sexdecillion', 'septendecillion', 'octodecillion', 'novemdecillion',
30
- 'vigintillion'
32
+ 'vigintillion',
31
33
  ]
32
34
 
35
+ export const cardinalMax = western(SCALES.length)
36
+ export const ordinalMax = western(SCALES.length)
37
+ export const currencyMax = western(SCALES.length)
38
+
33
39
  const HUNDRED = 'hundred'
34
40
  const ZERO = 'zero'
35
41
  const NEGATIVE = 'minus'
@@ -55,11 +61,10 @@ const segmentResult = { word: '', hasHundred: false }
55
61
 
56
62
  /**
57
63
  * Builds words for a 0-999 segment (British-style with "and" after hundreds).
58
- *
59
64
  * @param {number} n - Number 0-999
60
- * @returns {{ word: string, hasHundred: boolean }}
65
+ * @returns {{ word: string, hasHundred: boolean }} The segment words and whether a hundreds place was present
61
66
  */
62
- function buildSegment (n) {
67
+ function buildSegment(n) {
63
68
  if (n === 0) {
64
69
  segmentResult.word = ''
65
70
  segmentResult.hasHundred = false
@@ -74,9 +79,11 @@ function buildSegment (n) {
74
79
  let tensOnes = ''
75
80
  if (tens === 1) {
76
81
  tensOnes = TEENS[ones]
77
- } else if (tens >= 2) {
82
+ }
83
+ else if (tens >= 2) {
78
84
  tensOnes = ones > 0 ? TENS[tens] + '-' + ONES[ones] : TENS[tens]
79
- } else if (ones > 0) {
85
+ }
86
+ else if (ones > 0) {
80
87
  tensOnes = ONES[ones]
81
88
  }
82
89
 
@@ -84,11 +91,13 @@ function buildSegment (n) {
84
91
  if (hundreds > 0) {
85
92
  if (tensOnes) {
86
93
  segmentResult.word = ONES[hundreds] + ' ' + HUNDRED + ' and ' + tensOnes
87
- } else {
94
+ }
95
+ else {
88
96
  segmentResult.word = ONES[hundreds] + ' ' + HUNDRED
89
97
  }
90
98
  segmentResult.hasHundred = true
91
- } else {
99
+ }
100
+ else {
92
101
  segmentResult.word = tensOnes
93
102
  segmentResult.hasHundred = false
94
103
  }
@@ -102,11 +111,10 @@ function buildSegment (n) {
102
111
 
103
112
  /**
104
113
  * Converts a non-negative integer to English words.
105
- *
106
114
  * @param {bigint} n - Non-negative integer to convert
107
115
  * @returns {string} English words
108
116
  */
109
- function integerToWords (n) {
117
+ function integerToWords(n) {
110
118
  if (n === 0n) return ZERO
111
119
 
112
120
  // Fast path: numbers < 1000
@@ -136,11 +144,10 @@ function integerToWords (n) {
136
144
 
137
145
  /**
138
146
  * Builds words for numbers >= 1,000,000.
139
- *
140
147
  * @param {bigint} n - Number >= 1,000,000
141
148
  * @returns {string} English words
142
149
  */
143
- function buildLargeNumberWords (n) {
150
+ function buildLargeNumberWords(n) {
144
151
  const segments = []
145
152
  let temp = n
146
153
  while (temp > 0n) {
@@ -176,7 +183,8 @@ function buildLargeNumberWords (n) {
176
183
  if (i > 0) {
177
184
  result += ' ' + SCALES[i - 1]
178
185
  prevWasScale = true
179
- } else {
186
+ }
187
+ else {
180
188
  prevWasScale = false
181
189
  }
182
190
  }
@@ -186,11 +194,10 @@ function buildLargeNumberWords (n) {
186
194
 
187
195
  /**
188
196
  * Converts decimal digits to English words.
189
- *
190
197
  * @param {string} decimalPart - Decimal digits (without the point)
191
198
  * @returns {string} English words for decimal part
192
199
  */
193
- function decimalPartToWords (decimalPart) {
200
+ function decimalPartToWords(decimalPart) {
194
201
  let result = ''
195
202
 
196
203
  let i = 0
@@ -211,19 +218,20 @@ function decimalPartToWords (decimalPart) {
211
218
 
212
219
  /**
213
220
  * Converts a numeric value to Australian English words.
214
- *
215
221
  * @param {number | string | bigint} value - The numeric value to convert
216
222
  * @returns {string} The number in English words
217
223
  * @throws {TypeError} If value is not a valid numeric type
218
224
  * @throws {Error} If value is not a valid number format
219
- *
220
225
  * @example
221
226
  * toCardinal(42) // 'forty-two'
222
227
  * toCardinal(101) // 'one hundred and one'
223
228
  * toCardinal(1000000) // 'one million'
224
229
  */
225
- function toCardinal (value) {
230
+ function toCardinal(value) {
226
231
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
232
+ // Both the integer part and the decimal's significant digits are spelled via
233
+ // the scale builder, so both must clear the ceiling.
234
+ checkMax(integerPart, cardinalMax, decimalPart)
227
235
 
228
236
  let result = ''
229
237
 
@@ -246,11 +254,10 @@ function toCardinal (value) {
246
254
 
247
255
  /**
248
256
  * Builds ordinal words for a 0-999 segment.
249
- *
250
257
  * @param {number} n - Number 0-999
251
258
  * @returns {string} Ordinal words for this segment
252
259
  */
253
- function buildOrdinalSegment (n) {
260
+ function buildOrdinalSegment(n) {
254
261
  const ones = n % 10
255
262
  const tens = Math.trunc(n / 10) % 10
256
263
  const hundreds = Math.trunc(n / 100)
@@ -258,20 +265,24 @@ function buildOrdinalSegment (n) {
258
265
  let tensOnesOrdinal = ''
259
266
  if (tens === 1) {
260
267
  tensOnesOrdinal = ORDINAL_TEENS[ones]
261
- } else if (tens >= 2) {
268
+ }
269
+ else if (tens >= 2) {
262
270
  if (ones > 0) {
263
271
  tensOnesOrdinal = TENS[tens] + '-' + ORDINAL_ONES[ones]
264
- } else {
272
+ }
273
+ else {
265
274
  tensOnesOrdinal = ORDINAL_TENS[tens]
266
275
  }
267
- } else if (ones > 0) {
276
+ }
277
+ else if (ones > 0) {
268
278
  tensOnesOrdinal = ORDINAL_ONES[ones]
269
279
  }
270
280
 
271
281
  if (hundreds > 0) {
272
282
  if (tensOnesOrdinal) {
273
283
  return ONES[hundreds] + ' ' + HUNDRED + ' ' + tensOnesOrdinal
274
- } else {
284
+ }
285
+ else {
275
286
  return ONES[hundreds] + ' hundredth'
276
287
  }
277
288
  }
@@ -281,11 +292,10 @@ function buildOrdinalSegment (n) {
281
292
 
282
293
  /**
283
294
  * Converts a positive integer to ordinal words.
284
- *
285
295
  * @param {bigint} n - Positive integer to convert
286
296
  * @returns {string} Ordinal English words
287
297
  */
288
- function integerToOrdinal (n) {
298
+ function integerToOrdinal(n) {
289
299
  if (n < 1000n) {
290
300
  return buildOrdinalSegment(Number(n))
291
301
  }
@@ -307,11 +317,10 @@ function integerToOrdinal (n) {
307
317
 
308
318
  /**
309
319
  * Builds ordinal words for numbers >= 1,000,000.
310
- *
311
320
  * @param {bigint} n - Number >= 1,000,000
312
321
  * @returns {string} Ordinal English words
313
322
  */
314
- function buildLargeOrdinal (n) {
323
+ function buildLargeOrdinal(n) {
315
324
  const segments = []
316
325
  let temp = n
317
326
  while (temp > 0n) {
@@ -340,10 +349,12 @@ function buildLargeOrdinal (n) {
340
349
  if (isLowestSegment) {
341
350
  if (i === 0) {
342
351
  result += buildOrdinalSegment(segment)
343
- } else {
352
+ }
353
+ else {
344
354
  result += buildSegment(segment).word + ' ' + SCALES[i - 1] + 'th'
345
355
  }
346
- } else {
356
+ }
357
+ else {
347
358
  result += buildSegment(segment).word
348
359
  if (i > 0) {
349
360
  result += ' ' + SCALES[i - 1]
@@ -356,19 +367,18 @@ function buildLargeOrdinal (n) {
356
367
 
357
368
  /**
358
369
  * Converts a numeric value to Australian English ordinal words.
359
- *
360
370
  * @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
361
371
  * @returns {string} The number as ordinal words
362
372
  * @throws {TypeError} If value is not a valid numeric type
363
373
  * @throws {RangeError} If value is negative, zero, or has a decimal part
364
- *
365
374
  * @example
366
375
  * toOrdinal(1) // 'first'
367
376
  * toOrdinal(42) // 'forty-second'
368
377
  * toOrdinal(100) // 'one hundredth'
369
378
  */
370
- function toOrdinal (value) {
379
+ function toOrdinal(value) {
371
380
  const integerPart = parseOrdinalValue(value)
381
+ checkMax(integerPart, ordinalMax)
372
382
  return integerToOrdinal(integerPart)
373
383
  }
374
384
 
@@ -376,26 +386,31 @@ function toOrdinal (value) {
376
386
  // CURRENCY: toCurrency(value, options?)
377
387
  // ============================================================================
378
388
 
389
+ /**
390
+ * @typedef {object} CurrencyOptions
391
+ * @property {boolean} [and] - Use "and" between dollars and cents
392
+ */
393
+
394
+ /** @type {Required<CurrencyOptions>} */
395
+ export const currencyDefaults = { and: true }
396
+
379
397
  /**
380
398
  * Converts a numeric value to Australian English currency words.
381
- *
382
399
  * @param {number | string | bigint} value - The currency amount to convert
383
- * @param {Object} [options] - Optional configuration
384
- * @param {boolean} [options.and=true] - Use "and" between dollars and cents
400
+ * @param {CurrencyOptions} [options] - Optional configuration
385
401
  * @returns {string} The amount in Australian English currency words
386
402
  * @throws {TypeError} If value is not a valid numeric type
387
403
  * @throws {Error} If value is not a valid number format
388
- *
389
404
  * @example
390
405
  * toCurrency(42.50) // 'forty-two dollars and fifty cents'
391
406
  * toCurrency(1) // 'one dollar'
392
407
  * toCurrency(0.99) // 'ninety-nine cents'
393
408
  * toCurrency(42.50, { and: false }) // 'forty-two dollars fifty cents'
394
409
  */
395
- function toCurrency (value, options) {
396
- options = validateOptions(options)
410
+ function toCurrency(value, options) {
397
411
  const { isNegative, dollars, cents } = parseCurrencyValue(value)
398
- const { and: useAnd = true } = options
412
+ checkMax(dollars, currencyMax)
413
+ const { and: useAnd } = resolveOptions(options, currencyDefaults)
399
414
 
400
415
  let result = ''
401
416
  if (isNegative) result = NEGATIVE + ' '
package/src/en-BD.d.ts CHANGED
@@ -1,11 +1,24 @@
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 taka and paise
7
+ */
8
+ /** @type {Required<CurrencyOptions>} */
9
+ export const currencyDefaults: Required<CurrencyOptions>;
10
+ export type CurrencyOptions = {
11
+ /**
12
+ * - Use "and" between taka and paise
13
+ */
14
+ and?: boolean | undefined;
15
+ };
1
16
  /**
2
17
  * Converts a numeric value to English words using Indian numbering.
3
- *
4
18
  * @param {number | string | bigint} value - The numeric value to convert
5
19
  * @returns {string} The number in English words
6
20
  * @throws {TypeError} If value is not a valid numeric type
7
21
  * @throws {Error} If value is not a valid number format
8
- *
9
22
  * @example
10
23
  * toCardinal(42) // 'forty-two'
11
24
  * toCardinal(100000) // 'one lakh'
@@ -15,12 +28,10 @@
15
28
  export function toCardinal(value: number | string | bigint): string;
16
29
  /**
17
30
  * Converts a numeric value to English ordinal words using Indian numbering.
18
- *
19
31
  * @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
20
32
  * @returns {string} The number as ordinal words
21
33
  * @throws {TypeError} If value is not a valid numeric type
22
34
  * @throws {RangeError} If value is negative, zero, or has a decimal part
23
- *
24
35
  * @example
25
36
  * toOrdinal(1) // 'first'
26
37
  * toOrdinal(100000) // 'one lakhth'
@@ -29,14 +40,11 @@ export function toCardinal(value: number | string | bigint): string;
29
40
  export function toOrdinal(value: number | string | bigint): string;
30
41
  /**
31
42
  * Converts a numeric value to Bangladeshi English currency words.
32
- *
33
43
  * @param {number | string | bigint} value - The currency amount to convert
34
- * @param {Object} [options] - Optional configuration
35
- * @param {boolean} [options.and=true] - Use "and" between taka and paise
44
+ * @param {CurrencyOptions} [options] - Optional configuration
36
45
  * @returns {string} The amount in Bangladeshi English currency words
37
46
  * @throws {TypeError} If value is not a valid numeric type
38
47
  * @throws {Error} If value is not a valid number format
39
- *
40
48
  * @example
41
49
  * toCurrency(42.50) // 'forty-two taka and fifty paise'
42
50
  * toCurrency(100000) // 'one lakh taka'
@@ -44,6 +52,4 @@ export function toOrdinal(value: number | string | bigint): string;
44
52
  * toCurrency(0.50) // 'fifty paise'
45
53
  * toCurrency(42.50, { and: false }) // 'forty-two taka fifty paise'
46
54
  */
47
- export function toCurrency(value: number | string | bigint, options?: {
48
- and?: boolean | undefined;
49
- }): string;
55
+ export function toCurrency(value: number | string | bigint, options?: CurrencyOptions): string;
package/src/en-BD.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 { indian } from './utils/scale.js'
18
+ import { resolveOptions } from './utils/resolve-options.js'
17
19
 
18
20
  // ============================================================================
19
21
  // Vocabulary (module-level constants)
@@ -26,6 +28,11 @@ const TENS = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy',
26
28
  // Indian numbering scales: 10^3, 10^5, 10^7, 10^9, 10^11, 10^13, 10^15, 10^17
27
29
  const SCALES = ['thousand', 'lakh', 'crore', 'arab', 'kharab', 'neel', 'padma', 'shankh']
28
30
 
31
+ // 3-2-2 grouping: a 3-digit base segment then 2 digits per Indian scale word.
32
+ export const cardinalMax = indian(SCALES.length + 1)
33
+ export const ordinalMax = indian(SCALES.length + 1)
34
+ export const currencyMax = indian(SCALES.length + 1)
35
+
29
36
  const HUNDRED = 'hundred'
30
37
  const ZERO = 'zero'
31
38
  const NEGATIVE = 'minus'
@@ -50,11 +57,10 @@ const segmentResult = { word: '', hasHundred: false }
50
57
 
51
58
  /**
52
59
  * Builds words for a 0-999 segment (British-style with "and" after hundreds).
53
- *
54
60
  * @param {number} n - Number 0-999
55
- * @returns {{ word: string, hasHundred: boolean }}
61
+ * @returns {{ word: string, hasHundred: boolean }} The segment words and whether a hundred was emitted
56
62
  */
57
- function buildSegment (n) {
63
+ function buildSegment(n) {
58
64
  if (n === 0) {
59
65
  segmentResult.word = ''
60
66
  segmentResult.hasHundred = false
@@ -69,9 +75,11 @@ function buildSegment (n) {
69
75
  let tensOnes = ''
70
76
  if (tens === 1) {
71
77
  tensOnes = TEENS[ones]
72
- } else if (tens >= 2) {
78
+ }
79
+ else if (tens >= 2) {
73
80
  tensOnes = ones > 0 ? TENS[tens] + '-' + ONES[ones] : TENS[tens]
74
- } else if (ones > 0) {
81
+ }
82
+ else if (ones > 0) {
75
83
  tensOnes = ONES[ones]
76
84
  }
77
85
 
@@ -79,11 +87,13 @@ function buildSegment (n) {
79
87
  if (hundreds > 0) {
80
88
  if (tensOnes) {
81
89
  segmentResult.word = ONES[hundreds] + ' ' + HUNDRED + ' and ' + tensOnes
82
- } else {
90
+ }
91
+ else {
83
92
  segmentResult.word = ONES[hundreds] + ' ' + HUNDRED
84
93
  }
85
94
  segmentResult.hasHundred = true
86
- } else {
95
+ }
96
+ else {
87
97
  segmentResult.word = tensOnes
88
98
  segmentResult.hasHundred = false
89
99
  }
@@ -93,11 +103,10 @@ function buildSegment (n) {
93
103
 
94
104
  /**
95
105
  * Builds words for a 0-99 segment (no hundreds).
96
- *
97
106
  * @param {number} n - Number 0-99
98
- * @returns {string}
107
+ * @returns {string} The segment in words
99
108
  */
100
- function buildSmallSegment (n) {
109
+ function buildSmallSegment(n) {
101
110
  if (n === 0) return ''
102
111
 
103
112
  const ones = n % 10
@@ -105,7 +114,8 @@ function buildSmallSegment (n) {
105
114
 
106
115
  if (tens === 1) {
107
116
  return TEENS[ones]
108
- } else if (tens >= 2) {
117
+ }
118
+ else if (tens >= 2) {
109
119
  return ones > 0 ? TENS[tens] + '-' + ONES[ones] : TENS[tens]
110
120
  }
111
121
  return ONES[ones]
@@ -120,11 +130,10 @@ function buildSmallSegment (n) {
120
130
  *
121
131
  * Uses BigInt modulo for segment extraction.
122
132
  * South Asian 3-2-2 grouping: first 3 digits, then groups of 2.
123
- *
124
133
  * @param {bigint} n - Non-negative integer to convert
125
134
  * @returns {string} English words
126
135
  */
127
- function integerToWords (n) {
136
+ function integerToWords(n) {
128
137
  if (n === 0n) return ZERO
129
138
 
130
139
  // Fast path: numbers < 1000
@@ -161,7 +170,8 @@ function integerToWords (n) {
161
170
  words.push('and')
162
171
  }
163
172
  words.push(word)
164
- } else {
173
+ }
174
+ else {
165
175
  // Other segments are 0-99
166
176
  words.push(buildSmallSegment(segment))
167
177
  // Add scale word
@@ -176,11 +186,10 @@ function integerToWords (n) {
176
186
 
177
187
  /**
178
188
  * Converts decimal digits to English words.
179
- *
180
189
  * @param {string} decimalPart - Decimal digits (without the point)
181
190
  * @returns {string} English words for decimal part
182
191
  */
183
- function decimalPartToWords (decimalPart) {
192
+ function decimalPartToWords(decimalPart) {
184
193
  let result = ''
185
194
 
186
195
  // Handle leading zeros
@@ -203,20 +212,21 @@ function decimalPartToWords (decimalPart) {
203
212
 
204
213
  /**
205
214
  * Converts a numeric value to English words using Indian numbering.
206
- *
207
215
  * @param {number | string | bigint} value - The numeric value to convert
208
216
  * @returns {string} The number in English words
209
217
  * @throws {TypeError} If value is not a valid numeric type
210
218
  * @throws {Error} If value is not a valid number format
211
- *
212
219
  * @example
213
220
  * toCardinal(42) // 'forty-two'
214
221
  * toCardinal(100000) // 'one lakh'
215
222
  * toCardinal(10000000) // 'one crore'
216
223
  * toCardinal(1234567) // 'twelve lakh thirty-four thousand five hundred and sixty-seven'
217
224
  */
218
- function toCardinal (value) {
225
+ function toCardinal(value) {
219
226
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
227
+ // Both the integer part and the decimal's significant digits are spelled via
228
+ // the scale builder, so both must clear the ceiling.
229
+ checkMax(integerPart, cardinalMax, decimalPart)
220
230
 
221
231
  let result = ''
222
232
 
@@ -239,11 +249,10 @@ function toCardinal (value) {
239
249
 
240
250
  /**
241
251
  * Builds ordinal words for a 0-999 segment (final segment only).
242
- *
243
252
  * @param {number} n - Number 0-999
244
253
  * @returns {string} Ordinal words for this segment
245
254
  */
246
- function buildOrdinalSegment (n) {
255
+ function buildOrdinalSegment(n) {
247
256
  const ones = n % 10
248
257
  const tens = Math.trunc(n / 10) % 10
249
258
  const hundreds = Math.trunc(n / 100)
@@ -252,13 +261,16 @@ function buildOrdinalSegment (n) {
252
261
  let tensOnesOrdinal = ''
253
262
  if (tens === 1) {
254
263
  tensOnesOrdinal = ORDINAL_TEENS[ones]
255
- } else if (tens >= 2) {
264
+ }
265
+ else if (tens >= 2) {
256
266
  if (ones > 0) {
257
267
  tensOnesOrdinal = TENS[tens] + '-' + ORDINAL_ONES[ones]
258
- } else {
268
+ }
269
+ else {
259
270
  tensOnesOrdinal = ORDINAL_TENS[tens]
260
271
  }
261
- } else if (ones > 0) {
272
+ }
273
+ else if (ones > 0) {
262
274
  tensOnesOrdinal = ORDINAL_ONES[ones]
263
275
  }
264
276
 
@@ -266,7 +278,8 @@ function buildOrdinalSegment (n) {
266
278
  if (hundreds > 0) {
267
279
  if (tensOnesOrdinal) {
268
280
  return ONES[hundreds] + ' ' + HUNDRED + ' ' + tensOnesOrdinal
269
- } else {
281
+ }
282
+ else {
270
283
  return ONES[hundreds] + ' hundredth'
271
284
  }
272
285
  }
@@ -276,11 +289,10 @@ function buildOrdinalSegment (n) {
276
289
 
277
290
  /**
278
291
  * Converts a positive integer to ordinal words using Indian numbering.
279
- *
280
292
  * @param {bigint} n - Positive integer to convert
281
293
  * @returns {string} Ordinal English words
282
294
  */
283
- function integerToOrdinal (n) {
295
+ function integerToOrdinal(n) {
284
296
  // Fast path: numbers < 1000
285
297
  if (n < 1000n) {
286
298
  return buildOrdinalSegment(Number(n))
@@ -322,16 +334,19 @@ function integerToOrdinal (n) {
322
334
  if (i === 0) {
323
335
  // Units position: use ordinal segment
324
336
  words.push(buildOrdinalSegment(segment))
325
- } else {
337
+ }
338
+ else {
326
339
  // Scale position with no remainder below: "one lakhth"
327
340
  words.push(buildSmallSegment(segment))
328
341
  words.push(SCALES[i - 1] + 'th')
329
342
  }
330
- } else {
343
+ }
344
+ else {
331
345
  // Non-final segments are cardinal
332
346
  if (i === 0) {
333
347
  words.push(buildSegment(segment).word)
334
- } else {
348
+ }
349
+ else {
335
350
  words.push(buildSmallSegment(segment))
336
351
  words.push(SCALES[i - 1])
337
352
  }
@@ -343,19 +358,18 @@ function integerToOrdinal (n) {
343
358
 
344
359
  /**
345
360
  * Converts a numeric value to English ordinal words using Indian numbering.
346
- *
347
361
  * @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
348
362
  * @returns {string} The number as ordinal words
349
363
  * @throws {TypeError} If value is not a valid numeric type
350
364
  * @throws {RangeError} If value is negative, zero, or has a decimal part
351
- *
352
365
  * @example
353
366
  * toOrdinal(1) // 'first'
354
367
  * toOrdinal(100000) // 'one lakhth'
355
368
  * toOrdinal(100001) // 'one lakh first'
356
369
  */
357
- function toOrdinal (value) {
370
+ function toOrdinal(value) {
358
371
  const integerPart = parseOrdinalValue(value)
372
+ checkMax(integerPart, ordinalMax)
359
373
  return integerToOrdinal(integerPart)
360
374
  }
361
375
 
@@ -363,16 +377,21 @@ function toOrdinal (value) {
363
377
  // CURRENCY: toCurrency(value, options?)
364
378
  // ============================================================================
365
379
 
380
+ /**
381
+ * @typedef {object} CurrencyOptions
382
+ * @property {boolean} [and] - Use "and" between taka and paise
383
+ */
384
+
385
+ /** @type {Required<CurrencyOptions>} */
386
+ export const currencyDefaults = { and: true }
387
+
366
388
  /**
367
389
  * Converts a numeric value to Bangladeshi English currency words.
368
- *
369
390
  * @param {number | string | bigint} value - The currency amount to convert
370
- * @param {Object} [options] - Optional configuration
371
- * @param {boolean} [options.and=true] - Use "and" between taka and paise
391
+ * @param {CurrencyOptions} [options] - Optional configuration
372
392
  * @returns {string} The amount in Bangladeshi English currency words
373
393
  * @throws {TypeError} If value is not a valid numeric type
374
394
  * @throws {Error} If value is not a valid number format
375
- *
376
395
  * @example
377
396
  * toCurrency(42.50) // 'forty-two taka and fifty paise'
378
397
  * toCurrency(100000) // 'one lakh taka'
@@ -380,10 +399,10 @@ function toOrdinal (value) {
380
399
  * toCurrency(0.50) // 'fifty paise'
381
400
  * toCurrency(42.50, { and: false }) // 'forty-two taka fifty paise'
382
401
  */
383
- function toCurrency (value, options) {
384
- options = validateOptions(options)
402
+ function toCurrency(value, options) {
385
403
  const { isNegative, dollars: taka, cents: paise } = parseCurrencyValue(value)
386
- const { and: useAnd = true } = options
404
+ checkMax(taka, currencyMax)
405
+ const { and: useAnd } = resolveOptions(options, currencyDefaults)
387
406
 
388
407
  // Build result
389
408
  let result = ''