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/ro-RO.js
CHANGED
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
import { parseCardinalValue } from './utils/parse-cardinal.js'
|
|
14
14
|
import { parseCurrencyValue } from './utils/parse-currency.js'
|
|
15
15
|
import { parseOrdinalValue } from './utils/parse-ordinal.js'
|
|
16
|
-
import {
|
|
16
|
+
import { checkMax } from './utils/check-max.js'
|
|
17
|
+
import { bounded, western } from './utils/scale.js'
|
|
18
|
+
import { resolveOptions } from './utils/resolve-options.js'
|
|
17
19
|
|
|
18
20
|
// ============================================================================
|
|
19
21
|
// Vocabulary (module-level constants)
|
|
@@ -62,17 +64,28 @@ const SCALE_META = [
|
|
|
62
64
|
{ singular: 'cvintilion', plural: 'cvintilioane', article: 'un', feminine: false, needsDe: true },
|
|
63
65
|
{ singular: 'sextilion', plural: 'sextilioane', article: 'un', feminine: false, needsDe: true },
|
|
64
66
|
{ singular: 'septilion', plural: 'septilioane', article: 'un', feminine: false, needsDe: true },
|
|
65
|
-
{ singular: 'octilion', plural: 'octilioane', article: 'un', feminine: false, needsDe: true }
|
|
67
|
+
{ singular: 'octilion', plural: 'octilioane', article: 'un', feminine: false, needsDe: true },
|
|
66
68
|
]
|
|
67
69
|
|
|
70
|
+
// Cardinal scale words run thousand..octilion; past SCALE_META the builder drops
|
|
71
|
+
// the scale word. The ordinal spells its millions multiplier via spellUnder1000
|
|
72
|
+
// (0-999), so it overflows once that multiplier reaches 1000 — n must stay below 10^9.
|
|
73
|
+
export const cardinalMax = western(SCALE_META.length)
|
|
74
|
+
export const ordinalMax = bounded(9)
|
|
75
|
+
export const currencyMax = western(SCALE_META.length)
|
|
76
|
+
|
|
68
77
|
// ============================================================================
|
|
69
78
|
// Helper Functions
|
|
70
79
|
// ============================================================================
|
|
71
80
|
|
|
72
81
|
/**
|
|
73
82
|
* Spells number under 100.
|
|
83
|
+
* @param {number} n - Number 0-99
|
|
84
|
+
* @param {boolean} [feminine] - Use feminine forms
|
|
85
|
+
* @param {boolean} [masculineTeens] - Use masculine teen forms
|
|
86
|
+
* @returns {string} The number 0-99 in words
|
|
74
87
|
*/
|
|
75
|
-
function spellUnder100
|
|
88
|
+
function spellUnder100(n, feminine = false, masculineTeens = false) {
|
|
76
89
|
if (n === 0) return ''
|
|
77
90
|
if (n < 10) {
|
|
78
91
|
return feminine ? ONES_FEM[n] : ONES_MASC[n]
|
|
@@ -91,8 +104,12 @@ function spellUnder100 (n, feminine = false, masculineTeens = false) {
|
|
|
91
104
|
|
|
92
105
|
/**
|
|
93
106
|
* Spells number under 1000.
|
|
107
|
+
* @param {number} n - Number 0-999
|
|
108
|
+
* @param {boolean} [feminine] - Use feminine forms
|
|
109
|
+
* @param {boolean} [masculineTeens] - Use masculine teen forms
|
|
110
|
+
* @returns {string} The number 0-999 in words
|
|
94
111
|
*/
|
|
95
|
-
function spellUnder1000
|
|
112
|
+
function spellUnder1000(n, feminine = false, masculineTeens = false) {
|
|
96
113
|
if (n === 0) return ''
|
|
97
114
|
if (n < 100) return spellUnder100(n, feminine, masculineTeens)
|
|
98
115
|
|
|
@@ -107,8 +124,11 @@ function spellUnder1000 (n, feminine = false, masculineTeens = false) {
|
|
|
107
124
|
/**
|
|
108
125
|
* Builds scale word with proper pluralization and "de" insertion.
|
|
109
126
|
* Romanian always uses feminine forms (două, not doi) when counting scale words.
|
|
127
|
+
* @param {number} segment - Three-digit segment value (1-999)
|
|
128
|
+
* @param {number} scaleIndex - Scale position (1 = thousands, 2 = millions, ...)
|
|
129
|
+
* @returns {string} The scale phrase in words
|
|
110
130
|
*/
|
|
111
|
-
function buildScalePhrase
|
|
131
|
+
function buildScalePhrase(segment, scaleIndex) {
|
|
112
132
|
const meta = SCALE_META[scaleIndex - 1]
|
|
113
133
|
if (!meta) return spellUnder1000(segment, true)
|
|
114
134
|
|
|
@@ -137,12 +157,11 @@ function buildScalePhrase (segment, scaleIndex) {
|
|
|
137
157
|
|
|
138
158
|
/**
|
|
139
159
|
* Converts a non-negative integer to Romanian words.
|
|
140
|
-
*
|
|
141
160
|
* @param {bigint} n - Non-negative integer to convert
|
|
142
|
-
* @param {
|
|
161
|
+
* @param {string | {gender?: string}} gender - Gender for numbers ('feminine'/'masculine')
|
|
143
162
|
* @returns {string} Romanian words
|
|
144
163
|
*/
|
|
145
|
-
function integerToWords
|
|
164
|
+
function integerToWords(n, gender) {
|
|
146
165
|
if (n === 0n) return ZERO
|
|
147
166
|
|
|
148
167
|
// Fast path: numbers < 1000
|
|
@@ -157,12 +176,11 @@ function integerToWords (n, gender) {
|
|
|
157
176
|
/**
|
|
158
177
|
* Builds words for numbers >= 1000.
|
|
159
178
|
* Uses BigInt division for faster segment extraction.
|
|
160
|
-
*
|
|
161
179
|
* @param {bigint} n - Number >= 1000
|
|
162
|
-
* @param {
|
|
180
|
+
* @param {string | {gender?: string}} gender - Gender for numbers ('feminine'/'masculine')
|
|
163
181
|
* @returns {string} Romanian words
|
|
164
182
|
*/
|
|
165
|
-
function buildLargeNumberWords
|
|
183
|
+
function buildLargeNumberWords(n, gender) {
|
|
166
184
|
// Extract segments using BigInt division (faster than string slicing)
|
|
167
185
|
// Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)
|
|
168
186
|
const segmentValues = []
|
|
@@ -184,14 +202,16 @@ function buildLargeNumberWords (n, gender) {
|
|
|
184
202
|
// Units segment - use gender from options
|
|
185
203
|
const feminine = gender === 'feminine'
|
|
186
204
|
segmentWords = spellUnder1000(segment, feminine)
|
|
187
|
-
}
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
188
207
|
// Scale segment
|
|
189
208
|
segmentWords = buildScalePhrase(segment, i)
|
|
190
209
|
}
|
|
191
210
|
|
|
192
211
|
if (result && segmentWords) {
|
|
193
212
|
result += ' ' + segmentWords
|
|
194
|
-
}
|
|
213
|
+
}
|
|
214
|
+
else if (segmentWords) {
|
|
195
215
|
result = segmentWords
|
|
196
216
|
}
|
|
197
217
|
}
|
|
@@ -202,11 +222,10 @@ function buildLargeNumberWords (n, gender) {
|
|
|
202
222
|
/**
|
|
203
223
|
* Converts decimal digits to Romanian words.
|
|
204
224
|
* Decimals always use masculine forms.
|
|
205
|
-
*
|
|
206
225
|
* @param {string} decimalPart - Decimal digits (without the point)
|
|
207
226
|
* @returns {string} Romanian words for decimal part
|
|
208
227
|
*/
|
|
209
|
-
function decimalPartToWords
|
|
228
|
+
function decimalPartToWords(decimalPart) {
|
|
210
229
|
let result = ''
|
|
211
230
|
let i = 0
|
|
212
231
|
|
|
@@ -224,7 +243,8 @@ function decimalPartToWords (decimalPart) {
|
|
|
224
243
|
const n = BigInt(remainder)
|
|
225
244
|
if (n < 1000n) {
|
|
226
245
|
result += spellUnder1000(Number(n), false, true)
|
|
227
|
-
}
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
228
248
|
result += integerToWords(n, { gender: 'masculine' })
|
|
229
249
|
}
|
|
230
250
|
}
|
|
@@ -232,27 +252,37 @@ function decimalPartToWords (decimalPart) {
|
|
|
232
252
|
return result
|
|
233
253
|
}
|
|
234
254
|
|
|
255
|
+
/**
|
|
256
|
+
* @typedef {object} CardinalOptions
|
|
257
|
+
* @property {('masculine'|'feminine')} [gender] - Gender for numbers
|
|
258
|
+
*/
|
|
259
|
+
|
|
260
|
+
/** @type {Required<CardinalOptions>} */
|
|
261
|
+
export const cardinalDefaults = { gender: 'masculine' }
|
|
262
|
+
|
|
263
|
+
/** @type {{ gender: ReadonlyArray<Required<CardinalOptions>['gender']> }} */
|
|
264
|
+
export const cardinalValues = { gender: ['masculine', 'feminine'] }
|
|
265
|
+
|
|
235
266
|
/**
|
|
236
267
|
* Converts a numeric value to Romanian words.
|
|
237
|
-
*
|
|
238
268
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
239
|
-
* @param {
|
|
240
|
-
* @param {string} [options.gender='masculine'] - Gender for numbers
|
|
269
|
+
* @param {CardinalOptions} [options] - Conversion options
|
|
241
270
|
* @returns {string} The number in Romanian words
|
|
242
271
|
* @throws {TypeError} If value is not a valid numeric type
|
|
243
272
|
* @throws {Error} If value is not a valid number format
|
|
244
|
-
*
|
|
245
273
|
* @example
|
|
246
274
|
* toCardinal(21) // 'douăzeci și unu'
|
|
247
275
|
* toCardinal(1, { gender: 'feminine' }) // 'una'
|
|
248
276
|
* toCardinal(1000) // 'o mie'
|
|
249
277
|
*/
|
|
250
|
-
function toCardinal
|
|
251
|
-
options = validateOptions(options)
|
|
278
|
+
function toCardinal(value, options) {
|
|
252
279
|
const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
|
|
280
|
+
// Both the integer part and the decimal's significant digits are spelled via
|
|
281
|
+
// the scale builder, so both must clear the ceiling.
|
|
282
|
+
checkMax(integerPart, cardinalMax, decimalPart)
|
|
253
283
|
|
|
254
284
|
// Apply option defaults
|
|
255
|
-
const { gender
|
|
285
|
+
const { gender } = resolveOptions(options, cardinalDefaults, cardinalValues)
|
|
256
286
|
|
|
257
287
|
let result = ''
|
|
258
288
|
|
|
@@ -275,11 +305,10 @@ function toCardinal (value, options) {
|
|
|
275
305
|
|
|
276
306
|
/**
|
|
277
307
|
* Builds ordinal for tens and ones (0-99).
|
|
278
|
-
*
|
|
279
308
|
* @param {number} n - Number 0-99
|
|
280
309
|
* @returns {string} Ordinal word
|
|
281
310
|
*/
|
|
282
|
-
function buildOrdinalTensOnes
|
|
311
|
+
function buildOrdinalTensOnes(n) {
|
|
283
312
|
if (n === 0) return ''
|
|
284
313
|
if (n < 10) return ORDINAL_ONES[n]
|
|
285
314
|
if (n < 20) return ORDINAL_TEENS[n - 10]
|
|
@@ -296,11 +325,10 @@ function buildOrdinalTensOnes (n) {
|
|
|
296
325
|
|
|
297
326
|
/**
|
|
298
327
|
* Converts a non-negative integer to Romanian ordinal words.
|
|
299
|
-
*
|
|
300
328
|
* @param {bigint} n - Non-negative integer to convert
|
|
301
329
|
* @returns {string} Romanian ordinal words
|
|
302
330
|
*/
|
|
303
|
-
function integerToOrdinal
|
|
331
|
+
function integerToOrdinal(n) {
|
|
304
332
|
if (n === 0n) return ''
|
|
305
333
|
if (n === 1n) return ORDINAL_ONES[1]
|
|
306
334
|
|
|
@@ -336,7 +364,8 @@ function integerToOrdinal (n) {
|
|
|
336
364
|
let result
|
|
337
365
|
if (thousands === 1) {
|
|
338
366
|
result = 'o mie'
|
|
339
|
-
}
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
340
369
|
result = buildScalePhrase(thousands, 1)
|
|
341
370
|
}
|
|
342
371
|
|
|
@@ -368,7 +397,8 @@ function integerToOrdinal (n) {
|
|
|
368
397
|
let result
|
|
369
398
|
if (millions === 1) {
|
|
370
399
|
result = 'un milion'
|
|
371
|
-
}
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
372
402
|
result = buildScalePhrase(millions, 2)
|
|
373
403
|
}
|
|
374
404
|
|
|
@@ -377,18 +407,17 @@ function integerToOrdinal (n) {
|
|
|
377
407
|
|
|
378
408
|
/**
|
|
379
409
|
* Converts a numeric value to Romanian ordinal words.
|
|
380
|
-
*
|
|
381
410
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
382
411
|
* @returns {string} The ordinal in Romanian words
|
|
383
412
|
* @throws {TypeError} If value is not a valid numeric type
|
|
384
413
|
* @throws {Error} If value is not a positive integer
|
|
385
|
-
*
|
|
386
414
|
* @example
|
|
387
415
|
* toOrdinal(1) // 'primul'
|
|
388
416
|
* toOrdinal(21) // 'douăzeci și primul'
|
|
389
417
|
*/
|
|
390
|
-
function toOrdinal
|
|
418
|
+
function toOrdinal(value) {
|
|
391
419
|
const n = parseOrdinalValue(value)
|
|
420
|
+
checkMax(n, ordinalMax)
|
|
392
421
|
return integerToOrdinal(n)
|
|
393
422
|
}
|
|
394
423
|
|
|
@@ -398,18 +427,17 @@ function toOrdinal (value) {
|
|
|
398
427
|
|
|
399
428
|
/**
|
|
400
429
|
* Converts a numeric value to Romanian Leu currency words.
|
|
401
|
-
*
|
|
402
430
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
403
431
|
* @returns {string} The currency in Romanian words
|
|
404
432
|
* @throws {TypeError} If value is not a valid numeric type
|
|
405
433
|
* @throws {Error} If value is not a valid number format
|
|
406
|
-
*
|
|
407
434
|
* @example
|
|
408
435
|
* toCurrency(1) // 'un leu'
|
|
409
436
|
* toCurrency(2.50) // 'doi lei cincizeci de bani'
|
|
410
437
|
*/
|
|
411
|
-
function toCurrency
|
|
438
|
+
function toCurrency(value) {
|
|
412
439
|
const { isNegative, dollars, cents } = parseCurrencyValue(value)
|
|
440
|
+
checkMax(dollars, currencyMax)
|
|
413
441
|
|
|
414
442
|
const parts = []
|
|
415
443
|
|
|
@@ -421,9 +449,17 @@ function toCurrency (value) {
|
|
|
421
449
|
if (dollars > 0n || cents === 0n) {
|
|
422
450
|
if (dollars === 1n) {
|
|
423
451
|
parts.push('un ' + LEU_SINGULAR)
|
|
424
|
-
}
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
425
454
|
const leuWord = integerToWords(dollars, 'masculine')
|
|
426
|
-
|
|
455
|
+
// Romanian inserts "de" before the noun for the CLDR `other` category:
|
|
456
|
+
// count >= 20 whose last two digits are 00 or 20-99 — "douăzeci de lei",
|
|
457
|
+
// "o sută de lei", but "o sută unu lei" (101) and no "de" below 20. The
|
|
458
|
+
// bani path below applies the same rule; buildScalePhrase uses a related
|
|
459
|
+
// per-segment predicate for scale words.
|
|
460
|
+
const m = dollars % 100n
|
|
461
|
+
const needsDe = dollars >= 20n && (m === 0n || m >= 20n)
|
|
462
|
+
parts.push(leuWord + (needsDe ? ' de ' : ' ') + LEU_PLURAL)
|
|
427
463
|
}
|
|
428
464
|
}
|
|
429
465
|
|
|
@@ -432,10 +468,12 @@ function toCurrency (value) {
|
|
|
432
468
|
const centNum = Number(cents)
|
|
433
469
|
if (centNum === 1) {
|
|
434
470
|
parts.push('un ' + BAN_SINGULAR)
|
|
435
|
-
}
|
|
471
|
+
}
|
|
472
|
+
else if (centNum >= 20) {
|
|
436
473
|
const banWord = spellUnder100(centNum, false)
|
|
437
474
|
parts.push(banWord + ' de ' + BAN_PLURAL)
|
|
438
|
-
}
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
439
477
|
const banWord = spellUnder100(centNum, false)
|
|
440
478
|
parts.push(banWord + ' ' + BAN_PLURAL)
|
|
441
479
|
}
|
package/src/ru-RU.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 "и" between rubles and kopecks
|
|
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 "и" between rubles and kopecks
|
|
29
|
+
*/
|
|
30
|
+
and?: boolean | undefined;
|
|
31
|
+
};
|
|
1
32
|
/**
|
|
2
33
|
* Converts a numeric value to Russian 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 Russian 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 Russian 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., "первый", "сорок второй")
|
|
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) // 'первый'
|
|
22
47
|
* toOrdinal(2) // 'второй'
|
|
@@ -30,14 +55,11 @@ export function toCardinal(value: number | string | bigint, options?: {
|
|
|
30
55
|
export function toOrdinal(value: number | string | bigint): string;
|
|
31
56
|
/**
|
|
32
57
|
* Converts a numeric value to Russian currency words (Russian Ruble).
|
|
33
|
-
*
|
|
34
58
|
* @param {number | string | bigint} value - The currency amount to convert
|
|
35
|
-
* @param {
|
|
36
|
-
* @param {boolean} [options.and=true] - Use "и" between rubles and kopecks
|
|
59
|
+
* @param {CurrencyOptions} [options] - Optional configuration
|
|
37
60
|
* @returns {string} The amount in Russian currency words
|
|
38
61
|
* @throws {TypeError} If value is not a valid numeric type
|
|
39
62
|
* @throws {Error} If value is not a valid number format
|
|
40
|
-
*
|
|
41
63
|
* @example
|
|
42
64
|
* toCurrency(42.50) // 'сорок два рубля и пятьдесят копеек'
|
|
43
65
|
* toCurrency(1) // 'один рубль'
|
|
@@ -45,6 +67,4 @@ export function toOrdinal(value: number | string | bigint): string;
|
|
|
45
67
|
* toCurrency(0.01) // 'одна копейка'
|
|
46
68
|
* toCurrency(42.50, { and: false }) // 'сорок два рубля пятьдесят копеек'
|
|
47
69
|
*/
|
|
48
|
-
export function toCurrency(value: number | string | bigint, options?:
|
|
49
|
-
and?: boolean | undefined;
|
|
50
|
-
}): string;
|
|
70
|
+
export function toCurrency(value: number | string | bigint, options?: CurrencyOptions): string;
|