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/pt-BR.js CHANGED
@@ -14,7 +14,9 @@
14
14
  import { parseCardinalValue } from './utils/parse-cardinal.js'
15
15
  import { parseCurrencyValue } from './utils/parse-currency.js'
16
16
  import { parseOrdinalValue } from './utils/parse-ordinal.js'
17
- import { validateOptions } from './utils/validate-options.js'
17
+ import { checkMax } from './utils/check-max.js'
18
+ import { western } from './utils/scale.js'
19
+ import { resolveOptions } from './utils/resolve-options.js'
18
20
 
19
21
  // ============================================================================
20
22
  // Vocabulary (module-level constants)
@@ -43,12 +45,13 @@ const ORDINAL_HUNDREDS = ['', 'centésimo', 'ducentésimo', 'tricentésimo', 'qu
43
45
  // ============================================================================
44
46
 
45
47
  // Dicionário focado no uso no Brasil (centavos para dólar e euro em vez de cêntimos)
48
+ /** @type {Record<string, {major: string[], minor: string[]}>} */
46
49
  const CURRENCIES = {
47
50
  BRL: { major: ['real', 'reais'], minor: ['centavo', 'centavos'] },
48
51
  USD: { major: ['dólar', 'dólares'], minor: ['centavo', 'centavos'] },
49
52
  EUR: { major: ['euro', 'euros'], minor: ['centavo', 'centavos'] }, // No Brasil é comum falar "centavos de euro"
50
53
  GBP: { major: ['libra', 'libras'], minor: ['pêni', 'pence'] },
51
- JPY: { major: ['iene', 'ienes'], minor: ['sen', 'sen'] } // Iene não tem subdivisão usada no dia a dia
54
+ JPY: { major: ['iene', 'ienes'], minor: ['sen', 'sen'] }, // Iene não tem subdivisão usada no dia a dia
52
55
  }
53
56
 
54
57
  // Fallback para caso o usuário passe uma moeda não mapeada (ex: 'CAD')
@@ -61,8 +64,10 @@ const DEFAULT_CURRENCY_WORDS = { major: ['unidade', 'unidades'], minor: ['centav
61
64
  /**
62
65
  * Builds segment word for 0-999 with Portuguese "e" rules.
63
66
  * Returns the word and whether it's an exact hundred (for "cem" handling).
67
+ * @param {number} n - Number 0-999
68
+ * @returns {{word: string, isExactHundred: boolean, startsWithHundreds?: boolean}} The segment word and hundred-related flags
64
69
  */
65
- function buildSegment (n) {
70
+ function buildSegment(n) {
66
71
  if (n === 0) return { word: '', isExactHundred: false }
67
72
 
68
73
  // Special case: exact 100 is "cem"
@@ -83,14 +88,17 @@ function buildSegment (n) {
83
88
  if (tens === 1) {
84
89
  // Teens (10-19)
85
90
  parts.push(TEENS[ones])
86
- } else if (tens >= 2) {
91
+ }
92
+ else if (tens >= 2) {
87
93
  if (ones > 0) {
88
94
  // Tens + ones with "e": "vinte e um"
89
95
  parts.push(TENS[tens] + ' e ' + ONES[ones])
90
- } else {
96
+ }
97
+ else {
91
98
  parts.push(TENS[tens])
92
99
  }
93
- } else if (ones > 0) {
100
+ }
101
+ else if (ones > 0) {
94
102
  parts.push(ONES[ones])
95
103
  }
96
104
 
@@ -115,7 +123,7 @@ const SCALE_WORDS_SINGULAR = [
115
123
  'quatrilhão', // 5: 10^15
116
124
  'quintilhão', // 6: 10^18
117
125
  'sextilhão', // 7: 10^21
118
- 'setilhão' // 8: 10^24
126
+ 'setilhão', // 8: 10^24
119
127
  ]
120
128
 
121
129
  const SCALE_WORDS_PLURAL = [
@@ -127,20 +135,32 @@ const SCALE_WORDS_PLURAL = [
127
135
  'quatrilhões', // 5: 10^15
128
136
  'quintilhões', // 6: 10^18
129
137
  'sextilhões', // 7: 10^21
130
- 'setilhões' // 8: 10^24
138
+ 'setilhões', // 8: 10^24
131
139
  ]
132
140
 
141
+ // Scale ordinal words (short scale for pt-BR). Module-scope so its length can
142
+ // derive the ordinal ceiling and so it isn't rebuilt on every call.
143
+ const SCALE_ORDINAL = ['', 'milésimo', 'milionésimo', 'bilionésimo', 'trilionésimo', 'quatrilionésimo', 'quintilionésimo', 'sextilionésimo']
144
+
145
+ // Supported magnitude ceilings (checked at the public entry points). Cardinal
146
+ // scale words reach index SCALE_WORDS_SINGULAR.length-1 (setilhão, 10^24), so
147
+ // cardinals/currency must stay below 10^27. The ordinal of a number whose
148
+ // lowest non-zero group is a scale group uses SCALE_ORDINAL, which is shorter,
149
+ // so ordinals must stay below 10^(SCALE_ORDINAL.length * 3).
150
+ export const cardinalMax = western(SCALE_WORDS_SINGULAR.length - 1)
151
+ export const ordinalMax = western(SCALE_ORDINAL.length - 1)
152
+ export const currencyMax = western(SCALE_WORDS_SINGULAR.length - 1)
153
+
133
154
  // ============================================================================
134
155
  // Conversion Functions
135
156
  // ============================================================================
136
157
 
137
158
  /**
138
159
  * Converts a non-negative integer to Portuguese words.
139
- *
140
160
  * @param {bigint} n - Non-negative integer to convert
141
161
  * @returns {string} Portuguese words
142
162
  */
143
- function integerToWords (n) {
163
+ function integerToWords(n) {
144
164
  if (n === 0n) return ZERO
145
165
 
146
166
  // Fast path: numbers < 1000
@@ -157,7 +177,8 @@ function integerToWords (n) {
157
177
  if (thousands === 1) {
158
178
  // "mil" not "um mil"
159
179
  result = THOUSAND
160
- } else {
180
+ }
181
+ else {
161
182
  result = buildSegment(thousands).word + ' ' + THOUSAND
162
183
  }
163
184
 
@@ -166,7 +187,8 @@ function integerToWords (n) {
166
187
  // REGRA DO "E": Menor que 100 OU Centena Exata (ex: 500)
167
188
  if (!remainderResult.startsWithHundreds || remainderResult.isExactHundred) {
168
189
  result += ' e ' + remainderResult.word
169
- } else {
190
+ }
191
+ else {
170
192
  result += ' ' + remainderResult.word
171
193
  }
172
194
  }
@@ -181,11 +203,10 @@ function integerToWords (n) {
181
203
  /**
182
204
  * Builds words for numbers >= 1,000,000.
183
205
  * Uses BigInt division for faster segment extraction.
184
- *
185
206
  * @param {bigint} n - Number >= 1,000,000
186
207
  * @returns {string} Portuguese words
187
208
  */
188
- function buildLargeNumberWords (n) {
209
+ function buildLargeNumberWords(n) {
189
210
  // Extract segments using BigInt division
190
211
  const segments = []
191
212
  let temp = n
@@ -224,20 +245,24 @@ function buildLargeNumberWords (n) {
224
245
  // Units segment
225
246
  result += segmentResult.word
226
247
  prevWasScale = false
227
- } else if (i === 1) {
248
+ }
249
+ else if (i === 1) {
228
250
  // Thousands
229
251
  if (segment === 1) {
230
252
  result += THOUSAND
231
- } else {
253
+ }
254
+ else {
232
255
  result += segmentResult.word + ' ' + THOUSAND
233
256
  }
234
257
  prevWasScale = true
235
- } else {
258
+ }
259
+ else {
236
260
  // Million and above
237
261
  const scaleWord = segment === 1 ? SCALE_WORDS_SINGULAR[i] : SCALE_WORDS_PLURAL[i]
238
262
  if (segment === 1) {
239
263
  result += 'um ' + scaleWord
240
- } else {
264
+ }
265
+ else {
241
266
  result += segmentResult.word + ' ' + scaleWord
242
267
  }
243
268
  prevWasScale = true
@@ -249,11 +274,10 @@ function buildLargeNumberWords (n) {
249
274
 
250
275
  /**
251
276
  * Converts decimal digits to Portuguese words.
252
- *
253
277
  * @param {string} decimalPart - Decimal digits (without the point)
254
278
  * @returns {string} Portuguese words for decimal part
255
279
  */
256
- function decimalPartToWords (decimalPart) {
280
+ function decimalPartToWords(decimalPart) {
257
281
  let result = ''
258
282
 
259
283
  // Handle leading zeros
@@ -276,12 +300,14 @@ function decimalPartToWords (decimalPart) {
276
300
 
277
301
  /**
278
302
  * Converts a numeric value to Portuguese words.
279
- *
280
303
  * @param {number | string | bigint} value - The numeric value to convert
281
304
  * @returns {string} The number in Portuguese words
282
305
  */
283
- function toCardinal (value) {
306
+ function toCardinal(value) {
284
307
  const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
308
+ // Both the integer part and the decimal's significant digits are spelled via
309
+ // the scale builder, so both must clear the ceiling.
310
+ checkMax(integerPart, cardinalMax, decimalPart)
285
311
 
286
312
  let result = ''
287
313
 
@@ -304,11 +330,10 @@ function toCardinal (value) {
304
330
 
305
331
  /**
306
332
  * Builds ordinal words for 0-999.
307
- *
308
333
  * @param {number} n - Number 0-999
309
334
  * @returns {string} Portuguese ordinal words
310
335
  */
311
- function buildOrdinalSegment (n) {
336
+ function buildOrdinalSegment(n) {
312
337
  if (n === 0) return ''
313
338
 
314
339
  const ones = n % 10
@@ -326,12 +351,14 @@ function buildOrdinalSegment (n) {
326
351
  if (tens === 1) {
327
352
  // 10-19: use teens array (décimo, décimo primeiro, etc.)
328
353
  parts.push(ORDINAL_TEENS[ones])
329
- } else if (tens >= 2) {
354
+ }
355
+ else if (tens >= 2) {
330
356
  parts.push(ORDINAL_TENS[tens])
331
357
  if (ones > 0) {
332
358
  parts.push(ORDINAL_ONES[ones])
333
359
  }
334
- } else if (ones > 0) {
360
+ }
361
+ else if (ones > 0) {
335
362
  parts.push(ORDINAL_ONES[ones])
336
363
  }
337
364
 
@@ -340,11 +367,10 @@ function buildOrdinalSegment (n) {
340
367
 
341
368
  /**
342
369
  * Builds ordinal words for large numbers.
343
- *
344
370
  * @param {bigint} n - Non-negative integer
345
371
  * @returns {string} Portuguese ordinal words
346
372
  */
347
- function buildLargeOrdinal (n) {
373
+ function buildLargeOrdinal(n) {
348
374
  // Extract segments
349
375
  const segments = []
350
376
  let temp = n
@@ -362,9 +388,6 @@ function buildLargeOrdinal (n) {
362
388
  }
363
389
  }
364
390
 
365
- // Scale ordinal words (singular forms - short scale for pt-BR)
366
- const SCALE_ORDINAL = ['', 'milésimo', 'milionésimo', 'bilionésimo', 'trilionésimo', 'quatrilionésimo', 'quintilionésimo', 'sextilionésimo']
367
-
368
391
  let result = ''
369
392
 
370
393
  for (let i = segments.length - 1; i >= 0; i--) {
@@ -378,28 +401,35 @@ function buildLargeOrdinal (n) {
378
401
  if (i === 0) {
379
402
  // Units: just ordinal
380
403
  result += buildOrdinalSegment(segment)
381
- } else if (segment === 1 && i > 0) {
404
+ }
405
+ else if (segment === 1 && i > 0) {
382
406
  // Exact scale: "milésimo", "milionésimo", etc.
383
407
  result += SCALE_ORDINAL[i]
384
- } else {
408
+ }
409
+ else {
385
410
  // Segment + scale ordinal
386
411
  result += buildOrdinalSegment(segment) + ' ' + SCALE_ORDINAL[i]
387
412
  }
388
- } else {
413
+ }
414
+ else {
389
415
  // Higher segments use cardinal form
390
416
  if (i === 0) {
391
417
  result += buildSegment(segment).word
392
- } else if (i === 1) {
418
+ }
419
+ else if (i === 1) {
393
420
  if (segment === 1) {
394
421
  result += THOUSAND
395
- } else {
422
+ }
423
+ else {
396
424
  result += buildSegment(segment).word + ' ' + THOUSAND
397
425
  }
398
- } else {
426
+ }
427
+ else {
399
428
  const scaleWord = segment === 1 ? SCALE_WORDS_SINGULAR[i] : SCALE_WORDS_PLURAL[i]
400
429
  if (segment === 1) {
401
430
  result += 'um ' + scaleWord
402
- } else {
431
+ }
432
+ else {
403
433
  result += buildSegment(segment).word + ' ' + scaleWord
404
434
  }
405
435
  }
@@ -411,12 +441,12 @@ function buildLargeOrdinal (n) {
411
441
 
412
442
  /**
413
443
  * Converts a number to Portuguese ordinal words.
414
- *
415
444
  * @param {number | string | bigint} value - The number to convert
416
445
  * @returns {string} Portuguese ordinal words
417
446
  */
418
- function toOrdinal (value) {
447
+ function toOrdinal(value) {
419
448
  const n = parseOrdinalValue(value)
449
+ checkMax(n, ordinalMax)
420
450
 
421
451
  // Fast path: 1-9
422
452
  if (n < 10n) {
@@ -451,31 +481,40 @@ function toOrdinal (value) {
451
481
  // Currency Functions
452
482
  // ============================================================================
453
483
 
484
+ /**
485
+ * @typedef {object} CurrencyOptions
486
+ * @property {boolean} [and] - Include "e" between major and minor units
487
+ * @property {string} [currency] - Currency code (e.g., 'BRL', 'USD'); empty means auto-detect for pt-BR
488
+ */
489
+
490
+ /** @type {Required<CurrencyOptions>} */
491
+ export const currencyDefaults = { and: true, currency: '' }
492
+
454
493
  /**
455
494
  * Converts a number to Brazilian Portuguese currency words.
456
- *
457
495
  * @param {number | string | bigint} value - The amount to convert
458
- * @param {Object} [options]
459
- * @param {boolean} [options.and=true] - Include "e" between major and minor units
460
- * @param {string} [options.currency] - Currency code (e.g., 'BRL', 'USD')
496
+ * @param {CurrencyOptions} [options] Currency formatting options
461
497
  * @returns {string} Brazilian Portuguese currency words
462
- *
463
498
  * @example
464
499
  * toCurrency(42.50) // 'quarenta e dois reais e cinquenta centavos'
465
500
  * toCurrency(42.50, {currency: 'USD'}) // 'quarenta e dois dólares e cinquenta centavos'
466
501
  */
467
- function toCurrency (value, options) {
468
- options = validateOptions(options)
502
+ function toCurrency(value, options) {
469
503
  const { isNegative, dollars: majorUnits, cents: minorUnits } = parseCurrencyValue(value)
470
- const { and = true } = options
504
+ checkMax(majorUnits, currencyMax)
505
+ const { and, currency } = resolveOptions(options, currencyDefaults)
471
506
 
472
507
  // 1. Descobre a moeda informada ou busca automaticamente a padrão do país (pt-BR = BRL)
473
- let currencyCode = options.currency
508
+ let currencyCode = currency
474
509
  if (!currencyCode) {
475
510
  try {
476
- const localeInfo = new Intl.Locale('pt-BR')
511
+ // Intl Locale Info (getCurrencies) is a newer TC39 API present at
512
+ // runtime in modern engines but not yet in the TS ES2022 lib types;
513
+ // augment the type locally rather than widen the project's lib.
514
+ const localeInfo = /** @type {Intl.Locale & { getCurrencies(): string[] }} */ (new Intl.Locale('pt-BR'))
477
515
  currencyCode = localeInfo.getCurrencies?.()[0]
478
- } catch (e) {
516
+ }
517
+ catch {
479
518
  // Ignora erro em ambientes antigos (fallback garantido abaixo)
480
519
  }
481
520
  currencyCode = currencyCode || 'BRL' // Padrão absoluto para o Brasil
@@ -485,7 +524,7 @@ function toCurrency (value, options) {
485
524
  // 2. Busca os nomes no dicionário ou usa o fallback genérico
486
525
  const currencyWords = CURRENCIES[currencyCode] || {
487
526
  major: [currencyCode, currencyCode],
488
- minor: DEFAULT_CURRENCY_WORDS.minor
527
+ minor: DEFAULT_CURRENCY_WORDS.minor,
489
528
  }
490
529
 
491
530
  let result = ''
@@ -519,7 +558,8 @@ function toCurrency (value, options) {
519
558
  // Ignora adicionar unidade de centavos se a moeda não os tiver (ex: JPY onde minor é string vazia)
520
559
  if (minorUnit === '') {
521
560
  result += minorText
522
- } else {
561
+ }
562
+ else {
523
563
  result += minorText + ' ' + minorUnit
524
564
  }
525
565
  }
package/src/pt-PT.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] - Include "e" between euros and cents
7
+ */
8
+ /** @type {Required<CurrencyOptions>} */
9
+ export const currencyDefaults: Required<CurrencyOptions>;
10
+ export type CurrencyOptions = {
11
+ /**
12
+ * - Include "e" between euros and cents
13
+ */
14
+ and?: boolean | undefined;
15
+ };
1
16
  /**
2
17
  * Converts a numeric value to Portuguese 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 Portuguese 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(21) // 'vinte e um'
14
27
  * toCardinal(100) // 'cem'
@@ -17,10 +30,8 @@
17
30
  export function toCardinal(value: number | string | bigint): string;
18
31
  /**
19
32
  * Converts a number to Portuguese ordinal words.
20
- *
21
33
  * @param {number | string | bigint} value - The number to convert
22
34
  * @returns {string} Portuguese ordinal words
23
- *
24
35
  * @example
25
36
  * toOrdinal(1) // 'primeiro'
26
37
  * toOrdinal(21) // 'vigésimo primeiro'
@@ -29,17 +40,12 @@ export function toCardinal(value: number | string | bigint): string;
29
40
  export function toOrdinal(value: number | string | bigint): string;
30
41
  /**
31
42
  * Converts a number to Portuguese currency words (Euro).
32
- *
33
43
  * @param {number | string | bigint} value - The amount to convert
34
- * @param {Object} [options]
35
- * @param {boolean} [options.and=true] - Include "e" between euros and cents
44
+ * @param {CurrencyOptions} [options] Currency formatting options
36
45
  * @returns {string} Portuguese currency words
37
- *
38
46
  * @example
39
47
  * toCurrency(42.50) // 'quarenta e dois euros e cinquenta cêntimos'
40
48
  * toCurrency(1) // 'um euro'
41
49
  * toCurrency(0.01) // 'um cêntimo'
42
50
  */
43
- export function toCurrency(value: number | string | bigint, options?: {
44
- and?: boolean | undefined;
45
- }): string;
51
+ export function toCurrency(value: number | string | bigint, options?: CurrencyOptions): string;