n2words 5.0.0 → 5.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 +128 -42
- 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 +31 -22
- 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 +100 -38
- package/src/sr-Latn-RS.d.ts +35 -15
- package/src/sr-Latn-RS.js +100 -38
- 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
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
import { parseCardinalValue } from './utils/parse-cardinal.js'
|
|
15
15
|
import { parseCurrencyValue } from './utils/parse-currency.js'
|
|
16
16
|
import { parseOrdinalValue } from './utils/parse-ordinal.js'
|
|
17
|
-
import {
|
|
17
|
+
import { checkMax } from './utils/check-max.js'
|
|
18
|
+
import { western } from './utils/scale.js'
|
|
19
|
+
import { resolveOptions } from './utils/resolve-options.js'
|
|
18
20
|
|
|
19
21
|
// ============================================================================
|
|
20
22
|
// Vocabulary
|
|
@@ -56,7 +58,7 @@ const ORDINAL_SCALES = [
|
|
|
56
58
|
'трилионити',
|
|
57
59
|
'трилијардити',
|
|
58
60
|
'квадрилионити',
|
|
59
|
-
'квадрилијардити'
|
|
61
|
+
'квадрилијардити',
|
|
60
62
|
]
|
|
61
63
|
|
|
62
64
|
// ============================================================================
|
|
@@ -79,14 +81,27 @@ const SCALE_FORMS = [
|
|
|
79
81
|
['трилион', 'трилиона', 'трилиона'],
|
|
80
82
|
['трилијарда', 'трилијарде', 'трилијарда'],
|
|
81
83
|
['квадрилион', 'квадрилиона', 'квадрилиона'],
|
|
82
|
-
['квадрилијарда', 'квадрилијарде', 'квадрилијарда']
|
|
84
|
+
['квадрилијарда', 'квадрилијарде', 'квадрилијарда'],
|
|
83
85
|
]
|
|
84
86
|
|
|
87
|
+
// Supported magnitude ceilings (checked at the public entry points). Both the
|
|
88
|
+
// cardinal SCALE_FORMS and the ORDINAL_SCALES tables cover units + N scale
|
|
89
|
+
// groups, so values must stay below 10^((length + 1) * 3) = 10^30.
|
|
90
|
+
export const cardinalMax = western(SCALE_FORMS.length)
|
|
91
|
+
export const ordinalMax = western(ORDINAL_SCALES.length)
|
|
92
|
+
export const currencyMax = western(SCALE_FORMS.length)
|
|
93
|
+
|
|
85
94
|
// ============================================================================
|
|
86
95
|
// Segment Building
|
|
87
96
|
// ============================================================================
|
|
88
97
|
|
|
89
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Selects the correct plural form for a count.
|
|
100
|
+
* @param {number | bigint} n - The count
|
|
101
|
+
* @param {string[]} forms - Plural forms [one, few, many]
|
|
102
|
+
* @returns {string} The selected plural form
|
|
103
|
+
*/
|
|
104
|
+
function pluralize(n, forms) {
|
|
90
105
|
const num = typeof n === 'bigint' ? Number(n) : n
|
|
91
106
|
const lastDigit = num % 10
|
|
92
107
|
const lastTwoDigits = num % 100
|
|
@@ -100,7 +115,12 @@ function pluralize (n, forms) {
|
|
|
100
115
|
return forms[2]
|
|
101
116
|
}
|
|
102
117
|
|
|
103
|
-
|
|
118
|
+
/**
|
|
119
|
+
* Builds the masculine word form for a 0-999 segment.
|
|
120
|
+
* @param {number} n - Number 0-999
|
|
121
|
+
* @returns {string} The segment words
|
|
122
|
+
*/
|
|
123
|
+
function buildSegmentMasc(n) {
|
|
104
124
|
if (n === 0) return ''
|
|
105
125
|
|
|
106
126
|
const onesDigit = n % 10
|
|
@@ -119,14 +139,20 @@ function buildSegmentMasc (n) {
|
|
|
119
139
|
|
|
120
140
|
if (tensDigit === 1) {
|
|
121
141
|
parts.push(TEENS[onesDigit])
|
|
122
|
-
}
|
|
142
|
+
}
|
|
143
|
+
else if (onesDigit > 0) {
|
|
123
144
|
parts.push(ONES_MASC[onesDigit])
|
|
124
145
|
}
|
|
125
146
|
|
|
126
147
|
return parts.join(' ')
|
|
127
148
|
}
|
|
128
149
|
|
|
129
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Builds the feminine word form for a 0-999 segment.
|
|
152
|
+
* @param {number} n - Number 0-999
|
|
153
|
+
* @returns {string} The segment words
|
|
154
|
+
*/
|
|
155
|
+
function buildSegmentFem(n) {
|
|
130
156
|
if (n === 0) return ''
|
|
131
157
|
|
|
132
158
|
const onesDigit = n % 10
|
|
@@ -145,7 +171,8 @@ function buildSegmentFem (n) {
|
|
|
145
171
|
|
|
146
172
|
if (tensDigit === 1) {
|
|
147
173
|
parts.push(TEENS[onesDigit])
|
|
148
|
-
}
|
|
174
|
+
}
|
|
175
|
+
else if (onesDigit > 0) {
|
|
149
176
|
parts.push(ONES_FEM[onesDigit])
|
|
150
177
|
}
|
|
151
178
|
|
|
@@ -156,7 +183,13 @@ function buildSegmentFem (n) {
|
|
|
156
183
|
// Conversion Functions
|
|
157
184
|
// ============================================================================
|
|
158
185
|
|
|
159
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Converts a non-negative integer to Serbian words.
|
|
188
|
+
* @param {bigint} n - The integer to convert
|
|
189
|
+
* @param {('masculine'|'feminine')} gender - Grammatical gender
|
|
190
|
+
* @returns {string} The integer in words
|
|
191
|
+
*/
|
|
192
|
+
function integerToWords(n, gender) {
|
|
160
193
|
if (n === 0n) return ZERO
|
|
161
194
|
|
|
162
195
|
if (n < 1000n) {
|
|
@@ -166,7 +199,13 @@ function integerToWords (n, gender) {
|
|
|
166
199
|
return buildLargeNumberWords(n, gender)
|
|
167
200
|
}
|
|
168
201
|
|
|
169
|
-
|
|
202
|
+
/**
|
|
203
|
+
* Builds words for integers >= 1000 using scale decomposition.
|
|
204
|
+
* @param {bigint} n - The integer to convert (>= 1000)
|
|
205
|
+
* @param {('masculine'|'feminine')} gender - Grammatical gender
|
|
206
|
+
* @returns {string} The integer in words
|
|
207
|
+
*/
|
|
208
|
+
function buildLargeNumberWords(n, gender) {
|
|
170
209
|
const numStr = n.toString()
|
|
171
210
|
const len = numStr.length
|
|
172
211
|
|
|
@@ -193,7 +232,8 @@ function buildLargeNumberWords (n, gender) {
|
|
|
193
232
|
if (segment !== 0) {
|
|
194
233
|
if (scaleIndex === 0) {
|
|
195
234
|
parts.push(gender === 'feminine' ? buildSegmentFem(segment) : buildSegmentMasc(segment))
|
|
196
|
-
}
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
197
237
|
const scaleForms = SCALE_FORMS[scaleIndex - 1]
|
|
198
238
|
const scaleWord = pluralize(segment, scaleForms)
|
|
199
239
|
// Thousands (scaleIndex=1) are feminine, others masculine
|
|
@@ -209,7 +249,13 @@ function buildLargeNumberWords (n, gender) {
|
|
|
209
249
|
return parts.join(' ')
|
|
210
250
|
}
|
|
211
251
|
|
|
212
|
-
|
|
252
|
+
/**
|
|
253
|
+
* Converts the decimal-part digit string to Serbian words.
|
|
254
|
+
* @param {string} decimalPart - The decimal digits as a string
|
|
255
|
+
* @param {('masculine'|'feminine')} gender - Grammatical gender
|
|
256
|
+
* @returns {string} The decimal part in words
|
|
257
|
+
*/
|
|
258
|
+
function decimalPartToWords(decimalPart, gender) {
|
|
213
259
|
let result = ''
|
|
214
260
|
let i = 0
|
|
215
261
|
|
|
@@ -228,20 +274,31 @@ function decimalPartToWords (decimalPart, gender) {
|
|
|
228
274
|
return result
|
|
229
275
|
}
|
|
230
276
|
|
|
277
|
+
/**
|
|
278
|
+
* @typedef {object} CardinalOptions
|
|
279
|
+
* @property {('masculine'|'feminine')} [gender] - Grammatical gender
|
|
280
|
+
*/
|
|
281
|
+
|
|
282
|
+
/** @type {Required<CardinalOptions>} */
|
|
283
|
+
export const cardinalDefaults = { gender: 'masculine' }
|
|
284
|
+
|
|
285
|
+
/** @type {{ gender: ReadonlyArray<Required<CardinalOptions>['gender']> }} */
|
|
286
|
+
export const cardinalValues = { gender: ['masculine', 'feminine'] }
|
|
287
|
+
|
|
231
288
|
/**
|
|
232
289
|
* Converts a numeric value to Serbian (Cyrillic) words.
|
|
233
|
-
*
|
|
234
290
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
235
|
-
* @param {
|
|
236
|
-
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
291
|
+
* @param {CardinalOptions} [options] - Optional configuration
|
|
237
292
|
* @returns {string} The number in Serbian Cyrillic words
|
|
238
293
|
*/
|
|
239
|
-
function toCardinal
|
|
240
|
-
options = validateOptions(options)
|
|
294
|
+
function toCardinal(value, options) {
|
|
241
295
|
const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
|
|
296
|
+
// Both the integer part and the decimal's significant digits are spelled via
|
|
297
|
+
// the scale builder, so both must clear the ceiling.
|
|
298
|
+
checkMax(integerPart, cardinalMax, decimalPart)
|
|
242
299
|
|
|
243
300
|
// Apply option defaults
|
|
244
|
-
const { gender
|
|
301
|
+
const { gender } = resolveOptions(options, cardinalDefaults, cardinalValues)
|
|
245
302
|
|
|
246
303
|
let result = ''
|
|
247
304
|
|
|
@@ -265,11 +322,10 @@ function toCardinal (value, options) {
|
|
|
265
322
|
/**
|
|
266
323
|
* Builds ordinal for a 0-99 segment when it's the final (ordinal) part.
|
|
267
324
|
* Returns ordinal form: "први", "двадесет први", etc.
|
|
268
|
-
*
|
|
269
325
|
* @param {number} n - Number 0-99
|
|
270
326
|
* @returns {string} Ordinal words
|
|
271
327
|
*/
|
|
272
|
-
function buildOrdinalTensOnes
|
|
328
|
+
function buildOrdinalTensOnes(n) {
|
|
273
329
|
if (n === 0) return ''
|
|
274
330
|
|
|
275
331
|
const onesDigit = n % 10
|
|
@@ -300,11 +356,10 @@ function buildOrdinalTensOnes (n) {
|
|
|
300
356
|
*
|
|
301
357
|
* In Serbian ordinals, only the LAST component becomes ordinal.
|
|
302
358
|
* E.g., 121 = "сто двадесет први" (one hundred twenty first)
|
|
303
|
-
*
|
|
304
359
|
* @param {bigint} n - Positive integer to convert
|
|
305
360
|
* @returns {string} Ordinal Serbian words
|
|
306
361
|
*/
|
|
307
|
-
function integerToOrdinal
|
|
362
|
+
function integerToOrdinal(n) {
|
|
308
363
|
// Fast path: numbers < 100
|
|
309
364
|
if (n < 100n) {
|
|
310
365
|
return buildOrdinalTensOnes(Number(n))
|
|
@@ -352,11 +407,10 @@ function integerToOrdinal (n) {
|
|
|
352
407
|
/**
|
|
353
408
|
* Builds ordinal words for numbers >= 1,000,000.
|
|
354
409
|
* All segments except the final one are cardinal; final segment is ordinal.
|
|
355
|
-
*
|
|
356
410
|
* @param {bigint} n - Number >= 1,000,000
|
|
357
411
|
* @returns {string} Ordinal Serbian words
|
|
358
412
|
*/
|
|
359
|
-
function buildLargeOrdinal
|
|
413
|
+
function buildLargeOrdinal(n) {
|
|
360
414
|
const numStr = n.toString()
|
|
361
415
|
const len = numStr.length
|
|
362
416
|
|
|
@@ -394,22 +448,26 @@ function buildLargeOrdinal (n) {
|
|
|
394
448
|
// Units position (no scale)
|
|
395
449
|
if (isLastNonZero) {
|
|
396
450
|
parts.push(integerToOrdinal(BigInt(segment)))
|
|
397
|
-
}
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
398
453
|
parts.push(buildSegmentMasc(segment))
|
|
399
454
|
}
|
|
400
|
-
}
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
401
457
|
// Has scale word
|
|
402
458
|
if (isLastNonZero) {
|
|
403
459
|
// This scale position is the final ordinal
|
|
404
460
|
if (segment === 1) {
|
|
405
461
|
parts.push(ORDINAL_SCALES[scaleIndex - 1])
|
|
406
|
-
}
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
407
464
|
// Use cardinal segment + ordinal scale
|
|
408
465
|
const isFeminine = scaleIndex === 1 // thousands are feminine
|
|
409
466
|
const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
|
|
410
467
|
parts.push(segmentWord + ' ' + ORDINAL_SCALES[scaleIndex - 1])
|
|
411
468
|
}
|
|
412
|
-
}
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
413
471
|
// Not the final segment: use cardinal
|
|
414
472
|
const scaleForms = SCALE_FORMS[scaleIndex - 1]
|
|
415
473
|
const scaleWord = pluralize(segment, scaleForms)
|
|
@@ -428,12 +486,10 @@ function buildLargeOrdinal (n) {
|
|
|
428
486
|
|
|
429
487
|
/**
|
|
430
488
|
* Converts a numeric value to Serbian ordinal words (masculine nominative).
|
|
431
|
-
*
|
|
432
489
|
* @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
|
|
433
490
|
* @returns {string} The number as ordinal words (e.g., "први", "четрдесет други")
|
|
434
491
|
* @throws {TypeError} If value is not a valid numeric type
|
|
435
492
|
* @throws {RangeError} If value is negative, zero, or has a decimal part
|
|
436
|
-
*
|
|
437
493
|
* @example
|
|
438
494
|
* toOrdinal(1) // 'први'
|
|
439
495
|
* toOrdinal(2) // 'други'
|
|
@@ -443,8 +499,9 @@ function buildLargeOrdinal (n) {
|
|
|
443
499
|
* toOrdinal(100) // 'стоти'
|
|
444
500
|
* toOrdinal(1000) // 'хиљадити'
|
|
445
501
|
*/
|
|
446
|
-
function toOrdinal
|
|
502
|
+
function toOrdinal(value) {
|
|
447
503
|
const integerPart = parseOrdinalValue(value)
|
|
504
|
+
checkMax(integerPart, ordinalMax)
|
|
448
505
|
return integerToOrdinal(integerPart)
|
|
449
506
|
}
|
|
450
507
|
|
|
@@ -452,16 +509,21 @@ function toOrdinal (value) {
|
|
|
452
509
|
// CURRENCY: toCurrency(value, options?)
|
|
453
510
|
// ============================================================================
|
|
454
511
|
|
|
512
|
+
/**
|
|
513
|
+
* @typedef {object} CurrencyOptions
|
|
514
|
+
* @property {boolean} [and] - Use "и" between dinars and para
|
|
515
|
+
*/
|
|
516
|
+
|
|
517
|
+
/** @type {Required<CurrencyOptions>} */
|
|
518
|
+
export const currencyDefaults = { and: true }
|
|
519
|
+
|
|
455
520
|
/**
|
|
456
521
|
* Converts a numeric value to Serbian currency words (Serbian Dinar).
|
|
457
|
-
*
|
|
458
522
|
* @param {number | string | bigint} value - The currency amount to convert
|
|
459
|
-
* @param {
|
|
460
|
-
* @param {boolean} [options.and=true] - Use "и" between dinars and para
|
|
523
|
+
* @param {CurrencyOptions} [options] - Optional configuration
|
|
461
524
|
* @returns {string} The amount in Serbian currency words
|
|
462
525
|
* @throws {TypeError} If value is not a valid numeric type
|
|
463
526
|
* @throws {Error} If value is not a valid number format
|
|
464
|
-
*
|
|
465
527
|
* @example
|
|
466
528
|
* toCurrency(42.50) // 'четрдесет два динара и педесет пара'
|
|
467
529
|
* toCurrency(1) // 'један динар'
|
|
@@ -469,10 +531,10 @@ function toOrdinal (value) {
|
|
|
469
531
|
* toCurrency(0.01) // 'једна пара'
|
|
470
532
|
* toCurrency(42.50, { and: false }) // 'четрдесет два динара педесет пара'
|
|
471
533
|
*/
|
|
472
|
-
function toCurrency
|
|
473
|
-
options = validateOptions(options)
|
|
534
|
+
function toCurrency(value, options) {
|
|
474
535
|
const { isNegative, dollars: dinars, cents: para } = parseCurrencyValue(value)
|
|
475
|
-
|
|
536
|
+
checkMax(dinars, currencyMax)
|
|
537
|
+
const { and: useAnd } = resolveOptions(options, currencyDefaults)
|
|
476
538
|
|
|
477
539
|
// Build result
|
|
478
540
|
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;
|