n2words 4.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/README.md +14 -12
- 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 -0
- package/dist/pt-BR.umd.js +2 -0
- 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 +53 -36
- 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 +42 -0
- package/src/pt-BR.js +574 -0
- 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 +100 -38
- package/src/sr-Latn-RS.d.ts +35 -15
- package/src/sr-Latn-RS.js +100 -38
- 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/es-MX.js
CHANGED
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
import { parseCardinalValue } from './utils/parse-cardinal.js'
|
|
21
21
|
import { parseCurrencyValue } from './utils/parse-currency.js'
|
|
22
22
|
import { parseOrdinalValue } from './utils/parse-ordinal.js'
|
|
23
|
-
import {
|
|
23
|
+
import { checkMax } from './utils/check-max.js'
|
|
24
|
+
import { bounded, longScale } from './utils/scale.js'
|
|
25
|
+
import { resolveOptions } from './utils/resolve-options.js'
|
|
24
26
|
|
|
25
27
|
// ============================================================================
|
|
26
28
|
// Vocabulary (module-level constants)
|
|
@@ -45,6 +47,15 @@ const HUNDREDS_FEM = ['', 'cienta', 'doscientas', 'trescientas', 'cuatrocientas'
|
|
|
45
47
|
const SCALES = ['millón', 'billón', 'trillón', 'cuatrillón']
|
|
46
48
|
const SCALES_PLURAL = ['millones', 'billones', 'trillones', 'cuatrillones']
|
|
47
49
|
|
|
50
|
+
// Supported magnitude ceilings (checked at the public entry points). Each
|
|
51
|
+
// SCALES entry spans two segment groups (X and "mil X"); with the units group
|
|
52
|
+
// that's 2 * SCALES.length + 2 groups of 3 digits, so cardinals must stay below
|
|
53
|
+
// 10^30. Ordinals are bounded lower: the millions multiplier uses
|
|
54
|
+
// buildOrdinalSegment (0-999), so n must stay below 10^9.
|
|
55
|
+
export const cardinalMax = longScale(SCALES.length)
|
|
56
|
+
export const ordinalMax = bounded(9)
|
|
57
|
+
export const currencyMax = longScale(SCALES.length)
|
|
58
|
+
|
|
48
59
|
const THOUSAND = 'mil'
|
|
49
60
|
|
|
50
61
|
const ZERO = 'cero'
|
|
@@ -80,7 +91,7 @@ const CURRENCY_CONNECTOR = 'con'
|
|
|
80
91
|
* @param {boolean} feminine - Use feminine forms
|
|
81
92
|
* @returns {string} Spanish word
|
|
82
93
|
*/
|
|
83
|
-
function buildSegment
|
|
94
|
+
function buildSegment(n, feminine) {
|
|
84
95
|
if (n === 0) return ''
|
|
85
96
|
|
|
86
97
|
// Special case: exact 100 is "cien" (no gender)
|
|
@@ -102,22 +113,27 @@ function buildSegment (n, feminine) {
|
|
|
102
113
|
// Tens and ones
|
|
103
114
|
if (tensOnes === 0) {
|
|
104
115
|
// Just hundreds
|
|
105
|
-
}
|
|
116
|
+
}
|
|
117
|
+
else if (tensOnes < 10) {
|
|
106
118
|
// Single digit
|
|
107
119
|
const onesArr = feminine ? ONES_FEM : ONES_MASC
|
|
108
120
|
parts.push(onesArr[tensOnes])
|
|
109
|
-
}
|
|
121
|
+
}
|
|
122
|
+
else if (tensOnes < 20) {
|
|
110
123
|
// 10-19: teens
|
|
111
124
|
parts.push(TEENS[ones])
|
|
112
|
-
}
|
|
125
|
+
}
|
|
126
|
+
else if (tensOnes < 30) {
|
|
113
127
|
// 20-29: special twenties
|
|
114
128
|
const twentiesArr = feminine ? TWENTIES_FEM : TWENTIES_MASC
|
|
115
129
|
parts.push(twentiesArr[ones])
|
|
116
|
-
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
117
132
|
// 30-99: tens y ones
|
|
118
133
|
if (ones === 0) {
|
|
119
134
|
parts.push(TENS[tens])
|
|
120
|
-
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
121
137
|
const onesArr = feminine ? ONES_FEM : ONES_MASC
|
|
122
138
|
parts.push(TENS[tens] + ' y ' + onesArr[ones])
|
|
123
139
|
}
|
|
@@ -132,12 +148,11 @@ function buildSegment (n, feminine) {
|
|
|
132
148
|
|
|
133
149
|
/**
|
|
134
150
|
* Gets scale word for Spanish compound long scale.
|
|
135
|
-
*
|
|
136
151
|
* @param {number} scaleIndex - Scale level (1 = thousand, 2 = million, etc.)
|
|
137
152
|
* @param {bigint} segment - Segment value for pluralization
|
|
138
153
|
* @returns {string} Scale word
|
|
139
154
|
*/
|
|
140
|
-
function getScaleWord
|
|
155
|
+
function getScaleWord(scaleIndex, segment) {
|
|
141
156
|
if (scaleIndex === 1) return THOUSAND
|
|
142
157
|
|
|
143
158
|
// Even indices (2, 4, 6, 8): millón, billón, trillón, cuatrillón
|
|
@@ -147,7 +162,8 @@ function getScaleWord (scaleIndex, segment) {
|
|
|
147
162
|
const baseWord = SCALES[arrayIndex]
|
|
148
163
|
if (!baseWord) return ''
|
|
149
164
|
return segment > 1n ? SCALES_PLURAL[arrayIndex] : baseWord
|
|
150
|
-
}
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
151
167
|
// Compound: "mil millones" pattern
|
|
152
168
|
const arrayIndex = ((scaleIndex - 1) / 2) - 1
|
|
153
169
|
const pluralWord = SCALES_PLURAL[arrayIndex]
|
|
@@ -162,12 +178,11 @@ function getScaleWord (scaleIndex, segment) {
|
|
|
162
178
|
|
|
163
179
|
/**
|
|
164
180
|
* Converts a non-negative integer to Spanish words.
|
|
165
|
-
*
|
|
166
181
|
* @param {bigint} n - Non-negative integer to convert
|
|
167
182
|
* @param {boolean} feminine - Use feminine forms
|
|
168
183
|
* @returns {string} Spanish words
|
|
169
184
|
*/
|
|
170
|
-
function integerToWords
|
|
185
|
+
function integerToWords(n, feminine) {
|
|
171
186
|
if (n === 0n) return ZERO
|
|
172
187
|
|
|
173
188
|
// Fast path: numbers < 1000
|
|
@@ -184,13 +199,15 @@ function integerToWords (n, feminine) {
|
|
|
184
199
|
if (thousands === 1) {
|
|
185
200
|
// "mil" not "uno mil"
|
|
186
201
|
result = THOUSAND
|
|
187
|
-
}
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
188
204
|
// Use masculine for thousands segment, but check for "uno" → omit before mil
|
|
189
205
|
const thousandsWord = buildSegment(thousands, false)
|
|
190
206
|
// "uno mil" → "mil" (handled in joinSegments equivalent)
|
|
191
207
|
if (thousandsWord === 'uno' || thousandsWord === 'una') {
|
|
192
208
|
result = THOUSAND
|
|
193
|
-
}
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
194
211
|
result = thousandsWord + ' ' + THOUSAND
|
|
195
212
|
}
|
|
196
213
|
}
|
|
@@ -209,14 +226,14 @@ function integerToWords (n, feminine) {
|
|
|
209
226
|
/**
|
|
210
227
|
* Builds words for numbers >= 1,000,000.
|
|
211
228
|
* Uses BigInt division for faster segment extraction.
|
|
212
|
-
*
|
|
213
229
|
* @param {bigint} n - Number >= 1,000,000
|
|
214
230
|
* @param {boolean} feminine - Use feminine forms
|
|
215
231
|
* @returns {string} Spanish words
|
|
216
232
|
*/
|
|
217
|
-
function buildLargeNumberWords
|
|
233
|
+
function buildLargeNumberWords(n, feminine) {
|
|
218
234
|
// Extract segments using BigInt division (faster than string slicing)
|
|
219
235
|
// Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)
|
|
236
|
+
// Callers guard the magnitude (cardinalMax) before reaching here.
|
|
220
237
|
const segmentValues = []
|
|
221
238
|
let temp = n
|
|
222
239
|
while (temp > 0n) {
|
|
@@ -238,27 +255,33 @@ function buildLargeNumberWords (n, feminine) {
|
|
|
238
255
|
if (i === 0) {
|
|
239
256
|
// Units segment
|
|
240
257
|
result += buildSegment(Number(segment), feminine)
|
|
241
|
-
}
|
|
258
|
+
}
|
|
259
|
+
else if (i === 1) {
|
|
242
260
|
// Thousands: omit "uno" before mil
|
|
243
261
|
if (segment === 1n) {
|
|
244
262
|
result += THOUSAND
|
|
245
|
-
}
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
246
265
|
result += buildSegment(Number(segment), false) + ' ' + scaleWord
|
|
247
266
|
}
|
|
248
|
-
}
|
|
267
|
+
}
|
|
268
|
+
else if (i % 2 === 1) {
|
|
249
269
|
// Odd scale indices (3, 5, 7): "mil millones", "mil billones", etc.
|
|
250
270
|
// Omit "uno" before these compound scales
|
|
251
271
|
if (segment === 1n) {
|
|
252
272
|
result += scaleWord
|
|
253
|
-
}
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
254
275
|
result += buildSegment(Number(segment), false) + ' ' + scaleWord
|
|
255
276
|
}
|
|
256
|
-
}
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
257
279
|
// Even scale indices (2, 4, 6): millón, billón, trillón
|
|
258
280
|
if (segment === 1n) {
|
|
259
281
|
// "un millón" not "uno millón"
|
|
260
282
|
result += 'un ' + scaleWord
|
|
261
|
-
}
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
262
285
|
// Use masculine for scale segment
|
|
263
286
|
result += buildSegment(Number(segment), false) + ' ' + scaleWord
|
|
264
287
|
}
|
|
@@ -270,12 +293,11 @@ function buildLargeNumberWords (n, feminine) {
|
|
|
270
293
|
|
|
271
294
|
/**
|
|
272
295
|
* Converts decimal digits to Spanish words.
|
|
273
|
-
*
|
|
274
296
|
* @param {string} decimalPart - Decimal digits (without the point)
|
|
275
297
|
* @param {boolean} feminine - Use feminine forms
|
|
276
298
|
* @returns {string} Spanish words for decimal part
|
|
277
299
|
*/
|
|
278
|
-
function decimalPartToWords
|
|
300
|
+
function decimalPartToWords(decimalPart, feminine) {
|
|
279
301
|
let result = ''
|
|
280
302
|
|
|
281
303
|
// Handle leading zeros
|
|
@@ -296,27 +318,37 @@ function decimalPartToWords (decimalPart, feminine) {
|
|
|
296
318
|
return result
|
|
297
319
|
}
|
|
298
320
|
|
|
321
|
+
/**
|
|
322
|
+
* @typedef {object} CardinalOptions
|
|
323
|
+
* @property {('masculine'|'feminine')} [gender] - Grammatical gender
|
|
324
|
+
*/
|
|
325
|
+
|
|
326
|
+
/** @type {Required<CardinalOptions>} */
|
|
327
|
+
export const cardinalDefaults = { gender: 'masculine' }
|
|
328
|
+
|
|
329
|
+
/** @type {{ gender: ReadonlyArray<Required<CardinalOptions>['gender']> }} */
|
|
330
|
+
export const cardinalValues = { gender: ['masculine', 'feminine'] }
|
|
331
|
+
|
|
299
332
|
/**
|
|
300
333
|
* Converts a numeric value to Spanish words (long scale).
|
|
301
|
-
*
|
|
302
334
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
303
|
-
* @param {
|
|
304
|
-
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
335
|
+
* @param {CardinalOptions} [options] - Optional configuration
|
|
305
336
|
* @returns {string} The number in Spanish words
|
|
306
337
|
* @throws {TypeError} If value is not a valid numeric type
|
|
307
338
|
* @throws {Error} If value is not a valid number format
|
|
308
|
-
*
|
|
309
339
|
* @example
|
|
310
340
|
* toCardinal(21) // 'veintiuno'
|
|
311
341
|
* toCardinal(21, {gender: 'feminine'}) // 'veintiuna'
|
|
312
342
|
* toCardinal(1000000000) // 'mil millones'
|
|
313
343
|
*/
|
|
314
|
-
function toCardinal
|
|
315
|
-
options = validateOptions(options)
|
|
344
|
+
function toCardinal(value, options) {
|
|
316
345
|
const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
|
|
346
|
+
// Both the integer part and the decimal's significant digits are spelled via
|
|
347
|
+
// the scale builder, so both must clear the ceiling.
|
|
348
|
+
checkMax(integerPart, cardinalMax, decimalPart)
|
|
317
349
|
|
|
318
350
|
// Apply option defaults
|
|
319
|
-
const { gender
|
|
351
|
+
const { gender } = resolveOptions(options, cardinalDefaults, cardinalValues)
|
|
320
352
|
const feminine = gender === 'feminine'
|
|
321
353
|
|
|
322
354
|
let result = ''
|
|
@@ -340,12 +372,11 @@ function toCardinal (value, options) {
|
|
|
340
372
|
|
|
341
373
|
/**
|
|
342
374
|
* Builds ordinal word for a 0-999 segment.
|
|
343
|
-
*
|
|
344
375
|
* @param {number} n - Segment value 0-999
|
|
345
376
|
* @param {boolean} feminine - Use feminine forms
|
|
346
377
|
* @returns {string} Spanish ordinal word
|
|
347
378
|
*/
|
|
348
|
-
function buildOrdinalSegment
|
|
379
|
+
function buildOrdinalSegment(n, feminine) {
|
|
349
380
|
if (n === 0) return ''
|
|
350
381
|
|
|
351
382
|
const ones = n % 10
|
|
@@ -362,7 +393,8 @@ function buildOrdinalSegment (n, feminine) {
|
|
|
362
393
|
if (hundreds > 0) {
|
|
363
394
|
if (hundreds === 1) {
|
|
364
395
|
parts.push(hundredWord)
|
|
365
|
-
}
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
366
398
|
const prefixes = ['', '', 'du', 'tri', 'cuadri', 'quin', 'sex', 'septi', 'octi', 'noni']
|
|
367
399
|
parts.push(prefixes[hundreds] + hundredWord)
|
|
368
400
|
}
|
|
@@ -383,12 +415,11 @@ function buildOrdinalSegment (n, feminine) {
|
|
|
383
415
|
|
|
384
416
|
/**
|
|
385
417
|
* Converts a positive integer to Spanish ordinal words.
|
|
386
|
-
*
|
|
387
418
|
* @param {bigint} n - Positive integer to convert
|
|
388
419
|
* @param {boolean} feminine - Use feminine forms
|
|
389
420
|
* @returns {string} Spanish ordinal words
|
|
390
421
|
*/
|
|
391
|
-
function integerToOrdinal
|
|
422
|
+
function integerToOrdinal(n, feminine) {
|
|
392
423
|
const thousandWord = feminine ? ORDINAL_THOUSAND_FEM : ORDINAL_THOUSAND_MASC
|
|
393
424
|
const millionWord = feminine ? ORDINAL_MILLION_FEM : ORDINAL_MILLION_MASC
|
|
394
425
|
|
|
@@ -402,11 +433,12 @@ function integerToOrdinal (n, feminine) {
|
|
|
402
433
|
const thousands = Number(n / 1000n)
|
|
403
434
|
const remainder = Number(n % 1000n)
|
|
404
435
|
|
|
405
|
-
let result
|
|
436
|
+
let result
|
|
406
437
|
|
|
407
438
|
if (thousands === 1) {
|
|
408
439
|
result = thousandWord
|
|
409
|
-
}
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
410
442
|
result = buildOrdinalSegment(thousands, feminine) + ' ' + thousandWord
|
|
411
443
|
}
|
|
412
444
|
|
|
@@ -421,11 +453,12 @@ function integerToOrdinal (n, feminine) {
|
|
|
421
453
|
const millions = Number(n / 1_000_000n)
|
|
422
454
|
const remainder = n % 1_000_000n
|
|
423
455
|
|
|
424
|
-
let result
|
|
456
|
+
let result
|
|
425
457
|
|
|
426
458
|
if (millions === 1) {
|
|
427
459
|
result = millionWord
|
|
428
|
-
}
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
429
462
|
result = buildOrdinalSegment(millions, feminine) + ' ' + millionWord
|
|
430
463
|
}
|
|
431
464
|
|
|
@@ -436,26 +469,34 @@ function integerToOrdinal (n, feminine) {
|
|
|
436
469
|
return result
|
|
437
470
|
}
|
|
438
471
|
|
|
472
|
+
/**
|
|
473
|
+
* @typedef {object} OrdinalOptions
|
|
474
|
+
* @property {('masculine'|'feminine')} [gender] - Grammatical gender
|
|
475
|
+
*/
|
|
476
|
+
|
|
477
|
+
/** @type {Required<OrdinalOptions>} */
|
|
478
|
+
export const ordinalDefaults = { gender: 'masculine' }
|
|
479
|
+
|
|
480
|
+
/** @type {{ gender: ReadonlyArray<Required<OrdinalOptions>['gender']> }} */
|
|
481
|
+
export const ordinalValues = { gender: ['masculine', 'feminine'] }
|
|
482
|
+
|
|
439
483
|
/**
|
|
440
484
|
* Converts a numeric value to Spanish ordinal words.
|
|
441
|
-
*
|
|
442
485
|
* @param {number | string | bigint} value - The positive integer to convert
|
|
443
|
-
* @param {
|
|
444
|
-
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
486
|
+
* @param {OrdinalOptions} [options] - Optional configuration
|
|
445
487
|
* @returns {string} The number in Spanish ordinal words
|
|
446
488
|
* @throws {TypeError} If value is not a valid numeric type
|
|
447
489
|
* @throws {Error} If value is not a positive integer
|
|
448
|
-
*
|
|
449
490
|
* @example
|
|
450
491
|
* toOrdinal(1) // 'primero'
|
|
451
492
|
* toOrdinal(1, { gender: 'feminine' }) // 'primera'
|
|
452
493
|
* toOrdinal(21) // 'vigésimo primero'
|
|
453
494
|
*/
|
|
454
|
-
function toOrdinal
|
|
455
|
-
options = validateOptions(options)
|
|
495
|
+
function toOrdinal(value, options) {
|
|
456
496
|
const integerPart = parseOrdinalValue(value)
|
|
497
|
+
checkMax(integerPart, ordinalMax)
|
|
457
498
|
|
|
458
|
-
const { gender
|
|
499
|
+
const { gender } = resolveOptions(options, ordinalDefaults, ordinalValues)
|
|
459
500
|
const feminine = gender === 'feminine'
|
|
460
501
|
|
|
461
502
|
return integerToOrdinal(integerPart, feminine)
|
|
@@ -465,29 +506,34 @@ function toOrdinal (value, options) {
|
|
|
465
506
|
// CURRENCY: toCurrency(value, options?)
|
|
466
507
|
// ============================================================================
|
|
467
508
|
|
|
509
|
+
/**
|
|
510
|
+
* @typedef {object} CurrencyOptions
|
|
511
|
+
* @property {boolean} [and] - Use "con" between pesos and centavos
|
|
512
|
+
*/
|
|
513
|
+
|
|
514
|
+
/** @type {Required<CurrencyOptions>} */
|
|
515
|
+
export const currencyDefaults = { and: true }
|
|
516
|
+
|
|
468
517
|
/**
|
|
469
518
|
* Converts a numeric value to Mexican Peso currency words.
|
|
470
519
|
*
|
|
471
520
|
* Mexican currency uses masculine gender for pesos (el peso)
|
|
472
521
|
* and masculine for centavos (el centavo).
|
|
473
|
-
*
|
|
474
522
|
* @param {number | string | bigint} value - The currency amount to convert
|
|
475
|
-
* @param {
|
|
476
|
-
* @param {boolean} [options.and=true] - Use "con" between pesos and centavos
|
|
523
|
+
* @param {CurrencyOptions} [options] - Optional configuration
|
|
477
524
|
* @returns {string} The amount in Mexican currency words
|
|
478
525
|
* @throws {TypeError} If value is not a valid numeric type
|
|
479
526
|
* @throws {Error} If value is not a valid number format
|
|
480
|
-
*
|
|
481
527
|
* @example
|
|
482
528
|
* toCurrency(42.50) // 'cuarenta y dos pesos con cincuenta centavos'
|
|
483
529
|
* toCurrency(1) // 'un peso'
|
|
484
530
|
* toCurrency(0.99) // 'noventa y nueve centavos'
|
|
485
531
|
* toCurrency(42.50, { and: false }) // 'cuarenta y dos pesos cincuenta centavos'
|
|
486
532
|
*/
|
|
487
|
-
function toCurrency
|
|
488
|
-
options = validateOptions(options)
|
|
533
|
+
function toCurrency(value, options) {
|
|
489
534
|
const { isNegative, dollars: pesos, cents: centavos } = parseCurrencyValue(value)
|
|
490
|
-
|
|
535
|
+
checkMax(pesos, currencyMax)
|
|
536
|
+
const { and: useAnd } = resolveOptions(options, currencyDefaults)
|
|
491
537
|
|
|
492
538
|
let result = ''
|
|
493
539
|
if (isNegative) result = NEGATIVE + ' '
|
|
@@ -497,7 +543,8 @@ function toCurrency (value, options) {
|
|
|
497
543
|
// Use masculine for pesos, but "un peso" not "uno peso"
|
|
498
544
|
if (pesos === 1n) {
|
|
499
545
|
result += 'un ' + PESO
|
|
500
|
-
}
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
501
548
|
result += integerToWords(pesos, false) + ' ' + PESOS
|
|
502
549
|
}
|
|
503
550
|
}
|
|
@@ -510,7 +557,8 @@ function toCurrency (value, options) {
|
|
|
510
557
|
// Use masculine for centavos, but "un centavo" not "uno centavo"
|
|
511
558
|
if (centavos === 1n) {
|
|
512
559
|
result += 'un ' + CENTAVO
|
|
513
|
-
}
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
514
562
|
result += integerToWords(centavos, false) + ' ' + CENTAVOS
|
|
515
563
|
}
|
|
516
564
|
}
|
package/src/es-US.d.ts
CHANGED
|
@@ -1,58 +1,90 @@
|
|
|
1
|
+
export const cardinalMax: bigint;
|
|
2
|
+
export const ordinalMax: bigint;
|
|
3
|
+
export const currencyMax: bigint;
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {object} CardinalOptions
|
|
6
|
+
* @property {('masculine'|'feminine')} [gender] - Grammatical gender
|
|
7
|
+
*/
|
|
8
|
+
/** @type {Required<CardinalOptions>} */
|
|
9
|
+
export const cardinalDefaults: Required<CardinalOptions>;
|
|
10
|
+
/** @type {{ gender: ReadonlyArray<Required<CardinalOptions>['gender']> }} */
|
|
11
|
+
export const cardinalValues: {
|
|
12
|
+
gender: ReadonlyArray<Required<CardinalOptions>["gender"]>;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {object} OrdinalOptions
|
|
16
|
+
* @property {('masculine'|'feminine')} [gender] - Grammatical gender
|
|
17
|
+
*/
|
|
18
|
+
/** @type {Required<OrdinalOptions>} */
|
|
19
|
+
export const ordinalDefaults: Required<OrdinalOptions>;
|
|
20
|
+
/** @type {{ gender: ReadonlyArray<Required<OrdinalOptions>['gender']> }} */
|
|
21
|
+
export const ordinalValues: {
|
|
22
|
+
gender: ReadonlyArray<Required<OrdinalOptions>["gender"]>;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {object} CurrencyOptions
|
|
26
|
+
* @property {boolean} [and] - Use "con" between dollars and cents
|
|
27
|
+
*/
|
|
28
|
+
/** @type {Required<CurrencyOptions>} */
|
|
29
|
+
export const currencyDefaults: Required<CurrencyOptions>;
|
|
30
|
+
export type CardinalOptions = {
|
|
31
|
+
/**
|
|
32
|
+
* - Grammatical gender
|
|
33
|
+
*/
|
|
34
|
+
gender?: "feminine" | "masculine" | undefined;
|
|
35
|
+
};
|
|
36
|
+
export type OrdinalOptions = {
|
|
37
|
+
/**
|
|
38
|
+
* - Grammatical gender
|
|
39
|
+
*/
|
|
40
|
+
gender?: "feminine" | "masculine" | undefined;
|
|
41
|
+
};
|
|
42
|
+
export type CurrencyOptions = {
|
|
43
|
+
/**
|
|
44
|
+
* - Use "con" between dollars and cents
|
|
45
|
+
*/
|
|
46
|
+
and?: boolean | undefined;
|
|
47
|
+
};
|
|
1
48
|
/**
|
|
2
49
|
* Converts a numeric value to Spanish words (US short scale).
|
|
3
|
-
*
|
|
4
50
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
|
-
* @param {
|
|
6
|
-
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
51
|
+
* @param {CardinalOptions} [options] - Optional configuration
|
|
7
52
|
* @returns {string} The number in Spanish words
|
|
8
53
|
* @throws {TypeError} If value is not a valid numeric type
|
|
9
54
|
* @throws {Error} If value is not a valid number format
|
|
10
|
-
*
|
|
11
55
|
* @example
|
|
12
56
|
* toCardinal(21) // 'veintiuno'
|
|
13
57
|
* toCardinal(21, {gender: 'feminine'}) // 'veintiuna'
|
|
14
58
|
* toCardinal(1000000000) // 'un billón'
|
|
15
59
|
*/
|
|
16
|
-
export function toCardinal(value: number | string | bigint, options?:
|
|
17
|
-
gender?: "masculine" | "feminine" | undefined;
|
|
18
|
-
}): string;
|
|
60
|
+
export function toCardinal(value: number | string | bigint, options?: CardinalOptions): string;
|
|
19
61
|
/**
|
|
20
62
|
* Converts a numeric value to Spanish ordinal words.
|
|
21
|
-
*
|
|
22
63
|
* @param {number | string | bigint} value - The positive integer to convert
|
|
23
|
-
* @param {
|
|
24
|
-
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
64
|
+
* @param {OrdinalOptions} [options] - Optional configuration
|
|
25
65
|
* @returns {string} The number in Spanish ordinal words
|
|
26
66
|
* @throws {TypeError} If value is not a valid numeric type
|
|
27
67
|
* @throws {Error} If value is not a positive integer
|
|
28
|
-
*
|
|
29
68
|
* @example
|
|
30
69
|
* toOrdinal(1) // 'primero'
|
|
31
70
|
* toOrdinal(1, { gender: 'feminine' }) // 'primera'
|
|
32
71
|
* toOrdinal(21) // 'vigésimo primero'
|
|
33
72
|
*/
|
|
34
|
-
export function toOrdinal(value: number | string | bigint, options?:
|
|
35
|
-
gender?: "masculine" | "feminine" | undefined;
|
|
36
|
-
}): string;
|
|
73
|
+
export function toOrdinal(value: number | string | bigint, options?: OrdinalOptions): string;
|
|
37
74
|
/**
|
|
38
75
|
* Converts a numeric value to US Dollar currency words in Spanish.
|
|
39
76
|
*
|
|
40
77
|
* US Dollar uses masculine gender for dólares (el dólar)
|
|
41
78
|
* and masculine for centavos (el centavo).
|
|
42
|
-
*
|
|
43
79
|
* @param {number | string | bigint} value - The currency amount to convert
|
|
44
|
-
* @param {
|
|
45
|
-
* @param {boolean} [options.and=true] - Use "con" between dollars and cents
|
|
80
|
+
* @param {CurrencyOptions} [options] - Optional configuration
|
|
46
81
|
* @returns {string} The amount in Spanish US Dollar currency words
|
|
47
82
|
* @throws {TypeError} If value is not a valid numeric type
|
|
48
83
|
* @throws {Error} If value is not a valid number format
|
|
49
|
-
*
|
|
50
84
|
* @example
|
|
51
85
|
* toCurrency(42.50) // 'cuarenta y dos dólares con cincuenta centavos'
|
|
52
86
|
* toCurrency(1) // 'un dólar'
|
|
53
87
|
* toCurrency(0.99) // 'noventa y nueve centavos'
|
|
54
88
|
* toCurrency(42.50, { and: false }) // 'cuarenta y dos dólares cincuenta centavos'
|
|
55
89
|
*/
|
|
56
|
-
export function toCurrency(value: number | string | bigint, options?:
|
|
57
|
-
and?: boolean | undefined;
|
|
58
|
-
}): string;
|
|
90
|
+
export function toCurrency(value: number | string | bigint, options?: CurrencyOptions): string;
|