n2words 1.24.0 → 3.0.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 +49 -0
- package/README.md +183 -156
- package/dist/languages/am-Latn.js +3 -0
- package/dist/languages/am-Latn.js.map +1 -0
- package/dist/languages/am.js +3 -0
- package/dist/languages/am.js.map +1 -0
- package/dist/languages/ar.js +3 -2
- package/dist/languages/ar.js.map +1 -1
- package/dist/languages/az.js +3 -2
- package/dist/languages/az.js.map +1 -1
- package/dist/languages/bn.js +3 -2
- package/dist/languages/bn.js.map +1 -1
- package/dist/languages/cs.js +3 -2
- package/dist/languages/cs.js.map +1 -1
- package/dist/languages/da.js +3 -2
- package/dist/languages/da.js.map +1 -1
- package/dist/languages/de.js +3 -2
- package/dist/languages/de.js.map +1 -1
- package/dist/languages/el.js +3 -2
- package/dist/languages/el.js.map +1 -1
- package/dist/languages/en.js +3 -2
- package/dist/languages/en.js.map +1 -1
- package/dist/languages/es.js +3 -2
- package/dist/languages/es.js.map +1 -1
- package/dist/languages/fa.js +3 -2
- package/dist/languages/fa.js.map +1 -1
- package/dist/languages/fi.js +3 -0
- package/dist/languages/fi.js.map +1 -0
- package/dist/languages/fil.js +3 -2
- package/dist/languages/fil.js.map +1 -1
- package/dist/languages/fr-BE.js +3 -2
- package/dist/languages/fr-BE.js.map +1 -1
- package/dist/languages/fr.js +3 -2
- package/dist/languages/fr.js.map +1 -1
- package/dist/languages/gu.js +3 -2
- package/dist/languages/gu.js.map +1 -1
- package/dist/languages/ha.js +3 -0
- package/dist/languages/ha.js.map +1 -0
- package/dist/languages/hbo.js +3 -0
- package/dist/languages/hbo.js.map +1 -0
- package/dist/languages/he.js +3 -2
- package/dist/languages/he.js.map +1 -1
- package/dist/languages/hi.js +3 -2
- package/dist/languages/hi.js.map +1 -1
- package/dist/languages/hr.js +3 -2
- package/dist/languages/hr.js.map +1 -1
- package/dist/languages/hu.js +3 -2
- package/dist/languages/hu.js.map +1 -1
- package/dist/languages/id.js +3 -2
- package/dist/languages/id.js.map +1 -1
- package/dist/languages/it.js +3 -2
- package/dist/languages/it.js.map +1 -1
- package/dist/languages/ja.js +3 -2
- package/dist/languages/ja.js.map +1 -1
- package/dist/languages/kn.js +3 -2
- package/dist/languages/kn.js.map +1 -1
- package/dist/languages/ko.js +3 -2
- package/dist/languages/ko.js.map +1 -1
- package/dist/languages/lt.js +3 -2
- package/dist/languages/lt.js.map +1 -1
- package/dist/languages/lv.js +3 -2
- package/dist/languages/lv.js.map +1 -1
- package/dist/languages/mr.js +3 -2
- package/dist/languages/mr.js.map +1 -1
- package/dist/languages/ms.js +3 -2
- package/dist/languages/ms.js.map +1 -1
- package/dist/languages/nb.js +3 -2
- package/dist/languages/nb.js.map +1 -1
- package/dist/languages/nl.js +3 -2
- package/dist/languages/nl.js.map +1 -1
- package/dist/languages/pa.js +3 -0
- package/dist/languages/pa.js.map +1 -0
- package/dist/languages/pl.js +3 -2
- package/dist/languages/pl.js.map +1 -1
- package/dist/languages/pt.js +3 -2
- package/dist/languages/pt.js.map +1 -1
- package/dist/languages/ro.js +3 -2
- package/dist/languages/ro.js.map +1 -1
- package/dist/languages/ru.js +3 -2
- package/dist/languages/ru.js.map +1 -1
- package/dist/languages/sr-Cyrl.js +3 -0
- package/dist/languages/sr-Cyrl.js.map +1 -0
- package/dist/languages/sr-Latn.js +3 -2
- package/dist/languages/sr-Latn.js.map +1 -1
- package/dist/languages/sv.js +3 -2
- package/dist/languages/sv.js.map +1 -1
- package/dist/languages/sw.js +3 -2
- package/dist/languages/sw.js.map +1 -1
- package/dist/languages/ta.js +3 -2
- package/dist/languages/ta.js.map +1 -1
- package/dist/languages/te.js +3 -2
- package/dist/languages/te.js.map +1 -1
- package/dist/languages/th.js +3 -2
- package/dist/languages/th.js.map +1 -1
- package/dist/languages/tr.js +3 -2
- package/dist/languages/tr.js.map +1 -1
- package/dist/languages/uk.js +3 -2
- package/dist/languages/uk.js.map +1 -1
- package/dist/languages/ur.js +3 -2
- package/dist/languages/ur.js.map +1 -1
- package/dist/languages/vi.js +3 -2
- package/dist/languages/vi.js.map +1 -1
- package/dist/languages/zh-Hans.js +3 -2
- package/dist/languages/zh-Hans.js.map +1 -1
- package/dist/languages/zh-Hant.js +3 -0
- package/dist/languages/zh-Hant.js.map +1 -0
- package/dist/n2words.js +3 -2
- package/dist/n2words.js.map +1 -1
- package/lib/languages/am-Latn.d.ts +7 -0
- package/lib/languages/am-Latn.js +164 -0
- package/lib/languages/am.d.ts +7 -0
- package/lib/languages/am.js +164 -0
- package/lib/languages/ar.d.ts +17 -0
- package/lib/languages/ar.js +171 -209
- package/lib/languages/az.d.ts +7 -0
- package/lib/languages/az.js +167 -49
- package/lib/languages/bn.d.ts +7 -0
- package/lib/languages/bn.js +142 -123
- package/lib/languages/cs.d.ts +18 -0
- package/lib/languages/cs.js +303 -176
- package/lib/languages/da.d.ts +14 -0
- package/lib/languages/da.js +267 -139
- package/lib/languages/de.d.ts +17 -0
- package/lib/languages/de.js +310 -113
- package/lib/languages/el.d.ts +14 -0
- package/lib/languages/el.js +225 -98
- package/lib/languages/en.d.ts +17 -0
- package/lib/languages/en.js +235 -102
- package/lib/languages/es.d.ts +21 -0
- package/lib/languages/es.js +307 -125
- package/lib/languages/fa.d.ts +7 -0
- package/lib/languages/fa.js +115 -108
- package/lib/languages/fi.d.ts +14 -0
- package/lib/languages/fi.js +245 -0
- package/lib/languages/fil.d.ts +7 -0
- package/lib/languages/fil.js +199 -139
- package/lib/languages/fr-BE.d.ts +11 -0
- package/lib/languages/fr-BE.js +287 -48
- package/lib/languages/fr.d.ts +21 -0
- package/lib/languages/fr.js +343 -119
- package/lib/languages/gu.d.ts +7 -0
- package/lib/languages/gu.js +125 -144
- package/lib/languages/ha.d.ts +7 -0
- package/lib/languages/ha.js +230 -0
- package/lib/languages/hbo.d.ts +13 -0
- package/lib/languages/hbo.js +300 -0
- package/lib/languages/he.d.ts +13 -0
- package/lib/languages/he.js +230 -283
- package/lib/languages/hi.d.ts +7 -0
- package/lib/languages/hi.js +142 -123
- package/lib/languages/hr.d.ts +11 -0
- package/lib/languages/hr.js +190 -129
- package/lib/languages/hu.d.ts +7 -0
- package/lib/languages/hu.js +194 -133
- package/lib/languages/id.d.ts +7 -0
- package/lib/languages/id.js +167 -140
- package/lib/languages/it.d.ts +19 -0
- package/lib/languages/it.js +337 -108
- package/lib/languages/ja.d.ts +17 -0
- package/lib/languages/ja.js +224 -155
- package/lib/languages/kn.d.ts +7 -0
- package/lib/languages/kn.js +128 -62
- package/lib/languages/ko.d.ts +14 -0
- package/lib/languages/ko.js +250 -70
- package/lib/languages/lt.d.ts +18 -0
- package/lib/languages/lt.js +287 -148
- package/lib/languages/lv.d.ts +18 -0
- package/lib/languages/lv.js +291 -123
- package/lib/languages/mr.d.ts +7 -0
- package/lib/languages/mr.js +125 -144
- package/lib/languages/ms.d.ts +7 -0
- package/lib/languages/ms.js +171 -112
- package/lib/languages/nb.d.ts +14 -0
- package/lib/languages/nb.js +275 -100
- package/lib/languages/nl.d.ts +26 -0
- package/lib/languages/nl.js +307 -174
- package/lib/languages/pa.d.ts +7 -0
- package/lib/languages/pa.js +163 -0
- package/lib/languages/pl.d.ts +22 -0
- package/lib/languages/pl.js +299 -158
- package/lib/languages/pt.d.ts +17 -0
- package/lib/languages/pt.js +279 -120
- package/lib/languages/ro.d.ts +18 -0
- package/lib/languages/ro.js +214 -337
- package/lib/languages/ru.d.ts +11 -0
- package/lib/languages/ru.js +219 -95
- package/lib/languages/sr-Cyrl.d.ts +11 -0
- package/lib/languages/sr-Cyrl.js +215 -0
- package/lib/languages/sr-Latn.d.ts +11 -0
- package/lib/languages/sr-Latn.js +190 -132
- package/lib/languages/sv.d.ts +14 -0
- package/lib/languages/sv.js +280 -103
- package/lib/languages/sw.d.ts +7 -0
- package/lib/languages/sw.js +135 -103
- package/lib/languages/ta.d.ts +7 -0
- package/lib/languages/ta.js +133 -205
- package/lib/languages/te.d.ts +7 -0
- package/lib/languages/te.js +148 -213
- package/lib/languages/th.d.ts +7 -0
- package/lib/languages/th.js +139 -101
- package/lib/languages/tr.d.ts +18 -0
- package/lib/languages/tr.js +246 -66
- package/lib/languages/uk.d.ts +11 -0
- package/lib/languages/uk.js +197 -101
- package/lib/languages/ur.d.ts +7 -0
- package/lib/languages/ur.js +160 -123
- package/lib/languages/vi.d.ts +17 -0
- package/lib/languages/vi.js +287 -164
- package/lib/languages/zh-Hans.d.ts +11 -0
- package/lib/languages/zh-Hans.js +159 -142
- package/lib/languages/zh-Hant.d.ts +11 -0
- package/lib/languages/zh-Hant.js +202 -0
- package/lib/n2words.d.ts +53 -0
- package/lib/n2words.js +91 -227
- package/lib/utils/is-plain-object.d.ts +13 -0
- package/lib/utils/is-plain-object.js +17 -0
- package/lib/utils/parse-numeric.d.ts +17 -0
- package/lib/utils/parse-numeric.js +108 -0
- package/lib/utils/validate-options.d.ts +8 -0
- package/lib/utils/validate-options.js +16 -0
- package/package.json +118 -67
- package/dist/languages/pa-Guru.js +0 -2
- package/dist/languages/pa-Guru.js.map +0 -1
- package/lib/classes/abstract-language.js +0 -261
- package/lib/classes/greedy-scale-language.js +0 -195
- package/lib/classes/slavic-language.js +0 -251
- package/lib/classes/south-asian-language.js +0 -161
- package/lib/classes/turkic-language.js +0 -63
- package/lib/languages/pa-Guru.js +0 -126
- package/typings/classes/abstract-language.d.ts +0 -144
- package/typings/classes/greedy-scale-language.d.ts +0 -148
- package/typings/classes/slavic-language.d.ts +0 -145
- package/typings/classes/south-asian-language.d.ts +0 -101
- package/typings/classes/turkic-language.d.ts +0 -42
- package/typings/languages/ar.d.ts +0 -93
- package/typings/languages/az.d.ts +0 -25
- package/typings/languages/bn.d.ts +0 -1
- package/typings/languages/cs.d.ts +0 -120
- package/typings/languages/da.d.ts +0 -53
- package/typings/languages/de.d.ts +0 -26
- package/typings/languages/el.d.ts +0 -11
- package/typings/languages/en.d.ts +0 -30
- package/typings/languages/es.d.ts +0 -43
- package/typings/languages/fa.d.ts +0 -81
- package/typings/languages/fil.d.ts +0 -12
- package/typings/languages/fr-BE.d.ts +0 -41
- package/typings/languages/fr.d.ts +0 -43
- package/typings/languages/gu.d.ts +0 -12
- package/typings/languages/he.d.ts +0 -197
- package/typings/languages/hi.d.ts +0 -1
- package/typings/languages/hr.d.ts +0 -110
- package/typings/languages/hu.d.ts +0 -37
- package/typings/languages/id.d.ts +0 -69
- package/typings/languages/it.d.ts +0 -51
- package/typings/languages/ja.d.ts +0 -58
- package/typings/languages/kn.d.ts +0 -11
- package/typings/languages/ko.d.ts +0 -25
- package/typings/languages/lt.d.ts +0 -110
- package/typings/languages/lv.d.ts +0 -99
- package/typings/languages/mr.d.ts +0 -12
- package/typings/languages/ms.d.ts +0 -37
- package/typings/languages/nb.d.ts +0 -27
- package/typings/languages/nl.d.ts +0 -65
- package/typings/languages/pa-Guru.d.ts +0 -1
- package/typings/languages/pl.d.ts +0 -116
- package/typings/languages/pt.d.ts +0 -39
- package/typings/languages/ro.d.ts +0 -229
- package/typings/languages/ru.d.ts +0 -108
- package/typings/languages/sr-Latn.d.ts +0 -98
- package/typings/languages/sv.d.ts +0 -30
- package/typings/languages/sw.d.ts +0 -1
- package/typings/languages/ta.d.ts +0 -1
- package/typings/languages/te.d.ts +0 -1
- package/typings/languages/th.d.ts +0 -1
- package/typings/languages/tr.d.ts +0 -46
- package/typings/languages/uk.d.ts +0 -117
- package/typings/languages/ur.d.ts +0 -1
- package/typings/languages/vi.d.ts +0 -116
- package/typings/languages/zh-Hans.d.ts +0 -57
- package/typings/n2words.d.ts +0 -177
package/lib/languages/ko.js
CHANGED
|
@@ -1,83 +1,263 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Korean language converter - Functional Implementation
|
|
3
|
+
*
|
|
4
|
+
* A performance-optimized number-to-words converter using precomputed lookup tables.
|
|
5
|
+
*
|
|
6
|
+
* Key features:
|
|
7
|
+
* - Myriad-based (만) grouping - 4 digits
|
|
8
|
+
* - Implicit '일' (one) omission before scale words
|
|
9
|
+
* - Space separation after 만+ scales
|
|
10
|
+
* - Hangul numerals
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Vocabulary (module-level constants)
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
const ONES = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구']
|
|
20
|
+
|
|
21
|
+
const TEN = '십'
|
|
22
|
+
const HUNDRED = '백'
|
|
23
|
+
const THOUSAND = '천'
|
|
24
|
+
|
|
25
|
+
const ZERO = '영'
|
|
26
|
+
const NEGATIVE = '마이너스'
|
|
27
|
+
const DECIMAL_SEP = '점'
|
|
28
|
+
|
|
29
|
+
// Myriad scale words (powers of 10,000)
|
|
30
|
+
// 만 (10^4), 억 (10^8), 조 (10^12), 경 (10^16), etc.
|
|
31
|
+
const SCALES = ['만', '억', '조', '경', '해', '자', '양']
|
|
32
|
+
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// Precomputed Lookup Tables (built once at module load)
|
|
35
|
+
// ============================================================================
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Builds segment word for 0-9999 (4-digit myriad segment).
|
|
39
|
+
* Korean omits "일" before 십, 백, 천.
|
|
40
|
+
*/
|
|
41
|
+
function buildSegment (n) {
|
|
42
|
+
if (n === 0) return ''
|
|
43
|
+
|
|
44
|
+
const ones = n % 10
|
|
45
|
+
const tens = Math.floor(n / 10) % 10
|
|
46
|
+
const hundreds = Math.floor(n / 100) % 10
|
|
47
|
+
const thousands = Math.floor(n / 1000)
|
|
48
|
+
|
|
49
|
+
let result = ''
|
|
50
|
+
|
|
51
|
+
// Thousands
|
|
52
|
+
if (thousands > 0) {
|
|
53
|
+
if (thousands === 1) {
|
|
54
|
+
result += THOUSAND
|
|
55
|
+
} else {
|
|
56
|
+
result += ONES[thousands] + THOUSAND
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Hundreds
|
|
61
|
+
if (hundreds > 0) {
|
|
62
|
+
if (hundreds === 1) {
|
|
63
|
+
result += HUNDRED
|
|
64
|
+
} else {
|
|
65
|
+
result += ONES[hundreds] + HUNDRED
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Tens
|
|
70
|
+
if (tens > 0) {
|
|
71
|
+
if (tens === 1) {
|
|
72
|
+
result += TEN
|
|
73
|
+
} else {
|
|
74
|
+
result += ONES[tens] + TEN
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Ones
|
|
79
|
+
if (ones > 0) {
|
|
80
|
+
result += ONES[ones]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return result
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Precompute all 10000 segment words (0-9999) for myriad grouping
|
|
87
|
+
const SEGMENTS = new Array(10000)
|
|
88
|
+
|
|
89
|
+
for (let i = 0; i < 10000; i++) {
|
|
90
|
+
SEGMENTS[i] = buildSegment(i)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Conversion Functions
|
|
95
|
+
// ============================================================================
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Converts a non-negative integer to Korean words.
|
|
99
|
+
*
|
|
100
|
+
* @param {bigint} n - Non-negative integer to convert
|
|
101
|
+
* @returns {string} Korean words
|
|
102
|
+
*/
|
|
103
|
+
function integerToWords (n) {
|
|
104
|
+
if (n === 0n) return ZERO
|
|
105
|
+
|
|
106
|
+
// Fast path: numbers < 10000 (direct lookup)
|
|
107
|
+
if (n < 10000n) {
|
|
108
|
+
return SEGMENTS[Number(n)]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// For numbers >= 10000, use myriad decomposition
|
|
112
|
+
return buildLargeNumberWords(n)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Builds words for numbers >= 10000.
|
|
117
|
+
* Uses myriad (만) grouping - 4 digits per segment.
|
|
118
|
+
*
|
|
119
|
+
* @param {bigint} n - Number >= 10000
|
|
120
|
+
* @returns {string} Korean words
|
|
121
|
+
*/
|
|
122
|
+
function buildLargeNumberWords (n) {
|
|
123
|
+
const numStr = n.toString()
|
|
124
|
+
const len = numStr.length
|
|
125
|
+
|
|
126
|
+
// Build segments of 4 digits from right to left
|
|
127
|
+
const segments = []
|
|
128
|
+
const segmentSize = 4
|
|
129
|
+
|
|
130
|
+
const remainderLen = len % segmentSize
|
|
131
|
+
let pos = 0
|
|
132
|
+
if (remainderLen > 0) {
|
|
133
|
+
segments.push(Number(numStr.slice(0, remainderLen)))
|
|
134
|
+
pos = remainderLen
|
|
135
|
+
}
|
|
136
|
+
while (pos < len) {
|
|
137
|
+
segments.push(Number(numStr.slice(pos, pos + segmentSize)))
|
|
138
|
+
pos += segmentSize
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Convert segments to words
|
|
142
|
+
const parts = []
|
|
143
|
+
let scaleIndex = segments.length - 1
|
|
144
|
+
|
|
145
|
+
for (let i = 0; i < segments.length; i++) {
|
|
146
|
+
const segment = segments[i]
|
|
147
|
+
|
|
148
|
+
if (segment !== 0) {
|
|
149
|
+
if (scaleIndex === 0) {
|
|
150
|
+
// Units segment (no scale word)
|
|
151
|
+
parts.push({ word: SEGMENTS[segment], isScale: false })
|
|
152
|
+
} else {
|
|
153
|
+
// Segment with scale word
|
|
154
|
+
const scaleWord = SCALES[scaleIndex - 1]
|
|
155
|
+
|
|
156
|
+
// Korean omits segment when it's 1 before scale words
|
|
157
|
+
if (segment === 1) {
|
|
158
|
+
parts.push({ word: scaleWord, isScale: true })
|
|
159
|
+
} else {
|
|
160
|
+
parts.push({ word: SEGMENTS[segment], isScale: false })
|
|
161
|
+
parts.push({ word: scaleWord, isScale: true })
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
scaleIndex--
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Join with Korean spacing rules
|
|
170
|
+
return joinKoreanParts(parts)
|
|
171
|
+
}
|
|
2
172
|
|
|
3
173
|
/**
|
|
4
|
-
* Korean
|
|
174
|
+
* Joins parts with Korean spacing rules.
|
|
175
|
+
* - Concatenate without spaces within segments
|
|
176
|
+
* - Space after scale words before next number
|
|
5
177
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - Concatenated for smaller numbers
|
|
9
|
-
* - Omits '일' (1) before multipliers
|
|
178
|
+
* @param {Array} parts - Parts with isScale metadata
|
|
179
|
+
* @returns {string} Joined string
|
|
10
180
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
[
|
|
19
|
-
[
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
[8n, '팔'],
|
|
28
|
-
[7n, '칠'],
|
|
29
|
-
[6n, '육'],
|
|
30
|
-
[5n, '오'],
|
|
31
|
-
[4n, '사'],
|
|
32
|
-
[3n, '삼'],
|
|
33
|
-
[2n, '이'],
|
|
34
|
-
[1n, '일'],
|
|
35
|
-
[0n, '영']
|
|
36
|
-
]
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Merges two adjacent word-number pairs according to Korean grammar rules.
|
|
40
|
-
*
|
|
41
|
-
* Korean-specific rules:
|
|
42
|
-
* - Omits '일' (1) before multipliers <= 만 (10,000)
|
|
43
|
-
* - Concatenates without space for small numbers (< 만)
|
|
44
|
-
* - Separates with space for large numbers (>= 만)
|
|
45
|
-
* - Multiplies when right > left, adds when left > right
|
|
46
|
-
*
|
|
47
|
-
* @param {Object} leftPair The left operand as `{ word: number }`.
|
|
48
|
-
* @param {Object} rightPair The right operand as `{ word: number }`.
|
|
49
|
-
* @returns {Object} Merged pair with combined word and resulting number.
|
|
50
|
-
*/
|
|
51
|
-
mergeScales (leftPair, rightPair) {
|
|
52
|
-
const leftWord = Object.keys(leftPair)[0]
|
|
53
|
-
const rightWord = Object.keys(rightPair)[0]
|
|
54
|
-
const leftNumber = Object.values(leftPair)[0] // BigInt
|
|
55
|
-
const rightNumber = Object.values(rightPair)[0] // BigInt
|
|
56
|
-
|
|
57
|
-
// Implicit "일": omit 1 before multipliers up to 만 (10,000)
|
|
58
|
-
if (leftNumber === 1n && rightNumber <= 10_000n) return rightPair
|
|
59
|
-
// Concatenate (no space) for small numbers less than 만
|
|
60
|
-
if (leftNumber < 10_000n && leftNumber > rightNumber) return { [`${leftWord}${rightWord}`]: leftNumber + rightNumber }
|
|
61
|
-
// Space-separate for large numbers (>= 만) when adding
|
|
62
|
-
if (leftNumber >= 10_000n && leftNumber > rightNumber) return { [`${leftWord} ${rightWord}`]: leftNumber + rightNumber }
|
|
63
|
-
// Multiply for all scale combinations
|
|
64
|
-
return { [`${leftWord}${rightWord}`]: leftNumber * rightNumber }
|
|
181
|
+
function joinKoreanParts (parts) {
|
|
182
|
+
if (parts.length === 0) return ZERO
|
|
183
|
+
if (parts.length === 1) return parts[0].word
|
|
184
|
+
|
|
185
|
+
const result = []
|
|
186
|
+
|
|
187
|
+
for (let i = 0; i < parts.length; i++) {
|
|
188
|
+
const part = parts[i]
|
|
189
|
+
const prevPart = i > 0 ? parts[i - 1] : null
|
|
190
|
+
|
|
191
|
+
// Add space after scale words before next number
|
|
192
|
+
if (prevPart && prevPart.isScale && !part.isScale) {
|
|
193
|
+
result.push(' ')
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
result.push(part.word)
|
|
65
197
|
}
|
|
198
|
+
|
|
199
|
+
return result.join('')
|
|
66
200
|
}
|
|
67
201
|
|
|
68
202
|
/**
|
|
69
|
-
* Converts
|
|
203
|
+
* Converts decimal digits to Korean words.
|
|
70
204
|
*
|
|
71
|
-
* @param {
|
|
72
|
-
* @
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
205
|
+
* @param {string} decimalPart - Decimal digits (without the point)
|
|
206
|
+
* @returns {string} Korean words for decimal part (space-separated)
|
|
207
|
+
*/
|
|
208
|
+
function decimalPartToWords (decimalPart) {
|
|
209
|
+
const parts = []
|
|
210
|
+
|
|
211
|
+
// Handle leading zeros
|
|
212
|
+
let i = 0
|
|
213
|
+
while (i < decimalPart.length && decimalPart[i] === '0') {
|
|
214
|
+
parts.push(ZERO)
|
|
215
|
+
i++
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Convert remainder as a single number
|
|
219
|
+
const remainder = decimalPart.slice(i)
|
|
220
|
+
if (remainder) {
|
|
221
|
+
parts.push(integerToWords(BigInt(remainder)))
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return parts.join(' ')
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Converts a numeric value to Korean words.
|
|
229
|
+
*
|
|
230
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
231
|
+
* @returns {string} The number in Korean words
|
|
232
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
233
|
+
* @throws {Error} If value is not a valid number format
|
|
76
234
|
*
|
|
77
235
|
* @example
|
|
78
|
-
*
|
|
79
|
-
*
|
|
236
|
+
* toWords(21) // '이십일'
|
|
237
|
+
* toWords(10000) // '만'
|
|
238
|
+
* toWords(1000000) // '백만'
|
|
80
239
|
*/
|
|
81
|
-
|
|
82
|
-
|
|
240
|
+
function toWords (value) {
|
|
241
|
+
const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
|
|
242
|
+
|
|
243
|
+
const parts = []
|
|
244
|
+
|
|
245
|
+
if (isNegative) {
|
|
246
|
+
parts.push(NEGATIVE)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
parts.push(integerToWords(integerPart))
|
|
250
|
+
|
|
251
|
+
if (decimalPart) {
|
|
252
|
+
parts.push(DECIMAL_SEP)
|
|
253
|
+
parts.push(decimalPartToWords(decimalPart))
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return parts.join(' ')
|
|
83
257
|
}
|
|
258
|
+
|
|
259
|
+
// ============================================================================
|
|
260
|
+
// Public API
|
|
261
|
+
// ============================================================================
|
|
262
|
+
|
|
263
|
+
export { toWords }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a numeric value to Lithuanian words.
|
|
3
|
+
*
|
|
4
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
|
+
* @param {Object} [options] - Conversion options
|
|
6
|
+
* @param {string} [options.gender='masculine'] - Gender for numbers < 1000
|
|
7
|
+
* @returns {string} The number in Lithuanian words
|
|
8
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
9
|
+
* @throws {Error} If value is not a valid number format
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* toWords(42) // 'keturiasdešimt du'
|
|
13
|
+
* toWords(1, { gender: 'feminine' }) // 'viena'
|
|
14
|
+
* toWords(1000000) // 'vienas milijonas'
|
|
15
|
+
*/
|
|
16
|
+
export function toWords(value: number | string | bigint, options?: {
|
|
17
|
+
gender?: string | undefined;
|
|
18
|
+
}): string;
|