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.
- package/CHANGELOG.md +133 -40
- package/README.md +6 -4
- package/dist/am-ET.js +2 -2
- package/dist/am-ET.umd.js +2 -2
- package/dist/am-Latn-ET.js +2 -2
- package/dist/am-Latn-ET.umd.js +2 -2
- package/dist/ar-SA.js +2 -2
- package/dist/ar-SA.umd.js +2 -2
- package/dist/az-AZ.js +2 -2
- package/dist/az-AZ.umd.js +2 -2
- package/dist/bn-BD.js +2 -2
- package/dist/bn-BD.umd.js +2 -2
- package/dist/cs-CZ.js +2 -2
- package/dist/cs-CZ.umd.js +2 -2
- package/dist/da-DK.js +2 -2
- package/dist/da-DK.umd.js +2 -2
- package/dist/de-DE.js +2 -2
- package/dist/de-DE.umd.js +2 -2
- package/dist/el-GR.js +2 -2
- package/dist/el-GR.umd.js +2 -2
- package/dist/en-AU.js +2 -2
- package/dist/en-AU.umd.js +2 -2
- package/dist/en-BD.js +2 -2
- package/dist/en-BD.umd.js +2 -2
- package/dist/en-CA.js +2 -2
- package/dist/en-CA.umd.js +2 -2
- package/dist/en-GB.js +2 -2
- package/dist/en-GB.umd.js +2 -2
- package/dist/en-GH.js +2 -2
- package/dist/en-GH.umd.js +2 -2
- package/dist/en-IE.js +2 -2
- package/dist/en-IE.umd.js +2 -2
- package/dist/en-IN.js +2 -2
- package/dist/en-IN.umd.js +2 -2
- package/dist/en-KE.js +2 -2
- package/dist/en-KE.umd.js +2 -2
- package/dist/en-MY.js +2 -2
- package/dist/en-MY.umd.js +2 -2
- package/dist/en-NG.js +2 -2
- package/dist/en-NG.umd.js +2 -2
- package/dist/en-NZ.js +2 -2
- package/dist/en-NZ.umd.js +2 -2
- package/dist/en-PH.js +2 -2
- package/dist/en-PH.umd.js +2 -2
- package/dist/en-PK.js +2 -2
- package/dist/en-PK.umd.js +2 -2
- package/dist/en-SG.js +2 -2
- package/dist/en-SG.umd.js +2 -2
- package/dist/en-US.js +2 -2
- package/dist/en-US.umd.js +2 -2
- package/dist/en-ZA.js +2 -2
- package/dist/en-ZA.umd.js +2 -2
- package/dist/es-ES.js +2 -2
- package/dist/es-ES.umd.js +2 -2
- package/dist/es-MX.js +2 -2
- package/dist/es-MX.umd.js +2 -2
- package/dist/es-US.js +2 -2
- package/dist/es-US.umd.js +2 -2
- package/dist/fa-IR.js +2 -2
- package/dist/fa-IR.umd.js +2 -2
- package/dist/fi-FI.js +2 -2
- package/dist/fi-FI.umd.js +2 -2
- package/dist/fil-PH.js +2 -2
- package/dist/fil-PH.umd.js +2 -2
- package/dist/fr-BE.js +2 -2
- package/dist/fr-BE.umd.js +2 -2
- package/dist/fr-FR.js +2 -2
- package/dist/fr-FR.umd.js +2 -2
- package/dist/gu-IN.js +2 -2
- package/dist/gu-IN.umd.js +2 -2
- package/dist/ha-NG.js +2 -2
- package/dist/ha-NG.umd.js +2 -2
- package/dist/hbo-IL.js +2 -2
- package/dist/hbo-IL.umd.js +2 -2
- package/dist/he-IL.js +2 -2
- package/dist/he-IL.umd.js +2 -2
- package/dist/hi-IN.js +2 -2
- package/dist/hi-IN.umd.js +2 -2
- package/dist/hr-HR.js +2 -2
- package/dist/hr-HR.umd.js +2 -2
- package/dist/hu-HU.js +2 -2
- package/dist/hu-HU.umd.js +2 -2
- package/dist/id-ID.js +2 -2
- package/dist/id-ID.umd.js +2 -2
- package/dist/it-IT.js +2 -2
- package/dist/it-IT.umd.js +2 -2
- package/dist/ja-JP.js +2 -2
- package/dist/ja-JP.umd.js +2 -2
- package/dist/ka-GE.js +2 -2
- package/dist/ka-GE.umd.js +2 -2
- package/dist/kn-IN.js +2 -2
- package/dist/kn-IN.umd.js +2 -2
- package/dist/ko-KR.js +2 -2
- package/dist/ko-KR.umd.js +2 -2
- package/dist/lt-LT.js +2 -2
- package/dist/lt-LT.umd.js +2 -2
- package/dist/lv-LV.js +2 -2
- package/dist/lv-LV.umd.js +2 -2
- package/dist/mr-IN.js +2 -2
- package/dist/mr-IN.umd.js +2 -2
- package/dist/ms-MY.js +2 -2
- package/dist/ms-MY.umd.js +2 -2
- package/dist/nb-NO.js +2 -2
- package/dist/nb-NO.umd.js +2 -2
- package/dist/nl-NL.js +2 -2
- package/dist/nl-NL.umd.js +2 -2
- package/dist/pa-IN.js +2 -2
- package/dist/pa-IN.umd.js +2 -2
- package/dist/pl-PL.js +2 -2
- package/dist/pl-PL.umd.js +2 -2
- package/dist/pt-BR.js +2 -2
- package/dist/pt-BR.umd.js +2 -2
- package/dist/pt-PT.js +2 -2
- package/dist/pt-PT.umd.js +2 -2
- package/dist/ro-RO.js +2 -2
- package/dist/ro-RO.umd.js +2 -2
- package/dist/ru-RU.js +2 -2
- package/dist/ru-RU.umd.js +2 -2
- package/dist/sr-Cyrl-RS.js +2 -2
- package/dist/sr-Cyrl-RS.umd.js +2 -2
- package/dist/sr-Latn-RS.js +2 -2
- package/dist/sr-Latn-RS.umd.js +2 -2
- package/dist/sv-SE.js +2 -2
- package/dist/sv-SE.umd.js +2 -2
- package/dist/sw-KE.js +2 -2
- package/dist/sw-KE.umd.js +2 -2
- package/dist/ta-IN.js +2 -2
- package/dist/ta-IN.umd.js +2 -2
- package/dist/te-IN.js +2 -2
- package/dist/te-IN.umd.js +2 -2
- package/dist/th-TH.js +2 -2
- package/dist/th-TH.umd.js +2 -2
- package/dist/tr-TR.js +2 -2
- package/dist/tr-TR.umd.js +2 -2
- package/dist/uk-UA.js +2 -2
- package/dist/uk-UA.umd.js +2 -2
- package/dist/ur-PK.js +2 -2
- package/dist/ur-PK.umd.js +2 -2
- package/dist/vi-VN.js +2 -2
- package/dist/vi-VN.umd.js +2 -2
- package/dist/yo-NG.js +2 -2
- package/dist/yo-NG.umd.js +2 -2
- package/dist/zh-Hans-CN.js +2 -2
- package/dist/zh-Hans-CN.umd.js +2 -2
- package/dist/zh-Hant-TW.js +2 -2
- package/dist/zh-Hant-TW.umd.js +2 -2
- package/package.json +33 -24
- package/src/am-ET.d.ts +3 -5
- package/src/am-ET.js +41 -16
- package/src/am-Latn-ET.d.ts +3 -5
- package/src/am-Latn-ET.js +45 -16
- package/src/ar-SA.d.ts +44 -18
- package/src/ar-SA.js +93 -40
- package/src/az-AZ.d.ts +3 -5
- package/src/az-AZ.js +58 -20
- package/src/bn-BD.d.ts +3 -5
- package/src/bn-BD.js +32 -16
- package/src/cs-CZ.d.ts +3 -6
- package/src/cs-CZ.js +66 -42
- package/src/da-DK.d.ts +3 -6
- package/src/da-DK.js +53 -48
- package/src/de-DE.d.ts +17 -11
- package/src/de-DE.js +88 -57
- package/src/el-GR.d.ts +3 -6
- package/src/el-GR.js +45 -32
- package/src/en-AU.d.ts +17 -11
- package/src/en-AU.js +56 -41
- package/src/en-BD.d.ts +17 -11
- package/src/en-BD.js +60 -41
- package/src/en-CA.d.ts +36 -18
- package/src/en-CA.js +67 -46
- package/src/en-GB.d.ts +17 -11
- package/src/en-GB.js +56 -41
- package/src/en-GH.d.ts +32 -3
- package/src/en-GH.js +104 -26
- package/src/en-IE.d.ts +17 -11
- package/src/en-IE.js +56 -41
- package/src/en-IN.d.ts +17 -11
- package/src/en-IN.js +60 -41
- package/src/en-KE.d.ts +28 -3
- package/src/en-KE.js +93 -26
- package/src/en-MY.d.ts +26 -3
- package/src/en-MY.js +91 -26
- package/src/en-NG.d.ts +17 -11
- package/src/en-NG.js +56 -41
- package/src/en-NZ.d.ts +32 -3
- package/src/en-NZ.js +85 -31
- package/src/en-PH.d.ts +32 -3
- package/src/en-PH.js +97 -26
- package/src/en-PK.d.ts +17 -11
- package/src/en-PK.js +60 -41
- package/src/en-SG.d.ts +28 -3
- package/src/en-SG.js +93 -26
- package/src/en-US.d.ts +36 -18
- package/src/en-US.js +70 -47
- package/src/en-ZA.d.ts +17 -11
- package/src/en-ZA.js +56 -41
- package/src/es-ES.d.ts +53 -21
- package/src/es-ES.js +104 -56
- package/src/es-MX.d.ts +53 -21
- package/src/es-MX.js +104 -56
- package/src/es-US.d.ts +53 -21
- package/src/es-US.js +92 -51
- package/src/fa-IR.d.ts +3 -5
- package/src/fa-IR.js +28 -13
- package/src/fi-FI.d.ts +3 -6
- package/src/fi-FI.js +47 -29
- package/src/fil-PH.d.ts +3 -5
- package/src/fil-PH.js +61 -28
- package/src/fr-BE.d.ts +31 -15
- package/src/fr-BE.js +128 -57
- package/src/fr-FR.d.ts +31 -16
- package/src/fr-FR.js +97 -60
- package/src/gu-IN.d.ts +3 -5
- package/src/gu-IN.js +31 -16
- package/src/ha-NG.d.ts +3 -5
- package/src/ha-NG.js +55 -27
- package/src/hbo-IL.d.ts +26 -12
- package/src/hbo-IL.js +92 -51
- package/src/he-IL.d.ts +17 -10
- package/src/he-IL.js +92 -50
- package/src/hi-IN.d.ts +3 -5
- package/src/hi-IN.js +30 -17
- package/src/hr-HR.d.ts +21 -10
- package/src/hr-HR.js +89 -33
- package/src/hu-HU.d.ts +3 -5
- package/src/hu-HU.js +57 -23
- package/src/id-ID.d.ts +3 -5
- package/src/id-ID.js +56 -23
- package/src/it-IT.d.ts +17 -11
- package/src/it-IT.js +74 -43
- package/src/ja-JP.d.ts +3 -6
- package/src/ja-JP.js +39 -26
- package/src/ka-GE.d.ts +3 -6
- package/src/ka-GE.js +38 -26
- package/src/kn-IN.d.ts +3 -5
- package/src/kn-IN.js +31 -16
- package/src/ko-KR.d.ts +3 -6
- package/src/ko-KR.js +34 -26
- package/src/lt-LT.d.ts +21 -11
- package/src/lt-LT.js +64 -42
- package/src/lv-LV.d.ts +21 -11
- package/src/lv-LV.js +79 -51
- package/src/mr-IN.d.ts +3 -5
- package/src/mr-IN.js +31 -16
- package/src/ms-MY.d.ts +3 -5
- package/src/ms-MY.js +58 -24
- package/src/nb-NO.d.ts +3 -6
- package/src/nb-NO.js +54 -34
- package/src/nl-NL.d.ts +41 -20
- package/src/nl-NL.js +111 -69
- package/src/pa-IN.d.ts +3 -5
- package/src/pa-IN.js +32 -16
- package/src/pl-PL.d.ts +21 -11
- package/src/pl-PL.js +69 -45
- package/src/pt-BR.d.ts +22 -11
- package/src/pt-BR.js +93 -53
- package/src/pt-PT.d.ts +17 -11
- package/src/pt-PT.js +80 -48
- package/src/ro-RO.d.ts +21 -11
- package/src/ro-RO.js +77 -39
- package/src/ru-RU.d.ts +35 -15
- package/src/ru-RU.js +100 -38
- package/src/sr-Cyrl-RS.d.ts +35 -15
- package/src/sr-Cyrl-RS.js +106 -43
- package/src/sr-Latn-RS.d.ts +35 -15
- package/src/sr-Latn-RS.js +106 -43
- package/src/sv-SE.d.ts +3 -6
- package/src/sv-SE.js +53 -34
- package/src/sw-KE.d.ts +3 -5
- package/src/sw-KE.js +50 -20
- package/src/ta-IN.d.ts +3 -5
- package/src/ta-IN.js +29 -17
- package/src/te-IN.d.ts +3 -5
- package/src/te-IN.js +31 -16
- package/src/th-TH.d.ts +3 -5
- package/src/th-TH.js +42 -19
- package/src/tr-TR.d.ts +17 -11
- package/src/tr-TR.js +63 -37
- package/src/uk-UA.d.ts +21 -10
- package/src/uk-UA.js +89 -33
- package/src/ur-PK.d.ts +3 -5
- package/src/ur-PK.js +32 -16
- package/src/utils/check-max.d.ts +26 -0
- package/src/utils/check-max.js +33 -0
- package/src/utils/expand-scientific.d.ts +0 -4
- package/src/utils/expand-scientific.js +7 -9
- package/src/utils/is-plain-object.d.ts +3 -4
- package/src/utils/is-plain-object.js +3 -4
- package/src/utils/parse-cardinal.d.ts +1 -2
- package/src/utils/parse-cardinal.js +12 -9
- package/src/utils/parse-currency.d.ts +1 -2
- package/src/utils/parse-currency.js +9 -11
- package/src/utils/parse-ordinal.d.ts +0 -1
- package/src/utils/parse-ordinal.js +9 -10
- package/src/utils/resolve-options.d.ts +17 -0
- package/src/utils/resolve-options.js +56 -0
- package/src/utils/scale.d.ts +49 -0
- package/src/utils/scale.js +65 -0
- package/src/vi-VN.d.ts +3 -6
- package/src/vi-VN.js +41 -28
- package/src/yo-NG.d.ts +3 -5
- package/src/yo-NG.js +49 -33
- package/src/zh-Hans-CN.d.ts +45 -20
- package/src/zh-Hans-CN.js +84 -31
- package/src/zh-Hant-TW.d.ts +45 -20
- package/src/zh-Hant-TW.js +85 -34
- package/src/utils/validate-options.d.ts +0 -8
- 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 {
|
|
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
|
|
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
|
-
}
|
|
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
|
-
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
91
98
|
parts.push(TENS[tens])
|
|
92
99
|
}
|
|
93
|
-
}
|
|
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
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
|
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
|
-
}
|
|
248
|
+
}
|
|
249
|
+
else if (i === 1) {
|
|
228
250
|
// Thousands
|
|
229
251
|
if (segment === 1) {
|
|
230
252
|
result += THOUSAND
|
|
231
|
-
}
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
232
255
|
result += segmentResult.word + ' ' + THOUSAND
|
|
233
256
|
}
|
|
234
257
|
prevWasScale = true
|
|
235
|
-
}
|
|
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
|
-
}
|
|
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
|
|
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
|
|
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
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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
|
|
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
|
-
}
|
|
404
|
+
}
|
|
405
|
+
else if (segment === 1 && i > 0) {
|
|
382
406
|
// Exact scale: "milésimo", "milionésimo", etc.
|
|
383
407
|
result += SCALE_ORDINAL[i]
|
|
384
|
-
}
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
385
410
|
// Segment + scale ordinal
|
|
386
411
|
result += buildOrdinalSegment(segment) + ' ' + SCALE_ORDINAL[i]
|
|
387
412
|
}
|
|
388
|
-
}
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
389
415
|
// Higher segments use cardinal form
|
|
390
416
|
if (i === 0) {
|
|
391
417
|
result += buildSegment(segment).word
|
|
392
|
-
}
|
|
418
|
+
}
|
|
419
|
+
else if (i === 1) {
|
|
393
420
|
if (segment === 1) {
|
|
394
421
|
result += THOUSAND
|
|
395
|
-
}
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
396
424
|
result += buildSegment(segment).word + ' ' + THOUSAND
|
|
397
425
|
}
|
|
398
|
-
}
|
|
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
|
-
}
|
|
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
|
|
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 {
|
|
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
|
|
468
|
-
options = validateOptions(options)
|
|
502
|
+
function toCurrency(value, options) {
|
|
469
503
|
const { isNegative, dollars: majorUnits, cents: minorUnits } = parseCurrencyValue(value)
|
|
470
|
-
|
|
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 =
|
|
508
|
+
let currencyCode = currency
|
|
474
509
|
if (!currencyCode) {
|
|
475
510
|
try {
|
|
476
|
-
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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 {
|
|
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;
|