n2words 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/languages/am-Latn.js +2 -2
- package/dist/languages/am-Latn.js.map +1 -1
- package/dist/languages/am.js +2 -2
- package/dist/languages/am.js.map +1 -1
- package/dist/languages/ar.js +1 -1
- package/dist/languages/az.js +2 -2
- package/dist/languages/az.js.map +1 -1
- package/dist/languages/bn.js +2 -2
- package/dist/languages/bn.js.map +1 -1
- package/dist/languages/cs.js +2 -2
- package/dist/languages/cs.js.map +1 -1
- package/dist/languages/da.js +2 -2
- package/dist/languages/da.js.map +1 -1
- package/dist/languages/de.js +2 -2
- package/dist/languages/de.js.map +1 -1
- package/dist/languages/el.js +2 -2
- package/dist/languages/el.js.map +1 -1
- package/dist/languages/en.js +2 -2
- package/dist/languages/en.js.map +1 -1
- package/dist/languages/es.js +2 -2
- package/dist/languages/es.js.map +1 -1
- package/dist/languages/fa.js +1 -1
- package/dist/languages/fi.js +2 -2
- package/dist/languages/fi.js.map +1 -1
- package/dist/languages/fil.js +2 -2
- package/dist/languages/fil.js.map +1 -1
- package/dist/languages/fr-BE.js +2 -2
- package/dist/languages/fr-BE.js.map +1 -1
- package/dist/languages/fr.js +2 -2
- package/dist/languages/fr.js.map +1 -1
- package/dist/languages/gu.js +2 -2
- package/dist/languages/gu.js.map +1 -1
- package/dist/languages/ha.js +2 -2
- package/dist/languages/ha.js.map +1 -1
- package/dist/languages/hbo.js +2 -2
- package/dist/languages/hbo.js.map +1 -1
- package/dist/languages/he.js +2 -2
- package/dist/languages/he.js.map +1 -1
- package/dist/languages/hi.js +2 -2
- package/dist/languages/hi.js.map +1 -1
- package/dist/languages/hr.js +2 -2
- package/dist/languages/hr.js.map +1 -1
- package/dist/languages/hu.js +1 -1
- package/dist/languages/id.js +2 -2
- package/dist/languages/id.js.map +1 -1
- package/dist/languages/it.js +2 -2
- package/dist/languages/it.js.map +1 -1
- package/dist/languages/ja.js +2 -2
- package/dist/languages/ja.js.map +1 -1
- package/dist/languages/ka.js +3 -0
- package/dist/languages/ka.js.map +1 -0
- package/dist/languages/kn.js +2 -2
- package/dist/languages/kn.js.map +1 -1
- package/dist/languages/ko.js +2 -2
- package/dist/languages/ko.js.map +1 -1
- package/dist/languages/lt.js +2 -2
- package/dist/languages/lt.js.map +1 -1
- package/dist/languages/lv.js +2 -2
- package/dist/languages/lv.js.map +1 -1
- package/dist/languages/mr.js +2 -2
- package/dist/languages/mr.js.map +1 -1
- package/dist/languages/ms.js +2 -2
- package/dist/languages/ms.js.map +1 -1
- package/dist/languages/nb.js +2 -2
- package/dist/languages/nb.js.map +1 -1
- package/dist/languages/nl.js +2 -2
- package/dist/languages/nl.js.map +1 -1
- package/dist/languages/pa.js +2 -2
- package/dist/languages/pa.js.map +1 -1
- package/dist/languages/pl.js +2 -2
- package/dist/languages/pl.js.map +1 -1
- package/dist/languages/pt.js +2 -2
- package/dist/languages/pt.js.map +1 -1
- package/dist/languages/ro.js +1 -1
- package/dist/languages/ru.js +2 -2
- package/dist/languages/ru.js.map +1 -1
- package/dist/languages/sr-Cyrl.js +2 -2
- package/dist/languages/sr-Cyrl.js.map +1 -1
- package/dist/languages/sr-Latn.js +2 -2
- package/dist/languages/sr-Latn.js.map +1 -1
- package/dist/languages/sv.js +2 -2
- package/dist/languages/sv.js.map +1 -1
- package/dist/languages/sw.js +1 -1
- package/dist/languages/ta.js +2 -2
- package/dist/languages/ta.js.map +1 -1
- package/dist/languages/te.js +2 -2
- package/dist/languages/te.js.map +1 -1
- package/dist/languages/th.js +1 -1
- package/dist/languages/tr.js +2 -2
- package/dist/languages/tr.js.map +1 -1
- package/dist/languages/uk.js +2 -2
- package/dist/languages/uk.js.map +1 -1
- package/dist/languages/ur.js +2 -2
- package/dist/languages/ur.js.map +1 -1
- package/dist/languages/vi.js +2 -2
- package/dist/languages/vi.js.map +1 -1
- package/dist/languages/yo.js +3 -0
- package/dist/languages/yo.js.map +1 -0
- package/dist/languages/zh-Hans.js +1 -1
- package/dist/languages/zh-Hant.js +1 -1
- package/dist/n2words.js +2 -2
- package/dist/n2words.js.map +1 -1
- package/lib/languages/am-Latn.js +4 -9
- package/lib/languages/am.js +4 -9
- package/lib/languages/az.js +4 -9
- package/lib/languages/bn.js +51 -30
- package/lib/languages/cs.js +9 -26
- package/lib/languages/da.js +6 -13
- package/lib/languages/de.js +21 -33
- package/lib/languages/el.js +6 -13
- package/lib/languages/en.js +44 -58
- package/lib/languages/es.js +10 -30
- package/lib/languages/fi.js +6 -13
- package/lib/languages/fil.js +6 -17
- package/lib/languages/fr-BE.js +17 -26
- package/lib/languages/fr.js +18 -31
- package/lib/languages/gu.js +43 -30
- package/lib/languages/ha.js +4 -9
- package/lib/languages/hbo.js +7 -25
- package/lib/languages/he.js +7 -18
- package/lib/languages/hi.js +55 -30
- package/lib/languages/hr.js +61 -55
- package/lib/languages/id.js +8 -13
- package/lib/languages/it.js +64 -99
- package/lib/languages/ja.js +8 -20
- package/lib/languages/ka.d.ts +17 -0
- package/lib/languages/ka.js +291 -0
- package/lib/languages/kn.js +43 -30
- package/lib/languages/ko.js +6 -13
- package/lib/languages/lt.js +6 -15
- package/lib/languages/lv.js +6 -15
- package/lib/languages/mr.js +43 -30
- package/lib/languages/ms.js +8 -13
- package/lib/languages/nb.js +13 -23
- package/lib/languages/nl.js +11 -29
- package/lib/languages/pa.js +32 -37
- package/lib/languages/pl.js +7 -20
- package/lib/languages/pt.js +17 -31
- package/lib/languages/ru.js +64 -59
- package/lib/languages/sr-Cyrl.js +58 -52
- package/lib/languages/sr-Latn.js +58 -52
- package/lib/languages/sv.js +14 -24
- package/lib/languages/ta.js +55 -42
- package/lib/languages/te.js +33 -41
- package/lib/languages/tr.js +7 -18
- package/lib/languages/uk.js +61 -55
- package/lib/languages/ur.js +32 -37
- package/lib/languages/vi.js +23 -43
- package/lib/languages/yo.d.ts +7 -0
- package/lib/languages/yo.js +303 -0
- package/lib/n2words.d.ts +3 -1
- package/lib/n2words.js +4 -0
- package/package.json +1 -1
package/lib/languages/ru.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Russian language converter - Functional Implementation
|
|
3
3
|
*
|
|
4
|
-
* Self-contained
|
|
4
|
+
* Self-contained module with its own input validation, ready for subpath exports.
|
|
5
5
|
*
|
|
6
6
|
* Key features:
|
|
7
7
|
* - Three-form pluralization (one/few/many)
|
|
@@ -14,7 +14,39 @@ import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
|
14
14
|
import { validateOptions } from '../utils/validate-options.js'
|
|
15
15
|
|
|
16
16
|
// ============================================================================
|
|
17
|
-
//
|
|
17
|
+
// Vocabulary
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
const ONES_MASC = ['', 'один', 'два', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять']
|
|
21
|
+
const ONES_FEM = ['', 'одна', 'две', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять']
|
|
22
|
+
|
|
23
|
+
const TEENS = ['десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать', 'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать']
|
|
24
|
+
const TENS = ['', '', 'двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят', 'семьдесят', 'восемьдесят', 'девяносто']
|
|
25
|
+
|
|
26
|
+
// Irregular hundreds
|
|
27
|
+
const HUNDREDS = ['', 'сто', 'двести', 'триста', 'четыреста', 'пятьсот', 'шестьсот', 'семьсот', 'восемьсот', 'девятьсот']
|
|
28
|
+
|
|
29
|
+
const ZERO = 'ноль'
|
|
30
|
+
const NEGATIVE = 'минус'
|
|
31
|
+
const DECIMAL_SEP = 'запятая'
|
|
32
|
+
|
|
33
|
+
// Scale words: [singular, few, many]
|
|
34
|
+
// Thousands (index 0) are feminine, rest are masculine
|
|
35
|
+
const SCALE_FORMS = [
|
|
36
|
+
['тысяча', 'тысячи', 'тысяч'],
|
|
37
|
+
['миллион', 'миллиона', 'миллионов'],
|
|
38
|
+
['миллиард', 'миллиарда', 'миллиардов'],
|
|
39
|
+
['триллион', 'триллиона', 'триллионов'],
|
|
40
|
+
['квадриллион', 'квадриллиона', 'квадриллионов'],
|
|
41
|
+
['квинтиллион', 'квинтиллиона', 'квинтиллионов'],
|
|
42
|
+
['секстиллион', 'секстиллиона', 'секстиллионов'],
|
|
43
|
+
['септиллион', 'септиллиона', 'септиллионов'],
|
|
44
|
+
['октиллион', 'октиллиона', 'октиллионов'],
|
|
45
|
+
['нониллион', 'нониллиона', 'нониллионов']
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// Segment Building
|
|
18
50
|
// ============================================================================
|
|
19
51
|
|
|
20
52
|
function pluralize (n, forms) {
|
|
@@ -31,19 +63,7 @@ function pluralize (n, forms) {
|
|
|
31
63
|
return forms[2]
|
|
32
64
|
}
|
|
33
65
|
|
|
34
|
-
function
|
|
35
|
-
const masc = new Array(1000)
|
|
36
|
-
const fem = new Array(1000)
|
|
37
|
-
|
|
38
|
-
for (let i = 0; i < 1000; i++) {
|
|
39
|
-
masc[i] = buildSegment(i, onesMasc, teens, tens, hundreds)
|
|
40
|
-
fem[i] = buildSegment(i, onesFem, teens, tens, hundreds)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return { masc, fem }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function buildSegment (n, ones, teens, tens, hundreds) {
|
|
66
|
+
function buildSegmentMasc (n) {
|
|
47
67
|
if (n === 0) return ''
|
|
48
68
|
|
|
49
69
|
const onesDigit = n % 10
|
|
@@ -53,59 +73,47 @@ function buildSegment (n, ones, teens, tens, hundreds) {
|
|
|
53
73
|
const parts = []
|
|
54
74
|
|
|
55
75
|
if (hundredsDigit > 0) {
|
|
56
|
-
parts.push(
|
|
76
|
+
parts.push(HUNDREDS[hundredsDigit])
|
|
57
77
|
}
|
|
58
78
|
|
|
59
79
|
if (tensDigit > 1) {
|
|
60
|
-
parts.push(
|
|
80
|
+
parts.push(TENS[tensDigit])
|
|
61
81
|
}
|
|
62
82
|
|
|
63
83
|
if (tensDigit === 1) {
|
|
64
|
-
parts.push(
|
|
84
|
+
parts.push(TEENS[onesDigit])
|
|
65
85
|
} else if (onesDigit > 0) {
|
|
66
|
-
parts.push(
|
|
86
|
+
parts.push(ONES_MASC[onesDigit])
|
|
67
87
|
}
|
|
68
88
|
|
|
69
89
|
return parts.join(' ')
|
|
70
90
|
}
|
|
71
91
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// ============================================================================
|
|
75
|
-
|
|
76
|
-
const ONES_MASC = ['', 'один', 'два', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять']
|
|
77
|
-
const ONES_FEM = ['', 'одна', 'две', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять']
|
|
92
|
+
function buildSegmentFem (n) {
|
|
93
|
+
if (n === 0) return ''
|
|
78
94
|
|
|
79
|
-
const
|
|
80
|
-
const
|
|
95
|
+
const onesDigit = n % 10
|
|
96
|
+
const tensDigit = Math.floor(n / 10) % 10
|
|
97
|
+
const hundredsDigit = Math.floor(n / 100)
|
|
81
98
|
|
|
82
|
-
|
|
83
|
-
const HUNDREDS = ['', 'сто', 'двести', 'триста', 'четыреста', 'пятьсот', 'шестьсот', 'семьсот', 'восемьсот', 'девятьсот']
|
|
99
|
+
const parts = []
|
|
84
100
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
if (hundredsDigit > 0) {
|
|
102
|
+
parts.push(HUNDREDS[hundredsDigit])
|
|
103
|
+
}
|
|
88
104
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
['тысяча', 'тысячи', 'тысяч'],
|
|
93
|
-
['миллион', 'миллиона', 'миллионов'],
|
|
94
|
-
['миллиард', 'миллиарда', 'миллиардов'],
|
|
95
|
-
['триллион', 'триллиона', 'триллионов'],
|
|
96
|
-
['квадриллион', 'квадриллиона', 'квадриллионов'],
|
|
97
|
-
['квинтиллион', 'квинтиллиона', 'квинтиллионов'],
|
|
98
|
-
['секстиллион', 'секстиллиона', 'секстиллионов'],
|
|
99
|
-
['септиллион', 'септиллиона', 'септиллионов'],
|
|
100
|
-
['октиллион', 'октиллиона', 'октиллионов'],
|
|
101
|
-
['нониллион', 'нониллиона', 'нониллионов']
|
|
102
|
-
]
|
|
105
|
+
if (tensDigit > 1) {
|
|
106
|
+
parts.push(TENS[tensDigit])
|
|
107
|
+
}
|
|
103
108
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
if (tensDigit === 1) {
|
|
110
|
+
parts.push(TEENS[onesDigit])
|
|
111
|
+
} else if (onesDigit > 0) {
|
|
112
|
+
parts.push(ONES_FEM[onesDigit])
|
|
113
|
+
}
|
|
107
114
|
|
|
108
|
-
|
|
115
|
+
return parts.join(' ')
|
|
116
|
+
}
|
|
109
117
|
|
|
110
118
|
// ============================================================================
|
|
111
119
|
// Conversion Functions
|
|
@@ -117,8 +125,7 @@ function integerToWords (n, options = {}) {
|
|
|
117
125
|
const feminine = options.gender === 'feminine'
|
|
118
126
|
|
|
119
127
|
if (n < 1000n) {
|
|
120
|
-
|
|
121
|
-
return segments[Number(n)]
|
|
128
|
+
return feminine ? buildSegmentFem(Number(n)) : buildSegmentMasc(Number(n))
|
|
122
129
|
}
|
|
123
130
|
|
|
124
131
|
if (n < 1_000_000n) {
|
|
@@ -126,14 +133,13 @@ function integerToWords (n, options = {}) {
|
|
|
126
133
|
const remainder = Number(n % 1000n)
|
|
127
134
|
|
|
128
135
|
// Thousands are always feminine in Russian
|
|
129
|
-
const thousandsWord =
|
|
136
|
+
const thousandsWord = buildSegmentFem(thousands)
|
|
130
137
|
const scaleWord = pluralize(thousands, SCALE_FORMS[0])
|
|
131
138
|
|
|
132
139
|
let result = thousandsWord + ' ' + scaleWord
|
|
133
140
|
|
|
134
141
|
if (remainder > 0) {
|
|
135
|
-
|
|
136
|
-
result += ' ' + segments[remainder]
|
|
142
|
+
result += ' ' + (feminine ? buildSegmentFem(remainder) : buildSegmentMasc(remainder))
|
|
137
143
|
}
|
|
138
144
|
|
|
139
145
|
return result
|
|
@@ -169,15 +175,14 @@ function buildLargeNumberWords (n, options) {
|
|
|
169
175
|
|
|
170
176
|
if (segment !== 0) {
|
|
171
177
|
if (scaleIndex === 0) {
|
|
172
|
-
|
|
173
|
-
parts.push(segmentWords[segment])
|
|
178
|
+
parts.push(feminine ? buildSegmentFem(segment) : buildSegmentMasc(segment))
|
|
174
179
|
} else {
|
|
175
180
|
const scaleForms = SCALE_FORMS[scaleIndex - 1]
|
|
176
181
|
const scaleWord = pluralize(segment, scaleForms)
|
|
177
182
|
// Thousands (scaleIndex=1) are feminine, others masculine
|
|
178
183
|
const isFeminine = scaleIndex === 1
|
|
179
|
-
const
|
|
180
|
-
parts.push(
|
|
184
|
+
const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
|
|
185
|
+
parts.push(segmentWord + ' ' + scaleWord)
|
|
181
186
|
}
|
|
182
187
|
}
|
|
183
188
|
|
package/lib/languages/sr-Cyrl.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Serbian Cyrillic language converter - Functional Implementation
|
|
3
3
|
*
|
|
4
|
-
* Self-contained
|
|
4
|
+
* Self-contained module with its own input validation, ready for subpath exports.
|
|
5
5
|
*
|
|
6
6
|
* Key features:
|
|
7
7
|
* - Three-form pluralization (one/few/many)
|
|
@@ -15,7 +15,34 @@ import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
|
15
15
|
import { validateOptions } from '../utils/validate-options.js'
|
|
16
16
|
|
|
17
17
|
// ============================================================================
|
|
18
|
-
//
|
|
18
|
+
// Vocabulary
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
const ONES_MASC = ['', 'један', 'два', 'три', 'четири', 'пет', 'шест', 'седам', 'осам', 'девет']
|
|
22
|
+
const ONES_FEM = ['', 'једна', 'две', 'три', 'четири', 'пет', 'шест', 'седам', 'осам', 'девет']
|
|
23
|
+
const TEENS = ['десет', 'једанаест', 'дванаест', 'тринаест', 'четрнаест', 'петнаест', 'шеснаест', 'седамнаест', 'осамнаест', 'деветнаест']
|
|
24
|
+
const TENS = ['', '', 'двадесет', 'тридесет', 'четрдесет', 'педесет', 'шездесет', 'седамдесет', 'осамдесет', 'деведесет']
|
|
25
|
+
const HUNDREDS = ['', 'сто', 'двеста', 'триста', 'четиристо', 'петсто', 'шесто', 'седамсто', 'осамсто', 'девестo']
|
|
26
|
+
|
|
27
|
+
const ZERO = 'нула'
|
|
28
|
+
const NEGATIVE = 'минус'
|
|
29
|
+
const DECIMAL_SEP = 'запета'
|
|
30
|
+
|
|
31
|
+
// Scale words: [singular, few, many]
|
|
32
|
+
const SCALE_FORMS = [
|
|
33
|
+
['хиљада', 'хиљаде', 'хиљада'],
|
|
34
|
+
['милион', 'милиона', 'милиона'],
|
|
35
|
+
['милијарда', 'милијарде', 'милијарда'],
|
|
36
|
+
['билион', 'билиона', 'билиона'],
|
|
37
|
+
['билијарда', 'билијарде', 'билијарда'],
|
|
38
|
+
['трилион', 'трилиона', 'трилиона'],
|
|
39
|
+
['трилијарда', 'трилијарде', 'трилијарда'],
|
|
40
|
+
['квадрилион', 'квадрилиона', 'квадрилиона'],
|
|
41
|
+
['квадрилијарда', 'квадрилијарде', 'квадрилијарда']
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Segment Building
|
|
19
46
|
// ============================================================================
|
|
20
47
|
|
|
21
48
|
function pluralize (n, forms) {
|
|
@@ -32,19 +59,7 @@ function pluralize (n, forms) {
|
|
|
32
59
|
return forms[2]
|
|
33
60
|
}
|
|
34
61
|
|
|
35
|
-
function
|
|
36
|
-
const masc = new Array(1000)
|
|
37
|
-
const fem = new Array(1000)
|
|
38
|
-
|
|
39
|
-
for (let i = 0; i < 1000; i++) {
|
|
40
|
-
masc[i] = buildSegment(i, onesMasc, teens, tens, hundreds)
|
|
41
|
-
fem[i] = buildSegment(i, onesFem, teens, tens, hundreds)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return { masc, fem }
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function buildSegment (n, ones, teens, tens, hundreds) {
|
|
62
|
+
function buildSegmentMasc (n) {
|
|
48
63
|
if (n === 0) return ''
|
|
49
64
|
|
|
50
65
|
const onesDigit = n % 10
|
|
@@ -54,54 +69,47 @@ function buildSegment (n, ones, teens, tens, hundreds) {
|
|
|
54
69
|
const parts = []
|
|
55
70
|
|
|
56
71
|
if (hundredsDigit > 0) {
|
|
57
|
-
parts.push(
|
|
72
|
+
parts.push(HUNDREDS[hundredsDigit])
|
|
58
73
|
}
|
|
59
74
|
|
|
60
75
|
if (tensDigit > 1) {
|
|
61
|
-
parts.push(
|
|
76
|
+
parts.push(TENS[tensDigit])
|
|
62
77
|
}
|
|
63
78
|
|
|
64
79
|
if (tensDigit === 1) {
|
|
65
|
-
parts.push(
|
|
80
|
+
parts.push(TEENS[onesDigit])
|
|
66
81
|
} else if (onesDigit > 0) {
|
|
67
|
-
parts.push(
|
|
82
|
+
parts.push(ONES_MASC[onesDigit])
|
|
68
83
|
}
|
|
69
84
|
|
|
70
85
|
return parts.join(' ')
|
|
71
86
|
}
|
|
72
87
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// ============================================================================
|
|
88
|
+
function buildSegmentFem (n) {
|
|
89
|
+
if (n === 0) return ''
|
|
76
90
|
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
const TENS = ['', '', 'двадесет', 'тридесет', 'четрдесет', 'педесет', 'шездесет', 'седамдесет', 'осамдесет', 'деведесет']
|
|
81
|
-
const HUNDREDS = ['', 'сто', 'двеста', 'триста', 'четиристо', 'петсто', 'шесто', 'седамсто', 'осамсто', 'девестo']
|
|
91
|
+
const onesDigit = n % 10
|
|
92
|
+
const tensDigit = Math.floor(n / 10) % 10
|
|
93
|
+
const hundredsDigit = Math.floor(n / 100)
|
|
82
94
|
|
|
83
|
-
const
|
|
84
|
-
const NEGATIVE = 'минус'
|
|
85
|
-
const DECIMAL_SEP = 'запета'
|
|
95
|
+
const parts = []
|
|
86
96
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
['милион', 'милиона', 'милиона'],
|
|
91
|
-
['милијарда', 'милијарде', 'милијарда'],
|
|
92
|
-
['билион', 'билиона', 'билиона'],
|
|
93
|
-
['билијарда', 'билијарде', 'билијарда'],
|
|
94
|
-
['трилион', 'трилиона', 'трилиона'],
|
|
95
|
-
['трилијарда', 'трилијарде', 'трилијарда'],
|
|
96
|
-
['квадрилион', 'квадрилиона', 'квадрилиона'],
|
|
97
|
-
['квадрилијарда', 'квадрилијарде', 'квадрилијарда']
|
|
98
|
-
]
|
|
97
|
+
if (hundredsDigit > 0) {
|
|
98
|
+
parts.push(HUNDREDS[hundredsDigit])
|
|
99
|
+
}
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
if (tensDigit > 1) {
|
|
102
|
+
parts.push(TENS[tensDigit])
|
|
103
|
+
}
|
|
103
104
|
|
|
104
|
-
|
|
105
|
+
if (tensDigit === 1) {
|
|
106
|
+
parts.push(TEENS[onesDigit])
|
|
107
|
+
} else if (onesDigit > 0) {
|
|
108
|
+
parts.push(ONES_FEM[onesDigit])
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return parts.join(' ')
|
|
112
|
+
}
|
|
105
113
|
|
|
106
114
|
// ============================================================================
|
|
107
115
|
// Conversion Functions
|
|
@@ -111,8 +119,7 @@ function integerToWords (n, options = {}) {
|
|
|
111
119
|
if (n === 0n) return ZERO
|
|
112
120
|
|
|
113
121
|
if (n < 1000n) {
|
|
114
|
-
|
|
115
|
-
return segments[Number(n)]
|
|
122
|
+
return options.gender === 'feminine' ? buildSegmentFem(Number(n)) : buildSegmentMasc(Number(n))
|
|
116
123
|
}
|
|
117
124
|
|
|
118
125
|
return buildLargeNumberWords(n, options)
|
|
@@ -144,15 +151,14 @@ function buildLargeNumberWords (n, options) {
|
|
|
144
151
|
|
|
145
152
|
if (segment !== 0) {
|
|
146
153
|
if (scaleIndex === 0) {
|
|
147
|
-
|
|
148
|
-
parts.push(segmentWords[segment])
|
|
154
|
+
parts.push(options.gender === 'feminine' ? buildSegmentFem(segment) : buildSegmentMasc(segment))
|
|
149
155
|
} else {
|
|
150
156
|
const scaleForms = SCALE_FORMS[scaleIndex - 1]
|
|
151
157
|
const scaleWord = pluralize(segment, scaleForms)
|
|
152
158
|
// Thousands (scaleIndex=1) are feminine, others masculine
|
|
153
159
|
const isFeminine = scaleIndex === 1
|
|
154
|
-
const
|
|
155
|
-
parts.push(
|
|
160
|
+
const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
|
|
161
|
+
parts.push(segmentWord + ' ' + scaleWord)
|
|
156
162
|
}
|
|
157
163
|
}
|
|
158
164
|
|
package/lib/languages/sr-Latn.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Serbian Latin language converter - Functional Implementation
|
|
3
3
|
*
|
|
4
|
-
* Self-contained
|
|
4
|
+
* Self-contained module with its own input validation, ready for subpath exports.
|
|
5
5
|
*
|
|
6
6
|
* Key features:
|
|
7
7
|
* - Three-form pluralization (one/few/many)
|
|
@@ -15,7 +15,34 @@ import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
|
15
15
|
import { validateOptions } from '../utils/validate-options.js'
|
|
16
16
|
|
|
17
17
|
// ============================================================================
|
|
18
|
-
//
|
|
18
|
+
// Vocabulary
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
const ONES_MASC = ['', 'jedan', 'dva', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']
|
|
22
|
+
const ONES_FEM = ['', 'jedna', 'dve', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']
|
|
23
|
+
const TEENS = ['deset', 'jedanaest', 'dvanaest', 'trinaest', 'četrnaest', 'petnaest', 'šesnaest', 'sedamnaest', 'osamnaest', 'devetnaest']
|
|
24
|
+
const TENS = ['', '', 'dvadeset', 'trideset', 'četrdeset', 'pedeset', 'šezdeset', 'sedamdeset', 'osamdeset', 'devedeset']
|
|
25
|
+
const HUNDREDS = ['', 'sto', 'dvesta', 'trista', 'četiristo', 'petsto', 'šesto', 'sedamsto', 'osamsto', 'devetsto']
|
|
26
|
+
|
|
27
|
+
const ZERO = 'nula'
|
|
28
|
+
const NEGATIVE = 'minus'
|
|
29
|
+
const DECIMAL_SEP = 'zapeta'
|
|
30
|
+
|
|
31
|
+
// Scale words: [singular, few, many]
|
|
32
|
+
const SCALE_FORMS = [
|
|
33
|
+
['hiljada', 'hiljade', 'hiljada'],
|
|
34
|
+
['milion', 'miliona', 'miliona'],
|
|
35
|
+
['milijarda', 'milijarde', 'milijarda'],
|
|
36
|
+
['bilion', 'biliona', 'biliona'],
|
|
37
|
+
['bilijarda', 'bilijarde', 'bilijarda'],
|
|
38
|
+
['trilion', 'triliona', 'triliona'],
|
|
39
|
+
['trilijarda', 'trilijarde', 'trilijarda'],
|
|
40
|
+
['kvadrilion', 'kvadriliona', 'kvadriliona'],
|
|
41
|
+
['kvadrilijarda', 'kvadrilijarde', 'kvadrilijarda']
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Segment Building
|
|
19
46
|
// ============================================================================
|
|
20
47
|
|
|
21
48
|
function pluralize (n, forms) {
|
|
@@ -32,19 +59,7 @@ function pluralize (n, forms) {
|
|
|
32
59
|
return forms[2]
|
|
33
60
|
}
|
|
34
61
|
|
|
35
|
-
function
|
|
36
|
-
const masc = new Array(1000)
|
|
37
|
-
const fem = new Array(1000)
|
|
38
|
-
|
|
39
|
-
for (let i = 0; i < 1000; i++) {
|
|
40
|
-
masc[i] = buildSegment(i, onesMasc, teens, tens, hundreds)
|
|
41
|
-
fem[i] = buildSegment(i, onesFem, teens, tens, hundreds)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return { masc, fem }
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function buildSegment (n, ones, teens, tens, hundreds) {
|
|
62
|
+
function buildSegmentMasc (n) {
|
|
48
63
|
if (n === 0) return ''
|
|
49
64
|
|
|
50
65
|
const onesDigit = n % 10
|
|
@@ -54,54 +69,47 @@ function buildSegment (n, ones, teens, tens, hundreds) {
|
|
|
54
69
|
const parts = []
|
|
55
70
|
|
|
56
71
|
if (hundredsDigit > 0) {
|
|
57
|
-
parts.push(
|
|
72
|
+
parts.push(HUNDREDS[hundredsDigit])
|
|
58
73
|
}
|
|
59
74
|
|
|
60
75
|
if (tensDigit > 1) {
|
|
61
|
-
parts.push(
|
|
76
|
+
parts.push(TENS[tensDigit])
|
|
62
77
|
}
|
|
63
78
|
|
|
64
79
|
if (tensDigit === 1) {
|
|
65
|
-
parts.push(
|
|
80
|
+
parts.push(TEENS[onesDigit])
|
|
66
81
|
} else if (onesDigit > 0) {
|
|
67
|
-
parts.push(
|
|
82
|
+
parts.push(ONES_MASC[onesDigit])
|
|
68
83
|
}
|
|
69
84
|
|
|
70
85
|
return parts.join(' ')
|
|
71
86
|
}
|
|
72
87
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// ============================================================================
|
|
88
|
+
function buildSegmentFem (n) {
|
|
89
|
+
if (n === 0) return ''
|
|
76
90
|
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
const TENS = ['', '', 'dvadeset', 'trideset', 'četrdeset', 'pedeset', 'šezdeset', 'sedamdeset', 'osamdeset', 'devedeset']
|
|
81
|
-
const HUNDREDS = ['', 'sto', 'dvesta', 'trista', 'četiristo', 'petsto', 'šesto', 'sedamsto', 'osamsto', 'devetsto']
|
|
91
|
+
const onesDigit = n % 10
|
|
92
|
+
const tensDigit = Math.floor(n / 10) % 10
|
|
93
|
+
const hundredsDigit = Math.floor(n / 100)
|
|
82
94
|
|
|
83
|
-
const
|
|
84
|
-
const NEGATIVE = 'minus'
|
|
85
|
-
const DECIMAL_SEP = 'zapeta'
|
|
95
|
+
const parts = []
|
|
86
96
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
['milion', 'miliona', 'miliona'],
|
|
91
|
-
['milijarda', 'milijarde', 'milijarda'],
|
|
92
|
-
['bilion', 'biliona', 'biliona'],
|
|
93
|
-
['bilijarda', 'bilijarde', 'bilijarda'],
|
|
94
|
-
['trilion', 'triliona', 'triliona'],
|
|
95
|
-
['trilijarda', 'trilijarde', 'trilijarda'],
|
|
96
|
-
['kvadrilion', 'kvadriliona', 'kvadriliona'],
|
|
97
|
-
['kvadrilijarda', 'kvadrilijarde', 'kvadrilijarda']
|
|
98
|
-
]
|
|
97
|
+
if (hundredsDigit > 0) {
|
|
98
|
+
parts.push(HUNDREDS[hundredsDigit])
|
|
99
|
+
}
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
if (tensDigit > 1) {
|
|
102
|
+
parts.push(TENS[tensDigit])
|
|
103
|
+
}
|
|
103
104
|
|
|
104
|
-
|
|
105
|
+
if (tensDigit === 1) {
|
|
106
|
+
parts.push(TEENS[onesDigit])
|
|
107
|
+
} else if (onesDigit > 0) {
|
|
108
|
+
parts.push(ONES_FEM[onesDigit])
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return parts.join(' ')
|
|
112
|
+
}
|
|
105
113
|
|
|
106
114
|
// ============================================================================
|
|
107
115
|
// Conversion Functions
|
|
@@ -111,8 +119,7 @@ function integerToWords (n, options = {}) {
|
|
|
111
119
|
if (n === 0n) return ZERO
|
|
112
120
|
|
|
113
121
|
if (n < 1000n) {
|
|
114
|
-
|
|
115
|
-
return segments[Number(n)]
|
|
122
|
+
return options.gender === 'feminine' ? buildSegmentFem(Number(n)) : buildSegmentMasc(Number(n))
|
|
116
123
|
}
|
|
117
124
|
|
|
118
125
|
return buildLargeNumberWords(n, options)
|
|
@@ -144,15 +151,14 @@ function buildLargeNumberWords (n, options) {
|
|
|
144
151
|
|
|
145
152
|
if (segment !== 0) {
|
|
146
153
|
if (scaleIndex === 0) {
|
|
147
|
-
|
|
148
|
-
parts.push(segmentWords[segment])
|
|
154
|
+
parts.push(options.gender === 'feminine' ? buildSegmentFem(segment) : buildSegmentMasc(segment))
|
|
149
155
|
} else {
|
|
150
156
|
const scaleForms = SCALE_FORMS[scaleIndex - 1]
|
|
151
157
|
const scaleWord = pluralize(segment, scaleForms)
|
|
152
158
|
// Thousands (scaleIndex=1) are feminine, others masculine
|
|
153
159
|
const isFeminine = scaleIndex === 1
|
|
154
|
-
const
|
|
155
|
-
parts.push(
|
|
160
|
+
const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
|
|
161
|
+
parts.push(segmentWord + ' ' + scaleWord)
|
|
156
162
|
}
|
|
157
163
|
}
|
|
158
164
|
|
package/lib/languages/sv.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Swedish language converter - Functional Implementation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Self-contained module with its own input validation, ready for subpath exports.
|
|
5
5
|
*
|
|
6
6
|
* Key features:
|
|
7
7
|
* - Hyphenation for tens-ones (tjugo-ett)
|
|
@@ -32,7 +32,7 @@ const DECIMAL_SEP = 'komma'
|
|
|
32
32
|
const SCALES = ['tusen', 'miljon', 'miljard', 'biljon', 'biljard', 'triljon', 'triljard', 'kvadriljon']
|
|
33
33
|
|
|
34
34
|
// ============================================================================
|
|
35
|
-
//
|
|
35
|
+
// Segment Building
|
|
36
36
|
// ============================================================================
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -83,18 +83,6 @@ function buildSegment (n) {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
// Precompute all 1000 segment words (0-999)
|
|
87
|
-
const SEGMENTS = new Array(1000)
|
|
88
|
-
const SEGMENTS_HAS_HUNDRED = new Array(1000)
|
|
89
|
-
const SEGMENTS_LESS_THAN_100 = new Array(1000)
|
|
90
|
-
|
|
91
|
-
for (let i = 0; i < 1000; i++) {
|
|
92
|
-
const result = buildSegment(i)
|
|
93
|
-
SEGMENTS[i] = result.word
|
|
94
|
-
SEGMENTS_HAS_HUNDRED[i] = result.hasHundred
|
|
95
|
-
SEGMENTS_LESS_THAN_100[i] = result.lessThan100
|
|
96
|
-
}
|
|
97
|
-
|
|
98
86
|
// ============================================================================
|
|
99
87
|
// Conversion Functions
|
|
100
88
|
// ============================================================================
|
|
@@ -108,9 +96,9 @@ for (let i = 0; i < 1000; i++) {
|
|
|
108
96
|
function integerToWords (n) {
|
|
109
97
|
if (n === 0n) return ZERO
|
|
110
98
|
|
|
111
|
-
// Fast path: numbers < 1000
|
|
99
|
+
// Fast path: numbers < 1000
|
|
112
100
|
if (n < 1000n) {
|
|
113
|
-
return
|
|
101
|
+
return buildSegment(Number(n)).word
|
|
114
102
|
}
|
|
115
103
|
|
|
116
104
|
// Fast path: numbers < 1,000,000 (thousands)
|
|
@@ -119,15 +107,15 @@ function integerToWords (n) {
|
|
|
119
107
|
const remainder = Number(n % 1000n)
|
|
120
108
|
|
|
121
109
|
// Omit "ett" before tusen
|
|
122
|
-
let result = thousands === 1 ? SCALES[0] :
|
|
110
|
+
let result = thousands === 1 ? SCALES[0] : buildSegment(thousands).word + ' ' + SCALES[0]
|
|
123
111
|
|
|
124
112
|
if (remainder > 0) {
|
|
125
|
-
const
|
|
113
|
+
const remainderResult = buildSegment(remainder)
|
|
126
114
|
// Insert "och" if remainder < 100 (doesn't have hundred)
|
|
127
|
-
if (
|
|
128
|
-
result += ' och ' +
|
|
115
|
+
if (remainderResult.lessThan100) {
|
|
116
|
+
result += ' och ' + remainderResult.word
|
|
129
117
|
} else {
|
|
130
|
-
result += ' ' +
|
|
118
|
+
result += ' ' + remainderResult.word
|
|
131
119
|
}
|
|
132
120
|
}
|
|
133
121
|
|
|
@@ -171,11 +159,13 @@ function buildLargeNumberWords (n) {
|
|
|
171
159
|
const segment = segments[i]
|
|
172
160
|
|
|
173
161
|
if (segment !== 0) {
|
|
162
|
+
const segmentResult = buildSegment(segment)
|
|
163
|
+
|
|
174
164
|
if (scaleIndex === 0) {
|
|
175
165
|
// Units segment
|
|
176
166
|
parts.push({
|
|
177
|
-
word:
|
|
178
|
-
hasHundred:
|
|
167
|
+
word: segmentResult.word,
|
|
168
|
+
hasHundred: segmentResult.hasHundred,
|
|
179
169
|
isScale: false
|
|
180
170
|
})
|
|
181
171
|
} else {
|
|
@@ -191,7 +181,7 @@ function buildLargeNumberWords (n) {
|
|
|
191
181
|
segmentWord = 'en' // "en miljon"
|
|
192
182
|
}
|
|
193
183
|
} else {
|
|
194
|
-
segmentWord =
|
|
184
|
+
segmentWord = segmentResult.word
|
|
195
185
|
}
|
|
196
186
|
|
|
197
187
|
if (segmentWord) {
|