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/pt.js
CHANGED
|
@@ -1,112 +1,306 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Portuguese 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.
|
|
9
|
+
*
|
|
10
|
+
* Portuguese-specific rules (handled in precomputation):
|
|
11
|
+
* - "e" conjunction everywhere: vinte e um, cento e um, mil e um
|
|
12
|
+
* - "cem" for exact 100, "cento" for 100+ remainder
|
|
13
|
+
* - Irregular hundreds: duzentos, trezentos, quatrocentos, etc.
|
|
14
|
+
* - Compound scale: milhão (10^6), mil milhões (10^9), bilião (10^12)
|
|
15
|
+
* - Omit "um" before "mil"
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Vocabulary (module-level constants)
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
const ONES = ['', 'um', 'dois', 'três', 'quatro', 'cinco', 'seis', 'sete', 'oito', 'nove']
|
|
25
|
+
const TEENS = ['dez', 'onze', 'doze', 'treze', 'catorze', 'quinze', 'dezasseis', 'dezassete', 'dezoito', 'dezanove']
|
|
26
|
+
const TENS = ['', '', 'vinte', 'trinta', 'quarenta', 'cinquenta', 'sessenta', 'setenta', 'oitenta', 'noventa']
|
|
27
|
+
|
|
28
|
+
// Irregular hundreds
|
|
29
|
+
const HUNDREDS = ['', 'cento', 'duzentos', 'trezentos', 'quatrocentos', 'quinhentos', 'seiscentos', 'setecentos', 'oitocentos', 'novecentos']
|
|
30
|
+
|
|
31
|
+
const THOUSAND = 'mil'
|
|
32
|
+
const ZERO = 'zero'
|
|
33
|
+
const NEGATIVE = 'menos'
|
|
34
|
+
const DECIMAL_SEP = 'vírgula'
|
|
35
|
+
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Precomputed Lookup Tables (built once at module load)
|
|
38
|
+
// ============================================================================
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Builds segment word for 0-999 with Portuguese "e" rules.
|
|
42
|
+
* Returns the word and whether it's an exact hundred (for "cem" handling).
|
|
43
|
+
*/
|
|
44
|
+
function buildSegment (n) {
|
|
45
|
+
if (n === 0) return { word: '', isExactHundred: false }
|
|
46
|
+
|
|
47
|
+
// Special case: exact 100 is "cem"
|
|
48
|
+
if (n === 100) return { word: 'cem', isExactHundred: true }
|
|
49
|
+
|
|
50
|
+
const ones = n % 10
|
|
51
|
+
const tens = Math.floor(n / 10) % 10
|
|
52
|
+
const hundreds = Math.floor(n / 100)
|
|
53
|
+
|
|
54
|
+
const parts = []
|
|
55
|
+
|
|
56
|
+
// Hundreds
|
|
57
|
+
if (hundreds > 0) {
|
|
58
|
+
parts.push(HUNDREDS[hundreds])
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Tens and ones
|
|
62
|
+
if (tens === 1) {
|
|
63
|
+
// Teens (10-19)
|
|
64
|
+
parts.push(TEENS[ones])
|
|
65
|
+
} else if (tens >= 2) {
|
|
66
|
+
if (ones > 0) {
|
|
67
|
+
// Tens + ones with "e": "vinte e um"
|
|
68
|
+
parts.push(TENS[tens] + ' e ' + ONES[ones])
|
|
69
|
+
} else {
|
|
70
|
+
parts.push(TENS[tens])
|
|
71
|
+
}
|
|
72
|
+
} else if (ones > 0) {
|
|
73
|
+
parts.push(ONES[ones])
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Join hundreds with "e": "cento e um", "duzentos e trinta e um"
|
|
77
|
+
const word = parts.join(' e ')
|
|
78
|
+
|
|
79
|
+
return { word, isExactHundred: hundreds > 0 && tens === 0 && ones === 0 }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Precompute all 1000 segment words (0-999)
|
|
83
|
+
const SEGMENTS = new Array(1000)
|
|
84
|
+
const SEGMENTS_STARTS_WITH_HUNDREDS = new Array(1000)
|
|
85
|
+
|
|
86
|
+
for (let i = 0; i < 1000; i++) {
|
|
87
|
+
const result = buildSegment(i)
|
|
88
|
+
SEGMENTS[i] = result.word
|
|
89
|
+
// Precompute whether segment starts with hundreds (100-999)
|
|
90
|
+
SEGMENTS_STARTS_WITH_HUNDREDS[i] = i >= 100
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Scale Word Lookup (precomputed for common scales)
|
|
95
|
+
// ============================================================================
|
|
96
|
+
|
|
97
|
+
// Precompute scale words for singular and plural forms
|
|
98
|
+
// Index 1 = thousands, 2 = millions, 3 = billions (mil milhões), etc.
|
|
99
|
+
const SCALE_WORDS_SINGULAR = [
|
|
100
|
+
'', // 0 unused
|
|
101
|
+
THOUSAND, // 1: mil
|
|
102
|
+
'milhão', // 2: 10^6
|
|
103
|
+
'mil milhões', // 3: 10^9 (compound)
|
|
104
|
+
'bilião', // 4: 10^12
|
|
105
|
+
'mil biliões', // 5: 10^15 (compound)
|
|
106
|
+
'trilião', // 6: 10^18
|
|
107
|
+
'mil triliões', // 7: 10^21 (compound)
|
|
108
|
+
'quatrilião' // 8: 10^24
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
const SCALE_WORDS_PLURAL = [
|
|
112
|
+
'', // 0 unused
|
|
113
|
+
THOUSAND, // 1: mil (same)
|
|
114
|
+
'milhões', // 2: 10^6
|
|
115
|
+
'mil milhões', // 3: 10^9 (compound, same)
|
|
116
|
+
'biliões', // 4: 10^12
|
|
117
|
+
'mil biliões', // 5: 10^15 (compound, same)
|
|
118
|
+
'triliões', // 6: 10^18
|
|
119
|
+
'mil triliões', // 7: 10^21 (compound, same)
|
|
120
|
+
'quatriliões' // 8: 10^24
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// Conversion Functions
|
|
125
|
+
// ============================================================================
|
|
2
126
|
|
|
3
127
|
/**
|
|
4
|
-
*
|
|
128
|
+
* Converts a non-negative integer to Portuguese words.
|
|
5
129
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - "e" (and) conjunction for number combinations
|
|
9
|
-
* - Post-processing to normalize word flow
|
|
130
|
+
* @param {bigint} n - Non-negative integer to convert
|
|
131
|
+
* @returns {string} Portuguese words
|
|
10
132
|
*/
|
|
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
|
-
[8n, 'oito'],
|
|
43
|
-
[7n, 'sete'],
|
|
44
|
-
[6n, 'seis'],
|
|
45
|
-
[5n, 'cinco'],
|
|
46
|
-
[4n, 'quatro'],
|
|
47
|
-
[3n, 'três'],
|
|
48
|
-
[2n, 'dois'],
|
|
49
|
-
[1n, 'um'],
|
|
50
|
-
[0n, 'zero']
|
|
51
|
-
]
|
|
52
|
-
|
|
53
|
-
hundredsWords = {
|
|
54
|
-
1: 'cento',
|
|
55
|
-
2: 'duzentos',
|
|
56
|
-
3: 'trezentos',
|
|
57
|
-
4: 'quatrocentos',
|
|
58
|
-
5: 'quinhentos',
|
|
59
|
-
6: 'seiscentos',
|
|
60
|
-
7: 'setecentos',
|
|
61
|
-
8: 'oitocentos',
|
|
62
|
-
9: 'novecentos'
|
|
133
|
+
function integerToWords (n) {
|
|
134
|
+
if (n === 0n) return ZERO
|
|
135
|
+
|
|
136
|
+
// Fast path: numbers < 1000 (direct lookup)
|
|
137
|
+
if (n < 1000n) {
|
|
138
|
+
return SEGMENTS[Number(n)]
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Fast path: numbers < 1,000,000 (thousands)
|
|
142
|
+
if (n < 1_000_000n) {
|
|
143
|
+
const thousands = Number(n / 1000n)
|
|
144
|
+
const remainder = Number(n % 1000n)
|
|
145
|
+
|
|
146
|
+
let result
|
|
147
|
+
if (thousands === 1) {
|
|
148
|
+
// "mil" not "um mil"
|
|
149
|
+
result = THOUSAND
|
|
150
|
+
} else {
|
|
151
|
+
result = SEGMENTS[thousands] + ' ' + THOUSAND
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (remainder > 0) {
|
|
155
|
+
// Insert "e" before remainder if it doesn't start with hundreds (< 100)
|
|
156
|
+
if (!SEGMENTS_STARTS_WITH_HUNDREDS[remainder]) {
|
|
157
|
+
result += ' e ' + SEGMENTS[remainder]
|
|
158
|
+
} else {
|
|
159
|
+
result += ' ' + SEGMENTS[remainder]
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return result
|
|
63
164
|
}
|
|
64
165
|
|
|
65
|
-
//
|
|
66
|
-
|
|
166
|
+
// For numbers >= 1,000,000, use scale decomposition
|
|
167
|
+
return buildLargeNumberWords(n)
|
|
168
|
+
}
|
|
67
169
|
|
|
68
|
-
|
|
69
|
-
|
|
170
|
+
/**
|
|
171
|
+
* Builds words for numbers >= 1,000,000.
|
|
172
|
+
* Uses BigInt division for faster segment extraction.
|
|
173
|
+
*
|
|
174
|
+
* @param {bigint} n - Number >= 1,000,000
|
|
175
|
+
* @returns {string} Portuguese words
|
|
176
|
+
*/
|
|
177
|
+
function buildLargeNumberWords (n) {
|
|
178
|
+
// Extract segments using BigInt division
|
|
179
|
+
// Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)
|
|
180
|
+
const segments = []
|
|
181
|
+
let temp = n
|
|
182
|
+
while (temp > 0n) {
|
|
183
|
+
segments.push(Number(temp % 1000n))
|
|
184
|
+
temp = temp / 1000n
|
|
70
185
|
}
|
|
71
186
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const followingValue = Object.values(following)[0] // BigInt
|
|
79
|
-
|
|
80
|
-
// Implicit "um": omit before millions ("um milhão" → "milhão")
|
|
81
|
-
if (precedingValue === 1n) {
|
|
82
|
-
if (followingValue < 1_000_000n) return { [followingWord]: followingValue }
|
|
83
|
-
precedingWord = 'um'
|
|
84
|
-
} else if (precedingValue === 100n && followingValue % 1000n !== 0n) {
|
|
85
|
-
// Special handling: "cem" (100) becomes "cento" when followed by non-thousands
|
|
86
|
-
precedingWord = 'cento'
|
|
187
|
+
// Find the first non-zero segment index (lowest scale with value)
|
|
188
|
+
let firstNonZeroIdx = 0
|
|
189
|
+
for (let i = 0; i < segments.length; i++) {
|
|
190
|
+
if (segments[i] !== 0) {
|
|
191
|
+
firstNonZeroIdx = i
|
|
192
|
+
break
|
|
87
193
|
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Build result string directly
|
|
197
|
+
let result = ''
|
|
198
|
+
let prevWasScale = false
|
|
199
|
+
|
|
200
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
201
|
+
const segment = segments[i]
|
|
202
|
+
if (segment === 0) continue
|
|
88
203
|
|
|
89
|
-
|
|
90
|
-
|
|
204
|
+
const segmentWord = SEGMENTS[segment]
|
|
205
|
+
const isLastSegment = (i === firstNonZeroIdx)
|
|
206
|
+
|
|
207
|
+
// Add "e" before final segment if previous was scale and this doesn't start with hundreds
|
|
208
|
+
if (result && isLastSegment && prevWasScale && !SEGMENTS_STARTS_WITH_HUNDREDS[segment]) {
|
|
209
|
+
result += ' e'
|
|
91
210
|
}
|
|
92
211
|
|
|
93
|
-
|
|
94
|
-
if (followingWord === 'milião') followingWord = 'milhão'
|
|
212
|
+
if (result) result += ' '
|
|
95
213
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
214
|
+
if (i === 0) {
|
|
215
|
+
// Units segment
|
|
216
|
+
result += segmentWord
|
|
217
|
+
prevWasScale = false
|
|
218
|
+
} else if (i === 1) {
|
|
219
|
+
// Thousands
|
|
220
|
+
if (segment === 1) {
|
|
221
|
+
result += THOUSAND
|
|
222
|
+
} else {
|
|
223
|
+
result += segmentWord + ' ' + THOUSAND
|
|
224
|
+
}
|
|
225
|
+
prevWasScale = true
|
|
226
|
+
} else {
|
|
227
|
+
// Million and above - use precomputed scale arrays
|
|
228
|
+
const scaleWord = segment === 1 ? SCALE_WORDS_SINGULAR[i] : SCALE_WORDS_PLURAL[i]
|
|
229
|
+
if (segment === 1) {
|
|
230
|
+
result += 'um ' + scaleWord
|
|
231
|
+
} else {
|
|
232
|
+
result += segmentWord + ' ' + scaleWord
|
|
102
233
|
}
|
|
234
|
+
prevWasScale = true
|
|
103
235
|
}
|
|
236
|
+
}
|
|
104
237
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
238
|
+
return result
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Converts decimal digits to Portuguese words.
|
|
243
|
+
*
|
|
244
|
+
* @param {string} decimalPart - Decimal digits (without the point)
|
|
245
|
+
* @returns {string} Portuguese words for decimal part
|
|
246
|
+
*/
|
|
247
|
+
function decimalPartToWords (decimalPart) {
|
|
248
|
+
let result = ''
|
|
109
249
|
|
|
110
|
-
|
|
250
|
+
// Handle leading zeros
|
|
251
|
+
let i = 0
|
|
252
|
+
while (i < decimalPart.length && decimalPart[i] === '0') {
|
|
253
|
+
if (result) result += ' '
|
|
254
|
+
result += ZERO
|
|
255
|
+
i++
|
|
111
256
|
}
|
|
257
|
+
|
|
258
|
+
// Convert remainder as a single number
|
|
259
|
+
const remainder = decimalPart.slice(i)
|
|
260
|
+
if (remainder) {
|
|
261
|
+
if (result) result += ' '
|
|
262
|
+
result += integerToWords(BigInt(remainder))
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return result
|
|
112
266
|
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Converts a numeric value to Portuguese words.
|
|
270
|
+
*
|
|
271
|
+
* This is the main public API. It accepts any valid numeric input
|
|
272
|
+
* (number, string, or bigint) and handles parsing internally.
|
|
273
|
+
*
|
|
274
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
275
|
+
* @returns {string} The number in Portuguese words
|
|
276
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
277
|
+
* @throws {Error} If value is not a valid number format
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* toWords(21) // 'vinte e um'
|
|
281
|
+
* toWords(100) // 'cem'
|
|
282
|
+
* toWords(1000000) // 'um milhão'
|
|
283
|
+
*/
|
|
284
|
+
function toWords (value) {
|
|
285
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
286
|
+
|
|
287
|
+
let result = ''
|
|
288
|
+
|
|
289
|
+
if (isNegative) {
|
|
290
|
+
result = NEGATIVE + ' '
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
result += integerToWords(integerPart)
|
|
294
|
+
|
|
295
|
+
if (decimalPart) {
|
|
296
|
+
result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return result
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ============================================================================
|
|
303
|
+
// Public API
|
|
304
|
+
// ============================================================================
|
|
305
|
+
|
|
306
|
+
export { toWords }
|
package/lib/languages/ro.d.ts
CHANGED
|
@@ -1,158 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Romanian
|
|
2
|
+
* Converts a numeric value to Romanian words.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* -
|
|
7
|
-
*
|
|
4
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
|
+
* @param {Object} [options] - Conversion options
|
|
6
|
+
* @param {string} [options.gender='masculine'] - Gender for numbers
|
|
7
|
+
* @returns {string} The number in Romanian words
|
|
8
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
9
|
+
* @throws {Error} If value is not a valid number format
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* toWords(21) // 'douăzeci și unu'
|
|
13
|
+
* toWords(1, { gender: 'feminine' }) // 'una'
|
|
14
|
+
* toWords(1000) // 'o mie'
|
|
8
15
|
*/
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
1: string;
|
|
13
|
-
2: string;
|
|
14
|
-
3: string;
|
|
15
|
-
4: string;
|
|
16
|
-
5: string;
|
|
17
|
-
6: string;
|
|
18
|
-
7: string;
|
|
19
|
-
8: string;
|
|
20
|
-
9: string;
|
|
21
|
-
};
|
|
22
|
-
onesFeminineWords: {
|
|
23
|
-
1: string;
|
|
24
|
-
2: string;
|
|
25
|
-
3: string;
|
|
26
|
-
4: string;
|
|
27
|
-
5: string;
|
|
28
|
-
6: string;
|
|
29
|
-
7: string;
|
|
30
|
-
8: string;
|
|
31
|
-
9: string;
|
|
32
|
-
};
|
|
33
|
-
teensWords: {
|
|
34
|
-
0: string;
|
|
35
|
-
1: string;
|
|
36
|
-
2: string;
|
|
37
|
-
3: string;
|
|
38
|
-
4: string;
|
|
39
|
-
5: string;
|
|
40
|
-
6: string;
|
|
41
|
-
7: string;
|
|
42
|
-
8: string;
|
|
43
|
-
9: string;
|
|
44
|
-
};
|
|
45
|
-
teensMasculineWords: {
|
|
46
|
-
0: string;
|
|
47
|
-
1: string;
|
|
48
|
-
2: string;
|
|
49
|
-
3: string;
|
|
50
|
-
4: string;
|
|
51
|
-
5: string;
|
|
52
|
-
6: string;
|
|
53
|
-
7: string;
|
|
54
|
-
8: string;
|
|
55
|
-
9: string;
|
|
56
|
-
};
|
|
57
|
-
twentiesWords: {
|
|
58
|
-
2: string;
|
|
59
|
-
3: string;
|
|
60
|
-
4: string;
|
|
61
|
-
5: string;
|
|
62
|
-
6: string;
|
|
63
|
-
7: string;
|
|
64
|
-
8: string;
|
|
65
|
-
9: string;
|
|
66
|
-
};
|
|
67
|
-
hundredsWords: {
|
|
68
|
-
1: string;
|
|
69
|
-
2: string;
|
|
70
|
-
3: string;
|
|
71
|
-
4: string;
|
|
72
|
-
5: string;
|
|
73
|
-
6: string;
|
|
74
|
-
7: string;
|
|
75
|
-
8: string;
|
|
76
|
-
9: string;
|
|
77
|
-
};
|
|
78
|
-
/**
|
|
79
|
-
* Romanian big units.
|
|
80
|
-
* For each power group we keep: singular, plural, feminineUnits?, needsDe?
|
|
81
|
-
* - 10^3: mie/mii (feminine units in segment; "de" for segment >= 20)
|
|
82
|
-
* - 10^6: milion/milioane ("de" for segment >= 20)
|
|
83
|
-
* - 10^9: miliard/miliarde ("de" for segment >= 20)
|
|
84
|
-
*/
|
|
85
|
-
scaleMetadata: {
|
|
86
|
-
1: {
|
|
87
|
-
singular: string;
|
|
88
|
-
plural: string;
|
|
89
|
-
feminine: boolean;
|
|
90
|
-
needsDe: boolean;
|
|
91
|
-
};
|
|
92
|
-
2: {
|
|
93
|
-
singular: string;
|
|
94
|
-
plural: string;
|
|
95
|
-
feminine: boolean;
|
|
96
|
-
needsDe: boolean;
|
|
97
|
-
};
|
|
98
|
-
3: {
|
|
99
|
-
singular: string;
|
|
100
|
-
plural: string;
|
|
101
|
-
feminine: boolean;
|
|
102
|
-
needsDe: boolean;
|
|
103
|
-
};
|
|
104
|
-
4: {
|
|
105
|
-
singular: string;
|
|
106
|
-
plural: string;
|
|
107
|
-
feminine: boolean;
|
|
108
|
-
needsDe: boolean;
|
|
109
|
-
};
|
|
110
|
-
5: {
|
|
111
|
-
singular: string;
|
|
112
|
-
plural: string;
|
|
113
|
-
feminine: boolean;
|
|
114
|
-
needsDe: boolean;
|
|
115
|
-
};
|
|
116
|
-
6: {
|
|
117
|
-
singular: string;
|
|
118
|
-
plural: string;
|
|
119
|
-
feminine: boolean;
|
|
120
|
-
needsDe: boolean;
|
|
121
|
-
};
|
|
122
|
-
7: {
|
|
123
|
-
singular: string;
|
|
124
|
-
plural: string;
|
|
125
|
-
feminine: boolean;
|
|
126
|
-
needsDe: boolean;
|
|
127
|
-
};
|
|
128
|
-
8: {
|
|
129
|
-
singular: string;
|
|
130
|
-
plural: string;
|
|
131
|
-
feminine: boolean;
|
|
132
|
-
needsDe: boolean;
|
|
133
|
-
};
|
|
134
|
-
9: {
|
|
135
|
-
singular: string;
|
|
136
|
-
plural: string;
|
|
137
|
-
feminine: boolean;
|
|
138
|
-
needsDe: boolean;
|
|
139
|
-
};
|
|
140
|
-
10: {
|
|
141
|
-
singular: string;
|
|
142
|
-
plural: string;
|
|
143
|
-
feminine: boolean;
|
|
144
|
-
needsDe: boolean;
|
|
145
|
-
};
|
|
146
|
-
};
|
|
147
|
-
/** Split numeric string into BigInt segments of specified size from left to right. */
|
|
148
|
-
splitToSegments(n: any, x: any): bigint[];
|
|
149
|
-
extractDigits(value: any): any;
|
|
150
|
-
/** Romanian pluralization & "de" rule for big units. */
|
|
151
|
-
romanianPluralize(segment: any, form: any): string;
|
|
152
|
-
spellUnder100(n: any, feminineUnits?: boolean, masculineTeens?: boolean): any;
|
|
153
|
-
spellUnder1000(n: any, feminineUnits?: boolean, masculineTeens?: boolean): any;
|
|
154
|
-
/** Decimals always use masculine forms regardless of gender option. */
|
|
155
|
-
decimalIntegerToWords(integerPart: any): any;
|
|
156
|
-
integerToWords(integerPart: any): string;
|
|
157
|
-
}
|
|
158
|
-
import { AbstractLanguage } from '../classes/abstract-language.js';
|
|
16
|
+
export function toWords(value: number | string | bigint, options?: {
|
|
17
|
+
gender?: string | undefined;
|
|
18
|
+
}): string;
|