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/ro.js
CHANGED
|
@@ -1,273 +1,257 @@
|
|
|
1
|
-
import { AbstractLanguage } from '../classes/abstract-language.js'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Romanian language converter
|
|
2
|
+
* Romanian language converter - Functional Implementation
|
|
3
|
+
*
|
|
4
|
+
* A performance-optimized number-to-words converter using precomputed lookup tables.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Key features:
|
|
7
7
|
* - Gender agreement (unu/una, doi/două)
|
|
8
|
-
* - Complex pluralization (singular/plural forms)
|
|
9
8
|
* - "De" preposition insertion for groups >= 20
|
|
9
|
+
* - Complex scale word handling (mie/mii, milion/milioane)
|
|
10
|
+
* - Feminine units for thousands
|
|
10
11
|
*/
|
|
11
|
-
export class Romanian extends AbstractLanguage {
|
|
12
|
-
negativeWord = 'minus'
|
|
13
|
-
decimalSeparatorWord = 'virgulă'
|
|
14
|
-
zeroWord = 'zero'
|
|
15
|
-
|
|
16
|
-
onesWords = {
|
|
17
|
-
1: 'unu',
|
|
18
|
-
2: 'doi',
|
|
19
|
-
3: 'trei',
|
|
20
|
-
4: 'patru',
|
|
21
|
-
5: 'cinci',
|
|
22
|
-
6: 'șase',
|
|
23
|
-
7: 'șapte',
|
|
24
|
-
8: 'opt',
|
|
25
|
-
9: 'nouă'
|
|
26
|
-
}
|
|
27
12
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
2: 'două',
|
|
31
|
-
3: 'trei',
|
|
32
|
-
4: 'patru',
|
|
33
|
-
5: 'cinci',
|
|
34
|
-
6: 'șase',
|
|
35
|
-
7: 'șapte',
|
|
36
|
-
8: 'opt',
|
|
37
|
-
9: 'nouă'
|
|
38
|
-
}
|
|
13
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
14
|
+
import { validateOptions } from '../utils/validate-options.js'
|
|
39
15
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
2: 'douăsprezece',
|
|
44
|
-
3: 'treisprezece',
|
|
45
|
-
4: 'paisprezece',
|
|
46
|
-
5: 'cincisprezece',
|
|
47
|
-
6: 'șaisprezece',
|
|
48
|
-
7: 'șaptesprezece',
|
|
49
|
-
8: 'optsprezece',
|
|
50
|
-
9: 'nouăsprezece'
|
|
51
|
-
}
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Vocabulary (module-level constants)
|
|
18
|
+
// ============================================================================
|
|
52
19
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
8: 'optsprezece',
|
|
63
|
-
9: 'nouăsprezece'
|
|
64
|
-
}
|
|
20
|
+
const ONES_MASC = ['', 'unu', 'doi', 'trei', 'patru', 'cinci', 'șase', 'șapte', 'opt', 'nouă']
|
|
21
|
+
const ONES_FEM = ['', 'una', 'două', 'trei', 'patru', 'cinci', 'șase', 'șapte', 'opt', 'nouă']
|
|
22
|
+
|
|
23
|
+
const TEENS = ['zece', 'unsprezece', 'douăsprezece', 'treisprezece', 'paisprezece', 'cincisprezece', 'șaisprezece', 'șaptesprezece', 'optsprezece', 'nouăsprezece']
|
|
24
|
+
const TEENS_MASC = ['zece', 'unsprezece', 'doisprezece', 'treisprezece', 'paisprezece', 'cincisprezece', 'șaisprezece', 'șaptesprezece', 'optsprezece', 'nouăsprezece']
|
|
25
|
+
|
|
26
|
+
const TWENTIES = ['', '', 'douăzeci', 'treizeci', 'patruzeci', 'cincizeci', 'șaizeci', 'șaptezeci', 'optzeci', 'nouăzeci']
|
|
27
|
+
|
|
28
|
+
const HUNDREDS = ['', 'o sută', 'două sute', 'trei sute', 'patru sute', 'cinci sute', 'șase sute', 'șapte sute', 'opt sute', 'nouă sute']
|
|
65
29
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
30
|
+
const ZERO = 'zero'
|
|
31
|
+
const NEGATIVE = 'minus'
|
|
32
|
+
const DECIMAL_SEP = 'virgulă'
|
|
33
|
+
|
|
34
|
+
// Scale metadata: [singular, plural, article, feminine, needsDe]
|
|
35
|
+
// - singular: form for 1
|
|
36
|
+
// - plural: form for 2+
|
|
37
|
+
// - article: 'o' for feminine, 'un' for masculine
|
|
38
|
+
// - feminine: whether units should be feminine
|
|
39
|
+
// - needsDe: whether "de" is inserted for segment >= 20
|
|
40
|
+
const SCALE_META = [
|
|
41
|
+
{ singular: 'mie', plural: 'mii', article: 'o', feminine: true, needsDe: true },
|
|
42
|
+
{ singular: 'milion', plural: 'milioane', article: 'un', feminine: false, needsDe: true },
|
|
43
|
+
{ singular: 'miliard', plural: 'miliarde', article: 'un', feminine: false, needsDe: true },
|
|
44
|
+
{ singular: 'trilion', plural: 'trilioane', article: 'un', feminine: false, needsDe: true },
|
|
45
|
+
{ singular: 'cvadrilion', plural: 'cvadrilioane', article: 'un', feminine: false, needsDe: true },
|
|
46
|
+
{ singular: 'cvintilion', plural: 'cvintilioane', article: 'un', feminine: false, needsDe: true },
|
|
47
|
+
{ singular: 'sextilion', plural: 'sextilioane', article: 'un', feminine: false, needsDe: true },
|
|
48
|
+
{ singular: 'septilion', plural: 'septilioane', article: 'un', feminine: false, needsDe: true },
|
|
49
|
+
{ singular: 'octilion', plural: 'octilioane', article: 'un', feminine: false, needsDe: true }
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
// ============================================================================
|
|
53
|
+
// Helper Functions
|
|
54
|
+
// ============================================================================
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Spells number under 100.
|
|
58
|
+
*/
|
|
59
|
+
function spellUnder100 (n, feminine = false, masculineTeens = false) {
|
|
60
|
+
if (n === 0) return ''
|
|
61
|
+
if (n < 10) {
|
|
62
|
+
return feminine ? ONES_FEM[n] : ONES_MASC[n]
|
|
63
|
+
}
|
|
64
|
+
if (n < 20) {
|
|
65
|
+
return masculineTeens ? TEENS_MASC[n - 10] : TEENS[n - 10]
|
|
75
66
|
}
|
|
67
|
+
const t = Math.floor(n / 10)
|
|
68
|
+
const u = n % 10
|
|
69
|
+
if (u === 0) {
|
|
70
|
+
return TWENTIES[t]
|
|
71
|
+
}
|
|
72
|
+
const onesWord = feminine ? ONES_FEM[u] : ONES_MASC[u]
|
|
73
|
+
return TWENTIES[t] + ' și ' + onesWord
|
|
74
|
+
}
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Spells number under 1000.
|
|
78
|
+
*/
|
|
79
|
+
function spellUnder1000 (n, feminine = false, masculineTeens = false) {
|
|
80
|
+
if (n === 0) return ''
|
|
81
|
+
if (n < 100) return spellUnder100(n, feminine, masculineTeens)
|
|
82
|
+
|
|
83
|
+
const h = Math.floor(n / 100)
|
|
84
|
+
const r = n % 100
|
|
85
|
+
const hundredWord = HUNDREDS[h]
|
|
86
|
+
|
|
87
|
+
if (r === 0) return hundredWord
|
|
88
|
+
return hundredWord + ' ' + spellUnder100(r, feminine, masculineTeens)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Builds scale word with proper pluralization and "de" insertion.
|
|
93
|
+
* Romanian always uses feminine forms (două, not doi) when counting scale words.
|
|
94
|
+
*/
|
|
95
|
+
function buildScalePhrase (segment, scaleIndex) {
|
|
96
|
+
const meta = SCALE_META[scaleIndex - 1]
|
|
97
|
+
if (!meta) return spellUnder1000(segment, true)
|
|
98
|
+
|
|
99
|
+
if (segment === 1) {
|
|
100
|
+
return meta.article + ' ' + meta.singular
|
|
87
101
|
}
|
|
88
102
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
* - 10^3: mie/mii (feminine units in segment; "de" for segment >= 20)
|
|
93
|
-
* - 10^6: milion/milioane ("de" for segment >= 20)
|
|
94
|
-
* - 10^9: miliard/miliarde ("de" for segment >= 20)
|
|
95
|
-
*/
|
|
96
|
-
scaleMetadata = {
|
|
97
|
-
1: { singular: 'mie', plural: 'mii', feminine: true, needsDe: true }, // 10^3
|
|
98
|
-
2: { singular: 'milion', plural: 'milioane', feminine: false, needsDe: true }, // 10^6
|
|
99
|
-
3: { singular: 'miliard', plural: 'miliarde', feminine: false, needsDe: true }, // 10^9
|
|
100
|
-
4: { singular: 'trilion', plural: 'trilioane', feminine: false, needsDe: true }, // 10^12
|
|
101
|
-
5: { singular: 'cvadrilion', plural: 'cvadrilioane', feminine: false, needsDe: true }, // 10^15
|
|
102
|
-
6: { singular: 'cvintilion', plural: 'cvintilioane', feminine: false, needsDe: true }, // 10^18
|
|
103
|
-
7: { singular: 'sextilion', plural: 'sextilioane', feminine: false, needsDe: true }, // 10^21
|
|
104
|
-
8: { singular: 'septilion', plural: 'septilioane', feminine: false, needsDe: true }, // 10^24
|
|
105
|
-
9: { singular: 'octilion', plural: 'octilioane', feminine: false, needsDe: true }, // 10^27
|
|
106
|
-
10: { singular: 'decilion', plural: 'decilioane', feminine: false, needsDe: true } // 10^33
|
|
103
|
+
// Special case: 21 with scale words uses feminine "una"
|
|
104
|
+
if (segment === 21 && meta.needsDe) {
|
|
105
|
+
return 'douăzeci și una de ' + meta.plural
|
|
107
106
|
}
|
|
108
107
|
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
// Romanian always uses feminine when counting scale words (două milioane, not doi milioane)
|
|
109
|
+
const words = spellUnder1000(segment, true)
|
|
111
110
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
111
|
+
// "de" after >= 20
|
|
112
|
+
const needsDe = meta.needsDe && segment >= 20
|
|
113
|
+
const separator = needsDe ? ' de ' : ' '
|
|
116
114
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
115
|
+
return words + separator + meta.plural
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// Conversion Functions
|
|
120
|
+
// ============================================================================
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Converts a non-negative integer to Romanian words.
|
|
124
|
+
*
|
|
125
|
+
* @param {bigint} n - Non-negative integer to convert
|
|
126
|
+
* @param {Object} options - Conversion options
|
|
127
|
+
* @returns {string} Romanian words
|
|
128
|
+
*/
|
|
129
|
+
function integerToWords (n, options = {}) {
|
|
130
|
+
if (n === 0n) return ZERO
|
|
131
|
+
|
|
132
|
+
// Fast path: numbers < 1000
|
|
133
|
+
if (n < 1000n) {
|
|
134
|
+
const feminine = options.gender === 'feminine'
|
|
135
|
+
return spellUnder1000(Number(n), feminine)
|
|
137
136
|
}
|
|
138
137
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
return buildLargeNumberWords(n, options)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Builds words for numbers >= 1000.
|
|
143
|
+
* Uses BigInt division for faster segment extraction.
|
|
144
|
+
*
|
|
145
|
+
* @param {bigint} n - Number >= 1000
|
|
146
|
+
* @param {Object} options - Conversion options
|
|
147
|
+
* @returns {string} Romanian words
|
|
148
|
+
*/
|
|
149
|
+
function buildLargeNumberWords (n, options) {
|
|
150
|
+
// Extract segments using BigInt division (faster than string slicing)
|
|
151
|
+
// Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)
|
|
152
|
+
const segmentValues = []
|
|
153
|
+
let temp = n
|
|
154
|
+
while (temp > 0n) {
|
|
155
|
+
segmentValues.push(Number(temp % 1000n))
|
|
156
|
+
temp = temp / 1000n
|
|
143
157
|
}
|
|
144
158
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const n = Number(segment)
|
|
159
|
+
// Build result string directly (avoid regex cleanup)
|
|
160
|
+
let result = ''
|
|
148
161
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
162
|
+
for (let i = segmentValues.length - 1; i >= 0; i--) {
|
|
163
|
+
const segment = segmentValues[i]
|
|
164
|
+
if (segment === 0) continue
|
|
165
|
+
|
|
166
|
+
let segmentWords
|
|
167
|
+
if (i === 0) {
|
|
168
|
+
// Units segment - use gender from options
|
|
169
|
+
const feminine = options.gender === 'feminine'
|
|
170
|
+
segmentWords = spellUnder1000(segment, feminine)
|
|
171
|
+
} else {
|
|
172
|
+
// Scale segment
|
|
173
|
+
segmentWords = buildScalePhrase(segment, i)
|
|
153
174
|
}
|
|
154
175
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
176
|
+
if (result && segmentWords) {
|
|
177
|
+
result += ' ' + segmentWords
|
|
178
|
+
} else if (segmentWords) {
|
|
179
|
+
result = segmentWords
|
|
158
180
|
}
|
|
181
|
+
}
|
|
159
182
|
|
|
160
|
-
|
|
161
|
-
|
|
183
|
+
return result
|
|
184
|
+
}
|
|
162
185
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Converts decimal digits to Romanian words.
|
|
188
|
+
* Decimals always use masculine forms.
|
|
189
|
+
*
|
|
190
|
+
* @param {string} decimalPart - Decimal digits (without the point)
|
|
191
|
+
* @returns {string} Romanian words for decimal part
|
|
192
|
+
*/
|
|
193
|
+
function decimalPartToWords (decimalPart) {
|
|
194
|
+
let result = ''
|
|
195
|
+
let i = 0
|
|
166
196
|
|
|
167
|
-
|
|
197
|
+
// Handle leading zeros
|
|
198
|
+
while (i < decimalPart.length && decimalPart[i] === '0') {
|
|
199
|
+
if (result) result += ' '
|
|
200
|
+
result += ZERO
|
|
201
|
+
i++
|
|
168
202
|
}
|
|
169
203
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
204
|
+
// Convert remainder as a single number (masculine, with masculine teens)
|
|
205
|
+
const remainder = decimalPart.slice(i)
|
|
206
|
+
if (remainder) {
|
|
207
|
+
if (result) result += ' '
|
|
208
|
+
const n = BigInt(remainder)
|
|
209
|
+
if (n < 1000n) {
|
|
210
|
+
result += spellUnder1000(Number(n), false, true)
|
|
211
|
+
} else {
|
|
212
|
+
result += integerToWords(n, { gender: 'masculine' })
|
|
176
213
|
}
|
|
177
|
-
const t = Math.floor(n / 10)
|
|
178
|
-
const u = n % 10
|
|
179
|
-
return u
|
|
180
|
-
? `${this.twentiesWords[t]} și ${(feminineUnits ? this.onesFeminineWords : this.onesWords)[u]}`
|
|
181
|
-
: this.twentiesWords[t]
|
|
182
214
|
}
|
|
183
215
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const h = Math.floor(n / 100)
|
|
187
|
-
const r = n % 100
|
|
188
|
-
const hundredWords = this.hundredsWords[h]
|
|
189
|
-
if (!r) return hundredWords
|
|
190
|
-
// Standard readable form: "o sută unu" (for units) or "o sută cincizeci" (for tens)
|
|
191
|
-
const separator = ' '
|
|
192
|
-
return `${hundredWords}${separator}${this.spellUnder100(r, feminineUnits, masculineTeens)}`
|
|
193
|
-
}
|
|
216
|
+
return result
|
|
217
|
+
}
|
|
194
218
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (form) {
|
|
219
|
-
words.push(this.romanianPluralize(x, form))
|
|
220
|
-
} else {
|
|
221
|
-
words.push(this.spellUnder1000(Number(x), false))
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
return words.join(' ').replaceAll(/\s+/g, ' ').trim()
|
|
219
|
+
/**
|
|
220
|
+
* Converts a numeric value to Romanian words.
|
|
221
|
+
*
|
|
222
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
223
|
+
* @param {Object} [options] - Conversion options
|
|
224
|
+
* @param {string} [options.gender='masculine'] - Gender for numbers
|
|
225
|
+
* @returns {string} The number in Romanian words
|
|
226
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
227
|
+
* @throws {Error} If value is not a valid number format
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* toWords(21) // 'douăzeci și unu'
|
|
231
|
+
* toWords(1, { gender: 'feminine' }) // 'una'
|
|
232
|
+
* toWords(1000) // 'o mie'
|
|
233
|
+
*/
|
|
234
|
+
function toWords (value, options) {
|
|
235
|
+
options = validateOptions(options)
|
|
236
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
237
|
+
|
|
238
|
+
let result = ''
|
|
239
|
+
|
|
240
|
+
if (isNegative) {
|
|
241
|
+
result = NEGATIVE + ' '
|
|
226
242
|
}
|
|
227
243
|
|
|
228
|
-
integerToWords
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const words = []
|
|
233
|
-
const segments = this.splitToSegments(integerPart.toString(), 3)
|
|
234
|
-
let index = segments.length
|
|
235
|
-
for (const x of segments) {
|
|
236
|
-
let onesMap = []
|
|
237
|
-
index = index - 1
|
|
238
|
-
if (x === 0n) continue
|
|
239
|
-
const [n1, n2, n3] = this.extractDigits(x) // units, tens, hundreds (as BigInt)
|
|
240
|
-
// hundreds (only for the last group, not for thousands)
|
|
241
|
-
if (n3 > 0n && index === 0) {
|
|
242
|
-
words.push(this.hundredsWords[Number(n3)])
|
|
243
|
-
}
|
|
244
|
-
// tens & teens (only for the last group, not for thousands)
|
|
245
|
-
if (index === 0) {
|
|
246
|
-
if (n2 > 1n) {
|
|
247
|
-
words.push(this.twentiesWords[Number(n2)])
|
|
248
|
-
}
|
|
249
|
-
if (n2 === 1n) {
|
|
250
|
-
words.push(this.teensWords[Number(n1)])
|
|
251
|
-
} else if (n1 > 0n) {
|
|
252
|
-
// pick masculine/feminine units (only for the last group, not for thousands)
|
|
253
|
-
const feminineUnits = this.options.gender === 'feminine' && index === 0
|
|
254
|
-
onesMap = feminineUnits ? this.onesFeminineWords : this.onesWords
|
|
255
|
-
// if there is a twenty/treizeci/etc AND ones > 0 → add "și"
|
|
256
|
-
if (n2 > 1n) words.push('și')
|
|
257
|
-
words.push(onesMap[Number(n1)])
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
// big unit name (mie/mii, milion/milioane, …)
|
|
261
|
-
if (index > 0) {
|
|
262
|
-
const form = this.scaleMetadata[index]
|
|
263
|
-
if (form) {
|
|
264
|
-
words.push(this.romanianPluralize(x, form))
|
|
265
|
-
} else {
|
|
266
|
-
// For very large numbers beyond our defined units, just spell out the number
|
|
267
|
-
words.push(this.spellUnder1000(Number(x), true))
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
return words.join(' ').replaceAll(/\s+/g, ' ').trim()
|
|
244
|
+
result += integerToWords(integerPart, options)
|
|
245
|
+
|
|
246
|
+
if (decimalPart) {
|
|
247
|
+
result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
|
|
272
248
|
}
|
|
249
|
+
|
|
250
|
+
return result
|
|
273
251
|
}
|
|
252
|
+
|
|
253
|
+
// ============================================================================
|
|
254
|
+
// Public API
|
|
255
|
+
// ============================================================================
|
|
256
|
+
|
|
257
|
+
export { toWords }
|
package/lib/languages/ru.d.ts
CHANGED
|
@@ -1,85 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Russian
|
|
2
|
+
* Converts a numeric value to Russian 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 {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
7
|
+
* @returns {string} The number in Russian words
|
|
8
8
|
*/
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
2: string;
|
|
13
|
-
3: string;
|
|
14
|
-
4: string;
|
|
15
|
-
5: string;
|
|
16
|
-
6: string;
|
|
17
|
-
7: string;
|
|
18
|
-
8: string;
|
|
19
|
-
9: string;
|
|
20
|
-
};
|
|
21
|
-
onesFeminineWords: {
|
|
22
|
-
1: string;
|
|
23
|
-
2: string;
|
|
24
|
-
3: string;
|
|
25
|
-
4: string;
|
|
26
|
-
5: string;
|
|
27
|
-
6: string;
|
|
28
|
-
7: string;
|
|
29
|
-
8: string;
|
|
30
|
-
9: string;
|
|
31
|
-
};
|
|
32
|
-
teensWords: {
|
|
33
|
-
0: string;
|
|
34
|
-
1: string;
|
|
35
|
-
2: string;
|
|
36
|
-
3: string;
|
|
37
|
-
4: string;
|
|
38
|
-
5: string;
|
|
39
|
-
6: string;
|
|
40
|
-
7: string;
|
|
41
|
-
8: string;
|
|
42
|
-
9: string;
|
|
43
|
-
};
|
|
44
|
-
twentiesWords: {
|
|
45
|
-
2: string;
|
|
46
|
-
3: string;
|
|
47
|
-
4: string;
|
|
48
|
-
5: string;
|
|
49
|
-
6: string;
|
|
50
|
-
7: string;
|
|
51
|
-
8: string;
|
|
52
|
-
9: string;
|
|
53
|
-
};
|
|
54
|
-
hundredsWords: {
|
|
55
|
-
1: string;
|
|
56
|
-
2: string;
|
|
57
|
-
3: string;
|
|
58
|
-
4: string;
|
|
59
|
-
5: string;
|
|
60
|
-
6: string;
|
|
61
|
-
7: string;
|
|
62
|
-
8: string;
|
|
63
|
-
9: string;
|
|
64
|
-
};
|
|
65
|
-
pluralForms: {
|
|
66
|
-
1: string[];
|
|
67
|
-
2: string[];
|
|
68
|
-
3: string[];
|
|
69
|
-
4: string[];
|
|
70
|
-
5: string[];
|
|
71
|
-
6: string[];
|
|
72
|
-
7: string[];
|
|
73
|
-
8: string[];
|
|
74
|
-
9: string[];
|
|
75
|
-
10: string[];
|
|
76
|
-
};
|
|
77
|
-
/**
|
|
78
|
-
* Russian thousands (тысяча) are feminine, requiring одна/две forms.
|
|
79
|
-
* Other scales (million, billion, etc.) are masculine.
|
|
80
|
-
*/
|
|
81
|
-
scaleGenders: {
|
|
82
|
-
1: boolean;
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
import { SlavicLanguage } from '../classes/slavic-language.js';
|
|
9
|
+
export function toWords(value: number | string | bigint, options?: {
|
|
10
|
+
gender?: "masculine" | "feminine" | undefined;
|
|
11
|
+
}): string;
|