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/vi.d.ts
CHANGED
|
@@ -1,72 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Vietnamese
|
|
2
|
+
* Converts a numeric value to Vietnamese 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
|
+
* @returns {string} The number in Vietnamese words
|
|
9
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
10
|
+
* @throws {Error} If value is not a valid number format
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* toWords(42) // 'bốn mươi hai'
|
|
14
|
+
* toWords(101) // 'một trăm lẻ một'
|
|
15
|
+
* toWords(1000000) // 'một triệu'
|
|
8
16
|
*/
|
|
9
|
-
export
|
|
10
|
-
belowTwentyWords: {
|
|
11
|
-
0: string;
|
|
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
|
-
10: string;
|
|
22
|
-
11: string;
|
|
23
|
-
12: string;
|
|
24
|
-
13: string;
|
|
25
|
-
14: string;
|
|
26
|
-
15: string;
|
|
27
|
-
16: string;
|
|
28
|
-
17: string;
|
|
29
|
-
18: string;
|
|
30
|
-
19: string;
|
|
31
|
-
};
|
|
32
|
-
twentiesWords: {
|
|
33
|
-
20: string;
|
|
34
|
-
30: string;
|
|
35
|
-
40: string;
|
|
36
|
-
50: string;
|
|
37
|
-
60: string;
|
|
38
|
-
70: string;
|
|
39
|
-
80: string;
|
|
40
|
-
90: string;
|
|
41
|
-
};
|
|
42
|
-
scaleWords: {
|
|
43
|
-
1: string;
|
|
44
|
-
2: string;
|
|
45
|
-
3: string;
|
|
46
|
-
4: string;
|
|
47
|
-
5: string;
|
|
48
|
-
6: string;
|
|
49
|
-
7: string;
|
|
50
|
-
8: string;
|
|
51
|
-
9: string;
|
|
52
|
-
10: string;
|
|
53
|
-
11: string;
|
|
54
|
-
12: string;
|
|
55
|
-
13: string;
|
|
56
|
-
14: string;
|
|
57
|
-
15: string;
|
|
58
|
-
16: string;
|
|
59
|
-
17: string;
|
|
60
|
-
18: string;
|
|
61
|
-
19: string;
|
|
62
|
-
20: string;
|
|
63
|
-
};
|
|
64
|
-
/** Convert numbers less than 100 to Vietnamese words. */
|
|
65
|
-
convertLess100(number: any): any;
|
|
66
|
-
/** Convert numbers less than 1000 to Vietnamese words. */
|
|
67
|
-
convertLess1000(number: any): any;
|
|
68
|
-
/** Convert numbers greater than 1000 to Vietnamese words. */
|
|
69
|
-
convertMore1000(number: any): any;
|
|
70
|
-
integerToWords(integerPart: any): any;
|
|
71
|
-
}
|
|
72
|
-
import { AbstractLanguage } from '../classes/abstract-language.js';
|
|
17
|
+
export function toWords(value: number | string | bigint): string;
|
package/lib/languages/vi.js
CHANGED
|
@@ -1,147 +1,316 @@
|
|
|
1
|
-
import { AbstractLanguage } from '../classes/abstract-language.js'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Vietnamese language converter
|
|
2
|
+
* Vietnamese language converter - Functional Implementation v2
|
|
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.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
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
|
+
* Vietnamese-specific rules (handled in precomputation):
|
|
11
|
+
* - Special pronunciation: "lăm" for 5 in tens position, "mốt" for final 1
|
|
12
|
+
* - "Lẻ" (odd/extra) marker when tens place is zero after hundreds/scales
|
|
13
|
+
* - Short scale system with Vietnamese words (nghìn, triệu, tỷ)
|
|
10
14
|
*/
|
|
11
|
-
export class Vietnamese extends AbstractLanguage {
|
|
12
|
-
negativeWord = 'âm'
|
|
13
|
-
decimalSeparatorWord = 'phẩy'
|
|
14
|
-
zeroWord = 'không'
|
|
15
|
-
|
|
16
|
-
belowTwentyWords = {
|
|
17
|
-
0: 'không',
|
|
18
|
-
1: 'một',
|
|
19
|
-
2: 'hai',
|
|
20
|
-
3: 'ba',
|
|
21
|
-
4: 'bốn',
|
|
22
|
-
5: 'năm',
|
|
23
|
-
6: 'sáu',
|
|
24
|
-
7: 'bảy',
|
|
25
|
-
8: 'tám',
|
|
26
|
-
9: 'chín',
|
|
27
|
-
10: 'mười',
|
|
28
|
-
11: 'mười một',
|
|
29
|
-
12: 'mười hai',
|
|
30
|
-
13: 'mười ba',
|
|
31
|
-
14: 'mười bốn',
|
|
32
|
-
15: 'mười lăm',
|
|
33
|
-
16: 'mười sáu',
|
|
34
|
-
17: 'mười bảy',
|
|
35
|
-
18: 'mười tám',
|
|
36
|
-
19: 'mười chín'
|
|
37
|
-
}
|
|
38
15
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
90: 'chín mươi'
|
|
48
|
-
}
|
|
16
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Vocabulary (module-level constants)
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
// Base vocabulary for building lookup tables
|
|
23
|
+
const ONES = ['không', 'một', 'hai', 'ba', 'bốn', 'năm', 'sáu', 'bảy', 'tám', 'chín']
|
|
49
24
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
25
|
+
// Scale words indexed by scale level (0 = units, 1 = thousands, etc.)
|
|
26
|
+
const SCALES = [
|
|
27
|
+
'', 'nghìn', 'triệu', 'tỷ', 'nghìn tỷ', 'trăm nghìn tỷ',
|
|
28
|
+
'Quintillion', 'Sextillion', 'Septillion', 'Octillion',
|
|
29
|
+
'Nonillion', 'Decillion', 'Undecillion', 'Duodecillion',
|
|
30
|
+
'Tredecillion', 'Quattuordecillion', 'Sexdecillion',
|
|
31
|
+
'Septendecillion', 'Octodecillion', 'Novemdecillion', 'Vigintillion'
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
const HUNDRED = 'trăm'
|
|
35
|
+
const ZERO = 'không'
|
|
36
|
+
const NEGATIVE = 'âm'
|
|
37
|
+
const DECIMAL_SEP = 'phẩy'
|
|
38
|
+
const LE = 'lẻ' // "odd/extra" marker for gaps
|
|
39
|
+
|
|
40
|
+
// Special forms
|
|
41
|
+
const MOT_FINAL = 'mốt' // 1 in tens position (21, 31, etc.)
|
|
42
|
+
const LAM = 'lăm' // 5 in tens position (25, 35, etc.)
|
|
43
|
+
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Precomputed Lookup Tables (built once at module load)
|
|
46
|
+
// ============================================================================
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Builds word for 0-99 with special forms (mốt, lăm).
|
|
50
|
+
* Only used during table construction.
|
|
51
|
+
*/
|
|
52
|
+
function buildBelowHundred (n) {
|
|
53
|
+
if (n === 0) return ONES[0]
|
|
54
|
+
if (n < 10) return ONES[n]
|
|
55
|
+
|
|
56
|
+
// Teens: 10-19
|
|
57
|
+
if (n < 20) {
|
|
58
|
+
const ones = n - 10
|
|
59
|
+
if (ones === 0) return 'mười'
|
|
60
|
+
if (ones === 5) return 'mười lăm'
|
|
61
|
+
return 'mười ' + ONES[ones]
|
|
71
62
|
}
|
|
72
63
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
64
|
+
// 20-99
|
|
65
|
+
const ones = n % 10
|
|
66
|
+
const tens = Math.floor(n / 10)
|
|
67
|
+
const tensWord = ONES[tens] + ' mươi'
|
|
68
|
+
|
|
69
|
+
if (ones === 0) return tensWord
|
|
70
|
+
if (ones === 1) return tensWord + ' ' + MOT_FINAL
|
|
71
|
+
if (ones === 5) return tensWord + ' ' + LAM
|
|
72
|
+
return tensWord + ' ' + ONES[ones]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Builds segment word for 0-999.
|
|
77
|
+
* Only used during table construction.
|
|
78
|
+
*/
|
|
79
|
+
function buildSegment (n) {
|
|
80
|
+
if (n === 0) return ''
|
|
81
|
+
|
|
82
|
+
const hundreds = Math.floor(n / 100)
|
|
83
|
+
const remainder = n % 100
|
|
84
|
+
|
|
85
|
+
let result = ''
|
|
86
|
+
|
|
87
|
+
if (hundreds > 0) {
|
|
88
|
+
result = ONES[hundreds] + ' ' + HUNDRED
|
|
90
89
|
}
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
if (tensUnitsPart > 0 && tensUnitsPart < 10) {
|
|
101
|
-
if (words.length > 0) {
|
|
102
|
-
words.push('lẻ')
|
|
103
|
-
}
|
|
104
|
-
if (tensUnitsPart === 5) {
|
|
105
|
-
words.push('năm')
|
|
91
|
+
if (remainder > 0) {
|
|
92
|
+
if (remainder < 10) {
|
|
93
|
+
// Single digit after hundreds needs "lẻ"
|
|
94
|
+
if (result) {
|
|
95
|
+
result += ' ' + LE + ' '
|
|
96
|
+
// Use "năm" not "lăm" after lẻ
|
|
97
|
+
result += remainder === 5 ? 'năm' : ONES[remainder]
|
|
106
98
|
} else {
|
|
107
|
-
|
|
99
|
+
result = ONES[remainder]
|
|
108
100
|
}
|
|
101
|
+
} else {
|
|
102
|
+
// 10-99 after hundreds
|
|
103
|
+
if (result) result += ' '
|
|
104
|
+
result += BELOW_100[remainder]
|
|
109
105
|
}
|
|
110
|
-
if (tensUnitsPart >= 10) {
|
|
111
|
-
words.push(this.integerToWords(tensUnitsPart))
|
|
112
|
-
}
|
|
113
|
-
return words.join(' ')
|
|
114
106
|
}
|
|
115
107
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
108
|
+
return result
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Precompute all 100 below-hundred words (0-99)
|
|
112
|
+
// BELOW_100[n] gives the Vietnamese word for n
|
|
113
|
+
const BELOW_100 = new Array(100)
|
|
114
|
+
for (let i = 0; i < 100; i++) {
|
|
115
|
+
BELOW_100[i] = buildBelowHundred(i)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Precompute all 1000 segment words (0-999)
|
|
119
|
+
// SEGMENTS[n] gives the Vietnamese word for n within a segment
|
|
120
|
+
const SEGMENTS = new Array(1000)
|
|
121
|
+
for (let i = 0; i < 1000; i++) {
|
|
122
|
+
SEGMENTS[i] = buildSegment(i)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Precompute "lẻ" prefixed versions for small remainders after scale words
|
|
126
|
+
// LE_SEGMENTS[n] gives "lẻ X" for n in range 1-99
|
|
127
|
+
const LE_SEGMENTS = new Array(100)
|
|
128
|
+
LE_SEGMENTS[0] = ''
|
|
129
|
+
for (let i = 1; i < 10; i++) {
|
|
130
|
+
// Use "năm" not "lăm" after lẻ
|
|
131
|
+
LE_SEGMENTS[i] = LE + ' ' + (i === 5 ? 'năm' : ONES[i])
|
|
132
|
+
}
|
|
133
|
+
for (let i = 10; i < 100; i++) {
|
|
134
|
+
LE_SEGMENTS[i] = LE + ' ' + BELOW_100[i]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// Conversion Functions
|
|
139
|
+
// ============================================================================
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Converts a non-negative integer to Vietnamese words.
|
|
143
|
+
*
|
|
144
|
+
* @param {bigint} n - Non-negative integer to convert
|
|
145
|
+
* @returns {string} Vietnamese words
|
|
146
|
+
*/
|
|
147
|
+
function integerToWords (n) {
|
|
148
|
+
if (n === 0n) return ZERO
|
|
149
|
+
|
|
150
|
+
// Fast path: numbers < 100 (direct lookup)
|
|
151
|
+
if (n < 100n) {
|
|
152
|
+
return BELOW_100[Number(n)]
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Fast path: numbers < 1000 (direct lookup)
|
|
156
|
+
if (n < 1000n) {
|
|
157
|
+
return SEGMENTS[Number(n)]
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Fast path: numbers < 1,000,000 (thousands)
|
|
161
|
+
if (n < 1_000_000n) {
|
|
162
|
+
const thousands = Number(n / 1000n)
|
|
163
|
+
const remainder = Number(n % 1000n)
|
|
164
|
+
|
|
165
|
+
const thousandsWords = SEGMENTS[thousands] + ' ' + SCALES[1]
|
|
166
|
+
|
|
167
|
+
if (remainder === 0) {
|
|
168
|
+
return thousandsWords
|
|
124
169
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
words.push('lẻ')
|
|
130
|
-
}
|
|
131
|
-
words.push(this.integerToWords(r))
|
|
170
|
+
|
|
171
|
+
// Check if remainder needs "lẻ" marker (< 100)
|
|
172
|
+
if (remainder < 100) {
|
|
173
|
+
return thousandsWords + ' ' + LE_SEGMENTS[remainder]
|
|
132
174
|
}
|
|
133
|
-
|
|
175
|
+
|
|
176
|
+
return thousandsWords + ' ' + SEGMENTS[remainder]
|
|
134
177
|
}
|
|
135
178
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
179
|
+
// For numbers >= 1,000,000, use scale decomposition
|
|
180
|
+
return buildLargeNumberWords(n)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Builds words for numbers >= 1,000,000.
|
|
185
|
+
*
|
|
186
|
+
* @param {bigint} n - Number >= 1,000,000
|
|
187
|
+
* @returns {string} Vietnamese words
|
|
188
|
+
*/
|
|
189
|
+
function buildLargeNumberWords (n) {
|
|
190
|
+
const numStr = n.toString()
|
|
191
|
+
const len = numStr.length
|
|
192
|
+
|
|
193
|
+
// Build segments of 3 digits from right to left
|
|
194
|
+
const segments = []
|
|
195
|
+
const segmentSize = 3
|
|
196
|
+
|
|
197
|
+
const remainderLen = len % segmentSize
|
|
198
|
+
let pos = 0
|
|
199
|
+
if (remainderLen > 0) {
|
|
200
|
+
segments.push(Number(numStr.slice(0, remainderLen)))
|
|
201
|
+
pos = remainderLen
|
|
202
|
+
}
|
|
203
|
+
while (pos < len) {
|
|
204
|
+
segments.push(Number(numStr.slice(pos, pos + segmentSize)))
|
|
205
|
+
pos += segmentSize
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Convert segments to words
|
|
209
|
+
const parts = []
|
|
210
|
+
let scaleIndex = segments.length - 1
|
|
211
|
+
|
|
212
|
+
for (let i = 0; i < segments.length; i++) {
|
|
213
|
+
const segment = segments[i]
|
|
214
|
+
if (segment !== 0) {
|
|
215
|
+
const words = SEGMENTS[segment]
|
|
216
|
+
if (words) {
|
|
217
|
+
if (scaleIndex > 0) {
|
|
218
|
+
parts.push(words + ' ' + SCALES[scaleIndex])
|
|
219
|
+
} else {
|
|
220
|
+
parts.push(words)
|
|
221
|
+
}
|
|
144
222
|
}
|
|
145
223
|
}
|
|
224
|
+
scaleIndex--
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Join with "lẻ" logic for small remainders
|
|
228
|
+
const partsLen = parts.length
|
|
229
|
+
if (partsLen === 0) return ZERO
|
|
230
|
+
if (partsLen === 1) return parts[0]
|
|
231
|
+
|
|
232
|
+
// Check if final segment needs "lẻ" marker (remainder <= 99 after scale word)
|
|
233
|
+
const lastSegment = segments[segments.length - 1]
|
|
234
|
+
if (lastSegment > 0 && lastSegment <= 99) {
|
|
235
|
+
// Last segment is small (no hundreds), needs "lẻ" after scale word
|
|
236
|
+
let result = parts[0]
|
|
237
|
+
for (let i = 1; i < partsLen - 1; i++) {
|
|
238
|
+
result += ' ' + parts[i]
|
|
239
|
+
}
|
|
240
|
+
return result + ' ' + LE_SEGMENTS[lastSegment]
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Join with spaces
|
|
244
|
+
let result = parts[0]
|
|
245
|
+
for (let i = 1; i < partsLen; i++) {
|
|
246
|
+
result += ' ' + parts[i]
|
|
247
|
+
}
|
|
248
|
+
return result
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Converts decimal digits to Vietnamese words.
|
|
253
|
+
*
|
|
254
|
+
* @param {string} decimalPart - Decimal digits (without the point)
|
|
255
|
+
* @returns {string} Vietnamese words for decimal part
|
|
256
|
+
*/
|
|
257
|
+
function decimalPartToWords (decimalPart) {
|
|
258
|
+
let result = ''
|
|
259
|
+
|
|
260
|
+
// Handle leading zeros
|
|
261
|
+
let i = 0
|
|
262
|
+
while (i < decimalPart.length && decimalPart[i] === '0') {
|
|
263
|
+
if (result) result += ' '
|
|
264
|
+
result += ZERO
|
|
265
|
+
i++
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Convert remainder as a single number
|
|
269
|
+
const remainder = decimalPart.slice(i)
|
|
270
|
+
if (remainder) {
|
|
271
|
+
if (result) result += ' '
|
|
272
|
+
result += integerToWords(BigInt(remainder))
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return result
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Converts a numeric value to Vietnamese words.
|
|
280
|
+
*
|
|
281
|
+
* This is the main public API. It accepts any valid numeric input
|
|
282
|
+
* (number, string, or bigint) and handles parsing internally.
|
|
283
|
+
*
|
|
284
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
285
|
+
* @returns {string} The number in Vietnamese words
|
|
286
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
287
|
+
* @throws {Error} If value is not a valid number format
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* toWords(42) // 'bốn mươi hai'
|
|
291
|
+
* toWords(101) // 'một trăm lẻ một'
|
|
292
|
+
* toWords(1000000) // 'một triệu'
|
|
293
|
+
*/
|
|
294
|
+
function toWords (value) {
|
|
295
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
296
|
+
|
|
297
|
+
let result = ''
|
|
298
|
+
|
|
299
|
+
if (isNegative) {
|
|
300
|
+
result = NEGATIVE + ' '
|
|
146
301
|
}
|
|
302
|
+
|
|
303
|
+
result += integerToWords(integerPart)
|
|
304
|
+
|
|
305
|
+
if (decimalPart) {
|
|
306
|
+
result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return result
|
|
147
310
|
}
|
|
311
|
+
|
|
312
|
+
// ============================================================================
|
|
313
|
+
// Public API
|
|
314
|
+
// ============================================================================
|
|
315
|
+
|
|
316
|
+
export { toWords }
|
|
@@ -1,21 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Simplified Chinese
|
|
2
|
+
* Converts a numeric value to Simplified Chinese words.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
|
+
* @param {Object} [options] - Optional configuration
|
|
6
|
+
* @param {boolean} [options.formal=true] - Use formal/financial numerals
|
|
7
|
+
* @returns {string} The number in Simplified Chinese words
|
|
8
8
|
*/
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
/** Combines two word-sets according to Chinese grammar rules. */
|
|
13
|
-
combineWordSets(preceding: any, following: any): any;
|
|
14
|
-
/** Returns the number of digits in a number. */
|
|
15
|
-
digit(number_: any): any;
|
|
16
|
-
/** Counts the number of zero digits in a number. */
|
|
17
|
-
zeroDigit(number_: any): number;
|
|
18
|
-
/** Converts decimal digits to words by reading each digit individually. */
|
|
19
|
-
decimalDigitsToWords(decimalString: any): string[];
|
|
20
|
-
}
|
|
21
|
-
import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
|
|
9
|
+
export function toWords(value: number | string | bigint, options?: {
|
|
10
|
+
formal?: boolean | undefined;
|
|
11
|
+
}): string;
|