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/ja-JP.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 { myriad } from './utils/scale.js'
|
|
16
18
|
|
|
17
19
|
// ============================================================================
|
|
18
20
|
// Vocabulary (module-level constants)
|
|
@@ -40,9 +42,16 @@ const SCALES = [
|
|
|
40
42
|
'阿僧祇', // 10^56 (asōgi)
|
|
41
43
|
'那由他', // 10^60 (nayuta)
|
|
42
44
|
'不可思議', // 10^64 (fukashigi)
|
|
43
|
-
'無量大数' // 10^68 (muryōtaisū)
|
|
45
|
+
'無量大数', // 10^68 (muryōtaisū)
|
|
44
46
|
]
|
|
45
47
|
|
|
48
|
+
// Myriad (4-digit) grouping: each scale word covers a power of 10,000, so the
|
|
49
|
+
// first unsupported value is 10^((SCALES.length + 1) * 4). Ordinals (prefix) and
|
|
50
|
+
// currency build on the cardinal speller, so they share its ceiling.
|
|
51
|
+
export const cardinalMax = myriad(SCALES.length)
|
|
52
|
+
export const ordinalMax = myriad(SCALES.length)
|
|
53
|
+
export const currencyMax = myriad(SCALES.length)
|
|
54
|
+
|
|
46
55
|
const ZERO = '零'
|
|
47
56
|
const NEGATIVE = 'マイナス'
|
|
48
57
|
const DECIMAL_SEP = '点'
|
|
@@ -76,8 +85,10 @@ const THOUSAND = '千'
|
|
|
76
85
|
/**
|
|
77
86
|
* Builds segment word for 0-9999 with 一 omission rules.
|
|
78
87
|
* - Omit 一 before 十, 百, 千
|
|
88
|
+
* @param {number} n - Segment value (0-9999)
|
|
89
|
+
* @returns {string} Japanese kanji words for the segment
|
|
79
90
|
*/
|
|
80
|
-
function buildSegment
|
|
91
|
+
function buildSegment(n) {
|
|
81
92
|
if (n === 0) return ''
|
|
82
93
|
|
|
83
94
|
const ones = n % 10
|
|
@@ -91,7 +102,8 @@ function buildSegment (n) {
|
|
|
91
102
|
if (thousands > 0) {
|
|
92
103
|
if (thousands === 1) {
|
|
93
104
|
result += THOUSAND
|
|
94
|
-
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
95
107
|
result += ONES[thousands] + THOUSAND
|
|
96
108
|
}
|
|
97
109
|
}
|
|
@@ -100,7 +112,8 @@ function buildSegment (n) {
|
|
|
100
112
|
if (hundreds > 0) {
|
|
101
113
|
if (hundreds === 1) {
|
|
102
114
|
result += HUNDRED
|
|
103
|
-
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
104
117
|
result += ONES[hundreds] + HUNDRED
|
|
105
118
|
}
|
|
106
119
|
}
|
|
@@ -109,7 +122,8 @@ function buildSegment (n) {
|
|
|
109
122
|
if (tens > 0) {
|
|
110
123
|
if (tens === 1) {
|
|
111
124
|
result += TEN
|
|
112
|
-
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
113
127
|
result += ONES[tens] + TEN
|
|
114
128
|
}
|
|
115
129
|
}
|
|
@@ -128,11 +142,10 @@ function buildSegment (n) {
|
|
|
128
142
|
|
|
129
143
|
/**
|
|
130
144
|
* Converts a non-negative integer to Japanese words.
|
|
131
|
-
*
|
|
132
145
|
* @param {bigint} n - Non-negative integer to convert
|
|
133
146
|
* @returns {string} Japanese kanji words
|
|
134
147
|
*/
|
|
135
|
-
function integerToWords
|
|
148
|
+
function integerToWords(n) {
|
|
136
149
|
if (n === 0n) return ZERO
|
|
137
150
|
|
|
138
151
|
// Fast path: numbers < 10000
|
|
@@ -149,7 +162,8 @@ function integerToWords (n) {
|
|
|
149
162
|
let result
|
|
150
163
|
if (man === 1) {
|
|
151
164
|
result = '一' + SCALES[0] // 一万
|
|
152
|
-
}
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
153
167
|
result = buildSegment(man) + SCALES[0]
|
|
154
168
|
}
|
|
155
169
|
|
|
@@ -167,11 +181,10 @@ function integerToWords (n) {
|
|
|
167
181
|
/**
|
|
168
182
|
* Builds words for numbers >= 100,000,000.
|
|
169
183
|
* Uses BigInt modulo for 4-digit (myriad) segment extraction.
|
|
170
|
-
*
|
|
171
184
|
* @param {bigint} n - Number >= 100,000,000
|
|
172
185
|
* @returns {string} Japanese kanji words
|
|
173
186
|
*/
|
|
174
|
-
function buildLargeNumberWords
|
|
187
|
+
function buildLargeNumberWords(n) {
|
|
175
188
|
// Extract segments using BigInt modulo (faster than string slicing)
|
|
176
189
|
// Segments stored least-significant first (index 0 = units, 1 = 万, etc.)
|
|
177
190
|
const segments = []
|
|
@@ -192,10 +205,12 @@ function buildLargeNumberWords (n) {
|
|
|
192
205
|
// For scales >= 万, we need 一 before scale word when segment is 1
|
|
193
206
|
if (segment === 1) {
|
|
194
207
|
result += '一' + SCALES[i - 1]
|
|
195
|
-
}
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
196
210
|
result += buildSegment(segment) + SCALES[i - 1]
|
|
197
211
|
}
|
|
198
|
-
}
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
199
214
|
// Units segment (no scale word)
|
|
200
215
|
result += buildSegment(segment)
|
|
201
216
|
}
|
|
@@ -206,18 +221,18 @@ function buildLargeNumberWords (n) {
|
|
|
206
221
|
|
|
207
222
|
/**
|
|
208
223
|
* Converts decimal digits to Japanese words (digit by digit).
|
|
209
|
-
*
|
|
210
224
|
* @param {string} decimalPart - Decimal digits (without the point)
|
|
211
225
|
* @returns {string} Japanese kanji words for decimal part
|
|
212
226
|
*/
|
|
213
|
-
function decimalPartToWords
|
|
227
|
+
function decimalPartToWords(decimalPart) {
|
|
214
228
|
let result = ''
|
|
215
229
|
|
|
216
230
|
for (let i = 0; i < decimalPart.length; i++) {
|
|
217
231
|
const digit = parseInt(decimalPart[i], 10)
|
|
218
232
|
if (digit === 0) {
|
|
219
233
|
result += ZERO
|
|
220
|
-
}
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
221
236
|
result += ONES[digit]
|
|
222
237
|
}
|
|
223
238
|
}
|
|
@@ -230,19 +245,19 @@ function decimalPartToWords (decimalPart) {
|
|
|
230
245
|
*
|
|
231
246
|
* This is the main public API. It accepts any valid numeric input
|
|
232
247
|
* (number, string, or bigint) and handles parsing internally.
|
|
233
|
-
*
|
|
234
248
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
235
249
|
* @returns {string} The number in Japanese kanji words
|
|
236
250
|
* @throws {TypeError} If value is not a valid numeric type
|
|
237
251
|
* @throws {Error} If value is not a valid number format
|
|
238
|
-
*
|
|
239
252
|
* @example
|
|
240
253
|
* toCardinal(42) // '四十二'
|
|
241
254
|
* toCardinal(10000) // '一万'
|
|
242
255
|
* toCardinal(100000000) // '一億'
|
|
243
256
|
*/
|
|
244
|
-
function toCardinal
|
|
257
|
+
function toCardinal(value) {
|
|
245
258
|
const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
|
|
259
|
+
// The fraction is spelled digit by digit, so only the integer part has a ceiling.
|
|
260
|
+
checkMax(integerPart, cardinalMax)
|
|
246
261
|
|
|
247
262
|
let result = ''
|
|
248
263
|
|
|
@@ -267,29 +282,28 @@ function toCardinal (value) {
|
|
|
267
282
|
* Converts a positive integer to Japanese ordinal words.
|
|
268
283
|
*
|
|
269
284
|
* Japanese ordinals: 第 prefix + cardinal number.
|
|
270
|
-
*
|
|
271
285
|
* @param {bigint} n - Positive integer to convert
|
|
272
286
|
* @returns {string} Japanese ordinal words
|
|
273
287
|
*/
|
|
274
|
-
function integerToOrdinal
|
|
288
|
+
function integerToOrdinal(n) {
|
|
275
289
|
return ORDINAL_PREFIX + integerToWords(n)
|
|
276
290
|
}
|
|
277
291
|
|
|
278
292
|
/**
|
|
279
293
|
* Converts a numeric value to Japanese ordinal words.
|
|
280
|
-
*
|
|
281
294
|
* @param {number | string | bigint} value - The numeric value to convert (positive integer)
|
|
282
295
|
* @returns {string} The number as ordinal words
|
|
283
296
|
* @throws {TypeError} If value is not a valid numeric type
|
|
284
297
|
* @throws {RangeError} If value is negative, zero, or has a decimal part
|
|
285
|
-
*
|
|
286
298
|
* @example
|
|
287
299
|
* toOrdinal(1) // '第一'
|
|
288
300
|
* toOrdinal(10) // '第十'
|
|
289
301
|
* toOrdinal(100) // '第百'
|
|
290
302
|
*/
|
|
291
|
-
function toOrdinal
|
|
303
|
+
function toOrdinal(value) {
|
|
292
304
|
const integerPart = parseOrdinalValue(value)
|
|
305
|
+
// Ordinals prefix the cardinal speller, so they share its ceiling.
|
|
306
|
+
checkMax(integerPart, ordinalMax)
|
|
293
307
|
return integerToOrdinal(integerPart)
|
|
294
308
|
}
|
|
295
309
|
|
|
@@ -302,20 +316,19 @@ function toOrdinal (value) {
|
|
|
302
316
|
*
|
|
303
317
|
* Note: Sen (銭, 1/100 yen) is included for completeness but is rarely used
|
|
304
318
|
* in modern Japan. Most transactions are in whole yen.
|
|
305
|
-
*
|
|
306
319
|
* @param {number | string | bigint} value - The currency amount to convert
|
|
307
320
|
* @returns {string} The amount in Japanese currency words
|
|
308
321
|
* @throws {TypeError} If value is not a valid numeric type
|
|
309
322
|
* @throws {Error} If value is not a valid number format
|
|
310
|
-
*
|
|
311
323
|
* @example
|
|
312
324
|
* toCurrency(42) // '四十二円'
|
|
313
325
|
* toCurrency(1) // '一円'
|
|
314
326
|
* toCurrency(0.50) // '五十銭'
|
|
315
327
|
* toCurrency(42.50) // '四十二円五十銭'
|
|
316
328
|
*/
|
|
317
|
-
function toCurrency
|
|
329
|
+
function toCurrency(value) {
|
|
318
330
|
const { isNegative, dollars: yen, cents: sen } = parseCurrencyValue(value)
|
|
331
|
+
checkMax(yen, currencyMax)
|
|
319
332
|
|
|
320
333
|
// Build result
|
|
321
334
|
let result = ''
|
package/src/ka-GE.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
export const cardinalMax: bigint;
|
|
2
|
+
export const ordinalMax: bigint;
|
|
3
|
+
export const currencyMax: bigint;
|
|
1
4
|
/**
|
|
2
5
|
* Converts a numeric value to Georgian words.
|
|
3
6
|
*
|
|
4
7
|
* This is the main public API. It accepts any valid numeric input
|
|
5
8
|
* (number, string, or bigint) and handles parsing internally.
|
|
6
|
-
*
|
|
7
9
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
8
10
|
* @returns {string} The number in Georgian words
|
|
9
11
|
* @throws {TypeError} If value is not a valid numeric type
|
|
10
12
|
* @throws {Error} If value is not a valid number format
|
|
11
|
-
*
|
|
12
13
|
* @example
|
|
13
14
|
* toCardinal(21) // 'ოცდაერთი'
|
|
14
15
|
* toCardinal(100) // 'ასი'
|
|
@@ -17,12 +18,10 @@
|
|
|
17
18
|
export function toCardinal(value: number | string | bigint): string;
|
|
18
19
|
/**
|
|
19
20
|
* Converts a numeric value to Georgian ordinal words.
|
|
20
|
-
*
|
|
21
21
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
22
22
|
* @returns {string} The ordinal in Georgian words
|
|
23
23
|
* @throws {TypeError} If value is not a valid numeric type
|
|
24
24
|
* @throws {Error} If value is not a positive integer
|
|
25
|
-
*
|
|
26
25
|
* @example
|
|
27
26
|
* toOrdinal(1) // 'პირველი'
|
|
28
27
|
* toOrdinal(10) // 'მეათე'
|
|
@@ -31,12 +30,10 @@ export function toCardinal(value: number | string | bigint): string;
|
|
|
31
30
|
export function toOrdinal(value: number | string | bigint): string;
|
|
32
31
|
/**
|
|
33
32
|
* Converts a numeric value to Georgian Lari currency words.
|
|
34
|
-
*
|
|
35
33
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
36
34
|
* @returns {string} The currency in Georgian words
|
|
37
35
|
* @throws {TypeError} If value is not a valid numeric type
|
|
38
36
|
* @throws {Error} If value is not a valid number format
|
|
39
|
-
*
|
|
40
37
|
* @example
|
|
41
38
|
* toCurrency(1) // 'ერთი ლარი'
|
|
42
39
|
* toCurrency(2.50) // 'ორი ლარი ორმოცდაათი თეთრი'
|
package/src/ka-GE.js
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
import { parseCardinalValue } from './utils/parse-cardinal.js'
|
|
16
16
|
import { parseCurrencyValue } from './utils/parse-currency.js'
|
|
17
17
|
import { parseOrdinalValue } from './utils/parse-ordinal.js'
|
|
18
|
+
import { checkMax } from './utils/check-max.js'
|
|
19
|
+
import { western } from './utils/scale.js'
|
|
18
20
|
|
|
19
21
|
// ============================================================================
|
|
20
22
|
// Vocabulary (module-level constants)
|
|
@@ -41,6 +43,13 @@ const THOUSAND_STEM = 'ათას' // Without final vowel
|
|
|
41
43
|
// Scale words (short scale) - indexed by segment position
|
|
42
44
|
const SCALES = ['', '', 'მილიონი', 'მილიარდი', 'ტრილიონი', 'კვადრილიონი', 'კვინტილიონი', 'სექსტილიონი']
|
|
43
45
|
|
|
46
|
+
// 3-digit grouping; the table tops out at sextillion. Past it the builder clamps
|
|
47
|
+
// to the last scale word (`SCALES[scaleIndex] || SCALES[SCALES.length - 1]`),
|
|
48
|
+
// silently collapsing the magnitude — so cap there.
|
|
49
|
+
export const cardinalMax = western(SCALES.length - 1)
|
|
50
|
+
export const ordinalMax = western(SCALES.length - 1)
|
|
51
|
+
export const currencyMax = western(SCALES.length - 1)
|
|
52
|
+
|
|
44
53
|
const ZERO = 'ნული'
|
|
45
54
|
const NEGATIVE = 'მინუს'
|
|
46
55
|
const DECIMAL_SEP = 'მთელი'
|
|
@@ -64,7 +73,7 @@ const TETRI = 'თეთრი'
|
|
|
64
73
|
* @param {number} n - Number 0-99
|
|
65
74
|
* @returns {string} Georgian word
|
|
66
75
|
*/
|
|
67
|
-
function buildTens
|
|
76
|
+
function buildTens(n) {
|
|
68
77
|
if (n < 10) return ONES[n]
|
|
69
78
|
if (n < 20) return TEENS[n - 10]
|
|
70
79
|
|
|
@@ -89,7 +98,7 @@ function buildTens (n) {
|
|
|
89
98
|
* @param {number} n - Number 0-999
|
|
90
99
|
* @returns {{full: string, stem: string}} Georgian words
|
|
91
100
|
*/
|
|
92
|
-
function buildSegment
|
|
101
|
+
function buildSegment(n) {
|
|
93
102
|
if (n === 0) return { full: '', stem: '' }
|
|
94
103
|
if (n < 100) {
|
|
95
104
|
const word = buildTens(n)
|
|
@@ -106,7 +115,8 @@ function buildSegment (n) {
|
|
|
106
115
|
let hundredWord
|
|
107
116
|
if (hundreds === 1) {
|
|
108
117
|
hundredWord = remainder > 0 ? HUNDRED_STEM : HUNDRED
|
|
109
|
-
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
110
120
|
hundredWord = HUNDRED_PREFIXES[hundreds] + (remainder > 0 ? HUNDRED_STEM : HUNDRED)
|
|
111
121
|
}
|
|
112
122
|
|
|
@@ -129,11 +139,10 @@ function buildSegment (n) {
|
|
|
129
139
|
|
|
130
140
|
/**
|
|
131
141
|
* Converts a non-negative integer to Georgian words.
|
|
132
|
-
*
|
|
133
142
|
* @param {bigint} n - Non-negative integer to convert
|
|
134
143
|
* @returns {string} Georgian words
|
|
135
144
|
*/
|
|
136
|
-
function integerToWords
|
|
145
|
+
function integerToWords(n) {
|
|
137
146
|
if (n === 0n) return ZERO
|
|
138
147
|
|
|
139
148
|
// Fast path: numbers < 1000
|
|
@@ -151,7 +160,8 @@ function integerToWords (n) {
|
|
|
151
160
|
if (thousands === 1) {
|
|
152
161
|
// "ათასი" not "ერთი ათასი"
|
|
153
162
|
result = remainder > 0 ? THOUSAND_STEM : THOUSAND
|
|
154
|
-
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
155
165
|
// Use stem form before ათასი
|
|
156
166
|
const { stem: thousandsPart } = buildSegment(thousands)
|
|
157
167
|
result = thousandsPart + ' ' + (remainder > 0 ? THOUSAND_STEM : THOUSAND)
|
|
@@ -171,11 +181,10 @@ function integerToWords (n) {
|
|
|
171
181
|
|
|
172
182
|
/**
|
|
173
183
|
* Builds words for numbers >= 1,000,000.
|
|
174
|
-
*
|
|
175
184
|
* @param {bigint} n - Number >= 1,000,000
|
|
176
185
|
* @returns {string} Georgian words
|
|
177
186
|
*/
|
|
178
|
-
function buildLargeNumberWords
|
|
187
|
+
function buildLargeNumberWords(n) {
|
|
179
188
|
const numStr = n.toString()
|
|
180
189
|
const len = numStr.length
|
|
181
190
|
|
|
@@ -206,23 +215,27 @@ function buildLargeNumberWords (n) {
|
|
|
206
215
|
// Units (no scale)
|
|
207
216
|
const { full } = buildSegment(segment)
|
|
208
217
|
parts.push(full)
|
|
209
|
-
}
|
|
218
|
+
}
|
|
219
|
+
else if (scaleIndex === 1) {
|
|
210
220
|
// Thousands - check if there's a remainder
|
|
211
221
|
const hasRemainder = segments.slice(i + 1).some(s => s !== 0)
|
|
212
222
|
const thousandWord = hasRemainder ? THOUSAND_STEM : THOUSAND
|
|
213
223
|
|
|
214
224
|
if (segment === 1) {
|
|
215
225
|
parts.push(thousandWord)
|
|
216
|
-
}
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
217
228
|
const { stem } = buildSegment(segment)
|
|
218
229
|
parts.push(stem + ' ' + thousandWord)
|
|
219
230
|
}
|
|
220
|
-
}
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
221
233
|
// Million and above
|
|
222
234
|
const scaleWord = SCALES[scaleIndex] || SCALES[SCALES.length - 1]
|
|
223
235
|
if (segment === 1) {
|
|
224
236
|
parts.push('ერთი ' + scaleWord)
|
|
225
|
-
}
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
226
239
|
const { full } = buildSegment(segment)
|
|
227
240
|
parts.push(full + ' ' + scaleWord)
|
|
228
241
|
}
|
|
@@ -237,11 +250,10 @@ function buildLargeNumberWords (n) {
|
|
|
237
250
|
|
|
238
251
|
/**
|
|
239
252
|
* Converts decimal digits to Georgian words.
|
|
240
|
-
*
|
|
241
253
|
* @param {string} decimalPart - Decimal digits (without the point)
|
|
242
254
|
* @returns {string} Georgian words for decimal part
|
|
243
255
|
*/
|
|
244
|
-
function decimalPartToWords
|
|
256
|
+
function decimalPartToWords(decimalPart) {
|
|
245
257
|
let result = ''
|
|
246
258
|
|
|
247
259
|
// Handle leading zeros
|
|
@@ -267,19 +279,20 @@ function decimalPartToWords (decimalPart) {
|
|
|
267
279
|
*
|
|
268
280
|
* This is the main public API. It accepts any valid numeric input
|
|
269
281
|
* (number, string, or bigint) and handles parsing internally.
|
|
270
|
-
*
|
|
271
282
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
272
283
|
* @returns {string} The number in Georgian words
|
|
273
284
|
* @throws {TypeError} If value is not a valid numeric type
|
|
274
285
|
* @throws {Error} If value is not a valid number format
|
|
275
|
-
*
|
|
276
286
|
* @example
|
|
277
287
|
* toCardinal(21) // 'ოცდაერთი'
|
|
278
288
|
* toCardinal(100) // 'ასი'
|
|
279
289
|
* toCardinal(1000) // 'ათასი'
|
|
280
290
|
*/
|
|
281
|
-
function toCardinal
|
|
291
|
+
function toCardinal(value) {
|
|
282
292
|
const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
|
|
293
|
+
// Both the integer part and the decimal's significant digits are spelled via
|
|
294
|
+
// the scale builder, so both must clear the ceiling.
|
|
295
|
+
checkMax(integerPart, cardinalMax, decimalPart)
|
|
283
296
|
|
|
284
297
|
let result = ''
|
|
285
298
|
|
|
@@ -305,11 +318,10 @@ function toCardinal (value) {
|
|
|
305
318
|
* Georgian ordinals are formed by:
|
|
306
319
|
* - 1-9: special forms (პირველი, მეორე, მესამე, etc.)
|
|
307
320
|
* - 10+: მე- prefix + cardinal + -ე suffix
|
|
308
|
-
*
|
|
309
321
|
* @param {bigint} n - Non-negative integer to convert
|
|
310
322
|
* @returns {string} Georgian ordinal words
|
|
311
323
|
*/
|
|
312
|
-
function integerToOrdinal
|
|
324
|
+
function integerToOrdinal(n) {
|
|
313
325
|
if (n === 0n) return ''
|
|
314
326
|
if (n <= 9n) return ORDINAL_ONES[Number(n)]
|
|
315
327
|
|
|
@@ -321,7 +333,8 @@ function integerToOrdinal (n) {
|
|
|
321
333
|
let stem
|
|
322
334
|
if (lastChar === 'ი' || lastChar === 'ა') {
|
|
323
335
|
stem = cardinal.slice(0, -1)
|
|
324
|
-
}
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
325
338
|
stem = cardinal
|
|
326
339
|
}
|
|
327
340
|
|
|
@@ -330,19 +343,19 @@ function integerToOrdinal (n) {
|
|
|
330
343
|
|
|
331
344
|
/**
|
|
332
345
|
* Converts a numeric value to Georgian ordinal words.
|
|
333
|
-
*
|
|
334
346
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
335
347
|
* @returns {string} The ordinal in Georgian words
|
|
336
348
|
* @throws {TypeError} If value is not a valid numeric type
|
|
337
349
|
* @throws {Error} If value is not a positive integer
|
|
338
|
-
*
|
|
339
350
|
* @example
|
|
340
351
|
* toOrdinal(1) // 'პირველი'
|
|
341
352
|
* toOrdinal(10) // 'მეათე'
|
|
342
353
|
* toOrdinal(21) // 'მეოცდაერთე'
|
|
343
354
|
*/
|
|
344
|
-
function toOrdinal
|
|
355
|
+
function toOrdinal(value) {
|
|
345
356
|
const n = parseOrdinalValue(value)
|
|
357
|
+
// Ordinals build on the cardinal speller, so they share its ceiling.
|
|
358
|
+
checkMax(n, ordinalMax)
|
|
346
359
|
return integerToOrdinal(n)
|
|
347
360
|
}
|
|
348
361
|
|
|
@@ -352,18 +365,17 @@ function toOrdinal (value) {
|
|
|
352
365
|
|
|
353
366
|
/**
|
|
354
367
|
* Converts a numeric value to Georgian Lari currency words.
|
|
355
|
-
*
|
|
356
368
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
357
369
|
* @returns {string} The currency in Georgian words
|
|
358
370
|
* @throws {TypeError} If value is not a valid numeric type
|
|
359
371
|
* @throws {Error} If value is not a valid number format
|
|
360
|
-
*
|
|
361
372
|
* @example
|
|
362
373
|
* toCurrency(1) // 'ერთი ლარი'
|
|
363
374
|
* toCurrency(2.50) // 'ორი ლარი ორმოცდაათი თეთრი'
|
|
364
375
|
*/
|
|
365
|
-
function toCurrency
|
|
376
|
+
function toCurrency(value) {
|
|
366
377
|
const { isNegative, dollars, cents } = parseCurrencyValue(value)
|
|
378
|
+
checkMax(dollars, currencyMax)
|
|
367
379
|
|
|
368
380
|
const parts = []
|
|
369
381
|
|
package/src/kn-IN.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 Kannada words.
|
|
3
|
-
*
|
|
4
6
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
7
|
* @returns {string} The number in Kannada words
|
|
6
8
|
*/
|
|
7
9
|
export function toCardinal(value: number | string | bigint): string;
|
|
8
10
|
/**
|
|
9
11
|
* Converts a numeric value to Kannada 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) // 'ಎರಡನೇ'
|
|
@@ -21,12 +21,10 @@ export function toCardinal(value: number | string | bigint): string;
|
|
|
21
21
|
export function toOrdinal(value: number | string | bigint): string;
|
|
22
22
|
/**
|
|
23
23
|
* Converts a numeric value to Kannada currency words (Indian Rupee).
|
|
24
|
-
*
|
|
25
24
|
* @param {number | string | bigint} value - The currency amount to convert
|
|
26
25
|
* @returns {string} The amount in Kannada currency words
|
|
27
26
|
* @throws {TypeError} If value is not a valid numeric type
|
|
28
27
|
* @throws {Error} If value is not a valid number format
|
|
29
|
-
*
|
|
30
28
|
* @example
|
|
31
29
|
* toCurrency(42.50) // 'ನಲವತ್ತೆರಡು ರೂಪಾಯಿಗಳು ಐವತ್ತು ಪೈಸೆಗಳು'
|
|
32
30
|
* toCurrency(1) // 'ಒಂದು ರೂಪಾಯಿ'
|
package/src/kn-IN.js
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
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 { checkMax } from './utils/check-max.js'
|
|
18
|
+
import { indian } from './utils/scale.js'
|
|
17
19
|
|
|
18
20
|
// ============================================================================
|
|
19
21
|
// Vocabulary
|
|
@@ -56,20 +58,29 @@ const BELOW_HUNDRED = [
|
|
|
56
58
|
'ಅರವತ್ತು', 'ಅರವತ್ತೊಂದು', 'ಅರವತ್ತೆರಡು', 'ಅರವತ್ತಮೂರು', 'ಅರವತ್ತನಾಲ್ಕು', 'ಅರವತ್ತೈದು', 'ಅರವತ್ತಾರು', 'ಅರವತ್ತೇಳು', 'ಅರವತ್ತೆಂಟು', 'ಅರವತ್ತೊಂಬತ್ತು',
|
|
57
59
|
'ಎಪ್ಪತ್ತು', 'ಎಪ್ಪತ್ತೊಂದು', 'ಎಪ್ಪತ್ತೆರಡು', 'ಎಪ್ಪತ್ತಮೂರು', 'ಎಪ್ಪತ್ತನಾಲ್ಕು', 'ಎಪ್ಪತ್ತೈದು', 'ಎಪ್ಪತ್ತಾರು', 'ಎಪ್ಪತ್ತೇಳು', 'ಎಪ್ಪತ್ತೆಂಟು', 'ಎಪ್ಪತ್ತೊಂಬತ್ತು',
|
|
58
60
|
'ಎಂಬತ್ತು', 'ಎಂಬತ್ತೊಂದು', 'ಎಂಬತ್ತೆರಡು', 'ಎಂಬತ್ತಮೂರು', 'ಎಂಬತ್ತನಾಲ್ಕು', 'ಎಂಬತ್ತೈದು', 'ಎಂಬತ್ತಾರು', 'ಎಂಬತ್ತೇಳು', 'ಎಂಬತ್ತೆಂಟು', 'ಎಂಬತ್ತೊಂಬತ್ತು',
|
|
59
|
-
'ತೊಂಬತ್ತು', 'ತೊಂಬತ್ತೊಂದು', 'ತೊಂಬತ್ತೆರಡು', 'ತೊಂಬತ್ತಮೂರು', 'ತೊಂಬತ್ತನಾಲ್ಕು', 'ತೊಂಬತ್ತೈದು', 'ತೊಂಬತ್ತಾರು', 'ತೊಂಬತ್ತೇಳು', 'ತೊಂಬತ್ತೆಂಟು', 'ತೊಂಬತ್ತೊಂಬತ್ತು'
|
|
61
|
+
'ತೊಂಬತ್ತು', 'ತೊಂಬತ್ತೊಂದು', 'ತೊಂಬತ್ತೆರಡು', 'ತೊಂಬತ್ತಮೂರು', 'ತೊಂಬತ್ತನಾಲ್ಕು', 'ತೊಂಬತ್ತೈದು', 'ತೊಂಬತ್ತಾರು', 'ತೊಂಬತ್ತೇಳು', 'ತೊಂಬತ್ತೆಂಟು', 'ತೊಂಬತ್ತೊಂಬತ್ತು',
|
|
60
62
|
]
|
|
61
63
|
|
|
62
64
|
// Scale words: index 0 = units (empty), 1 = thousand, 2 = lakh, 3 = crore, etc.
|
|
63
65
|
const SCALE_WORDS = ['', 'ಸಾವಿರ', 'ಲಕ್ಷ', 'ಕೋಟಿ', 'ಅಬ್ಜ', 'ಖರ್ವ', 'ನೀಲ', 'ಪದ್ಮ', 'ಶಂಖ']
|
|
64
66
|
|
|
67
|
+
// 3-2-2 Indian grouping: a 3-digit base segment, then 2 digits per scale word
|
|
68
|
+
// (SCALE_WORDS[0] = '' is the units slot). Past the table the scale word is
|
|
69
|
+
// dropped, which collapses the magnitude — so cap there.
|
|
70
|
+
export const cardinalMax = indian(SCALE_WORDS.length)
|
|
71
|
+
export const ordinalMax = indian(SCALE_WORDS.length)
|
|
72
|
+
export const currencyMax = indian(SCALE_WORDS.length)
|
|
73
|
+
|
|
65
74
|
// ============================================================================
|
|
66
75
|
// Segment Building
|
|
67
76
|
// ============================================================================
|
|
68
77
|
|
|
69
78
|
/**
|
|
70
79
|
* Builds words for a 0-999 segment.
|
|
80
|
+
* @param {number} n - Segment value (0-999)
|
|
81
|
+
* @returns {string} Kannada words for the segment
|
|
71
82
|
*/
|
|
72
|
-
function buildSegment
|
|
83
|
+
function buildSegment(n) {
|
|
73
84
|
if (n === 0) return ''
|
|
74
85
|
if (n < 100) return BELOW_HUNDRED[n]
|
|
75
86
|
|
|
@@ -91,11 +102,10 @@ function buildSegment (n) {
|
|
|
91
102
|
*
|
|
92
103
|
* Uses BigInt modulo for segment extraction (faster than string slicing).
|
|
93
104
|
* South Asian 3-2-2 grouping: first 3 digits, then groups of 2.
|
|
94
|
-
*
|
|
95
105
|
* @param {bigint} n - Non-negative integer to convert
|
|
96
106
|
* @returns {string} Kannada words
|
|
97
107
|
*/
|
|
98
|
-
function integerToWords
|
|
108
|
+
function integerToWords(n) {
|
|
99
109
|
if (n === 0n) return ZERO
|
|
100
110
|
|
|
101
111
|
// Fast path: numbers < 1000 (direct lookup)
|
|
@@ -121,7 +131,8 @@ function integerToWords (n) {
|
|
|
121
131
|
|
|
122
132
|
if (i === 0) {
|
|
123
133
|
words.push(buildSegment(segment))
|
|
124
|
-
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
125
136
|
words.push(BELOW_HUNDRED[segment])
|
|
126
137
|
}
|
|
127
138
|
|
|
@@ -133,7 +144,12 @@ function integerToWords (n) {
|
|
|
133
144
|
return words.join(' ')
|
|
134
145
|
}
|
|
135
146
|
|
|
136
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Reads a decimal-fraction digit string per-digit in Kannada.
|
|
149
|
+
* @param {string} decimalPart - Digits after the decimal separator
|
|
150
|
+
* @returns {string} Per-digit Kannada words
|
|
151
|
+
*/
|
|
152
|
+
function decimalPartToWords(decimalPart) {
|
|
137
153
|
// Per-digit decimal reading
|
|
138
154
|
const digits = []
|
|
139
155
|
for (const char of decimalPart) {
|
|
@@ -145,12 +161,13 @@ function decimalPartToWords (decimalPart) {
|
|
|
145
161
|
|
|
146
162
|
/**
|
|
147
163
|
* Converts a numeric value to Kannada words.
|
|
148
|
-
*
|
|
149
164
|
* @param {number | string | bigint} value - The numeric value to convert
|
|
150
165
|
* @returns {string} The number in Kannada words
|
|
151
166
|
*/
|
|
152
|
-
function toCardinal
|
|
167
|
+
function toCardinal(value) {
|
|
153
168
|
const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
|
|
169
|
+
// The fraction is spelled digit by digit, so only the integer part has a ceiling.
|
|
170
|
+
checkMax(integerPart, cardinalMax)
|
|
154
171
|
|
|
155
172
|
let result = ''
|
|
156
173
|
|
|
@@ -175,11 +192,10 @@ function toCardinal (value) {
|
|
|
175
192
|
* Converts a positive integer to Kannada ordinal words.
|
|
176
193
|
*
|
|
177
194
|
* Kannada ordinals: First 6 are special, then add -ನೇ suffix.
|
|
178
|
-
*
|
|
179
195
|
* @param {bigint} n - Positive integer to convert
|
|
180
196
|
* @returns {string} Kannada ordinal words
|
|
181
197
|
*/
|
|
182
|
-
function integerToOrdinal
|
|
198
|
+
function integerToOrdinal(n) {
|
|
183
199
|
// Special ordinals for 1-6
|
|
184
200
|
if (n >= 1n && n <= 6n) {
|
|
185
201
|
return ORDINAL_SPECIAL[Number(n)]
|
|
@@ -192,19 +208,19 @@ function integerToOrdinal (n) {
|
|
|
192
208
|
|
|
193
209
|
/**
|
|
194
210
|
* Converts a numeric value to Kannada ordinal words.
|
|
195
|
-
*
|
|
196
211
|
* @param {number | string | bigint} value - The numeric value to convert (positive integer)
|
|
197
212
|
* @returns {string} The number as ordinal words
|
|
198
213
|
* @throws {TypeError} If value is not a valid numeric type
|
|
199
214
|
* @throws {RangeError} If value is negative, zero, or has a decimal part
|
|
200
|
-
*
|
|
201
215
|
* @example
|
|
202
216
|
* toOrdinal(1) // 'ಮೊದಲನೇ'
|
|
203
217
|
* toOrdinal(2) // 'ಎರಡನೇ'
|
|
204
218
|
* toOrdinal(10) // 'ಹತ್ತುನೇ'
|
|
205
219
|
*/
|
|
206
|
-
function toOrdinal
|
|
220
|
+
function toOrdinal(value) {
|
|
207
221
|
const integerPart = parseOrdinalValue(value)
|
|
222
|
+
// Ordinals build on the cardinal speller, so they share its ceiling.
|
|
223
|
+
checkMax(integerPart, ordinalMax)
|
|
208
224
|
return integerToOrdinal(integerPart)
|
|
209
225
|
}
|
|
210
226
|
|
|
@@ -214,19 +230,18 @@ function toOrdinal (value) {
|
|
|
214
230
|
|
|
215
231
|
/**
|
|
216
232
|
* Converts a numeric value to Kannada currency words (Indian Rupee).
|
|
217
|
-
*
|
|
218
233
|
* @param {number | string | bigint} value - The currency amount to convert
|
|
219
234
|
* @returns {string} The amount in Kannada currency words
|
|
220
235
|
* @throws {TypeError} If value is not a valid numeric type
|
|
221
236
|
* @throws {Error} If value is not a valid number format
|
|
222
|
-
*
|
|
223
237
|
* @example
|
|
224
238
|
* toCurrency(42.50) // 'ನಲವತ್ತೆರಡು ರೂಪಾಯಿಗಳು ಐವತ್ತು ಪೈಸೆಗಳು'
|
|
225
239
|
* toCurrency(1) // 'ಒಂದು ರೂಪಾಯಿ'
|
|
226
240
|
* toCurrency(0.01) // 'ಒಂದು ಪೈಸೆ'
|
|
227
241
|
*/
|
|
228
|
-
function toCurrency
|
|
242
|
+
function toCurrency(value) {
|
|
229
243
|
const { isNegative, dollars: rupees, cents: paise } = parseCurrencyValue(value)
|
|
244
|
+
checkMax(rupees, currencyMax)
|
|
230
245
|
|
|
231
246
|
// Build result
|
|
232
247
|
let result = ''
|