n2words 1.24.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 +183 -156
- 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 -2
- package/dist/languages/ar.js.map +1 -1
- package/dist/languages/az.js +3 -2
- package/dist/languages/az.js.map +1 -1
- package/dist/languages/bn.js +3 -2
- package/dist/languages/bn.js.map +1 -1
- package/dist/languages/cs.js +3 -2
- package/dist/languages/cs.js.map +1 -1
- package/dist/languages/da.js +3 -2
- package/dist/languages/da.js.map +1 -1
- package/dist/languages/de.js +3 -2
- package/dist/languages/de.js.map +1 -1
- package/dist/languages/el.js +3 -2
- package/dist/languages/el.js.map +1 -1
- package/dist/languages/en.js +3 -2
- package/dist/languages/en.js.map +1 -1
- package/dist/languages/es.js +3 -2
- package/dist/languages/es.js.map +1 -1
- package/dist/languages/fa.js +3 -2
- package/dist/languages/fa.js.map +1 -1
- package/dist/languages/fi.js +3 -0
- package/dist/languages/fi.js.map +1 -0
- package/dist/languages/fil.js +3 -2
- package/dist/languages/fil.js.map +1 -1
- package/dist/languages/fr-BE.js +3 -2
- package/dist/languages/fr-BE.js.map +1 -1
- package/dist/languages/fr.js +3 -2
- package/dist/languages/fr.js.map +1 -1
- package/dist/languages/gu.js +3 -2
- package/dist/languages/gu.js.map +1 -1
- 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 -2
- package/dist/languages/he.js.map +1 -1
- package/dist/languages/hi.js +3 -2
- package/dist/languages/hi.js.map +1 -1
- package/dist/languages/hr.js +3 -2
- package/dist/languages/hr.js.map +1 -1
- package/dist/languages/hu.js +3 -2
- package/dist/languages/hu.js.map +1 -1
- package/dist/languages/id.js +3 -2
- package/dist/languages/id.js.map +1 -1
- package/dist/languages/it.js +3 -2
- package/dist/languages/it.js.map +1 -1
- package/dist/languages/ja.js +3 -2
- package/dist/languages/ja.js.map +1 -1
- package/dist/languages/kn.js +3 -2
- package/dist/languages/kn.js.map +1 -1
- package/dist/languages/ko.js +3 -2
- package/dist/languages/ko.js.map +1 -1
- package/dist/languages/lt.js +3 -2
- package/dist/languages/lt.js.map +1 -1
- package/dist/languages/lv.js +3 -2
- package/dist/languages/lv.js.map +1 -1
- package/dist/languages/mr.js +3 -2
- package/dist/languages/mr.js.map +1 -1
- package/dist/languages/ms.js +3 -2
- package/dist/languages/ms.js.map +1 -1
- package/dist/languages/nb.js +3 -2
- package/dist/languages/nb.js.map +1 -1
- package/dist/languages/nl.js +3 -2
- package/dist/languages/nl.js.map +1 -1
- package/dist/languages/pa.js +3 -0
- package/dist/languages/pa.js.map +1 -0
- package/dist/languages/pl.js +3 -2
- package/dist/languages/pl.js.map +1 -1
- package/dist/languages/pt.js +3 -2
- package/dist/languages/pt.js.map +1 -1
- package/dist/languages/ro.js +3 -2
- package/dist/languages/ro.js.map +1 -1
- package/dist/languages/ru.js +3 -2
- package/dist/languages/ru.js.map +1 -1
- package/dist/languages/sr-Cyrl.js +3 -0
- package/dist/languages/sr-Cyrl.js.map +1 -0
- package/dist/languages/sr-Latn.js +3 -2
- package/dist/languages/sr-Latn.js.map +1 -1
- package/dist/languages/sv.js +3 -2
- package/dist/languages/sv.js.map +1 -1
- package/dist/languages/sw.js +3 -2
- package/dist/languages/sw.js.map +1 -1
- package/dist/languages/ta.js +3 -2
- package/dist/languages/ta.js.map +1 -1
- package/dist/languages/te.js +3 -2
- package/dist/languages/te.js.map +1 -1
- package/dist/languages/th.js +3 -2
- package/dist/languages/th.js.map +1 -1
- package/dist/languages/tr.js +3 -2
- package/dist/languages/tr.js.map +1 -1
- package/dist/languages/uk.js +3 -2
- package/dist/languages/uk.js.map +1 -1
- package/dist/languages/ur.js +3 -2
- package/dist/languages/ur.js.map +1 -1
- package/dist/languages/vi.js +3 -2
- package/dist/languages/vi.js.map +1 -1
- package/dist/languages/zh-Hans.js +3 -2
- package/dist/languages/zh-Hans.js.map +1 -1
- package/dist/languages/zh-Hant.js +3 -0
- package/dist/languages/zh-Hant.js.map +1 -0
- package/dist/n2words.js +3 -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 +17 -0
- package/lib/languages/ar.js +171 -209
- package/lib/languages/az.d.ts +7 -0
- package/lib/languages/az.js +167 -49
- package/lib/languages/bn.d.ts +7 -0
- package/lib/languages/bn.js +142 -123
- package/lib/languages/cs.d.ts +18 -0
- package/lib/languages/cs.js +303 -176
- package/lib/languages/da.d.ts +14 -0
- package/lib/languages/da.js +267 -139
- package/lib/languages/de.d.ts +17 -0
- package/lib/languages/de.js +310 -113
- package/lib/languages/el.d.ts +14 -0
- package/lib/languages/el.js +225 -98
- package/lib/languages/en.d.ts +17 -0
- package/lib/languages/en.js +235 -102
- package/lib/languages/es.d.ts +21 -0
- package/lib/languages/es.js +307 -125
- package/lib/languages/fa.d.ts +7 -0
- package/lib/languages/fa.js +115 -108
- package/lib/languages/fi.d.ts +14 -0
- package/lib/languages/fi.js +245 -0
- package/lib/languages/fil.d.ts +7 -0
- package/lib/languages/fil.js +199 -139
- package/lib/languages/fr-BE.d.ts +11 -0
- package/lib/languages/fr-BE.js +287 -48
- package/lib/languages/fr.d.ts +21 -0
- package/lib/languages/fr.js +343 -119
- package/lib/languages/gu.d.ts +7 -0
- package/lib/languages/gu.js +125 -144
- package/lib/languages/ha.d.ts +7 -0
- package/lib/languages/ha.js +230 -0
- package/lib/languages/hbo.d.ts +13 -0
- package/lib/languages/hbo.js +300 -0
- package/lib/languages/he.d.ts +13 -0
- package/lib/languages/he.js +230 -283
- package/lib/languages/hi.d.ts +7 -0
- package/lib/languages/hi.js +142 -123
- package/lib/languages/hr.d.ts +11 -0
- package/lib/languages/hr.js +190 -129
- package/lib/languages/hu.d.ts +7 -0
- package/lib/languages/hu.js +194 -133
- package/lib/languages/id.d.ts +7 -0
- package/lib/languages/id.js +167 -140
- package/lib/languages/it.d.ts +19 -0
- package/lib/languages/it.js +337 -108
- package/lib/languages/ja.d.ts +17 -0
- package/lib/languages/ja.js +224 -155
- package/lib/languages/kn.d.ts +7 -0
- package/lib/languages/kn.js +128 -62
- package/lib/languages/ko.d.ts +14 -0
- package/lib/languages/ko.js +250 -70
- package/lib/languages/lt.d.ts +18 -0
- package/lib/languages/lt.js +287 -148
- package/lib/languages/lv.d.ts +18 -0
- package/lib/languages/lv.js +291 -123
- package/lib/languages/mr.d.ts +7 -0
- package/lib/languages/mr.js +125 -144
- package/lib/languages/ms.d.ts +7 -0
- package/lib/languages/ms.js +171 -112
- package/lib/languages/nb.d.ts +14 -0
- package/lib/languages/nb.js +275 -100
- package/lib/languages/nl.d.ts +26 -0
- package/lib/languages/nl.js +307 -174
- package/lib/languages/pa.d.ts +7 -0
- package/lib/languages/pa.js +163 -0
- package/lib/languages/pl.d.ts +22 -0
- package/lib/languages/pl.js +299 -158
- package/lib/languages/pt.d.ts +17 -0
- package/lib/languages/pt.js +279 -120
- package/lib/languages/ro.d.ts +18 -0
- package/lib/languages/ro.js +214 -337
- package/lib/languages/ru.d.ts +11 -0
- package/lib/languages/ru.js +219 -95
- package/lib/languages/sr-Cyrl.d.ts +11 -0
- package/lib/languages/sr-Cyrl.js +215 -0
- package/lib/languages/sr-Latn.d.ts +11 -0
- package/lib/languages/sr-Latn.js +190 -132
- package/lib/languages/sv.d.ts +14 -0
- package/lib/languages/sv.js +280 -103
- package/lib/languages/sw.d.ts +7 -0
- package/lib/languages/sw.js +135 -103
- package/lib/languages/ta.d.ts +7 -0
- package/lib/languages/ta.js +133 -205
- package/lib/languages/te.d.ts +7 -0
- package/lib/languages/te.js +148 -213
- package/lib/languages/th.d.ts +7 -0
- package/lib/languages/th.js +139 -101
- package/lib/languages/tr.d.ts +18 -0
- package/lib/languages/tr.js +246 -66
- package/lib/languages/uk.d.ts +11 -0
- package/lib/languages/uk.js +197 -101
- package/lib/languages/ur.d.ts +7 -0
- package/lib/languages/ur.js +160 -123
- package/lib/languages/vi.d.ts +17 -0
- package/lib/languages/vi.js +287 -164
- package/lib/languages/zh-Hans.d.ts +11 -0
- package/lib/languages/zh-Hans.js +159 -142
- package/lib/languages/zh-Hant.d.ts +11 -0
- package/lib/languages/zh-Hant.js +202 -0
- package/lib/n2words.d.ts +53 -0
- package/lib/n2words.js +91 -227
- 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 +118 -67
- package/dist/languages/pa-Guru.js +0 -2
- package/dist/languages/pa-Guru.js.map +0 -1
- package/lib/classes/abstract-language.js +0 -261
- package/lib/classes/greedy-scale-language.js +0 -195
- package/lib/classes/slavic-language.js +0 -251
- package/lib/classes/south-asian-language.js +0 -161
- package/lib/classes/turkic-language.js +0 -63
- package/lib/languages/pa-Guru.js +0 -126
- package/typings/classes/abstract-language.d.ts +0 -144
- package/typings/classes/greedy-scale-language.d.ts +0 -148
- package/typings/classes/slavic-language.d.ts +0 -145
- package/typings/classes/south-asian-language.d.ts +0 -101
- package/typings/classes/turkic-language.d.ts +0 -42
- package/typings/languages/ar.d.ts +0 -93
- package/typings/languages/az.d.ts +0 -25
- package/typings/languages/bn.d.ts +0 -1
- package/typings/languages/cs.d.ts +0 -120
- package/typings/languages/da.d.ts +0 -53
- package/typings/languages/de.d.ts +0 -26
- package/typings/languages/el.d.ts +0 -11
- package/typings/languages/en.d.ts +0 -30
- package/typings/languages/es.d.ts +0 -43
- package/typings/languages/fa.d.ts +0 -81
- package/typings/languages/fil.d.ts +0 -12
- package/typings/languages/fr-BE.d.ts +0 -41
- package/typings/languages/fr.d.ts +0 -43
- package/typings/languages/gu.d.ts +0 -12
- package/typings/languages/he.d.ts +0 -197
- package/typings/languages/hi.d.ts +0 -1
- package/typings/languages/hr.d.ts +0 -110
- package/typings/languages/hu.d.ts +0 -37
- package/typings/languages/id.d.ts +0 -69
- package/typings/languages/it.d.ts +0 -51
- package/typings/languages/ja.d.ts +0 -58
- package/typings/languages/kn.d.ts +0 -11
- package/typings/languages/ko.d.ts +0 -25
- package/typings/languages/lt.d.ts +0 -110
- package/typings/languages/lv.d.ts +0 -99
- package/typings/languages/mr.d.ts +0 -12
- package/typings/languages/ms.d.ts +0 -37
- package/typings/languages/nb.d.ts +0 -27
- package/typings/languages/nl.d.ts +0 -65
- package/typings/languages/pa-Guru.d.ts +0 -1
- package/typings/languages/pl.d.ts +0 -116
- package/typings/languages/pt.d.ts +0 -39
- package/typings/languages/ro.d.ts +0 -229
- package/typings/languages/ru.d.ts +0 -108
- package/typings/languages/sr-Latn.d.ts +0 -98
- package/typings/languages/sv.d.ts +0 -30
- package/typings/languages/sw.d.ts +0 -1
- package/typings/languages/ta.d.ts +0 -1
- package/typings/languages/te.d.ts +0 -1
- package/typings/languages/th.d.ts +0 -1
- package/typings/languages/tr.d.ts +0 -46
- package/typings/languages/uk.d.ts +0 -117
- package/typings/languages/ur.d.ts +0 -1
- package/typings/languages/vi.d.ts +0 -116
- package/typings/languages/zh-Hans.d.ts +0 -57
- package/typings/n2words.d.ts +0 -177
package/lib/languages/sr-Latn.js
CHANGED
|
@@ -1,157 +1,215 @@
|
|
|
1
|
-
import SlavicLanguage from '../classes/slavic-language.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {Object} SlavicOptions
|
|
5
|
-
* @property {boolean} [feminine=false] Use feminine forms for numbers.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
/**
|
|
9
|
-
* Serbian language converter
|
|
2
|
+
* Serbian Latin language converter - Functional Implementation
|
|
10
3
|
*
|
|
11
|
-
*
|
|
12
|
-
* - Serbian number words (jedan/jedna, dva/dve, tri, četiri...)
|
|
13
|
-
* - Gender-aware forms (masculine/feminine)
|
|
14
|
-
* - Slavic three-form pluralization (hiljada/hiljade/hiljada)
|
|
15
|
-
* - Latin script representation
|
|
4
|
+
* Self-contained converter using shared Slavic utilities.
|
|
16
5
|
*
|
|
17
|
-
* Key
|
|
18
|
-
* - Three-form pluralization
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* -
|
|
23
|
-
* - Large number handling via SCALE[] array with indexed [singular, few, many] forms
|
|
24
|
-
* - Gender-specific number forms for 1 and 2 (masculine/feminine dual forms)
|
|
25
|
-
*
|
|
26
|
-
* Features:
|
|
27
|
-
* - Dual gender forms for 1 and 2 (jedan/jedna, dva/dve)
|
|
28
|
-
* - Similar structure to Croatian with different orthography
|
|
29
|
-
* - Latin script (Serbian can use both Latin and Cyrillic)
|
|
30
|
-
*
|
|
31
|
-
* Inherits from SlavicLanguage for complex pluralization algorithms.
|
|
6
|
+
* Key features:
|
|
7
|
+
* - Three-form pluralization (one/few/many)
|
|
8
|
+
* - Gender: thousands are feminine, millions+ are masculine
|
|
9
|
+
* - Irregular hundreds (dvesta, trista, etc.)
|
|
10
|
+
* - Long scale naming with -ard forms
|
|
11
|
+
* - Latin script
|
|
32
12
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
13
|
+
|
|
14
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
15
|
+
import { validateOptions } from '../utils/validate-options.js'
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Slavic Utilities (inlined for performance)
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
function pluralize (n, forms) {
|
|
22
|
+
const num = typeof n === 'bigint' ? Number(n) : n
|
|
23
|
+
const lastDigit = num % 10
|
|
24
|
+
const lastTwoDigits = num % 100
|
|
25
|
+
|
|
26
|
+
if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
|
|
27
|
+
return forms[2]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (lastDigit === 1) return forms[0]
|
|
31
|
+
if (lastDigit >= 2 && lastDigit <= 4) return forms[1]
|
|
32
|
+
return forms[2]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function buildAllSegments (onesMasc, onesFem, teens, tens, hundreds) {
|
|
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)
|
|
47
42
|
}
|
|
48
43
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
44
|
+
return { masc, fem }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function buildSegment (n, ones, teens, tens, hundreds) {
|
|
48
|
+
if (n === 0) return ''
|
|
49
|
+
|
|
50
|
+
const onesDigit = n % 10
|
|
51
|
+
const tensDigit = Math.floor(n / 10) % 10
|
|
52
|
+
const hundredsDigit = Math.floor(n / 100)
|
|
53
|
+
|
|
54
|
+
const parts = []
|
|
55
|
+
|
|
56
|
+
if (hundredsDigit > 0) {
|
|
57
|
+
parts.push(hundreds[hundredsDigit])
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
3: 'trideset',
|
|
65
|
-
4: 'četrdeset',
|
|
66
|
-
5: 'pedeset',
|
|
67
|
-
6: 'šezdeset',
|
|
68
|
-
7: 'sedamdeset',
|
|
69
|
-
8: 'osamdeset',
|
|
70
|
-
9: 'devedeset'
|
|
60
|
+
if (tensDigit > 1) {
|
|
61
|
+
parts.push(tens[tensDigit])
|
|
71
62
|
}
|
|
72
63
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
4: 'četiristo',
|
|
78
|
-
5: 'petsto',
|
|
79
|
-
6: 'šesto',
|
|
80
|
-
7: 'sedamsto',
|
|
81
|
-
8: 'osamsto',
|
|
82
|
-
9: 'devetsto'
|
|
64
|
+
if (tensDigit === 1) {
|
|
65
|
+
parts.push(teens[onesDigit])
|
|
66
|
+
} else if (onesDigit > 0) {
|
|
67
|
+
parts.push(ones[onesDigit])
|
|
83
68
|
}
|
|
84
69
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
['hiljada', 'hiljade', 'hiljada', true], // 10 ^ 3
|
|
88
|
-
['milion', 'miliona', 'miliona', false], // 10 ^ 6
|
|
89
|
-
['milijarda', 'milijarde', 'milijarda', false], // 10 ^ 9
|
|
90
|
-
['bilion', 'biliona', 'biliona', false], // 10 ^ 12
|
|
91
|
-
['bilijarda', 'bilijarde', 'bilijarda', false], // 10 ^ 15
|
|
92
|
-
['trilion', 'triliona', 'triliona', false], // 10 ^ 18
|
|
93
|
-
['trilijarda', 'trilijarde', 'trilijarda', false], // 10 ^ 21
|
|
94
|
-
['kvadrilion', 'kvadriliona', 'kvadriliona', false], // 10 ^ 24
|
|
95
|
-
['kvadrilijarda', 'kvadrilijarde', 'kvadrilijarda', false], // 10 ^ 27
|
|
96
|
-
['kvintilion', 'kvintiliona', 'kvintiliona', false] // 10 ^ 30
|
|
97
|
-
]
|
|
98
|
-
|
|
99
|
-
pluralize (n, forms) {
|
|
100
|
-
const lastDigit = n % 10n
|
|
101
|
-
const lastTwoDigits = n % 100n
|
|
102
|
-
|
|
103
|
-
if ((lastTwoDigits < 10n || lastTwoDigits > 20n) && lastDigit === 1n) {
|
|
104
|
-
return forms[0]
|
|
105
|
-
}
|
|
70
|
+
return parts.join(' ')
|
|
71
|
+
}
|
|
106
72
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// Vocabulary
|
|
75
|
+
// ============================================================================
|
|
110
76
|
|
|
111
|
-
|
|
77
|
+
const ONES_MASC = ['', 'jedan', 'dva', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']
|
|
78
|
+
const ONES_FEM = ['', 'jedna', 'dve', 'tri', 'četiri', 'pet', 'šest', 'sedam', 'osam', 'devet']
|
|
79
|
+
const TEENS = ['deset', 'jedanaest', 'dvanaest', 'trinaest', 'četrnaest', 'petnaest', 'šesnaest', 'sedamnaest', 'osamnaest', 'devetnaest']
|
|
80
|
+
const TENS = ['', '', 'dvadeset', 'trideset', 'četrdeset', 'pedeset', 'šezdeset', 'sedamdeset', 'osamdeset', 'devedeset']
|
|
81
|
+
const HUNDREDS = ['', 'sto', 'dvesta', 'trista', 'četiristo', 'petsto', 'šesto', 'sedamsto', 'osamsto', 'devetsto']
|
|
82
|
+
|
|
83
|
+
const ZERO = 'nula'
|
|
84
|
+
const NEGATIVE = 'minus'
|
|
85
|
+
const DECIMAL_SEP = 'zapeta'
|
|
86
|
+
|
|
87
|
+
// Scale words: [singular, few, many]
|
|
88
|
+
const SCALE_FORMS = [
|
|
89
|
+
['hiljada', 'hiljade', 'hiljada'],
|
|
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
|
+
]
|
|
99
|
+
|
|
100
|
+
// ============================================================================
|
|
101
|
+
// Precomputed Lookup Tables
|
|
102
|
+
// ============================================================================
|
|
103
|
+
|
|
104
|
+
const { masc: SEGMENTS_MASC, fem: SEGMENTS_FEM } = buildAllSegments(ONES_MASC, ONES_FEM, TEENS, TENS, HUNDREDS)
|
|
105
|
+
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// Conversion Functions
|
|
108
|
+
// ============================================================================
|
|
109
|
+
|
|
110
|
+
function integerToWords (n, options = {}) {
|
|
111
|
+
if (n === 0n) return ZERO
|
|
112
|
+
|
|
113
|
+
if (n < 1000n) {
|
|
114
|
+
const segments = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC
|
|
115
|
+
return segments[Number(n)]
|
|
112
116
|
}
|
|
113
117
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
118
|
+
return buildLargeNumberWords(n, options)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function buildLargeNumberWords (n, options) {
|
|
122
|
+
const numStr = n.toString()
|
|
123
|
+
const len = numStr.length
|
|
124
|
+
|
|
125
|
+
const segments = []
|
|
126
|
+
const segmentSize = 3
|
|
127
|
+
|
|
128
|
+
const remainderLen = len % segmentSize
|
|
129
|
+
let pos = 0
|
|
130
|
+
if (remainderLen > 0) {
|
|
131
|
+
segments.push(Number(numStr.slice(0, remainderLen)))
|
|
132
|
+
pos = remainderLen
|
|
133
|
+
}
|
|
134
|
+
while (pos < len) {
|
|
135
|
+
segments.push(Number(numStr.slice(pos, pos + segmentSize)))
|
|
136
|
+
pos += segmentSize
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const parts = []
|
|
140
|
+
let scaleIndex = segments.length - 1
|
|
141
|
+
|
|
142
|
+
for (let i = 0; i < segments.length; i++) {
|
|
143
|
+
const segment = segments[i]
|
|
144
|
+
|
|
145
|
+
if (segment !== 0) {
|
|
146
|
+
if (scaleIndex === 0) {
|
|
147
|
+
const segmentWords = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC
|
|
148
|
+
parts.push(segmentWords[segment])
|
|
149
|
+
} else {
|
|
150
|
+
const scaleForms = SCALE_FORMS[scaleIndex - 1]
|
|
151
|
+
const scaleWord = pluralize(segment, scaleForms)
|
|
152
|
+
// Thousands (scaleIndex=1) are feminine, others masculine
|
|
153
|
+
const isFeminine = scaleIndex === 1
|
|
154
|
+
const segmentWords = isFeminine ? SEGMENTS_FEM : SEGMENTS_MASC
|
|
155
|
+
parts.push(segmentWords[segment] + ' ' + scaleWord)
|
|
139
156
|
}
|
|
140
157
|
}
|
|
141
|
-
|
|
158
|
+
|
|
159
|
+
scaleIndex--
|
|
142
160
|
}
|
|
161
|
+
|
|
162
|
+
return parts.join(' ')
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function decimalPartToWords (decimalPart, options) {
|
|
166
|
+
let result = ''
|
|
167
|
+
let i = 0
|
|
168
|
+
|
|
169
|
+
while (i < decimalPart.length && decimalPart[i] === '0') {
|
|
170
|
+
if (result) result += ' '
|
|
171
|
+
result += ZERO
|
|
172
|
+
i++
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const remainder = decimalPart.slice(i)
|
|
176
|
+
if (remainder) {
|
|
177
|
+
if (result) result += ' '
|
|
178
|
+
result += integerToWords(BigInt(remainder), options)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return result
|
|
143
182
|
}
|
|
144
183
|
|
|
145
184
|
/**
|
|
146
|
-
* Converts a
|
|
185
|
+
* Converts a numeric value to Serbian (Latin) words.
|
|
147
186
|
*
|
|
148
|
-
* @param {number|string|bigint} value The
|
|
149
|
-
* @param {Object} [options
|
|
150
|
-
* @param {
|
|
151
|
-
* @returns {string} The number
|
|
152
|
-
* @throws {TypeError} If value is NaN or invalid type.
|
|
153
|
-
* @throws {Error} If value is an invalid number string.
|
|
187
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
188
|
+
* @param {Object} [options] - Optional configuration
|
|
189
|
+
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
190
|
+
* @returns {string} The number in Serbian Latin words
|
|
154
191
|
*/
|
|
155
|
-
|
|
156
|
-
|
|
192
|
+
function toWords (value, options) {
|
|
193
|
+
options = validateOptions(options)
|
|
194
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
195
|
+
|
|
196
|
+
let result = ''
|
|
197
|
+
|
|
198
|
+
if (isNegative) {
|
|
199
|
+
result = NEGATIVE + ' '
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
result += integerToWords(integerPart, options)
|
|
203
|
+
|
|
204
|
+
if (decimalPart) {
|
|
205
|
+
result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return result
|
|
157
209
|
}
|
|
210
|
+
|
|
211
|
+
// ============================================================================
|
|
212
|
+
// Exports
|
|
213
|
+
// ============================================================================
|
|
214
|
+
|
|
215
|
+
export { toWords }
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a numeric value to Swedish words.
|
|
3
|
+
*
|
|
4
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
|
+
* @returns {string} The number in Swedish words
|
|
6
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
7
|
+
* @throws {Error} If value is not a valid number format
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* toWords(42) // 'fyrtio-två'
|
|
11
|
+
* toWords(101) // 'hundra och ett'
|
|
12
|
+
* toWords(1000000) // 'en miljon'
|
|
13
|
+
*/
|
|
14
|
+
export function toWords(value: number | string | bigint): string;
|