n2words 2.0.0 → 3.0.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 +49 -0
- package/README.md +86 -188
- package/dist/languages/am-Latn.js +3 -0
- package/dist/languages/am-Latn.js.map +1 -0
- package/dist/languages/am.js +3 -0
- package/dist/languages/am.js.map +1 -0
- package/dist/languages/ar.js +3 -0
- package/dist/languages/ar.js.map +1 -0
- package/dist/languages/az.js +3 -0
- package/dist/languages/az.js.map +1 -0
- package/dist/languages/bn.js +3 -0
- package/dist/languages/bn.js.map +1 -0
- package/dist/languages/cs.js +3 -0
- package/dist/languages/cs.js.map +1 -0
- package/dist/languages/da.js +3 -0
- package/dist/languages/da.js.map +1 -0
- package/dist/languages/de.js +3 -0
- package/dist/languages/de.js.map +1 -0
- package/dist/languages/el.js +3 -0
- package/dist/languages/el.js.map +1 -0
- package/dist/languages/en.js +3 -0
- package/dist/languages/en.js.map +1 -0
- package/dist/languages/es.js +3 -0
- package/dist/languages/es.js.map +1 -0
- package/dist/languages/fa.js +3 -0
- package/dist/languages/fa.js.map +1 -0
- package/dist/languages/fi.js +3 -0
- package/dist/languages/fi.js.map +1 -0
- package/dist/languages/fil.js +3 -0
- package/dist/languages/fil.js.map +1 -0
- package/dist/languages/fr-BE.js +3 -0
- package/dist/languages/fr-BE.js.map +1 -0
- package/dist/languages/fr.js +3 -0
- package/dist/languages/fr.js.map +1 -0
- package/dist/languages/gu.js +3 -0
- package/dist/languages/gu.js.map +1 -0
- package/dist/languages/ha.js +3 -0
- package/dist/languages/ha.js.map +1 -0
- package/dist/languages/hbo.js +3 -0
- package/dist/languages/hbo.js.map +1 -0
- package/dist/languages/he.js +3 -0
- package/dist/languages/he.js.map +1 -0
- package/dist/languages/hi.js +3 -0
- package/dist/languages/hi.js.map +1 -0
- package/dist/languages/hr.js +3 -0
- package/dist/languages/hr.js.map +1 -0
- package/dist/languages/hu.js +3 -0
- package/dist/languages/hu.js.map +1 -0
- package/dist/languages/id.js +3 -0
- package/dist/languages/id.js.map +1 -0
- package/dist/languages/it.js +3 -0
- package/dist/languages/it.js.map +1 -0
- package/dist/languages/ja.js +3 -0
- package/dist/languages/ja.js.map +1 -0
- package/dist/languages/kn.js +3 -0
- package/dist/languages/kn.js.map +1 -0
- package/dist/languages/ko.js +3 -0
- package/dist/languages/ko.js.map +1 -0
- package/dist/languages/lt.js +3 -0
- package/dist/languages/lt.js.map +1 -0
- package/dist/languages/lv.js +3 -0
- package/dist/languages/lv.js.map +1 -0
- package/dist/languages/mr.js +3 -0
- package/dist/languages/mr.js.map +1 -0
- package/dist/languages/ms.js +3 -0
- package/dist/languages/ms.js.map +1 -0
- package/dist/languages/nb.js +3 -0
- package/dist/languages/nb.js.map +1 -0
- package/dist/languages/nl.js +3 -0
- package/dist/languages/nl.js.map +1 -0
- package/dist/languages/pa.js +3 -0
- package/dist/languages/pa.js.map +1 -0
- package/dist/languages/pl.js +3 -0
- package/dist/languages/pl.js.map +1 -0
- package/dist/languages/pt.js +3 -0
- package/dist/languages/pt.js.map +1 -0
- package/dist/languages/ro.js +3 -0
- package/dist/languages/ro.js.map +1 -0
- package/dist/languages/ru.js +3 -0
- package/dist/languages/ru.js.map +1 -0
- package/dist/languages/sr-Cyrl.js +3 -0
- package/dist/languages/sr-Cyrl.js.map +1 -0
- package/dist/languages/sr-Latn.js +3 -0
- package/dist/languages/sr-Latn.js.map +1 -0
- package/dist/languages/sv.js +3 -0
- package/dist/languages/sv.js.map +1 -0
- package/dist/languages/sw.js +3 -0
- package/dist/languages/sw.js.map +1 -0
- package/dist/languages/ta.js +3 -0
- package/dist/languages/ta.js.map +1 -0
- package/dist/languages/te.js +3 -0
- package/dist/languages/te.js.map +1 -0
- package/dist/languages/th.js +3 -0
- package/dist/languages/th.js.map +1 -0
- package/dist/languages/tr.js +3 -0
- package/dist/languages/tr.js.map +1 -0
- package/dist/languages/uk.js +3 -0
- package/dist/languages/uk.js.map +1 -0
- package/dist/languages/ur.js +3 -0
- package/dist/languages/ur.js.map +1 -0
- package/dist/languages/vi.js +3 -0
- package/dist/languages/vi.js.map +1 -0
- package/dist/languages/zh-Hans.js +3 -0
- package/dist/languages/zh-Hans.js.map +1 -0
- package/dist/languages/zh-Hant.js +3 -0
- package/dist/languages/zh-Hant.js.map +1 -0
- package/dist/n2words.js +2 -2
- package/dist/n2words.js.map +1 -1
- package/lib/languages/am-Latn.d.ts +7 -0
- package/lib/languages/am-Latn.js +164 -0
- package/lib/languages/am.d.ts +7 -0
- package/lib/languages/am.js +164 -0
- package/lib/languages/ar.d.ts +14 -27
- package/lib/languages/ar.js +175 -129
- package/lib/languages/az.d.ts +4 -9
- package/lib/languages/az.js +171 -37
- package/lib/languages/bn.d.ts +4 -8
- package/lib/languages/bn.js +138 -124
- package/lib/languages/cs.d.ts +15 -85
- package/lib/languages/cs.js +310 -114
- package/lib/languages/da.d.ts +11 -12
- package/lib/languages/da.js +276 -101
- package/lib/languages/de.d.ts +14 -11
- package/lib/languages/de.js +317 -86
- package/lib/languages/el.d.ts +11 -11
- package/lib/languages/el.js +231 -78
- package/lib/languages/en.d.ts +14 -13
- package/lib/languages/en.js +242 -72
- package/lib/languages/es.d.ts +18 -12
- package/lib/languages/es.js +317 -103
- package/lib/languages/fa.d.ts +4 -44
- package/lib/languages/fa.js +112 -122
- package/lib/languages/fi.d.ts +14 -0
- package/lib/languages/fi.js +245 -0
- package/lib/languages/fil.d.ts +4 -13
- package/lib/languages/fil.js +207 -106
- package/lib/languages/fr-BE.d.ts +8 -8
- package/lib/languages/fr-BE.js +294 -19
- package/lib/languages/fr.d.ts +18 -12
- package/lib/languages/fr.js +352 -89
- package/lib/languages/gu.d.ts +4 -8
- package/lib/languages/gu.js +130 -125
- package/lib/languages/ha.d.ts +7 -0
- package/lib/languages/ha.js +230 -0
- package/lib/languages/hbo.d.ts +10 -110
- package/lib/languages/hbo.js +263 -214
- package/lib/languages/he.d.ts +10 -77
- package/lib/languages/he.js +242 -172
- package/lib/languages/hi.d.ts +4 -8
- package/lib/languages/hi.js +138 -124
- package/lib/languages/hr.d.ts +8 -77
- package/lib/languages/hr.js +194 -89
- package/lib/languages/hu.d.ts +4 -19
- package/lib/languages/hu.js +198 -119
- package/lib/languages/id.d.ts +4 -34
- package/lib/languages/id.js +171 -129
- package/lib/languages/it.d.ts +16 -34
- package/lib/languages/it.js +339 -94
- package/lib/languages/ja.d.ts +14 -14
- package/lib/languages/ja.js +233 -111
- package/lib/languages/kn.d.ts +4 -8
- package/lib/languages/kn.js +130 -35
- package/lib/languages/ko.d.ts +11 -11
- package/lib/languages/ko.js +257 -49
- package/lib/languages/lt.d.ts +15 -67
- package/lib/languages/lt.js +296 -122
- package/lib/languages/lv.d.ts +15 -67
- package/lib/languages/lv.js +297 -106
- package/lib/languages/mr.d.ts +4 -8
- package/lib/languages/mr.js +130 -125
- package/lib/languages/ms.d.ts +4 -28
- package/lib/languages/ms.js +171 -116
- package/lib/languages/nb.d.ts +11 -9
- package/lib/languages/nb.js +282 -87
- package/lib/languages/nl.d.ts +23 -13
- package/lib/languages/nl.js +317 -133
- package/lib/languages/pa.d.ts +4 -8
- package/lib/languages/pa.js +156 -124
- package/lib/languages/pl.d.ts +19 -77
- package/lib/languages/pl.js +307 -87
- package/lib/languages/pt.d.ts +14 -26
- package/lib/languages/pt.js +286 -92
- package/lib/languages/ro.d.ts +15 -155
- package/lib/languages/ro.js +219 -235
- package/lib/languages/ru.d.ts +8 -82
- package/lib/languages/ru.js +222 -78
- package/lib/languages/sr-Cyrl.d.ts +8 -77
- package/lib/languages/sr-Cyrl.js +191 -89
- package/lib/languages/sr-Latn.d.ts +8 -77
- package/lib/languages/sr-Latn.js +191 -89
- package/lib/languages/sv.d.ts +11 -11
- package/lib/languages/sv.js +288 -74
- package/lib/languages/sw.d.ts +4 -36
- package/lib/languages/sw.js +133 -106
- package/lib/languages/ta.d.ts +4 -17
- package/lib/languages/ta.js +129 -201
- package/lib/languages/te.d.ts +4 -19
- package/lib/languages/te.js +141 -196
- package/lib/languages/th.d.ts +4 -14
- package/lib/languages/th.js +135 -91
- package/lib/languages/tr.d.ts +15 -9
- package/lib/languages/tr.js +256 -49
- package/lib/languages/uk.d.ts +8 -82
- package/lib/languages/uk.js +200 -78
- package/lib/languages/ur.d.ts +4 -8
- package/lib/languages/ur.js +156 -124
- package/lib/languages/vi.d.ts +14 -69
- package/lib/languages/vi.js +294 -125
- package/lib/languages/zh-Hans.d.ts +8 -18
- package/lib/languages/zh-Hans.js +163 -92
- package/lib/languages/zh-Hant.d.ts +8 -18
- package/lib/languages/zh-Hant.js +181 -90
- package/lib/n2words.d.ts +53 -209
- package/lib/n2words.js +111 -530
- package/lib/utils/is-plain-object.d.ts +13 -0
- package/lib/utils/is-plain-object.js +17 -0
- package/lib/utils/parse-numeric.d.ts +17 -0
- package/lib/utils/parse-numeric.js +108 -0
- package/lib/utils/validate-options.d.ts +8 -0
- package/lib/utils/validate-options.js +16 -0
- package/package.json +26 -14
- package/dist/ArabicConverter.js +0 -3
- package/dist/ArabicConverter.js.map +0 -1
- package/dist/AzerbaijaniConverter.js +0 -3
- package/dist/AzerbaijaniConverter.js.map +0 -1
- package/dist/BanglaConverter.js +0 -3
- package/dist/BanglaConverter.js.map +0 -1
- package/dist/BiblicalHebrewConverter.js +0 -3
- package/dist/BiblicalHebrewConverter.js.map +0 -1
- package/dist/CroatianConverter.js +0 -3
- package/dist/CroatianConverter.js.map +0 -1
- package/dist/CzechConverter.js +0 -3
- package/dist/CzechConverter.js.map +0 -1
- package/dist/DanishConverter.js +0 -3
- package/dist/DanishConverter.js.map +0 -1
- package/dist/DutchConverter.js +0 -3
- package/dist/DutchConverter.js.map +0 -1
- package/dist/EnglishConverter.js +0 -3
- package/dist/EnglishConverter.js.map +0 -1
- package/dist/FilipinoConverter.js +0 -3
- package/dist/FilipinoConverter.js.map +0 -1
- package/dist/FrenchBelgiumConverter.js +0 -3
- package/dist/FrenchBelgiumConverter.js.map +0 -1
- package/dist/FrenchConverter.js +0 -3
- package/dist/FrenchConverter.js.map +0 -1
- package/dist/GermanConverter.js +0 -3
- package/dist/GermanConverter.js.map +0 -1
- package/dist/GreekConverter.js +0 -3
- package/dist/GreekConverter.js.map +0 -1
- package/dist/GujaratiConverter.js +0 -3
- package/dist/GujaratiConverter.js.map +0 -1
- package/dist/HebrewConverter.js +0 -3
- package/dist/HebrewConverter.js.map +0 -1
- package/dist/HindiConverter.js +0 -3
- package/dist/HindiConverter.js.map +0 -1
- package/dist/HungarianConverter.js +0 -3
- package/dist/HungarianConverter.js.map +0 -1
- package/dist/IndonesianConverter.js +0 -3
- package/dist/IndonesianConverter.js.map +0 -1
- package/dist/ItalianConverter.js +0 -3
- package/dist/ItalianConverter.js.map +0 -1
- package/dist/JapaneseConverter.js +0 -3
- package/dist/JapaneseConverter.js.map +0 -1
- package/dist/KannadaConverter.js +0 -3
- package/dist/KannadaConverter.js.map +0 -1
- package/dist/KoreanConverter.js +0 -3
- package/dist/KoreanConverter.js.map +0 -1
- package/dist/LatvianConverter.js +0 -3
- package/dist/LatvianConverter.js.map +0 -1
- package/dist/LithuanianConverter.js +0 -3
- package/dist/LithuanianConverter.js.map +0 -1
- package/dist/MalayConverter.js +0 -3
- package/dist/MalayConverter.js.map +0 -1
- package/dist/MarathiConverter.js +0 -3
- package/dist/MarathiConverter.js.map +0 -1
- package/dist/NorwegianBokmalConverter.js +0 -3
- package/dist/NorwegianBokmalConverter.js.map +0 -1
- package/dist/PersianConverter.js +0 -3
- package/dist/PersianConverter.js.map +0 -1
- package/dist/PolishConverter.js +0 -3
- package/dist/PolishConverter.js.map +0 -1
- package/dist/PortugueseConverter.js +0 -3
- package/dist/PortugueseConverter.js.map +0 -1
- package/dist/PunjabiConverter.js +0 -3
- package/dist/PunjabiConverter.js.map +0 -1
- package/dist/RomanianConverter.js +0 -3
- package/dist/RomanianConverter.js.map +0 -1
- package/dist/RussianConverter.js +0 -3
- package/dist/RussianConverter.js.map +0 -1
- package/dist/SerbianCyrillicConverter.js +0 -3
- package/dist/SerbianCyrillicConverter.js.map +0 -1
- package/dist/SerbianLatinConverter.js +0 -3
- package/dist/SerbianLatinConverter.js.map +0 -1
- package/dist/SimplifiedChineseConverter.js +0 -3
- package/dist/SimplifiedChineseConverter.js.map +0 -1
- package/dist/SpanishConverter.js +0 -3
- package/dist/SpanishConverter.js.map +0 -1
- package/dist/SwahiliConverter.js +0 -3
- package/dist/SwahiliConverter.js.map +0 -1
- package/dist/SwedishConverter.js +0 -3
- package/dist/SwedishConverter.js.map +0 -1
- package/dist/TamilConverter.js +0 -3
- package/dist/TamilConverter.js.map +0 -1
- package/dist/TeluguConverter.js +0 -3
- package/dist/TeluguConverter.js.map +0 -1
- package/dist/ThaiConverter.js +0 -3
- package/dist/ThaiConverter.js.map +0 -1
- package/dist/TraditionalChineseConverter.js +0 -3
- package/dist/TraditionalChineseConverter.js.map +0 -1
- package/dist/TurkishConverter.js +0 -3
- package/dist/TurkishConverter.js.map +0 -1
- package/dist/UkrainianConverter.js +0 -3
- package/dist/UkrainianConverter.js.map +0 -1
- package/dist/UrduConverter.js +0 -3
- package/dist/UrduConverter.js.map +0 -1
- package/dist/VietnameseConverter.js +0 -3
- package/dist/VietnameseConverter.js.map +0 -1
- package/lib/classes/abstract-language.d.ts +0 -178
- package/lib/classes/abstract-language.js +0 -268
- package/lib/classes/greedy-scale-language.d.ts +0 -109
- package/lib/classes/greedy-scale-language.js +0 -201
- package/lib/classes/slavic-language.d.ts +0 -148
- package/lib/classes/slavic-language.js +0 -281
- package/lib/classes/south-asian-language.d.ts +0 -70
- package/lib/classes/south-asian-language.js +0 -154
- package/lib/classes/turkic-language.d.ts +0 -26
- package/lib/classes/turkic-language.js +0 -59
package/lib/languages/es.d.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Spanish
|
|
2
|
+
* Converts a numeric value to Spanish words.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
4
|
+
* This is the main public API. It accepts any valid numeric input
|
|
5
|
+
* (number, string, or bigint) and handles parsing internally.
|
|
6
|
+
*
|
|
7
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
8
|
+
* @param {Object} [options] - Optional configuration
|
|
9
|
+
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
10
|
+
* @returns {string} The number in Spanish words
|
|
11
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
12
|
+
* @throws {Error} If value is not a valid number format
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* toWords(21) // 'veintiuno'
|
|
16
|
+
* toWords(21, {gender: 'feminine'}) // 'veintiuna'
|
|
17
|
+
* toWords(1000000) // 'un millón'
|
|
8
18
|
*/
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
/** Combines two word-sets with Spanish gender and conjunction rules. */
|
|
13
|
-
combineWordSets(preceding: any, following: any): any;
|
|
14
|
-
}
|
|
15
|
-
import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
|
|
19
|
+
export function toWords(value: number | string | bigint, options?: {
|
|
20
|
+
gender?: "masculine" | "feminine" | undefined;
|
|
21
|
+
}): string;
|
package/lib/languages/es.js
CHANGED
|
@@ -1,121 +1,335 @@
|
|
|
1
|
-
import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Spanish language converter
|
|
2
|
+
* Spanish language converter - Functional Implementation
|
|
3
|
+
*
|
|
4
|
+
* A performance-optimized number-to-words converter using precomputed lookup tables.
|
|
5
|
+
* Self-contained module with its own input validation, ready for subpath exports.
|
|
6
|
+
*
|
|
7
|
+
* Key optimization: Precompute all segment values (0-999) at module load.
|
|
8
|
+
* This eliminates all per-call string manipulation for segment conversion.
|
|
5
9
|
*
|
|
6
|
-
*
|
|
7
|
-
* - Gender agreement
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
+
* Spanish-specific rules (handled in precomputation):
|
|
11
|
+
* - Gender agreement: uno/una, veintiuno/veintiuna, hundreds
|
|
12
|
+
* - Special twenties: veinte, veintiuno, veintidós, ... veintinueve
|
|
13
|
+
* - "y" conjunction: treinta y uno (only 30-99 with ones)
|
|
14
|
+
* - "cien" for exact 100, "ciento/cienta" otherwise
|
|
15
|
+
* - Irregular hundreds: quinientos, setecientos, novecientos
|
|
16
|
+
* - Compound long scale: millón, mil millones, billón, mil billones
|
|
17
|
+
* - "un" before millón (not "uno"), omit before mil
|
|
10
18
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (this.options.gender === 'feminine') {
|
|
71
|
-
this.scaleWords = this.scaleWords.map(([value, word]) => {
|
|
72
|
-
if (word === 'veintiuno') return [value, 'veintiuna']
|
|
73
|
-
if (word === 'uno') return [value, 'una']
|
|
74
|
-
return [value, word]
|
|
75
|
-
})
|
|
76
|
-
}
|
|
19
|
+
|
|
20
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
21
|
+
import { validateOptions } from '../utils/validate-options.js'
|
|
22
|
+
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// Vocabulary (module-level constants)
|
|
25
|
+
// ============================================================================
|
|
26
|
+
|
|
27
|
+
const ONES_MASC = ['', 'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve']
|
|
28
|
+
const ONES_FEM = ['', 'una', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve']
|
|
29
|
+
|
|
30
|
+
const TEENS = ['diez', 'once', 'doce', 'trece', 'catorce', 'quince', 'dieciseis', 'diecisiete', 'dieciocho', 'diecinueve']
|
|
31
|
+
|
|
32
|
+
// 20-29 have special compound forms
|
|
33
|
+
const TWENTIES_MASC = ['veinte', 'veintiuno', 'veintidós', 'veintitrés', 'veinticuatro', 'veinticinco', 'veintiséis', 'veintisiete', 'veintiocho', 'veintinueve']
|
|
34
|
+
const TWENTIES_FEM = ['veinte', 'veintiuna', 'veintidós', 'veintitrés', 'veinticuatro', 'veinticinco', 'veintiséis', 'veintisiete', 'veintiocho', 'veintinueve']
|
|
35
|
+
|
|
36
|
+
const TENS = ['', '', '', 'treinta', 'cuarenta', 'cincuenta', 'sesenta', 'setenta', 'ochenta', 'noventa']
|
|
37
|
+
|
|
38
|
+
// Irregular hundreds
|
|
39
|
+
const HUNDREDS_MASC = ['', 'ciento', 'doscientos', 'trescientos', 'cuatrocientos', 'quinientos', 'seiscientos', 'setecientos', 'ochocientos', 'novecientos']
|
|
40
|
+
const HUNDREDS_FEM = ['', 'cienta', 'doscientas', 'trescientas', 'cuatrocientas', 'quinientas', 'seiscientas', 'setecientas', 'ochocientas', 'novecientas']
|
|
41
|
+
|
|
42
|
+
// Scale words (compound long scale)
|
|
43
|
+
const SCALES = ['millón', 'billón', 'trillón', 'cuatrillón']
|
|
44
|
+
const SCALES_PLURAL = ['millones', 'billones', 'trillones', 'cuatrillones']
|
|
45
|
+
|
|
46
|
+
const THOUSAND = 'mil'
|
|
47
|
+
const ZERO = 'cero'
|
|
48
|
+
const NEGATIVE = 'menos'
|
|
49
|
+
const DECIMAL_SEP = 'punto'
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Precomputed Lookup Tables (built once at module load)
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Builds segment word for 0-999.
|
|
57
|
+
* @param {number} n - Segment value
|
|
58
|
+
* @param {boolean} feminine - Use feminine forms
|
|
59
|
+
* @returns {string} Spanish word
|
|
60
|
+
*/
|
|
61
|
+
function buildSegment (n, feminine) {
|
|
62
|
+
if (n === 0) return ''
|
|
63
|
+
|
|
64
|
+
// Special case: exact 100 is "cien" (no gender)
|
|
65
|
+
if (n === 100) return 'cien'
|
|
66
|
+
|
|
67
|
+
const ones = n % 10
|
|
68
|
+
const tens = Math.floor(n / 10) % 10
|
|
69
|
+
const hundreds = Math.floor(n / 100)
|
|
70
|
+
const tensOnes = n % 100
|
|
71
|
+
|
|
72
|
+
const parts = []
|
|
73
|
+
|
|
74
|
+
// Hundreds
|
|
75
|
+
if (hundreds > 0) {
|
|
76
|
+
const hundredsArr = feminine ? HUNDREDS_FEM : HUNDREDS_MASC
|
|
77
|
+
parts.push(hundredsArr[hundreds])
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
80
|
+
// Tens and ones
|
|
81
|
+
if (tensOnes === 0) {
|
|
82
|
+
// Just hundreds
|
|
83
|
+
} else if (tensOnes < 10) {
|
|
84
|
+
// Single digit
|
|
85
|
+
const onesArr = feminine ? ONES_FEM : ONES_MASC
|
|
86
|
+
parts.push(onesArr[tensOnes])
|
|
87
|
+
} else if (tensOnes < 20) {
|
|
88
|
+
// 10-19: teens
|
|
89
|
+
parts.push(TEENS[ones])
|
|
90
|
+
} else if (tensOnes < 30) {
|
|
91
|
+
// 20-29: special twenties
|
|
92
|
+
const twentiesArr = feminine ? TWENTIES_FEM : TWENTIES_MASC
|
|
93
|
+
parts.push(twentiesArr[ones])
|
|
94
|
+
} else {
|
|
95
|
+
// 30-99: tens y ones
|
|
96
|
+
if (ones === 0) {
|
|
97
|
+
parts.push(TENS[tens])
|
|
98
|
+
} else {
|
|
99
|
+
const onesArr = feminine ? ONES_FEM : ONES_MASC
|
|
100
|
+
parts.push(TENS[tens] + ' y ' + onesArr[ones])
|
|
92
101
|
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return parts.join(' ')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Precompute all 1000 segment words (0-999) for masculine
|
|
108
|
+
const SEGMENTS_MASC = new Array(1000)
|
|
109
|
+
for (let i = 0; i < 1000; i++) {
|
|
110
|
+
SEGMENTS_MASC[i] = buildSegment(i, false)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Precompute all 1000 segment words (0-999) for feminine
|
|
114
|
+
const SEGMENTS_FEM = new Array(1000)
|
|
115
|
+
for (let i = 0; i < 1000; i++) {
|
|
116
|
+
SEGMENTS_FEM[i] = buildSegment(i, true)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// Helper Functions
|
|
121
|
+
// ============================================================================
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Gets scale word for Spanish compound long scale.
|
|
125
|
+
*
|
|
126
|
+
* @param {number} scaleIndex - Scale level (1 = thousand, 2 = million, etc.)
|
|
127
|
+
* @param {bigint} segment - Segment value for pluralization
|
|
128
|
+
* @returns {string} Scale word
|
|
129
|
+
*/
|
|
130
|
+
function getScaleWord (scaleIndex, segment) {
|
|
131
|
+
if (scaleIndex === 1) return THOUSAND
|
|
132
|
+
|
|
133
|
+
// Even indices (2, 4, 6, 8): millón, billón, trillón, cuatrillón
|
|
134
|
+
// Odd indices > 1 (3, 5, 7): mil millones, mil billones, mil trillones
|
|
135
|
+
if (scaleIndex % 2 === 0) {
|
|
136
|
+
const arrayIndex = (scaleIndex / 2) - 1
|
|
137
|
+
const baseWord = SCALES[arrayIndex]
|
|
138
|
+
if (!baseWord) return ''
|
|
139
|
+
return segment > 1n ? SCALES_PLURAL[arrayIndex] : baseWord
|
|
140
|
+
} else {
|
|
141
|
+
// Compound: "mil millones" pattern
|
|
142
|
+
const arrayIndex = ((scaleIndex - 1) / 2) - 1
|
|
143
|
+
const pluralWord = SCALES_PLURAL[arrayIndex]
|
|
144
|
+
if (!pluralWord) return THOUSAND
|
|
145
|
+
return THOUSAND + ' ' + pluralWord
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// Conversion Functions
|
|
151
|
+
// ============================================================================
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Converts a non-negative integer to Spanish words.
|
|
155
|
+
*
|
|
156
|
+
* @param {bigint} n - Non-negative integer to convert
|
|
157
|
+
* @param {boolean} feminine - Use feminine forms
|
|
158
|
+
* @returns {string} Spanish words
|
|
159
|
+
*/
|
|
160
|
+
function integerToWords (n, feminine) {
|
|
161
|
+
if (n === 0n) return ZERO
|
|
162
|
+
|
|
163
|
+
const segments = feminine ? SEGMENTS_FEM : SEGMENTS_MASC
|
|
164
|
+
|
|
165
|
+
// Fast path: numbers < 1000 (direct lookup)
|
|
166
|
+
if (n < 1000n) {
|
|
167
|
+
return segments[Number(n)]
|
|
168
|
+
}
|
|
93
169
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
170
|
+
// Fast path: numbers < 1,000,000 (thousands)
|
|
171
|
+
if (n < 1_000_000n) {
|
|
172
|
+
const thousands = Number(n / 1000n)
|
|
173
|
+
const remainder = Number(n % 1000n)
|
|
174
|
+
|
|
175
|
+
let result
|
|
176
|
+
if (thousands === 1) {
|
|
177
|
+
// "mil" not "uno mil"
|
|
178
|
+
result = THOUSAND
|
|
179
|
+
} else {
|
|
180
|
+
// Use masculine for thousands segment, but check for "uno" → omit before mil
|
|
181
|
+
const thousandsWord = SEGMENTS_MASC[thousands]
|
|
182
|
+
// "uno mil" → "mil" (handled in joinSegments equivalent)
|
|
183
|
+
if (thousandsWord === 'uno' || thousandsWord === 'una') {
|
|
184
|
+
result = THOUSAND
|
|
185
|
+
} else {
|
|
186
|
+
result = thousandsWord + ' ' + THOUSAND
|
|
97
187
|
}
|
|
98
|
-
return { [`${precedingWord} ${followingWord}`]: precedingValue + followingValue }
|
|
99
188
|
}
|
|
100
189
|
|
|
101
|
-
if (
|
|
102
|
-
|
|
190
|
+
if (remainder > 0) {
|
|
191
|
+
result += ' ' + segments[remainder]
|
|
103
192
|
}
|
|
104
193
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
194
|
+
return result
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// For numbers >= 1,000,000, use scale decomposition
|
|
198
|
+
return buildLargeNumberWords(n, feminine)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Builds words for numbers >= 1,000,000.
|
|
203
|
+
* Uses BigInt division for faster segment extraction.
|
|
204
|
+
*
|
|
205
|
+
* @param {bigint} n - Number >= 1,000,000
|
|
206
|
+
* @param {boolean} feminine - Use feminine forms
|
|
207
|
+
* @returns {string} Spanish words
|
|
208
|
+
*/
|
|
209
|
+
function buildLargeNumberWords (n, feminine) {
|
|
210
|
+
// Extract segments using BigInt division (faster than string slicing)
|
|
211
|
+
// Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)
|
|
212
|
+
const segmentValues = []
|
|
213
|
+
let temp = n
|
|
214
|
+
while (temp > 0n) {
|
|
215
|
+
segmentValues.push(temp % 1000n)
|
|
216
|
+
temp = temp / 1000n
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const segments = feminine ? SEGMENTS_FEM : SEGMENTS_MASC
|
|
220
|
+
|
|
221
|
+
// Build result string directly
|
|
222
|
+
let result = ''
|
|
223
|
+
|
|
224
|
+
for (let i = segmentValues.length - 1; i >= 0; i--) {
|
|
225
|
+
const segment = segmentValues[i]
|
|
226
|
+
if (segment === 0n) continue
|
|
227
|
+
|
|
228
|
+
const scaleWord = i > 0 ? getScaleWord(i, segment) : ''
|
|
229
|
+
|
|
230
|
+
if (result) result += ' '
|
|
231
|
+
|
|
232
|
+
if (i === 0) {
|
|
233
|
+
// Units segment
|
|
234
|
+
result += segments[Number(segment)]
|
|
235
|
+
} else if (i === 1) {
|
|
236
|
+
// Thousands: omit "uno" before mil
|
|
237
|
+
if (segment === 1n) {
|
|
238
|
+
result += THOUSAND
|
|
239
|
+
} else {
|
|
240
|
+
result += SEGMENTS_MASC[Number(segment)] + ' ' + scaleWord
|
|
241
|
+
}
|
|
242
|
+
} else if (i % 2 === 1) {
|
|
243
|
+
// Odd scale indices (3, 5, 7): "mil millones", "mil billones", etc.
|
|
244
|
+
// Omit "uno" before these compound scales
|
|
245
|
+
if (segment === 1n) {
|
|
246
|
+
result += scaleWord
|
|
247
|
+
} else {
|
|
248
|
+
result += SEGMENTS_MASC[Number(segment)] + ' ' + scaleWord
|
|
113
249
|
}
|
|
114
|
-
followingWord += 't' + genderStem + 's'
|
|
115
250
|
} else {
|
|
116
|
-
|
|
251
|
+
// Even scale indices (2, 4, 6): millón, billón, trillón
|
|
252
|
+
if (segment === 1n) {
|
|
253
|
+
// "un millón" not "uno millón"
|
|
254
|
+
result += 'un ' + scaleWord
|
|
255
|
+
} else {
|
|
256
|
+
// Use masculine for scale segment
|
|
257
|
+
result += SEGMENTS_MASC[Number(segment)] + ' ' + scaleWord
|
|
258
|
+
}
|
|
117
259
|
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return result
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Converts decimal digits to Spanish words.
|
|
267
|
+
*
|
|
268
|
+
* @param {string} decimalPart - Decimal digits (without the point)
|
|
269
|
+
* @param {boolean} feminine - Use feminine forms
|
|
270
|
+
* @returns {string} Spanish words for decimal part
|
|
271
|
+
*/
|
|
272
|
+
function decimalPartToWords (decimalPart, feminine) {
|
|
273
|
+
let result = ''
|
|
274
|
+
|
|
275
|
+
// Handle leading zeros
|
|
276
|
+
let i = 0
|
|
277
|
+
while (i < decimalPart.length && decimalPart[i] === '0') {
|
|
278
|
+
if (result) result += ' '
|
|
279
|
+
result += ZERO
|
|
280
|
+
i++
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Convert remainder as a single number
|
|
284
|
+
const remainder = decimalPart.slice(i)
|
|
285
|
+
if (remainder) {
|
|
286
|
+
if (result) result += ' '
|
|
287
|
+
result += integerToWords(BigInt(remainder), feminine)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return result
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Converts a numeric value to Spanish words.
|
|
295
|
+
*
|
|
296
|
+
* This is the main public API. It accepts any valid numeric input
|
|
297
|
+
* (number, string, or bigint) and handles parsing internally.
|
|
298
|
+
*
|
|
299
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
300
|
+
* @param {Object} [options] - Optional configuration
|
|
301
|
+
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
302
|
+
* @returns {string} The number in Spanish words
|
|
303
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
304
|
+
* @throws {Error} If value is not a valid number format
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* toWords(21) // 'veintiuno'
|
|
308
|
+
* toWords(21, {gender: 'feminine'}) // 'veintiuna'
|
|
309
|
+
* toWords(1000000) // 'un millón'
|
|
310
|
+
*/
|
|
311
|
+
function toWords (value, options) {
|
|
312
|
+
options = validateOptions(options)
|
|
313
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
314
|
+
const feminine = options.gender === 'feminine'
|
|
315
|
+
|
|
316
|
+
let result = ''
|
|
118
317
|
|
|
119
|
-
|
|
318
|
+
if (isNegative) {
|
|
319
|
+
result = NEGATIVE + ' '
|
|
120
320
|
}
|
|
321
|
+
|
|
322
|
+
result += integerToWords(integerPart, feminine)
|
|
323
|
+
|
|
324
|
+
if (decimalPart) {
|
|
325
|
+
result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, feminine)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return result
|
|
121
329
|
}
|
|
330
|
+
|
|
331
|
+
// ============================================================================
|
|
332
|
+
// Public API
|
|
333
|
+
// ============================================================================
|
|
334
|
+
|
|
335
|
+
export { toWords }
|
package/lib/languages/fa.d.ts
CHANGED
|
@@ -1,47 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Persian
|
|
2
|
+
* Converts a numeric value to Persian words.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - Recursive conversion for larger numbers
|
|
4
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
|
+
* @returns {string} The number in Persian words
|
|
7
6
|
*/
|
|
8
|
-
export
|
|
9
|
-
/**
|
|
10
|
-
* Words for digits 1-9.
|
|
11
|
-
* @type {Object.<number, string>}
|
|
12
|
-
*/
|
|
13
|
-
onesWords: {
|
|
14
|
-
[x: number]: string;
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Words for teen numbers (10-19).
|
|
18
|
-
* @type {Object.<number, string>}
|
|
19
|
-
*/
|
|
20
|
-
teensWords: {
|
|
21
|
-
[x: number]: string;
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* Words for multiples of ten (20-90).
|
|
25
|
-
* @type {Object.<number, string>}
|
|
26
|
-
*/
|
|
27
|
-
tensWords: {
|
|
28
|
-
[x: number]: string;
|
|
29
|
-
};
|
|
30
|
-
/**
|
|
31
|
-
* Words for hundreds (100-900).
|
|
32
|
-
* @type {Object.<number, string>}
|
|
33
|
-
*/
|
|
34
|
-
hundredsWords: {
|
|
35
|
-
[x: number]: string;
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* Scale magnitude words.
|
|
39
|
-
* @type {Object.<number, string>}
|
|
40
|
-
*/
|
|
41
|
-
scaleWords: {
|
|
42
|
-
[x: number]: string;
|
|
43
|
-
};
|
|
44
|
-
/** Converts integer part using categorized word tables. */
|
|
45
|
-
integerToWords(integerPart: any): any;
|
|
46
|
-
}
|
|
47
|
-
import { AbstractLanguage } from '../classes/abstract-language.js';
|
|
7
|
+
export function toWords(value: number | string | bigint): string;
|