n2words 4.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 +53 -0
- package/README.md +14 -12
- 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 -0
- package/dist/pt-BR.umd.js +2 -0
- 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 +53 -36
- 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 +42 -0
- package/src/pt-BR.js +574 -0
- 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/uk-UA.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 { western } from './utils/scale.js'
|
|
18
|
+
import { resolveOptions } from './utils/resolve-options.js'
|
|
17
19
|
|
|
18
20
|
// ============================================================================
|
|
19
21
|
// Vocabulary
|
|
@@ -71,14 +73,20 @@ const SCALE_FORMS = [
|
|
|
71
73
|
['квiнтильйон', 'квiнтильйони', 'квiнтильйонiв'],
|
|
72
74
|
['секстильйон', 'секстильйони', 'секстильйонiв'],
|
|
73
75
|
['септильйон', 'септильйони', 'септильйонiв'],
|
|
74
|
-
['октильйон', 'октильйони', 'октильйонiв']
|
|
76
|
+
['октильйон', 'октильйони', 'октильйонiв'],
|
|
75
77
|
]
|
|
76
78
|
|
|
77
79
|
// ============================================================================
|
|
78
80
|
// Segment Building
|
|
79
81
|
// ============================================================================
|
|
80
82
|
|
|
81
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Selects the correct plural form for a count.
|
|
85
|
+
* @param {number | bigint} n - The count
|
|
86
|
+
* @param {string[]} forms - Plural forms [one, few, many]
|
|
87
|
+
* @returns {string} The selected plural form
|
|
88
|
+
*/
|
|
89
|
+
function pluralize(n, forms) {
|
|
82
90
|
const num = typeof n === 'bigint' ? Number(n) : n
|
|
83
91
|
const lastDigit = num % 10
|
|
84
92
|
const lastTwoDigits = num % 100
|
|
@@ -92,7 +100,12 @@ function pluralize (n, forms) {
|
|
|
92
100
|
return forms[2]
|
|
93
101
|
}
|
|
94
102
|
|
|
95
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Builds the masculine words for a 0-999 segment.
|
|
105
|
+
* @param {number} n - Number 0-999
|
|
106
|
+
* @returns {string} Segment words
|
|
107
|
+
*/
|
|
108
|
+
function buildSegmentMasc(n) {
|
|
96
109
|
if (n === 0) return ''
|
|
97
110
|
|
|
98
111
|
const onesDigit = n % 10
|
|
@@ -111,14 +124,20 @@ function buildSegmentMasc (n) {
|
|
|
111
124
|
|
|
112
125
|
if (tensDigit === 1) {
|
|
113
126
|
parts.push(TEENS[onesDigit])
|
|
114
|
-
}
|
|
127
|
+
}
|
|
128
|
+
else if (onesDigit > 0) {
|
|
115
129
|
parts.push(ONES_MASC[onesDigit])
|
|
116
130
|
}
|
|
117
131
|
|
|
118
132
|
return parts.join(' ')
|
|
119
133
|
}
|
|
120
134
|
|
|
121
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Builds the feminine words for a 0-999 segment.
|
|
137
|
+
* @param {number} n - Number 0-999
|
|
138
|
+
* @returns {string} Segment words
|
|
139
|
+
*/
|
|
140
|
+
function buildSegmentFem(n) {
|
|
122
141
|
if (n === 0) return ''
|
|
123
142
|
|
|
124
143
|
const onesDigit = n % 10
|
|
@@ -137,7 +156,8 @@ function buildSegmentFem (n) {
|
|
|
137
156
|
|
|
138
157
|
if (tensDigit === 1) {
|
|
139
158
|
parts.push(TEENS[onesDigit])
|
|
140
|
-
}
|
|
159
|
+
}
|
|
160
|
+
else if (onesDigit > 0) {
|
|
141
161
|
parts.push(ONES_FEM[onesDigit])
|
|
142
162
|
}
|
|
143
163
|
|
|
@@ -148,7 +168,13 @@ function buildSegmentFem (n) {
|
|
|
148
168
|
// Conversion Functions
|
|
149
169
|
// ============================================================================
|
|
150
170
|
|
|
151
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Converts a non-negative integer to Ukrainian words.
|
|
173
|
+
* @param {bigint} n - The integer to convert
|
|
174
|
+
* @param {('masculine'|'feminine')} gender - Grammatical gender
|
|
175
|
+
* @returns {string} The integer in Ukrainian words
|
|
176
|
+
*/
|
|
177
|
+
function integerToWords(n, gender) {
|
|
152
178
|
if (n === 0n) return ZERO
|
|
153
179
|
|
|
154
180
|
if (n < 1000n) {
|
|
@@ -158,7 +184,13 @@ function integerToWords (n, gender) {
|
|
|
158
184
|
return buildLargeNumberWords(n, gender)
|
|
159
185
|
}
|
|
160
186
|
|
|
161
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Builds Ukrainian words for numbers >= 1000.
|
|
189
|
+
* @param {bigint} n - The integer to convert
|
|
190
|
+
* @param {('masculine'|'feminine')} gender - Grammatical gender
|
|
191
|
+
* @returns {string} The integer in Ukrainian words
|
|
192
|
+
*/
|
|
193
|
+
function buildLargeNumberWords(n, gender) {
|
|
162
194
|
const numStr = n.toString()
|
|
163
195
|
const len = numStr.length
|
|
164
196
|
|
|
@@ -185,7 +217,8 @@ function buildLargeNumberWords (n, gender) {
|
|
|
185
217
|
if (segment !== 0) {
|
|
186
218
|
if (scaleIndex === 0) {
|
|
187
219
|
parts.push(gender === 'feminine' ? buildSegmentFem(segment) : buildSegmentMasc(segment))
|
|
188
|
-
}
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
189
222
|
const scaleForms = SCALE_FORMS[scaleIndex - 1]
|
|
190
223
|
const scaleWord = pluralize(segment, scaleForms)
|
|
191
224
|
// Thousands (scaleIndex=1) are feminine, others masculine
|
|
@@ -201,7 +234,13 @@ function buildLargeNumberWords (n, gender) {
|
|
|
201
234
|
return parts.join(' ')
|
|
202
235
|
}
|
|
203
236
|
|
|
204
|
-
|
|
237
|
+
/**
|
|
238
|
+
* Converts a decimal fractional string to Ukrainian words.
|
|
239
|
+
* @param {string} decimalPart - The fractional digits
|
|
240
|
+
* @param {('masculine'|'feminine')} gender - Grammatical gender
|
|
241
|
+
* @returns {string} The fractional part in Ukrainian words
|
|
242
|
+
*/
|
|
243
|
+
function decimalPartToWords(decimalPart, gender) {
|
|
205
244
|
let result = ''
|
|
206
245
|
let i = 0
|
|
207
246
|
|
|
@@ -220,20 +259,38 @@ function decimalPartToWords (decimalPart, gender) {
|
|
|
220
259
|
return result
|
|
221
260
|
}
|
|
222
261
|
|
|
262
|
+
// Supported magnitude ceilings (checked at the public entry points). Both
|
|
263
|
+
// tables are indexed [scaleIndex - 1] (units separate), so the ceiling is
|
|
264
|
+
// 10^((length + 1) * 3): cardinal/currency 10^30, ordinal 10^15.
|
|
265
|
+
export const cardinalMax = western(SCALE_FORMS.length)
|
|
266
|
+
export const ordinalMax = western(ORDINAL_SCALES.length)
|
|
267
|
+
export const currencyMax = western(SCALE_FORMS.length)
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @typedef {object} CardinalOptions
|
|
271
|
+
* @property {('masculine'|'feminine')} [gender] - Grammatical gender
|
|
272
|
+
*/
|
|
273
|
+
|
|
274
|
+
/** @type {Required<CardinalOptions>} */
|
|
275
|
+
export const cardinalDefaults = { gender: 'masculine' }
|
|
276
|
+
|
|
277
|
+
/** @type {{ gender: ReadonlyArray<Required<CardinalOptions>['gender']> }} */
|
|
278
|
+
export const cardinalValues = { gender: ['masculine', 'feminine'] }
|
|
279
|
+
|
|
223
280
|
/**
|
|
224
281
|
* Converts a numeric value to Ukrainian words.
|
|
225
|
-
*
|
|
226
282
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
227
|
-
* @param {
|
|
228
|
-
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
283
|
+
* @param {CardinalOptions} [options] - Optional configuration
|
|
229
284
|
* @returns {string} The number in Ukrainian words
|
|
230
285
|
*/
|
|
231
|
-
function toCardinal
|
|
232
|
-
options = validateOptions(options)
|
|
286
|
+
function toCardinal(value, options) {
|
|
233
287
|
const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
|
|
288
|
+
// Both the integer part and the decimal's significant digits are spelled via
|
|
289
|
+
// the scale builder, so both must clear the ceiling.
|
|
290
|
+
checkMax(integerPart, cardinalMax, decimalPart)
|
|
234
291
|
|
|
235
292
|
// Apply option defaults
|
|
236
|
-
const { gender
|
|
293
|
+
const { gender } = resolveOptions(options, cardinalDefaults, cardinalValues)
|
|
237
294
|
|
|
238
295
|
let result = ''
|
|
239
296
|
|
|
@@ -256,11 +313,10 @@ function toCardinal (value, options) {
|
|
|
256
313
|
|
|
257
314
|
/**
|
|
258
315
|
* Builds ordinal for a 0-99 segment when it's the final (ordinal) part.
|
|
259
|
-
*
|
|
260
316
|
* @param {number} n - Number 0-99
|
|
261
317
|
* @returns {string} Ordinal words
|
|
262
318
|
*/
|
|
263
|
-
function buildOrdinalTensOnes
|
|
319
|
+
function buildOrdinalTensOnes(n) {
|
|
264
320
|
if (n === 0) return ''
|
|
265
321
|
|
|
266
322
|
const onesDigit = n % 10
|
|
@@ -283,11 +339,10 @@ function buildOrdinalTensOnes (n) {
|
|
|
283
339
|
|
|
284
340
|
/**
|
|
285
341
|
* Converts a positive integer to Ukrainian ordinal words (masculine nominative).
|
|
286
|
-
*
|
|
287
342
|
* @param {bigint} n - Positive integer to convert
|
|
288
343
|
* @returns {string} Ordinal Ukrainian words
|
|
289
344
|
*/
|
|
290
|
-
function integerToOrdinal
|
|
345
|
+
function integerToOrdinal(n) {
|
|
291
346
|
if (n < 100n) {
|
|
292
347
|
return buildOrdinalTensOnes(Number(n))
|
|
293
348
|
}
|
|
@@ -325,11 +380,10 @@ function integerToOrdinal (n) {
|
|
|
325
380
|
|
|
326
381
|
/**
|
|
327
382
|
* Builds ordinal words for numbers >= 1,000,000.
|
|
328
|
-
*
|
|
329
383
|
* @param {bigint} n - Number >= 1,000,000
|
|
330
384
|
* @returns {string} Ordinal Ukrainian words
|
|
331
385
|
*/
|
|
332
|
-
function buildLargeOrdinal
|
|
386
|
+
function buildLargeOrdinal(n) {
|
|
333
387
|
const numStr = n.toString()
|
|
334
388
|
const len = numStr.length
|
|
335
389
|
|
|
@@ -364,19 +418,23 @@ function buildLargeOrdinal (n) {
|
|
|
364
418
|
if (scaleIndex === 0) {
|
|
365
419
|
if (isLastNonZero) {
|
|
366
420
|
parts.push(integerToOrdinal(BigInt(segment)))
|
|
367
|
-
}
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
368
423
|
parts.push(buildSegmentMasc(segment))
|
|
369
424
|
}
|
|
370
|
-
}
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
371
427
|
if (isLastNonZero) {
|
|
372
428
|
if (segment === 1) {
|
|
373
429
|
parts.push(ORDINAL_SCALES[scaleIndex - 1])
|
|
374
|
-
}
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
375
432
|
const isFeminine = scaleIndex === 1
|
|
376
433
|
const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
|
|
377
434
|
parts.push(segmentWord + ' ' + ORDINAL_SCALES[scaleIndex - 1])
|
|
378
435
|
}
|
|
379
|
-
}
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
380
438
|
const scaleForms = SCALE_FORMS[scaleIndex - 1]
|
|
381
439
|
const scaleWord = pluralize(segment, scaleForms)
|
|
382
440
|
const isFeminine = scaleIndex === 1
|
|
@@ -394,12 +452,10 @@ function buildLargeOrdinal (n) {
|
|
|
394
452
|
|
|
395
453
|
/**
|
|
396
454
|
* Converts a numeric value to Ukrainian ordinal words (masculine nominative).
|
|
397
|
-
*
|
|
398
455
|
* @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
|
|
399
456
|
* @returns {string} The number as ordinal words
|
|
400
457
|
* @throws {TypeError} If value is not a valid numeric type
|
|
401
458
|
* @throws {RangeError} If value is negative, zero, or has a decimal part
|
|
402
|
-
*
|
|
403
459
|
* @example
|
|
404
460
|
* toOrdinal(1) // 'перший'
|
|
405
461
|
* toOrdinal(2) // 'другий'
|
|
@@ -407,8 +463,9 @@ function buildLargeOrdinal (n) {
|
|
|
407
463
|
* toOrdinal(100) // 'сотий'
|
|
408
464
|
* toOrdinal(1000) // 'тисячний'
|
|
409
465
|
*/
|
|
410
|
-
function toOrdinal
|
|
466
|
+
function toOrdinal(value) {
|
|
411
467
|
const integerPart = parseOrdinalValue(value)
|
|
468
|
+
checkMax(integerPart, ordinalMax)
|
|
412
469
|
return integerToOrdinal(integerPart)
|
|
413
470
|
}
|
|
414
471
|
|
|
@@ -418,20 +475,19 @@ function toOrdinal (value) {
|
|
|
418
475
|
|
|
419
476
|
/**
|
|
420
477
|
* Converts a numeric value to Ukrainian currency words (Hryvnia).
|
|
421
|
-
*
|
|
422
478
|
* @param {number | string | bigint} value - The currency amount to convert
|
|
423
479
|
* @returns {string} The amount in Ukrainian currency words
|
|
424
480
|
* @throws {TypeError} If value is not a valid numeric type
|
|
425
481
|
* @throws {Error} If value is not a valid number format
|
|
426
|
-
*
|
|
427
482
|
* @example
|
|
428
483
|
* toCurrency(42) // 'сорок двi гривнi'
|
|
429
484
|
* toCurrency(1) // 'одна гривня'
|
|
430
485
|
* toCurrency(1.50) // 'одна гривня п\'ятдесят копiйок'
|
|
431
486
|
* toCurrency(-5) // 'мiнус п\'ять гривень'
|
|
432
487
|
*/
|
|
433
|
-
function toCurrency
|
|
488
|
+
function toCurrency(value) {
|
|
434
489
|
const { isNegative, dollars: hryvnia, cents: kopiyky } = parseCurrencyValue(value)
|
|
490
|
+
checkMax(hryvnia, currencyMax)
|
|
435
491
|
|
|
436
492
|
let result = ''
|
|
437
493
|
if (isNegative) {
|
package/src/ur-PK.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
+
export const cardinalMax: bigint;
|
|
2
|
+
export const ordinalMax: bigint;
|
|
3
|
+
export const currencyMax: bigint;
|
|
1
4
|
/**
|
|
2
5
|
* Converts a numeric value to Urdu words.
|
|
3
|
-
*
|
|
4
6
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
7
|
* @returns {string} The number in Urdu words
|
|
6
8
|
*/
|
|
7
9
|
export function toCardinal(value: number | string | bigint): string;
|
|
8
10
|
/**
|
|
9
11
|
* Converts a numeric value to Urdu ordinal words.
|
|
10
|
-
*
|
|
11
12
|
* @param {number | string | bigint} value - The numeric value to convert (positive integer)
|
|
12
13
|
* @returns {string} The number as ordinal words
|
|
13
14
|
* @throws {TypeError} If value is not a valid numeric type
|
|
14
15
|
* @throws {RangeError} If value is negative, zero, or has a decimal part
|
|
15
|
-
*
|
|
16
16
|
* @example
|
|
17
17
|
* toOrdinal(1) // 'پہلا'
|
|
18
18
|
* toOrdinal(2) // 'دوسرا'
|
|
@@ -22,12 +22,10 @@ export function toCardinal(value: number | string | bigint): string;
|
|
|
22
22
|
export function toOrdinal(value: number | string | bigint): string;
|
|
23
23
|
/**
|
|
24
24
|
* Converts a numeric value to Urdu currency words (Pakistani Rupee).
|
|
25
|
-
*
|
|
26
25
|
* @param {number | string | bigint} value - The currency amount to convert
|
|
27
26
|
* @returns {string} The amount in Urdu currency words
|
|
28
27
|
* @throws {TypeError} If value is not a valid numeric type
|
|
29
28
|
* @throws {Error} If value is not a valid number format
|
|
30
|
-
*
|
|
31
29
|
* @example
|
|
32
30
|
* toCurrency(42.50) // 'بیالیس روپے پچاس پیسے'
|
|
33
31
|
* toCurrency(1) // 'ایک روپیہ'
|
package/src/ur-PK.js
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
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 { checkMax } from './utils/check-max.js'
|
|
17
|
+
import { indian } from './utils/scale.js'
|
|
16
18
|
|
|
17
19
|
// ============================================================================
|
|
18
20
|
// Vocabulary
|
|
@@ -55,20 +57,29 @@ const BELOW_HUNDRED = [
|
|
|
55
57
|
'ساٹھ', 'اکسٹھ', 'باسٹھ', 'ترسٹھ', 'چونسٹھ', 'پینسٹھ', 'چھیاسٹھ', 'سڑسٹھ', 'اڑسٹھ', 'انہتر',
|
|
56
58
|
'ستر', 'اکہتر', 'بہتر', 'تہتر', 'چوہتر', 'پچھتر', 'چھہتر', 'ستتر', 'اٹھہتر', 'اناسی',
|
|
57
59
|
'اسی', 'اکیاسی', 'بیاسی', 'تریاسی', 'چوراسی', 'پچاسی', 'چھیاسی', 'ستاسی', 'اٹھاسی', 'نواسی',
|
|
58
|
-
'نوے', 'اکانوے', 'بانوے', 'ترانوے', 'چورانوے', 'پچانوے', 'چھیانوے', 'ستانوے', 'اٹھانوے', 'ننانوے'
|
|
60
|
+
'نوے', 'اکانوے', 'بانوے', 'ترانوے', 'چورانوے', 'پچانوے', 'چھیانوے', 'ستانوے', 'اٹھانوے', 'ننانوے',
|
|
59
61
|
]
|
|
60
62
|
|
|
61
63
|
// Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.
|
|
62
64
|
const SCALE_WORDS = ['', 'ہزار', 'لاکھ', 'کروڑ', 'ارب', 'کھرب', 'نیل', 'پدم', 'شنکھ']
|
|
63
65
|
|
|
66
|
+
// 3-2-2 Indian grouping: a 3-digit base segment, then 2 digits per scale word
|
|
67
|
+
// (SCALE_WORDS[0] = '' is the units slot). Past the table the scale word is
|
|
68
|
+
// dropped, which collapses the magnitude — so cap there.
|
|
69
|
+
export const cardinalMax = indian(SCALE_WORDS.length)
|
|
70
|
+
export const ordinalMax = indian(SCALE_WORDS.length)
|
|
71
|
+
export const currencyMax = indian(SCALE_WORDS.length)
|
|
72
|
+
|
|
64
73
|
// ============================================================================
|
|
65
74
|
// Segment Building
|
|
66
75
|
// ============================================================================
|
|
67
76
|
|
|
68
77
|
/**
|
|
69
78
|
* Builds words for a 0-999 segment.
|
|
79
|
+
* @param {number} n - Segment value (0-999)
|
|
80
|
+
* @returns {string} Urdu words for the segment
|
|
70
81
|
*/
|
|
71
|
-
function buildSegment
|
|
82
|
+
function buildSegment(n) {
|
|
72
83
|
if (n === 0) return ''
|
|
73
84
|
if (n < 100) return BELOW_HUNDRED[n]
|
|
74
85
|
|
|
@@ -90,11 +101,10 @@ function buildSegment (n) {
|
|
|
90
101
|
*
|
|
91
102
|
* Uses BigInt modulo for segment extraction (faster than string slicing).
|
|
92
103
|
* South Asian 3-2-2 grouping: first 3 digits, then groups of 2.
|
|
93
|
-
*
|
|
94
104
|
* @param {bigint} n - Non-negative integer to convert
|
|
95
105
|
* @returns {string} Urdu words
|
|
96
106
|
*/
|
|
97
|
-
function integerToWords
|
|
107
|
+
function integerToWords(n) {
|
|
98
108
|
if (n === 0n) return ZERO
|
|
99
109
|
|
|
100
110
|
// Fast path: numbers < 1000 (direct lookup)
|
|
@@ -120,7 +130,8 @@ function integerToWords (n) {
|
|
|
120
130
|
|
|
121
131
|
if (i === 0) {
|
|
122
132
|
words.push(buildSegment(segment))
|
|
123
|
-
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
124
135
|
words.push(BELOW_HUNDRED[segment])
|
|
125
136
|
}
|
|
126
137
|
|
|
@@ -132,7 +143,12 @@ function integerToWords (n) {
|
|
|
132
143
|
return words.join(' ')
|
|
133
144
|
}
|
|
134
145
|
|
|
135
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Converts the fractional digit string to Urdu words.
|
|
148
|
+
* @param {string} decimalPart - Digits after the decimal separator
|
|
149
|
+
* @returns {string} Urdu words for the decimal part
|
|
150
|
+
*/
|
|
151
|
+
function decimalPartToWords(decimalPart) {
|
|
136
152
|
let result = ''
|
|
137
153
|
let i = 0
|
|
138
154
|
|
|
@@ -153,12 +169,14 @@ function decimalPartToWords (decimalPart) {
|
|
|
153
169
|
|
|
154
170
|
/**
|
|
155
171
|
* Converts a numeric value to Urdu words.
|
|
156
|
-
*
|
|
157
172
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
158
173
|
* @returns {string} The number in Urdu words
|
|
159
174
|
*/
|
|
160
|
-
function toCardinal
|
|
175
|
+
function toCardinal(value) {
|
|
161
176
|
const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
|
|
177
|
+
// Both the integer part and the decimal's significant digits are spelled via
|
|
178
|
+
// the scale builder, so both must clear the ceiling.
|
|
179
|
+
checkMax(integerPart, cardinalMax, decimalPart)
|
|
162
180
|
|
|
163
181
|
let result = ''
|
|
164
182
|
|
|
@@ -183,11 +201,10 @@ function toCardinal (value) {
|
|
|
183
201
|
* Converts a positive integer to Urdu ordinal words.
|
|
184
202
|
*
|
|
185
203
|
* Urdu ordinals: First 6 are irregular, then add -واں suffix.
|
|
186
|
-
*
|
|
187
204
|
* @param {bigint} n - Positive integer to convert
|
|
188
205
|
* @returns {string} Urdu ordinal words
|
|
189
206
|
*/
|
|
190
|
-
function integerToOrdinal
|
|
207
|
+
function integerToOrdinal(n) {
|
|
191
208
|
// Special ordinals for 1-6
|
|
192
209
|
if (n >= 1n && n <= 6n) {
|
|
193
210
|
return ORDINAL_SPECIAL[Number(n)]
|
|
@@ -200,20 +217,20 @@ function integerToOrdinal (n) {
|
|
|
200
217
|
|
|
201
218
|
/**
|
|
202
219
|
* Converts a numeric value to Urdu ordinal words.
|
|
203
|
-
*
|
|
204
220
|
* @param {number | string | bigint} value - The numeric value to convert (positive integer)
|
|
205
221
|
* @returns {string} The number as ordinal words
|
|
206
222
|
* @throws {TypeError} If value is not a valid numeric type
|
|
207
223
|
* @throws {RangeError} If value is negative, zero, or has a decimal part
|
|
208
|
-
*
|
|
209
224
|
* @example
|
|
210
225
|
* toOrdinal(1) // 'پہلا'
|
|
211
226
|
* toOrdinal(2) // 'دوسرا'
|
|
212
227
|
* toOrdinal(3) // 'تیسرا'
|
|
213
228
|
* toOrdinal(10) // 'دسواں'
|
|
214
229
|
*/
|
|
215
|
-
function toOrdinal
|
|
230
|
+
function toOrdinal(value) {
|
|
216
231
|
const integerPart = parseOrdinalValue(value)
|
|
232
|
+
// Ordinals build on the cardinal speller, so they share its ceiling.
|
|
233
|
+
checkMax(integerPart, ordinalMax)
|
|
217
234
|
return integerToOrdinal(integerPart)
|
|
218
235
|
}
|
|
219
236
|
|
|
@@ -223,19 +240,18 @@ function toOrdinal (value) {
|
|
|
223
240
|
|
|
224
241
|
/**
|
|
225
242
|
* Converts a numeric value to Urdu currency words (Pakistani Rupee).
|
|
226
|
-
*
|
|
227
243
|
* @param {number | string | bigint} value - The currency amount to convert
|
|
228
244
|
* @returns {string} The amount in Urdu currency words
|
|
229
245
|
* @throws {TypeError} If value is not a valid numeric type
|
|
230
246
|
* @throws {Error} If value is not a valid number format
|
|
231
|
-
*
|
|
232
247
|
* @example
|
|
233
248
|
* toCurrency(42.50) // 'بیالیس روپے پچاس پیسے'
|
|
234
249
|
* toCurrency(1) // 'ایک روپیہ'
|
|
235
250
|
* toCurrency(0.01) // 'ایک پیسہ'
|
|
236
251
|
*/
|
|
237
|
-
function toCurrency
|
|
252
|
+
function toCurrency(value) {
|
|
238
253
|
const { isNegative, dollars: rupees, cents: paise } = parseCurrencyValue(value)
|
|
254
|
+
checkMax(rupees, currencyMax)
|
|
239
255
|
|
|
240
256
|
// Build result
|
|
241
257
|
let result = ''
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Range guard — the throwing counterpart of the scale-range producers.
|
|
3
|
+
*
|
|
4
|
+
* A precondition validator in the same family as `parseCardinalValue` /
|
|
5
|
+
* `resolveOptions`: the language calls it and it throws on a value past the
|
|
6
|
+
* form's ceiling, e.g. `checkMax(integerPart, cardinalMax, decimalPart)`. The
|
|
7
|
+
* `*Max` it consumes is produced by the pure helpers in `scale.js`.
|
|
8
|
+
* @module check-max
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Throws a `RangeError` when `value` — or its integer-spelled fraction —
|
|
12
|
+
* reaches a form's ceiling (`max`, the smallest value the form refuses).
|
|
13
|
+
*
|
|
14
|
+
* Runs at the entry point: an O(1) short-circuit before any spelling is built —
|
|
15
|
+
* the in-range path is just the bigint comparison, plus a single
|
|
16
|
+
* `BigInt(fraction)` parse when an integer-spelled fraction is supplied. The
|
|
17
|
+
* message renders `10^N - 1` when the ceiling is an exact power of ten,
|
|
18
|
+
* otherwise the raw maximum.
|
|
19
|
+
* @param {bigint} value The integer magnitude to test (integer part, dollars, …)
|
|
20
|
+
* @param {bigint | null} max The form's ceiling, or `null` for no limit (never throws)
|
|
21
|
+
* @param {string} [fraction] The decimal digit string — pass it **only** when the
|
|
22
|
+
* fraction is spelled via the scale builder; digit-by-digit and ordinal /
|
|
23
|
+
* currency forms omit it so long fractions stay valid.
|
|
24
|
+
* @throws {RangeError} when the value is out of range
|
|
25
|
+
*/
|
|
26
|
+
export function checkMax(value: bigint, max: bigint | null, fraction?: string): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Range guard — the throwing counterpart of the scale-range producers.
|
|
3
|
+
*
|
|
4
|
+
* A precondition validator in the same family as `parseCardinalValue` /
|
|
5
|
+
* `resolveOptions`: the language calls it and it throws on a value past the
|
|
6
|
+
* form's ceiling, e.g. `checkMax(integerPart, cardinalMax, decimalPart)`. The
|
|
7
|
+
* `*Max` it consumes is produced by the pure helpers in `scale.js`.
|
|
8
|
+
* @module check-max
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Throws a `RangeError` when `value` — or its integer-spelled fraction —
|
|
13
|
+
* reaches a form's ceiling (`max`, the smallest value the form refuses).
|
|
14
|
+
*
|
|
15
|
+
* Runs at the entry point: an O(1) short-circuit before any spelling is built —
|
|
16
|
+
* the in-range path is just the bigint comparison, plus a single
|
|
17
|
+
* `BigInt(fraction)` parse when an integer-spelled fraction is supplied. The
|
|
18
|
+
* message renders `10^N - 1` when the ceiling is an exact power of ten,
|
|
19
|
+
* otherwise the raw maximum.
|
|
20
|
+
* @param {bigint} value The integer magnitude to test (integer part, dollars, …)
|
|
21
|
+
* @param {bigint | null} max The form's ceiling, or `null` for no limit (never throws)
|
|
22
|
+
* @param {string} [fraction] The decimal digit string — pass it **only** when the
|
|
23
|
+
* fraction is spelled via the scale builder; digit-by-digit and ordinal /
|
|
24
|
+
* currency forms omit it so long fractions stay valid.
|
|
25
|
+
* @throws {RangeError} when the value is out of range
|
|
26
|
+
*/
|
|
27
|
+
export function checkMax(value, max, fraction) {
|
|
28
|
+
if (max === null) return
|
|
29
|
+
if (value < max && !(fraction && BigInt(fraction) >= max)) return
|
|
30
|
+
const exponent = max.toString().length - 1
|
|
31
|
+
const largest = max === 10n ** BigInt(exponent) ? `10^${exponent} - 1` : `${max - 1n}`
|
|
32
|
+
throw new RangeError(`Number too large to convert: the largest supported value is ${largest}`)
|
|
33
|
+
}
|
|
@@ -6,10 +6,8 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* Expands scientific notation to full decimal form.
|
|
8
8
|
* Handles arbitrarily large exponents without precision loss.
|
|
9
|
-
*
|
|
10
9
|
* @param {string} str - String in scientific notation (e.g., "1e21", "1.5e-3")
|
|
11
10
|
* @returns {string} Full decimal representation (e.g., "1000000000000000000000", "0.0015")
|
|
12
|
-
*
|
|
13
11
|
* @example
|
|
14
12
|
* expandScientificNotation("1e21") // "1000000000000000000000"
|
|
15
13
|
* expandScientificNotation("1.5e3") // "1500"
|
|
@@ -18,14 +16,12 @@
|
|
|
18
16
|
export function expandScientificNotation(str: string): string;
|
|
19
17
|
/**
|
|
20
18
|
* Converts a number to decimal string, expanding scientific notation if needed.
|
|
21
|
-
*
|
|
22
19
|
* @param {number} value - The number to convert
|
|
23
20
|
* @returns {string} Decimal string representation
|
|
24
21
|
*/
|
|
25
22
|
export function numberToString(value: number): string;
|
|
26
23
|
/**
|
|
27
24
|
* Checks if a string contains scientific notation.
|
|
28
|
-
*
|
|
29
25
|
* @param {string} str - String to check
|
|
30
26
|
* @returns {boolean} True if string contains 'e' or 'E'
|
|
31
27
|
*/
|
|
@@ -7,16 +7,14 @@
|
|
|
7
7
|
/**
|
|
8
8
|
* Expands scientific notation to full decimal form.
|
|
9
9
|
* Handles arbitrarily large exponents without precision loss.
|
|
10
|
-
*
|
|
11
10
|
* @param {string} str - String in scientific notation (e.g., "1e21", "1.5e-3")
|
|
12
11
|
* @returns {string} Full decimal representation (e.g., "1000000000000000000000", "0.0015")
|
|
13
|
-
*
|
|
14
12
|
* @example
|
|
15
13
|
* expandScientificNotation("1e21") // "1000000000000000000000"
|
|
16
14
|
* expandScientificNotation("1.5e3") // "1500"
|
|
17
15
|
* expandScientificNotation("1e-3") // "0.001"
|
|
18
16
|
*/
|
|
19
|
-
export function expandScientificNotation
|
|
17
|
+
export function expandScientificNotation(str) {
|
|
20
18
|
let [mantissa, expStr] = str.toLowerCase().split('e')
|
|
21
19
|
const exp = parseInt(expStr, 10)
|
|
22
20
|
|
|
@@ -36,30 +34,30 @@ export function expandScientificNotation (str) {
|
|
|
36
34
|
|
|
37
35
|
if (newDotPosition >= digits.length) {
|
|
38
36
|
return sign + digits + '0'.repeat(newDotPosition - digits.length)
|
|
39
|
-
}
|
|
37
|
+
}
|
|
38
|
+
else if (newDotPosition <= 0) {
|
|
40
39
|
return sign + '0.' + '0'.repeat(-newDotPosition) + digits
|
|
41
|
-
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
42
|
return sign + digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Converts a number to decimal string, expanding scientific notation if needed.
|
|
48
|
-
*
|
|
49
48
|
* @param {number} value - The number to convert
|
|
50
49
|
* @returns {string} Decimal string representation
|
|
51
50
|
*/
|
|
52
|
-
export function numberToString
|
|
51
|
+
export function numberToString(value) {
|
|
53
52
|
const str = value.toString()
|
|
54
53
|
return hasScientificNotation(str) ? expandScientificNotation(str) : str
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
/**
|
|
58
57
|
* Checks if a string contains scientific notation.
|
|
59
|
-
*
|
|
60
58
|
* @param {string} str - String to check
|
|
61
59
|
* @returns {boolean} True if string contains 'e' or 'E'
|
|
62
60
|
*/
|
|
63
|
-
export function hasScientificNotation
|
|
61
|
+
export function hasScientificNotation(str) {
|
|
64
62
|
return str.includes('e') || str.includes('E')
|
|
65
63
|
}
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
* - Object.create(null): null-prototype object
|
|
7
7
|
*
|
|
8
8
|
* This excludes arrays, class instances, Map, Set, and other object types.
|
|
9
|
-
*
|
|
10
|
-
* @
|
|
11
|
-
* @returns {boolean} True if value is a plain object
|
|
9
|
+
* @param {unknown} value Value to check
|
|
10
|
+
* @returns {value is object} True if value is a plain object
|
|
12
11
|
*/
|
|
13
|
-
export function isPlainObject(value:
|
|
12
|
+
export function isPlainObject(value: unknown): value is object;
|
|
@@ -6,11 +6,10 @@
|
|
|
6
6
|
* - Object.create(null): null-prototype object
|
|
7
7
|
*
|
|
8
8
|
* This excludes arrays, class instances, Map, Set, and other object types.
|
|
9
|
-
*
|
|
10
|
-
* @
|
|
11
|
-
* @returns {boolean} True if value is a plain object
|
|
9
|
+
* @param {unknown} value Value to check
|
|
10
|
+
* @returns {value is object} True if value is a plain object
|
|
12
11
|
*/
|
|
13
|
-
export function isPlainObject
|
|
12
|
+
export function isPlainObject(value) {
|
|
14
13
|
if (value === null || typeof value !== 'object') return false
|
|
15
14
|
const proto = Object.getPrototypeOf(value)
|
|
16
15
|
return proto === null || proto === Object.prototype
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Parses a value for cardinal conversion.
|
|
3
3
|
* Cardinals accept any numeric value: integers, decimals, negatives.
|
|
4
|
-
*
|
|
5
4
|
* @param {number|string|bigint} value - The value to parse
|
|
6
|
-
* @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}
|
|
5
|
+
* @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}} The parsed cardinal components
|
|
7
6
|
* @throws {TypeError} If value is not number, string, or bigint
|
|
8
7
|
* @throws {RangeError} If value is not finite
|
|
9
8
|
*/
|