n2words 5.0.0 → 5.1.1
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 +133 -40
- package/README.md +6 -4
- package/dist/am-ET.js +2 -2
- package/dist/am-ET.umd.js +2 -2
- package/dist/am-Latn-ET.js +2 -2
- package/dist/am-Latn-ET.umd.js +2 -2
- package/dist/ar-SA.js +2 -2
- package/dist/ar-SA.umd.js +2 -2
- package/dist/az-AZ.js +2 -2
- package/dist/az-AZ.umd.js +2 -2
- package/dist/bn-BD.js +2 -2
- package/dist/bn-BD.umd.js +2 -2
- package/dist/cs-CZ.js +2 -2
- package/dist/cs-CZ.umd.js +2 -2
- package/dist/da-DK.js +2 -2
- package/dist/da-DK.umd.js +2 -2
- package/dist/de-DE.js +2 -2
- package/dist/de-DE.umd.js +2 -2
- package/dist/el-GR.js +2 -2
- package/dist/el-GR.umd.js +2 -2
- package/dist/en-AU.js +2 -2
- package/dist/en-AU.umd.js +2 -2
- package/dist/en-BD.js +2 -2
- package/dist/en-BD.umd.js +2 -2
- package/dist/en-CA.js +2 -2
- package/dist/en-CA.umd.js +2 -2
- package/dist/en-GB.js +2 -2
- package/dist/en-GB.umd.js +2 -2
- package/dist/en-GH.js +2 -2
- package/dist/en-GH.umd.js +2 -2
- package/dist/en-IE.js +2 -2
- package/dist/en-IE.umd.js +2 -2
- package/dist/en-IN.js +2 -2
- package/dist/en-IN.umd.js +2 -2
- package/dist/en-KE.js +2 -2
- package/dist/en-KE.umd.js +2 -2
- package/dist/en-MY.js +2 -2
- package/dist/en-MY.umd.js +2 -2
- package/dist/en-NG.js +2 -2
- package/dist/en-NG.umd.js +2 -2
- package/dist/en-NZ.js +2 -2
- package/dist/en-NZ.umd.js +2 -2
- package/dist/en-PH.js +2 -2
- package/dist/en-PH.umd.js +2 -2
- package/dist/en-PK.js +2 -2
- package/dist/en-PK.umd.js +2 -2
- package/dist/en-SG.js +2 -2
- package/dist/en-SG.umd.js +2 -2
- package/dist/en-US.js +2 -2
- package/dist/en-US.umd.js +2 -2
- package/dist/en-ZA.js +2 -2
- package/dist/en-ZA.umd.js +2 -2
- package/dist/es-ES.js +2 -2
- package/dist/es-ES.umd.js +2 -2
- package/dist/es-MX.js +2 -2
- package/dist/es-MX.umd.js +2 -2
- package/dist/es-US.js +2 -2
- package/dist/es-US.umd.js +2 -2
- package/dist/fa-IR.js +2 -2
- package/dist/fa-IR.umd.js +2 -2
- package/dist/fi-FI.js +2 -2
- package/dist/fi-FI.umd.js +2 -2
- package/dist/fil-PH.js +2 -2
- package/dist/fil-PH.umd.js +2 -2
- package/dist/fr-BE.js +2 -2
- package/dist/fr-BE.umd.js +2 -2
- package/dist/fr-FR.js +2 -2
- package/dist/fr-FR.umd.js +2 -2
- package/dist/gu-IN.js +2 -2
- package/dist/gu-IN.umd.js +2 -2
- package/dist/ha-NG.js +2 -2
- package/dist/ha-NG.umd.js +2 -2
- package/dist/hbo-IL.js +2 -2
- package/dist/hbo-IL.umd.js +2 -2
- package/dist/he-IL.js +2 -2
- package/dist/he-IL.umd.js +2 -2
- package/dist/hi-IN.js +2 -2
- package/dist/hi-IN.umd.js +2 -2
- package/dist/hr-HR.js +2 -2
- package/dist/hr-HR.umd.js +2 -2
- package/dist/hu-HU.js +2 -2
- package/dist/hu-HU.umd.js +2 -2
- package/dist/id-ID.js +2 -2
- package/dist/id-ID.umd.js +2 -2
- package/dist/it-IT.js +2 -2
- package/dist/it-IT.umd.js +2 -2
- package/dist/ja-JP.js +2 -2
- package/dist/ja-JP.umd.js +2 -2
- package/dist/ka-GE.js +2 -2
- package/dist/ka-GE.umd.js +2 -2
- package/dist/kn-IN.js +2 -2
- package/dist/kn-IN.umd.js +2 -2
- package/dist/ko-KR.js +2 -2
- package/dist/ko-KR.umd.js +2 -2
- package/dist/lt-LT.js +2 -2
- package/dist/lt-LT.umd.js +2 -2
- package/dist/lv-LV.js +2 -2
- package/dist/lv-LV.umd.js +2 -2
- package/dist/mr-IN.js +2 -2
- package/dist/mr-IN.umd.js +2 -2
- package/dist/ms-MY.js +2 -2
- package/dist/ms-MY.umd.js +2 -2
- package/dist/nb-NO.js +2 -2
- package/dist/nb-NO.umd.js +2 -2
- package/dist/nl-NL.js +2 -2
- package/dist/nl-NL.umd.js +2 -2
- package/dist/pa-IN.js +2 -2
- package/dist/pa-IN.umd.js +2 -2
- package/dist/pl-PL.js +2 -2
- package/dist/pl-PL.umd.js +2 -2
- package/dist/pt-BR.js +2 -2
- package/dist/pt-BR.umd.js +2 -2
- package/dist/pt-PT.js +2 -2
- package/dist/pt-PT.umd.js +2 -2
- package/dist/ro-RO.js +2 -2
- package/dist/ro-RO.umd.js +2 -2
- package/dist/ru-RU.js +2 -2
- package/dist/ru-RU.umd.js +2 -2
- package/dist/sr-Cyrl-RS.js +2 -2
- package/dist/sr-Cyrl-RS.umd.js +2 -2
- package/dist/sr-Latn-RS.js +2 -2
- package/dist/sr-Latn-RS.umd.js +2 -2
- package/dist/sv-SE.js +2 -2
- package/dist/sv-SE.umd.js +2 -2
- package/dist/sw-KE.js +2 -2
- package/dist/sw-KE.umd.js +2 -2
- package/dist/ta-IN.js +2 -2
- package/dist/ta-IN.umd.js +2 -2
- package/dist/te-IN.js +2 -2
- package/dist/te-IN.umd.js +2 -2
- package/dist/th-TH.js +2 -2
- package/dist/th-TH.umd.js +2 -2
- package/dist/tr-TR.js +2 -2
- package/dist/tr-TR.umd.js +2 -2
- package/dist/uk-UA.js +2 -2
- package/dist/uk-UA.umd.js +2 -2
- package/dist/ur-PK.js +2 -2
- package/dist/ur-PK.umd.js +2 -2
- package/dist/vi-VN.js +2 -2
- package/dist/vi-VN.umd.js +2 -2
- package/dist/yo-NG.js +2 -2
- package/dist/yo-NG.umd.js +2 -2
- package/dist/zh-Hans-CN.js +2 -2
- package/dist/zh-Hans-CN.umd.js +2 -2
- package/dist/zh-Hant-TW.js +2 -2
- package/dist/zh-Hant-TW.umd.js +2 -2
- package/package.json +33 -24
- package/src/am-ET.d.ts +3 -5
- package/src/am-ET.js +41 -16
- package/src/am-Latn-ET.d.ts +3 -5
- package/src/am-Latn-ET.js +45 -16
- package/src/ar-SA.d.ts +44 -18
- package/src/ar-SA.js +93 -40
- package/src/az-AZ.d.ts +3 -5
- package/src/az-AZ.js +58 -20
- package/src/bn-BD.d.ts +3 -5
- package/src/bn-BD.js +32 -16
- package/src/cs-CZ.d.ts +3 -6
- package/src/cs-CZ.js +66 -42
- package/src/da-DK.d.ts +3 -6
- package/src/da-DK.js +53 -48
- package/src/de-DE.d.ts +17 -11
- package/src/de-DE.js +88 -57
- package/src/el-GR.d.ts +3 -6
- package/src/el-GR.js +45 -32
- package/src/en-AU.d.ts +17 -11
- package/src/en-AU.js +56 -41
- package/src/en-BD.d.ts +17 -11
- package/src/en-BD.js +60 -41
- package/src/en-CA.d.ts +36 -18
- package/src/en-CA.js +67 -46
- package/src/en-GB.d.ts +17 -11
- package/src/en-GB.js +56 -41
- package/src/en-GH.d.ts +32 -3
- package/src/en-GH.js +104 -26
- package/src/en-IE.d.ts +17 -11
- package/src/en-IE.js +56 -41
- package/src/en-IN.d.ts +17 -11
- package/src/en-IN.js +60 -41
- package/src/en-KE.d.ts +28 -3
- package/src/en-KE.js +93 -26
- package/src/en-MY.d.ts +26 -3
- package/src/en-MY.js +91 -26
- package/src/en-NG.d.ts +17 -11
- package/src/en-NG.js +56 -41
- package/src/en-NZ.d.ts +32 -3
- package/src/en-NZ.js +85 -31
- package/src/en-PH.d.ts +32 -3
- package/src/en-PH.js +97 -26
- package/src/en-PK.d.ts +17 -11
- package/src/en-PK.js +60 -41
- package/src/en-SG.d.ts +28 -3
- package/src/en-SG.js +93 -26
- package/src/en-US.d.ts +36 -18
- package/src/en-US.js +70 -47
- package/src/en-ZA.d.ts +17 -11
- package/src/en-ZA.js +56 -41
- package/src/es-ES.d.ts +53 -21
- package/src/es-ES.js +104 -56
- package/src/es-MX.d.ts +53 -21
- package/src/es-MX.js +104 -56
- package/src/es-US.d.ts +53 -21
- package/src/es-US.js +92 -51
- package/src/fa-IR.d.ts +3 -5
- package/src/fa-IR.js +28 -13
- package/src/fi-FI.d.ts +3 -6
- package/src/fi-FI.js +47 -29
- package/src/fil-PH.d.ts +3 -5
- package/src/fil-PH.js +61 -28
- package/src/fr-BE.d.ts +31 -15
- package/src/fr-BE.js +128 -57
- package/src/fr-FR.d.ts +31 -16
- package/src/fr-FR.js +97 -60
- package/src/gu-IN.d.ts +3 -5
- package/src/gu-IN.js +31 -16
- package/src/ha-NG.d.ts +3 -5
- package/src/ha-NG.js +55 -27
- package/src/hbo-IL.d.ts +26 -12
- package/src/hbo-IL.js +92 -51
- package/src/he-IL.d.ts +17 -10
- package/src/he-IL.js +92 -50
- package/src/hi-IN.d.ts +3 -5
- package/src/hi-IN.js +30 -17
- package/src/hr-HR.d.ts +21 -10
- package/src/hr-HR.js +89 -33
- package/src/hu-HU.d.ts +3 -5
- package/src/hu-HU.js +57 -23
- package/src/id-ID.d.ts +3 -5
- package/src/id-ID.js +56 -23
- package/src/it-IT.d.ts +17 -11
- package/src/it-IT.js +74 -43
- package/src/ja-JP.d.ts +3 -6
- package/src/ja-JP.js +39 -26
- package/src/ka-GE.d.ts +3 -6
- package/src/ka-GE.js +38 -26
- package/src/kn-IN.d.ts +3 -5
- package/src/kn-IN.js +31 -16
- package/src/ko-KR.d.ts +3 -6
- package/src/ko-KR.js +34 -26
- package/src/lt-LT.d.ts +21 -11
- package/src/lt-LT.js +64 -42
- package/src/lv-LV.d.ts +21 -11
- package/src/lv-LV.js +79 -51
- package/src/mr-IN.d.ts +3 -5
- package/src/mr-IN.js +31 -16
- package/src/ms-MY.d.ts +3 -5
- package/src/ms-MY.js +58 -24
- package/src/nb-NO.d.ts +3 -6
- package/src/nb-NO.js +54 -34
- package/src/nl-NL.d.ts +41 -20
- package/src/nl-NL.js +111 -69
- package/src/pa-IN.d.ts +3 -5
- package/src/pa-IN.js +32 -16
- package/src/pl-PL.d.ts +21 -11
- package/src/pl-PL.js +69 -45
- package/src/pt-BR.d.ts +22 -11
- package/src/pt-BR.js +93 -53
- package/src/pt-PT.d.ts +17 -11
- package/src/pt-PT.js +80 -48
- package/src/ro-RO.d.ts +21 -11
- package/src/ro-RO.js +77 -39
- package/src/ru-RU.d.ts +35 -15
- package/src/ru-RU.js +100 -38
- package/src/sr-Cyrl-RS.d.ts +35 -15
- package/src/sr-Cyrl-RS.js +106 -43
- package/src/sr-Latn-RS.d.ts +35 -15
- package/src/sr-Latn-RS.js +106 -43
- package/src/sv-SE.d.ts +3 -6
- package/src/sv-SE.js +53 -34
- package/src/sw-KE.d.ts +3 -5
- package/src/sw-KE.js +50 -20
- package/src/ta-IN.d.ts +3 -5
- package/src/ta-IN.js +29 -17
- package/src/te-IN.d.ts +3 -5
- package/src/te-IN.js +31 -16
- package/src/th-TH.d.ts +3 -5
- package/src/th-TH.js +42 -19
- package/src/tr-TR.d.ts +17 -11
- package/src/tr-TR.js +63 -37
- package/src/uk-UA.d.ts +21 -10
- package/src/uk-UA.js +89 -33
- package/src/ur-PK.d.ts +3 -5
- package/src/ur-PK.js +32 -16
- package/src/utils/check-max.d.ts +26 -0
- package/src/utils/check-max.js +33 -0
- package/src/utils/expand-scientific.d.ts +0 -4
- package/src/utils/expand-scientific.js +7 -9
- package/src/utils/is-plain-object.d.ts +3 -4
- package/src/utils/is-plain-object.js +3 -4
- package/src/utils/parse-cardinal.d.ts +1 -2
- package/src/utils/parse-cardinal.js +12 -9
- package/src/utils/parse-currency.d.ts +1 -2
- package/src/utils/parse-currency.js +9 -11
- package/src/utils/parse-ordinal.d.ts +0 -1
- package/src/utils/parse-ordinal.js +9 -10
- package/src/utils/resolve-options.d.ts +17 -0
- package/src/utils/resolve-options.js +56 -0
- package/src/utils/scale.d.ts +49 -0
- package/src/utils/scale.js +65 -0
- package/src/vi-VN.d.ts +3 -6
- package/src/vi-VN.js +41 -28
- package/src/yo-NG.d.ts +3 -5
- package/src/yo-NG.js +49 -33
- package/src/zh-Hans-CN.d.ts +45 -20
- package/src/zh-Hans-CN.js +84 -31
- package/src/zh-Hant-TW.d.ts +45 -20
- package/src/zh-Hant-TW.js +85 -34
- package/src/utils/validate-options.d.ts +0 -8
- package/src/utils/validate-options.js +0 -16
package/src/sr-Cyrl-RS.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Key features:
|
|
7
7
|
* - Three-form pluralization (one/few/many)
|
|
8
|
-
* - Gender:
|
|
8
|
+
* - Gender by scale word: the -arda forms (hiljada, milijarda, bilijarda, ...)
|
|
9
|
+
* are feminine; the -ion forms (milion, bilion, ...) are masculine
|
|
9
10
|
* - Irregular hundreds
|
|
10
11
|
* - Long scale naming with -ard forms
|
|
11
12
|
* - Cyrillic script
|
|
@@ -14,7 +15,9 @@
|
|
|
14
15
|
import { parseCardinalValue } from './utils/parse-cardinal.js'
|
|
15
16
|
import { parseCurrencyValue } from './utils/parse-currency.js'
|
|
16
17
|
import { parseOrdinalValue } from './utils/parse-ordinal.js'
|
|
17
|
-
import {
|
|
18
|
+
import { checkMax } from './utils/check-max.js'
|
|
19
|
+
import { western } from './utils/scale.js'
|
|
20
|
+
import { resolveOptions } from './utils/resolve-options.js'
|
|
18
21
|
|
|
19
22
|
// ============================================================================
|
|
20
23
|
// Vocabulary
|
|
@@ -56,7 +59,7 @@ const ORDINAL_SCALES = [
|
|
|
56
59
|
'трилионити',
|
|
57
60
|
'трилијардити',
|
|
58
61
|
'квадрилионити',
|
|
59
|
-
'квадрилијардити'
|
|
62
|
+
'квадрилијардити',
|
|
60
63
|
]
|
|
61
64
|
|
|
62
65
|
// ============================================================================
|
|
@@ -79,14 +82,27 @@ const SCALE_FORMS = [
|
|
|
79
82
|
['трилион', 'трилиона', 'трилиона'],
|
|
80
83
|
['трилијарда', 'трилијарде', 'трилијарда'],
|
|
81
84
|
['квадрилион', 'квадрилиона', 'квадрилиона'],
|
|
82
|
-
['квадрилијарда', 'квадрилијарде', 'квадрилијарда']
|
|
85
|
+
['квадрилијарда', 'квадрилијарде', 'квадрилијарда'],
|
|
83
86
|
]
|
|
84
87
|
|
|
88
|
+
// Supported magnitude ceilings (checked at the public entry points). Both the
|
|
89
|
+
// cardinal SCALE_FORMS and the ORDINAL_SCALES tables cover units + N scale
|
|
90
|
+
// groups, so values must stay below 10^((length + 1) * 3) = 10^30.
|
|
91
|
+
export const cardinalMax = western(SCALE_FORMS.length)
|
|
92
|
+
export const ordinalMax = western(ORDINAL_SCALES.length)
|
|
93
|
+
export const currencyMax = western(SCALE_FORMS.length)
|
|
94
|
+
|
|
85
95
|
// ============================================================================
|
|
86
96
|
// Segment Building
|
|
87
97
|
// ============================================================================
|
|
88
98
|
|
|
89
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Selects the correct plural form for a count.
|
|
101
|
+
* @param {number | bigint} n - The count
|
|
102
|
+
* @param {string[]} forms - Plural forms [one, few, many]
|
|
103
|
+
* @returns {string} The selected plural form
|
|
104
|
+
*/
|
|
105
|
+
function pluralize(n, forms) {
|
|
90
106
|
const num = typeof n === 'bigint' ? Number(n) : n
|
|
91
107
|
const lastDigit = num % 10
|
|
92
108
|
const lastTwoDigits = num % 100
|
|
@@ -100,7 +116,12 @@ function pluralize (n, forms) {
|
|
|
100
116
|
return forms[2]
|
|
101
117
|
}
|
|
102
118
|
|
|
103
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Builds the masculine word form for a 0-999 segment.
|
|
121
|
+
* @param {number} n - Number 0-999
|
|
122
|
+
* @returns {string} The segment words
|
|
123
|
+
*/
|
|
124
|
+
function buildSegmentMasc(n) {
|
|
104
125
|
if (n === 0) return ''
|
|
105
126
|
|
|
106
127
|
const onesDigit = n % 10
|
|
@@ -119,14 +140,20 @@ function buildSegmentMasc (n) {
|
|
|
119
140
|
|
|
120
141
|
if (tensDigit === 1) {
|
|
121
142
|
parts.push(TEENS[onesDigit])
|
|
122
|
-
}
|
|
143
|
+
}
|
|
144
|
+
else if (onesDigit > 0) {
|
|
123
145
|
parts.push(ONES_MASC[onesDigit])
|
|
124
146
|
}
|
|
125
147
|
|
|
126
148
|
return parts.join(' ')
|
|
127
149
|
}
|
|
128
150
|
|
|
129
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Builds the feminine word form for a 0-999 segment.
|
|
153
|
+
* @param {number} n - Number 0-999
|
|
154
|
+
* @returns {string} The segment words
|
|
155
|
+
*/
|
|
156
|
+
function buildSegmentFem(n) {
|
|
130
157
|
if (n === 0) return ''
|
|
131
158
|
|
|
132
159
|
const onesDigit = n % 10
|
|
@@ -145,7 +172,8 @@ function buildSegmentFem (n) {
|
|
|
145
172
|
|
|
146
173
|
if (tensDigit === 1) {
|
|
147
174
|
parts.push(TEENS[onesDigit])
|
|
148
|
-
}
|
|
175
|
+
}
|
|
176
|
+
else if (onesDigit > 0) {
|
|
149
177
|
parts.push(ONES_FEM[onesDigit])
|
|
150
178
|
}
|
|
151
179
|
|
|
@@ -156,7 +184,13 @@ function buildSegmentFem (n) {
|
|
|
156
184
|
// Conversion Functions
|
|
157
185
|
// ============================================================================
|
|
158
186
|
|
|
159
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Converts a non-negative integer to Serbian words.
|
|
189
|
+
* @param {bigint} n - The integer to convert
|
|
190
|
+
* @param {('masculine'|'feminine')} gender - Grammatical gender
|
|
191
|
+
* @returns {string} The integer in words
|
|
192
|
+
*/
|
|
193
|
+
function integerToWords(n, gender) {
|
|
160
194
|
if (n === 0n) return ZERO
|
|
161
195
|
|
|
162
196
|
if (n < 1000n) {
|
|
@@ -166,7 +200,13 @@ function integerToWords (n, gender) {
|
|
|
166
200
|
return buildLargeNumberWords(n, gender)
|
|
167
201
|
}
|
|
168
202
|
|
|
169
|
-
|
|
203
|
+
/**
|
|
204
|
+
* Builds words for integers >= 1000 using scale decomposition.
|
|
205
|
+
* @param {bigint} n - The integer to convert (>= 1000)
|
|
206
|
+
* @param {('masculine'|'feminine')} gender - Grammatical gender
|
|
207
|
+
* @returns {string} The integer in words
|
|
208
|
+
*/
|
|
209
|
+
function buildLargeNumberWords(n, gender) {
|
|
170
210
|
const numStr = n.toString()
|
|
171
211
|
const len = numStr.length
|
|
172
212
|
|
|
@@ -193,11 +233,12 @@ function buildLargeNumberWords (n, gender) {
|
|
|
193
233
|
if (segment !== 0) {
|
|
194
234
|
if (scaleIndex === 0) {
|
|
195
235
|
parts.push(gender === 'feminine' ? buildSegmentFem(segment) : buildSegmentMasc(segment))
|
|
196
|
-
}
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
197
238
|
const scaleForms = SCALE_FORMS[scaleIndex - 1]
|
|
198
239
|
const scaleWord = pluralize(segment, scaleForms)
|
|
199
|
-
//
|
|
200
|
-
const isFeminine = scaleIndex === 1
|
|
240
|
+
// -arda scale words (hiljada, milijarda, ...) are feminine; -ion words masculine
|
|
241
|
+
const isFeminine = scaleIndex % 2 === 1
|
|
201
242
|
const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
|
|
202
243
|
parts.push(segmentWord + ' ' + scaleWord)
|
|
203
244
|
}
|
|
@@ -209,7 +250,13 @@ function buildLargeNumberWords (n, gender) {
|
|
|
209
250
|
return parts.join(' ')
|
|
210
251
|
}
|
|
211
252
|
|
|
212
|
-
|
|
253
|
+
/**
|
|
254
|
+
* Converts the decimal-part digit string to Serbian words.
|
|
255
|
+
* @param {string} decimalPart - The decimal digits as a string
|
|
256
|
+
* @param {('masculine'|'feminine')} gender - Grammatical gender
|
|
257
|
+
* @returns {string} The decimal part in words
|
|
258
|
+
*/
|
|
259
|
+
function decimalPartToWords(decimalPart, gender) {
|
|
213
260
|
let result = ''
|
|
214
261
|
let i = 0
|
|
215
262
|
|
|
@@ -228,20 +275,31 @@ function decimalPartToWords (decimalPart, gender) {
|
|
|
228
275
|
return result
|
|
229
276
|
}
|
|
230
277
|
|
|
278
|
+
/**
|
|
279
|
+
* @typedef {object} CardinalOptions
|
|
280
|
+
* @property {('masculine'|'feminine')} [gender] - Grammatical gender
|
|
281
|
+
*/
|
|
282
|
+
|
|
283
|
+
/** @type {Required<CardinalOptions>} */
|
|
284
|
+
export const cardinalDefaults = { gender: 'masculine' }
|
|
285
|
+
|
|
286
|
+
/** @type {{ gender: ReadonlyArray<Required<CardinalOptions>['gender']> }} */
|
|
287
|
+
export const cardinalValues = { gender: ['masculine', 'feminine'] }
|
|
288
|
+
|
|
231
289
|
/**
|
|
232
290
|
* Converts a numeric value to Serbian (Cyrillic) words.
|
|
233
|
-
*
|
|
234
291
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
235
|
-
* @param {
|
|
236
|
-
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
292
|
+
* @param {CardinalOptions} [options] - Optional configuration
|
|
237
293
|
* @returns {string} The number in Serbian Cyrillic words
|
|
238
294
|
*/
|
|
239
|
-
function toCardinal
|
|
240
|
-
options = validateOptions(options)
|
|
295
|
+
function toCardinal(value, options) {
|
|
241
296
|
const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
|
|
297
|
+
// Both the integer part and the decimal's significant digits are spelled via
|
|
298
|
+
// the scale builder, so both must clear the ceiling.
|
|
299
|
+
checkMax(integerPart, cardinalMax, decimalPart)
|
|
242
300
|
|
|
243
301
|
// Apply option defaults
|
|
244
|
-
const { gender
|
|
302
|
+
const { gender } = resolveOptions(options, cardinalDefaults, cardinalValues)
|
|
245
303
|
|
|
246
304
|
let result = ''
|
|
247
305
|
|
|
@@ -265,11 +323,10 @@ function toCardinal (value, options) {
|
|
|
265
323
|
/**
|
|
266
324
|
* Builds ordinal for a 0-99 segment when it's the final (ordinal) part.
|
|
267
325
|
* Returns ordinal form: "први", "двадесет први", etc.
|
|
268
|
-
*
|
|
269
326
|
* @param {number} n - Number 0-99
|
|
270
327
|
* @returns {string} Ordinal words
|
|
271
328
|
*/
|
|
272
|
-
function buildOrdinalTensOnes
|
|
329
|
+
function buildOrdinalTensOnes(n) {
|
|
273
330
|
if (n === 0) return ''
|
|
274
331
|
|
|
275
332
|
const onesDigit = n % 10
|
|
@@ -300,11 +357,10 @@ function buildOrdinalTensOnes (n) {
|
|
|
300
357
|
*
|
|
301
358
|
* In Serbian ordinals, only the LAST component becomes ordinal.
|
|
302
359
|
* E.g., 121 = "сто двадесет први" (one hundred twenty first)
|
|
303
|
-
*
|
|
304
360
|
* @param {bigint} n - Positive integer to convert
|
|
305
361
|
* @returns {string} Ordinal Serbian words
|
|
306
362
|
*/
|
|
307
|
-
function integerToOrdinal
|
|
363
|
+
function integerToOrdinal(n) {
|
|
308
364
|
// Fast path: numbers < 100
|
|
309
365
|
if (n < 100n) {
|
|
310
366
|
return buildOrdinalTensOnes(Number(n))
|
|
@@ -352,11 +408,10 @@ function integerToOrdinal (n) {
|
|
|
352
408
|
/**
|
|
353
409
|
* Builds ordinal words for numbers >= 1,000,000.
|
|
354
410
|
* All segments except the final one are cardinal; final segment is ordinal.
|
|
355
|
-
*
|
|
356
411
|
* @param {bigint} n - Number >= 1,000,000
|
|
357
412
|
* @returns {string} Ordinal Serbian words
|
|
358
413
|
*/
|
|
359
|
-
function buildLargeOrdinal
|
|
414
|
+
function buildLargeOrdinal(n) {
|
|
360
415
|
const numStr = n.toString()
|
|
361
416
|
const len = numStr.length
|
|
362
417
|
|
|
@@ -394,26 +449,30 @@ function buildLargeOrdinal (n) {
|
|
|
394
449
|
// Units position (no scale)
|
|
395
450
|
if (isLastNonZero) {
|
|
396
451
|
parts.push(integerToOrdinal(BigInt(segment)))
|
|
397
|
-
}
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
398
454
|
parts.push(buildSegmentMasc(segment))
|
|
399
455
|
}
|
|
400
|
-
}
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
401
458
|
// Has scale word
|
|
402
459
|
if (isLastNonZero) {
|
|
403
460
|
// This scale position is the final ordinal
|
|
404
461
|
if (segment === 1) {
|
|
405
462
|
parts.push(ORDINAL_SCALES[scaleIndex - 1])
|
|
406
|
-
}
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
407
465
|
// Use cardinal segment + ordinal scale
|
|
408
|
-
const isFeminine = scaleIndex === 1 //
|
|
466
|
+
const isFeminine = scaleIndex % 2 === 1 // feminine at -arda scales (hiljada, milijarda, ...)
|
|
409
467
|
const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
|
|
410
468
|
parts.push(segmentWord + ' ' + ORDINAL_SCALES[scaleIndex - 1])
|
|
411
469
|
}
|
|
412
|
-
}
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
413
472
|
// Not the final segment: use cardinal
|
|
414
473
|
const scaleForms = SCALE_FORMS[scaleIndex - 1]
|
|
415
474
|
const scaleWord = pluralize(segment, scaleForms)
|
|
416
|
-
const isFeminine = scaleIndex === 1
|
|
475
|
+
const isFeminine = scaleIndex % 2 === 1
|
|
417
476
|
const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
|
|
418
477
|
parts.push(segmentWord + ' ' + scaleWord)
|
|
419
478
|
}
|
|
@@ -428,12 +487,10 @@ function buildLargeOrdinal (n) {
|
|
|
428
487
|
|
|
429
488
|
/**
|
|
430
489
|
* Converts a numeric value to Serbian ordinal words (masculine nominative).
|
|
431
|
-
*
|
|
432
490
|
* @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
|
|
433
491
|
* @returns {string} The number as ordinal words (e.g., "први", "четрдесет други")
|
|
434
492
|
* @throws {TypeError} If value is not a valid numeric type
|
|
435
493
|
* @throws {RangeError} If value is negative, zero, or has a decimal part
|
|
436
|
-
*
|
|
437
494
|
* @example
|
|
438
495
|
* toOrdinal(1) // 'први'
|
|
439
496
|
* toOrdinal(2) // 'други'
|
|
@@ -443,8 +500,9 @@ function buildLargeOrdinal (n) {
|
|
|
443
500
|
* toOrdinal(100) // 'стоти'
|
|
444
501
|
* toOrdinal(1000) // 'хиљадити'
|
|
445
502
|
*/
|
|
446
|
-
function toOrdinal
|
|
503
|
+
function toOrdinal(value) {
|
|
447
504
|
const integerPart = parseOrdinalValue(value)
|
|
505
|
+
checkMax(integerPart, ordinalMax)
|
|
448
506
|
return integerToOrdinal(integerPart)
|
|
449
507
|
}
|
|
450
508
|
|
|
@@ -452,16 +510,21 @@ function toOrdinal (value) {
|
|
|
452
510
|
// CURRENCY: toCurrency(value, options?)
|
|
453
511
|
// ============================================================================
|
|
454
512
|
|
|
513
|
+
/**
|
|
514
|
+
* @typedef {object} CurrencyOptions
|
|
515
|
+
* @property {boolean} [and] - Use "и" between dinars and para
|
|
516
|
+
*/
|
|
517
|
+
|
|
518
|
+
/** @type {Required<CurrencyOptions>} */
|
|
519
|
+
export const currencyDefaults = { and: true }
|
|
520
|
+
|
|
455
521
|
/**
|
|
456
522
|
* Converts a numeric value to Serbian currency words (Serbian Dinar).
|
|
457
|
-
*
|
|
458
523
|
* @param {number | string | bigint} value - The currency amount to convert
|
|
459
|
-
* @param {
|
|
460
|
-
* @param {boolean} [options.and=true] - Use "и" between dinars and para
|
|
524
|
+
* @param {CurrencyOptions} [options] - Optional configuration
|
|
461
525
|
* @returns {string} The amount in Serbian currency words
|
|
462
526
|
* @throws {TypeError} If value is not a valid numeric type
|
|
463
527
|
* @throws {Error} If value is not a valid number format
|
|
464
|
-
*
|
|
465
528
|
* @example
|
|
466
529
|
* toCurrency(42.50) // 'четрдесет два динара и педесет пара'
|
|
467
530
|
* toCurrency(1) // 'један динар'
|
|
@@ -469,10 +532,10 @@ function toOrdinal (value) {
|
|
|
469
532
|
* toCurrency(0.01) // 'једна пара'
|
|
470
533
|
* toCurrency(42.50, { and: false }) // 'четрдесет два динара педесет пара'
|
|
471
534
|
*/
|
|
472
|
-
function toCurrency
|
|
473
|
-
options = validateOptions(options)
|
|
535
|
+
function toCurrency(value, options) {
|
|
474
536
|
const { isNegative, dollars: dinars, cents: para } = parseCurrencyValue(value)
|
|
475
|
-
|
|
537
|
+
checkMax(dinars, currencyMax)
|
|
538
|
+
const { and: useAnd } = resolveOptions(options, currencyDefaults)
|
|
476
539
|
|
|
477
540
|
// Build result
|
|
478
541
|
let result = ''
|
package/src/sr-Latn-RS.d.ts
CHANGED
|
@@ -1,22 +1,47 @@
|
|
|
1
|
+
export const cardinalMax: bigint;
|
|
2
|
+
export const ordinalMax: bigint;
|
|
3
|
+
export const currencyMax: bigint;
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {object} CardinalOptions
|
|
6
|
+
* @property {('masculine'|'feminine')} [gender] - Grammatical gender
|
|
7
|
+
*/
|
|
8
|
+
/** @type {Required<CardinalOptions>} */
|
|
9
|
+
export const cardinalDefaults: Required<CardinalOptions>;
|
|
10
|
+
/** @type {{ gender: ReadonlyArray<Required<CardinalOptions>['gender']> }} */
|
|
11
|
+
export const cardinalValues: {
|
|
12
|
+
gender: ReadonlyArray<Required<CardinalOptions>["gender"]>;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {object} CurrencyOptions
|
|
16
|
+
* @property {boolean} [and] - Use "i" between dinars and para
|
|
17
|
+
*/
|
|
18
|
+
/** @type {Required<CurrencyOptions>} */
|
|
19
|
+
export const currencyDefaults: Required<CurrencyOptions>;
|
|
20
|
+
export type CardinalOptions = {
|
|
21
|
+
/**
|
|
22
|
+
* - Grammatical gender
|
|
23
|
+
*/
|
|
24
|
+
gender?: "feminine" | "masculine" | undefined;
|
|
25
|
+
};
|
|
26
|
+
export type CurrencyOptions = {
|
|
27
|
+
/**
|
|
28
|
+
* - Use "i" between dinars and para
|
|
29
|
+
*/
|
|
30
|
+
and?: boolean | undefined;
|
|
31
|
+
};
|
|
1
32
|
/**
|
|
2
33
|
* Converts a numeric value to Serbian (Latin) words.
|
|
3
|
-
*
|
|
4
34
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
|
-
* @param {
|
|
6
|
-
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
35
|
+
* @param {CardinalOptions} [options] - Optional configuration
|
|
7
36
|
* @returns {string} The number in Serbian Latin words
|
|
8
37
|
*/
|
|
9
|
-
export function toCardinal(value: number | string | bigint, options?:
|
|
10
|
-
gender?: "masculine" | "feminine" | undefined;
|
|
11
|
-
}): string;
|
|
38
|
+
export function toCardinal(value: number | string | bigint, options?: CardinalOptions): string;
|
|
12
39
|
/**
|
|
13
40
|
* Converts a numeric value to Serbian ordinal words (masculine nominative).
|
|
14
|
-
*
|
|
15
41
|
* @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
|
|
16
42
|
* @returns {string} The number as ordinal words (e.g., "prvi", "četrdeset drugi")
|
|
17
43
|
* @throws {TypeError} If value is not a valid numeric type
|
|
18
44
|
* @throws {RangeError} If value is negative, zero, or has a decimal part
|
|
19
|
-
*
|
|
20
45
|
* @example
|
|
21
46
|
* toOrdinal(1) // 'prvi'
|
|
22
47
|
* toOrdinal(2) // 'drugi'
|
|
@@ -29,14 +54,11 @@ export function toCardinal(value: number | string | bigint, options?: {
|
|
|
29
54
|
export function toOrdinal(value: number | string | bigint): string;
|
|
30
55
|
/**
|
|
31
56
|
* Converts a numeric value to Serbian currency words (Serbian Dinar).
|
|
32
|
-
*
|
|
33
57
|
* @param {number | string | bigint} value - The currency amount to convert
|
|
34
|
-
* @param {
|
|
35
|
-
* @param {boolean} [options.and=true] - Use "i" between dinars and para
|
|
58
|
+
* @param {CurrencyOptions} [options] - Optional configuration
|
|
36
59
|
* @returns {string} The amount in Serbian currency words
|
|
37
60
|
* @throws {TypeError} If value is not a valid numeric type
|
|
38
61
|
* @throws {Error} If value is not a valid number format
|
|
39
|
-
*
|
|
40
62
|
* @example
|
|
41
63
|
* toCurrency(42.50) // 'četrdeset dva dinara i pedeset para'
|
|
42
64
|
* toCurrency(1) // 'jedan dinar'
|
|
@@ -44,6 +66,4 @@ export function toOrdinal(value: number | string | bigint): string;
|
|
|
44
66
|
* toCurrency(0.01) // 'jedna para'
|
|
45
67
|
* toCurrency(42.50, { and: false }) // 'četrdeset dva dinara pedeset para'
|
|
46
68
|
*/
|
|
47
|
-
export function toCurrency(value: number | string | bigint, options?:
|
|
48
|
-
and?: boolean | undefined;
|
|
49
|
-
}): string;
|
|
69
|
+
export function toCurrency(value: number | string | bigint, options?: CurrencyOptions): string;
|