n2words 3.0.0 → 3.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 +15 -0
- package/dist/languages/am-Latn.js +2 -2
- package/dist/languages/am-Latn.js.map +1 -1
- package/dist/languages/am.js +2 -2
- package/dist/languages/am.js.map +1 -1
- package/dist/languages/ar.js +1 -1
- package/dist/languages/az.js +2 -2
- package/dist/languages/az.js.map +1 -1
- package/dist/languages/bn.js +2 -2
- package/dist/languages/bn.js.map +1 -1
- package/dist/languages/cs.js +2 -2
- package/dist/languages/cs.js.map +1 -1
- package/dist/languages/da.js +2 -2
- package/dist/languages/da.js.map +1 -1
- package/dist/languages/de.js +2 -2
- package/dist/languages/de.js.map +1 -1
- package/dist/languages/el.js +2 -2
- package/dist/languages/el.js.map +1 -1
- package/dist/languages/en.js +2 -2
- package/dist/languages/en.js.map +1 -1
- package/dist/languages/es.js +2 -2
- package/dist/languages/es.js.map +1 -1
- package/dist/languages/fa.js +1 -1
- package/dist/languages/fi.js +2 -2
- package/dist/languages/fi.js.map +1 -1
- package/dist/languages/fil.js +2 -2
- package/dist/languages/fil.js.map +1 -1
- package/dist/languages/fr-BE.js +2 -2
- package/dist/languages/fr-BE.js.map +1 -1
- package/dist/languages/fr.js +2 -2
- package/dist/languages/fr.js.map +1 -1
- package/dist/languages/gu.js +2 -2
- package/dist/languages/gu.js.map +1 -1
- package/dist/languages/ha.js +2 -2
- package/dist/languages/ha.js.map +1 -1
- package/dist/languages/hbo.js +2 -2
- package/dist/languages/hbo.js.map +1 -1
- package/dist/languages/he.js +2 -2
- package/dist/languages/he.js.map +1 -1
- package/dist/languages/hi.js +2 -2
- package/dist/languages/hi.js.map +1 -1
- package/dist/languages/hr.js +2 -2
- package/dist/languages/hr.js.map +1 -1
- package/dist/languages/hu.js +1 -1
- package/dist/languages/id.js +2 -2
- package/dist/languages/id.js.map +1 -1
- package/dist/languages/it.js +2 -2
- package/dist/languages/it.js.map +1 -1
- package/dist/languages/ja.js +2 -2
- package/dist/languages/ja.js.map +1 -1
- package/dist/languages/ka.js +3 -0
- package/dist/languages/ka.js.map +1 -0
- package/dist/languages/kn.js +2 -2
- package/dist/languages/kn.js.map +1 -1
- package/dist/languages/ko.js +2 -2
- package/dist/languages/ko.js.map +1 -1
- package/dist/languages/lt.js +2 -2
- package/dist/languages/lt.js.map +1 -1
- package/dist/languages/lv.js +2 -2
- package/dist/languages/lv.js.map +1 -1
- package/dist/languages/mr.js +2 -2
- package/dist/languages/mr.js.map +1 -1
- package/dist/languages/ms.js +2 -2
- package/dist/languages/ms.js.map +1 -1
- package/dist/languages/nb.js +2 -2
- package/dist/languages/nb.js.map +1 -1
- package/dist/languages/nl.js +2 -2
- package/dist/languages/nl.js.map +1 -1
- package/dist/languages/pa.js +2 -2
- package/dist/languages/pa.js.map +1 -1
- package/dist/languages/pl.js +2 -2
- package/dist/languages/pl.js.map +1 -1
- package/dist/languages/pt.js +2 -2
- package/dist/languages/pt.js.map +1 -1
- package/dist/languages/ro.js +1 -1
- package/dist/languages/ru.js +2 -2
- package/dist/languages/ru.js.map +1 -1
- package/dist/languages/sr-Cyrl.js +2 -2
- package/dist/languages/sr-Cyrl.js.map +1 -1
- package/dist/languages/sr-Latn.js +2 -2
- package/dist/languages/sr-Latn.js.map +1 -1
- package/dist/languages/sv.js +2 -2
- package/dist/languages/sv.js.map +1 -1
- package/dist/languages/sw.js +1 -1
- package/dist/languages/ta.js +2 -2
- package/dist/languages/ta.js.map +1 -1
- package/dist/languages/te.js +2 -2
- package/dist/languages/te.js.map +1 -1
- package/dist/languages/th.js +1 -1
- package/dist/languages/tr.js +2 -2
- package/dist/languages/tr.js.map +1 -1
- package/dist/languages/uk.js +2 -2
- package/dist/languages/uk.js.map +1 -1
- package/dist/languages/ur.js +2 -2
- package/dist/languages/ur.js.map +1 -1
- package/dist/languages/vi.js +2 -2
- package/dist/languages/vi.js.map +1 -1
- package/dist/languages/yo.js +3 -0
- package/dist/languages/yo.js.map +1 -0
- package/dist/languages/zh-Hans.js +1 -1
- package/dist/languages/zh-Hant.js +1 -1
- package/dist/n2words.js +2 -2
- package/dist/n2words.js.map +1 -1
- package/lib/languages/am-Latn.js +4 -9
- package/lib/languages/am.js +4 -9
- package/lib/languages/az.js +4 -9
- package/lib/languages/bn.js +51 -30
- package/lib/languages/cs.js +9 -26
- package/lib/languages/da.js +6 -13
- package/lib/languages/de.js +21 -33
- package/lib/languages/el.js +6 -13
- package/lib/languages/en.js +44 -58
- package/lib/languages/es.js +10 -30
- package/lib/languages/fi.js +6 -13
- package/lib/languages/fil.js +6 -17
- package/lib/languages/fr-BE.js +17 -26
- package/lib/languages/fr.js +18 -31
- package/lib/languages/gu.js +43 -30
- package/lib/languages/ha.js +4 -9
- package/lib/languages/hbo.js +7 -25
- package/lib/languages/he.js +7 -18
- package/lib/languages/hi.js +55 -30
- package/lib/languages/hr.js +61 -55
- package/lib/languages/id.js +8 -13
- package/lib/languages/it.js +64 -99
- package/lib/languages/ja.js +8 -20
- package/lib/languages/ka.d.ts +17 -0
- package/lib/languages/ka.js +291 -0
- package/lib/languages/kn.js +43 -30
- package/lib/languages/ko.js +6 -13
- package/lib/languages/lt.js +6 -15
- package/lib/languages/lv.js +6 -15
- package/lib/languages/mr.js +43 -30
- package/lib/languages/ms.js +8 -13
- package/lib/languages/nb.js +13 -23
- package/lib/languages/nl.js +11 -29
- package/lib/languages/pa.js +32 -37
- package/lib/languages/pl.js +7 -20
- package/lib/languages/pt.js +17 -31
- package/lib/languages/ru.js +64 -59
- package/lib/languages/sr-Cyrl.js +58 -52
- package/lib/languages/sr-Latn.js +58 -52
- package/lib/languages/sv.js +14 -24
- package/lib/languages/ta.js +55 -42
- package/lib/languages/te.js +33 -41
- package/lib/languages/tr.js +7 -18
- package/lib/languages/uk.js +61 -55
- package/lib/languages/ur.js +32 -37
- package/lib/languages/vi.js +23 -43
- package/lib/languages/yo.d.ts +7 -0
- package/lib/languages/yo.js +303 -0
- package/lib/n2words.d.ts +3 -1
- package/lib/n2words.js +4 -0
- package/package.json +1 -1
package/lib/languages/id.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Indonesian language converter - Functional Implementation
|
|
3
3
|
*
|
|
4
|
-
* Self-contained
|
|
4
|
+
* Self-contained module with its own input validation, ready for subpath exports.
|
|
5
5
|
*
|
|
6
6
|
* Key features:
|
|
7
7
|
* - "Se-" prefix for 100 (seratus) and 1000 (seribu)
|
|
@@ -29,7 +29,7 @@ const NEGATIVE = 'min'
|
|
|
29
29
|
const DECIMAL_SEP = 'koma'
|
|
30
30
|
|
|
31
31
|
// ============================================================================
|
|
32
|
-
//
|
|
32
|
+
// Segment Building
|
|
33
33
|
// ============================================================================
|
|
34
34
|
|
|
35
35
|
function buildSegment (n) {
|
|
@@ -68,11 +68,6 @@ function buildSegment (n) {
|
|
|
68
68
|
return parts.join(' ')
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
const SEGMENTS = new Array(1000)
|
|
72
|
-
for (let i = 0; i < 1000; i++) {
|
|
73
|
-
SEGMENTS[i] = buildSegment(i)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
71
|
// ============================================================================
|
|
77
72
|
// Conversion Functions
|
|
78
73
|
// ============================================================================
|
|
@@ -81,7 +76,7 @@ function integerToWords (n) {
|
|
|
81
76
|
if (n === 0n) return ZERO
|
|
82
77
|
|
|
83
78
|
if (n < 1000n) {
|
|
84
|
-
return
|
|
79
|
+
return buildSegment(Number(n))
|
|
85
80
|
}
|
|
86
81
|
|
|
87
82
|
if (n < 1_000_000n) {
|
|
@@ -92,11 +87,11 @@ function integerToWords (n) {
|
|
|
92
87
|
if (thousands === 1) {
|
|
93
88
|
result = 'se' + THOUSAND_WORD
|
|
94
89
|
} else {
|
|
95
|
-
result =
|
|
90
|
+
result = buildSegment(thousands) + ' ' + THOUSAND_WORD
|
|
96
91
|
}
|
|
97
92
|
|
|
98
93
|
if (remainder > 0) {
|
|
99
|
-
result += ' ' +
|
|
94
|
+
result += ' ' + buildSegment(remainder)
|
|
100
95
|
}
|
|
101
96
|
|
|
102
97
|
return result
|
|
@@ -131,17 +126,17 @@ function buildLargeNumberWords (n) {
|
|
|
131
126
|
|
|
132
127
|
if (segment !== 0) {
|
|
133
128
|
if (scaleIndex === 0) {
|
|
134
|
-
parts.push(
|
|
129
|
+
parts.push(buildSegment(segment))
|
|
135
130
|
} else if (scaleIndex === 1) {
|
|
136
131
|
if (segment === 1) {
|
|
137
132
|
parts.push('se' + THOUSAND_WORD)
|
|
138
133
|
} else {
|
|
139
|
-
parts.push(
|
|
134
|
+
parts.push(buildSegment(segment) + ' ' + THOUSAND_WORD)
|
|
140
135
|
}
|
|
141
136
|
} else {
|
|
142
137
|
// Indonesian: "satu juta" not "sejuta"
|
|
143
138
|
const scaleWord = SCALE_WORDS[scaleIndex - 2]
|
|
144
|
-
parts.push(
|
|
139
|
+
parts.push(buildSegment(segment) + ' ' + scaleWord)
|
|
145
140
|
}
|
|
146
141
|
}
|
|
147
142
|
|
package/lib/languages/it.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Italian language converter - Functional Implementation
|
|
2
|
+
* Italian language converter - Functional Implementation
|
|
3
3
|
*
|
|
4
|
-
* A performance-optimized number-to-words converter using precomputed lookup tables.
|
|
5
4
|
* Self-contained module with its own input validation, ready for subpath exports.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
* This eliminates all per-call string manipulation for segment conversion.
|
|
9
|
-
*
|
|
10
|
-
* Italian-specific rules (handled in precomputation):
|
|
6
|
+
* Italian-specific rules:
|
|
11
7
|
* - Concatenation without spaces within segments ("ventotto" not "venti otto")
|
|
12
8
|
* - Phonetic vowel elision: "venti" + "otto" → "ventotto"
|
|
13
9
|
* - Accent on final "tre" in compounds: "ventitré"
|
|
@@ -22,12 +18,16 @@ import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
|
22
18
|
// Vocabulary (module-level constants)
|
|
23
19
|
// ============================================================================
|
|
24
20
|
|
|
25
|
-
// Base vocabulary
|
|
21
|
+
// Base vocabulary
|
|
26
22
|
const ONES = ['', 'uno', 'due', 'tre', 'quattro', 'cinque', 'sei', 'sette', 'otto', 'nove']
|
|
27
23
|
const TEENS = ['dieci', 'undici', 'dodici', 'tredici', 'quattordici', 'quindici', 'sedici', 'diciassette', 'diciotto', 'diciannove']
|
|
28
24
|
const TENS = ['', '', 'venti', 'trenta', 'quaranta', 'cinquanta', 'sessanta', 'settanta', 'ottanta', 'novanta']
|
|
29
25
|
const HUNDREDS = ['', 'cento', 'duecento', 'trecento', 'quattrocento', 'cinquecento', 'seicento', 'settecento', 'ottocento', 'novecento']
|
|
30
26
|
|
|
27
|
+
// Pre-elided tens stems (drop final vowel before 'uno'/'otto')
|
|
28
|
+
// vent- (from venti), trent- (from trenta), etc.
|
|
29
|
+
const TENS_STEM = ['', '', 'vent', 'trent', 'quarant', 'cinquant', 'sessant', 'settant', 'ottant', 'novant']
|
|
30
|
+
|
|
31
31
|
const ZERO = 'zero'
|
|
32
32
|
const NEGATIVE = 'meno'
|
|
33
33
|
const DECIMAL_SEP = 'virgola'
|
|
@@ -40,38 +40,19 @@ const THOUSAND_PLURAL_SUFFIX = 'mila'
|
|
|
40
40
|
const SCALE_PREFIXES = ['m', 'b', 'tr', 'quadr', 'quint', 'sest', 'sett', 'ott', 'nov', 'dec']
|
|
41
41
|
|
|
42
42
|
// ============================================================================
|
|
43
|
-
//
|
|
43
|
+
// Segment Building
|
|
44
44
|
// ============================================================================
|
|
45
45
|
|
|
46
|
-
/**
|
|
47
|
-
* Applies Italian phonetic vowel elision rules.
|
|
48
|
-
* Only used during table construction.
|
|
49
|
-
*/
|
|
50
|
-
function applyPhoneticRules (str) {
|
|
51
|
-
return str
|
|
52
|
-
.replace(/io/g, 'o')
|
|
53
|
-
.replace(/ao/g, 'o')
|
|
54
|
-
.replace(/oo/g, 'o')
|
|
55
|
-
.replace(/iu/g, 'u')
|
|
56
|
-
.replace(/au/g, 'u')
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Adds accent to final "tre" in a word.
|
|
61
|
-
* Only used during table construction.
|
|
62
|
-
*/
|
|
63
|
-
function accentuateTre (word) {
|
|
64
|
-
if (word.length > 3 && word.slice(-3) === 'tre') {
|
|
65
|
-
return word.slice(0, -3) + 'tré'
|
|
66
|
-
}
|
|
67
|
-
return word
|
|
68
|
-
}
|
|
69
|
-
|
|
70
46
|
/**
|
|
71
47
|
* Builds the segment word for a number 0-999.
|
|
72
|
-
*
|
|
48
|
+
* Handles Italian phonetic elision inline (no regex).
|
|
49
|
+
*
|
|
50
|
+
* Elision rules:
|
|
51
|
+
* - Tens ending in vowel + uno/otto → drop tens vowel: ventuno, ventotto
|
|
52
|
+
* - Hundreds cento + otto/ottanta → centotto, centottanta (drop 'o')
|
|
53
|
+
* - Final 'tre' in compounds becomes 'tré': ventitré, trentatré
|
|
73
54
|
*/
|
|
74
|
-
function
|
|
55
|
+
function buildSegment (n) {
|
|
75
56
|
if (n === 0) return ''
|
|
76
57
|
|
|
77
58
|
const ones = n % 10
|
|
@@ -82,86 +63,70 @@ function buildSegmentWord (n) {
|
|
|
82
63
|
|
|
83
64
|
// Hundreds
|
|
84
65
|
if (hundreds > 0) {
|
|
85
|
-
|
|
66
|
+
// Elision: *cento + otto/ottanta → *centotto/centottanta (drop final 'o')
|
|
67
|
+
// Only applies when tens = 8 (ottanta) or tens = 0 and ones = 8 (otto)
|
|
68
|
+
if (tens === 8 || (tens === 0 && ones === 8)) {
|
|
69
|
+
// Remove final 'o' from hundreds: cento→cent, duecento→duecent, etc.
|
|
70
|
+
result = HUNDREDS[hundreds].slice(0, -1)
|
|
71
|
+
} else {
|
|
72
|
+
result = HUNDREDS[hundreds]
|
|
73
|
+
}
|
|
86
74
|
}
|
|
87
75
|
|
|
88
76
|
// Tens and ones
|
|
89
77
|
if (tens === 0 && ones === 0) {
|
|
90
|
-
// Nothing more
|
|
78
|
+
// Nothing more (just hundreds)
|
|
91
79
|
} else if (tens === 1) {
|
|
92
80
|
// Teens: 10-19
|
|
93
81
|
result += TEENS[ones]
|
|
94
82
|
} else if (tens >= 2) {
|
|
95
|
-
// 20-99
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
result += ONES[ones]
|
|
83
|
+
// 20-99: handle elision for uno (1) and otto (8)
|
|
84
|
+
if (ones === 1 || ones === 8) {
|
|
85
|
+
// Use stem form: vent + uno = ventuno, vent + otto = ventotto
|
|
86
|
+
result += TENS_STEM[tens] + ONES[ones]
|
|
87
|
+
} else if (ones === 3) {
|
|
88
|
+
// Final tre becomes tré
|
|
89
|
+
result += TENS[tens] + 'tré'
|
|
90
|
+
} else if (ones > 0) {
|
|
91
|
+
result += TENS[tens] + ONES[ones]
|
|
92
|
+
} else {
|
|
93
|
+
result += TENS[tens]
|
|
99
94
|
}
|
|
100
95
|
} else if (ones > 0) {
|
|
101
96
|
// 1-9 (tens === 0)
|
|
102
|
-
|
|
97
|
+
if (ones === 3 && hundreds > 0) {
|
|
98
|
+
// centotré, duecentotré, etc.
|
|
99
|
+
result += 'tré'
|
|
100
|
+
} else {
|
|
101
|
+
result += ONES[ones]
|
|
102
|
+
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
return accentuateTre(applyPhoneticRules(result))
|
|
105
|
+
return result
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
/**
|
|
110
109
|
* Builds segment word with "un" for scale context (millions, billions).
|
|
111
|
-
*
|
|
110
|
+
* Same as buildSegment but returns "un" for 1 instead of "uno".
|
|
112
111
|
*/
|
|
113
|
-
function
|
|
112
|
+
function buildSegmentForScale (n) {
|
|
114
113
|
if (n === 0) return ''
|
|
115
114
|
if (n === 1) return 'un' // "un milione" not "uno milione"
|
|
116
|
-
|
|
117
|
-
const ones = n % 10
|
|
118
|
-
const tens = Math.floor(n / 10) % 10
|
|
119
|
-
const hundreds = Math.floor(n / 100)
|
|
120
|
-
|
|
121
|
-
let result = ''
|
|
122
|
-
|
|
123
|
-
if (hundreds > 0) {
|
|
124
|
-
result = HUNDREDS[hundreds]
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (tens === 0 && ones === 0) {
|
|
128
|
-
// Nothing more
|
|
129
|
-
} else if (tens === 1) {
|
|
130
|
-
result += TEENS[ones]
|
|
131
|
-
} else if (tens >= 2) {
|
|
132
|
-
result += TENS[tens]
|
|
133
|
-
if (ones > 0) {
|
|
134
|
-
result += ONES[ones]
|
|
135
|
-
}
|
|
136
|
-
} else if (ones > 0) {
|
|
137
|
-
// 1-9 with tens === 0
|
|
138
|
-
// "un" only for exactly 1, others normal
|
|
139
|
-
result += ONES[ones]
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return accentuateTre(applyPhoneticRules(result))
|
|
115
|
+
return buildSegment(n)
|
|
143
116
|
}
|
|
144
117
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
// Precompute segment words for scale context (uses "un" for 1)
|
|
153
|
-
const SEGMENTS_SCALE = new Array(1000)
|
|
154
|
-
for (let i = 0; i < 1000; i++) {
|
|
155
|
-
SEGMENTS_SCALE[i] = buildSegmentWordForScale(i)
|
|
156
|
-
}
|
|
118
|
+
/**
|
|
119
|
+
* Builds thousands word for 1-999 thousand.
|
|
120
|
+
* Handles elision: tre + mila = tremila (no accent), otto + mila = ottomila
|
|
121
|
+
*/
|
|
122
|
+
function buildThousands (n) {
|
|
123
|
+
if (n === 0) return ''
|
|
124
|
+
if (n === 1) return THOUSAND_SINGULAR // "mille"
|
|
157
125
|
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
THOUSANDS[1] = THOUSAND_SINGULAR // "mille"
|
|
163
|
-
for (let i = 2; i < 1000; i++) {
|
|
164
|
-
THOUSANDS[i] = applyPhoneticRules(SEGMENTS[i] + THOUSAND_PLURAL_SUFFIX)
|
|
126
|
+
// Build segment and append "mila"
|
|
127
|
+
// Note: elision of segment ending vowel + 'mila' not needed (mila starts with 'm')
|
|
128
|
+
// But we need to handle cases like "ottomila" (no double-o issue since we build directly)
|
|
129
|
+
return buildSegment(n) + THOUSAND_PLURAL_SUFFIX
|
|
165
130
|
}
|
|
166
131
|
|
|
167
132
|
// ============================================================================
|
|
@@ -207,9 +172,9 @@ function getScaleWordPlural (scaleIndex) {
|
|
|
207
172
|
function integerToWords (n) {
|
|
208
173
|
if (n === 0n) return ZERO
|
|
209
174
|
|
|
210
|
-
// Fast path: numbers < 1000
|
|
175
|
+
// Fast path: numbers < 1000
|
|
211
176
|
if (n < 1000n) {
|
|
212
|
-
return
|
|
177
|
+
return buildSegment(Number(n))
|
|
213
178
|
}
|
|
214
179
|
|
|
215
180
|
// Fast path: numbers < 1,000,000 (thousands)
|
|
@@ -218,11 +183,11 @@ function integerToWords (n) {
|
|
|
218
183
|
const remainder = Number(n % 1000n)
|
|
219
184
|
|
|
220
185
|
if (remainder === 0) {
|
|
221
|
-
return
|
|
186
|
+
return buildThousands(thousands)
|
|
222
187
|
}
|
|
223
188
|
|
|
224
189
|
// Concatenate thousands + remainder
|
|
225
|
-
return
|
|
190
|
+
return buildThousands(thousands) + buildSegment(remainder)
|
|
226
191
|
}
|
|
227
192
|
|
|
228
193
|
// For numbers >= 1,000,000, use scale decomposition
|
|
@@ -259,17 +224,17 @@ function buildLargeNumberWords (n) {
|
|
|
259
224
|
|
|
260
225
|
if (scaleIndex >= 2) {
|
|
261
226
|
// Millions and above: "segment scaleWord"
|
|
262
|
-
const segmentWords =
|
|
227
|
+
const segmentWords = buildSegmentForScale(segNum)
|
|
263
228
|
const scaleWord = segment === 1n
|
|
264
229
|
? getScaleWordSingular(scaleIndex)
|
|
265
230
|
: getScaleWordPlural(scaleIndex)
|
|
266
231
|
parts.push(segmentWords + ' ' + scaleWord)
|
|
267
232
|
} else if (scaleIndex === 1) {
|
|
268
|
-
// Thousands
|
|
269
|
-
parts.push(
|
|
233
|
+
// Thousands
|
|
234
|
+
parts.push(buildThousands(segNum))
|
|
270
235
|
} else {
|
|
271
236
|
// Units (scaleIndex === 0): just the segment
|
|
272
|
-
parts.push(
|
|
237
|
+
parts.push(buildSegment(segNum))
|
|
273
238
|
}
|
|
274
239
|
}
|
|
275
240
|
|
package/lib/languages/ja.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Japanese language converter - Functional Implementation
|
|
3
3
|
*
|
|
4
|
-
* A performance-optimized number-to-words converter using precomputed lookup tables.
|
|
5
4
|
* Self-contained module with its own input validation, ready for subpath exports.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
* This eliminates all per-call string manipulation for segment conversion.
|
|
9
|
-
*
|
|
10
|
-
* Japanese-specific rules (handled in precomputation):
|
|
6
|
+
* Japanese-specific rules:
|
|
11
7
|
* - Myriad (万-based) grouping: 4 digits per segment instead of 3
|
|
12
8
|
* - 一 omission: Omit "一" before 十, 百, 千 but NOT before 万 and higher scales
|
|
13
9
|
* - Kanji numerals: 零一二三四五六七八九
|
|
@@ -55,13 +51,12 @@ const HUNDRED = '百'
|
|
|
55
51
|
const THOUSAND = '千'
|
|
56
52
|
|
|
57
53
|
// ============================================================================
|
|
58
|
-
//
|
|
54
|
+
// Segment Building
|
|
59
55
|
// ============================================================================
|
|
60
56
|
|
|
61
57
|
/**
|
|
62
58
|
* Builds segment word for 0-9999 with 一 omission rules.
|
|
63
59
|
* - Omit 一 before 十, 百, 千
|
|
64
|
-
* Only used during table construction.
|
|
65
60
|
*/
|
|
66
61
|
function buildSegment (n) {
|
|
67
62
|
if (n === 0) return ''
|
|
@@ -108,13 +103,6 @@ function buildSegment (n) {
|
|
|
108
103
|
return result
|
|
109
104
|
}
|
|
110
105
|
|
|
111
|
-
// Precompute all 10000 segment words (0-9999)
|
|
112
|
-
// SEGMENTS[n] gives the Japanese word for n within a segment
|
|
113
|
-
const SEGMENTS = new Array(10000)
|
|
114
|
-
for (let i = 0; i < 10000; i++) {
|
|
115
|
-
SEGMENTS[i] = buildSegment(i)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
106
|
// ============================================================================
|
|
119
107
|
// Conversion Functions
|
|
120
108
|
// ============================================================================
|
|
@@ -128,9 +116,9 @@ for (let i = 0; i < 10000; i++) {
|
|
|
128
116
|
function integerToWords (n) {
|
|
129
117
|
if (n === 0n) return ZERO
|
|
130
118
|
|
|
131
|
-
// Fast path: numbers < 10000
|
|
119
|
+
// Fast path: numbers < 10000
|
|
132
120
|
if (n < 10000n) {
|
|
133
|
-
return
|
|
121
|
+
return buildSegment(Number(n))
|
|
134
122
|
}
|
|
135
123
|
|
|
136
124
|
// Fast path: numbers < 100,000,000 (万 range)
|
|
@@ -143,11 +131,11 @@ function integerToWords (n) {
|
|
|
143
131
|
if (man === 1) {
|
|
144
132
|
result = '一' + SCALES[0] // 一万
|
|
145
133
|
} else {
|
|
146
|
-
result =
|
|
134
|
+
result = buildSegment(man) + SCALES[0]
|
|
147
135
|
}
|
|
148
136
|
|
|
149
137
|
if (remainder > 0) {
|
|
150
|
-
result +=
|
|
138
|
+
result += buildSegment(remainder)
|
|
151
139
|
}
|
|
152
140
|
|
|
153
141
|
return result
|
|
@@ -186,11 +174,11 @@ function buildLargeNumberWords (n) {
|
|
|
186
174
|
if (segment === 1) {
|
|
187
175
|
result += '一' + SCALES[i - 1]
|
|
188
176
|
} else {
|
|
189
|
-
result +=
|
|
177
|
+
result += buildSegment(segment) + SCALES[i - 1]
|
|
190
178
|
}
|
|
191
179
|
} else {
|
|
192
180
|
// Units segment (no scale word)
|
|
193
|
-
result +=
|
|
181
|
+
result += buildSegment(segment)
|
|
194
182
|
}
|
|
195
183
|
}
|
|
196
184
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a numeric value to Georgian words.
|
|
3
|
+
*
|
|
4
|
+
* This is the main public API. It accepts any valid numeric input
|
|
5
|
+
* (number, string, or bigint) and handles parsing internally.
|
|
6
|
+
*
|
|
7
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
8
|
+
* @returns {string} The number in Georgian words
|
|
9
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
10
|
+
* @throws {Error} If value is not a valid number format
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* toWords(21) // 'ოცდაერთი'
|
|
14
|
+
* toWords(100) // 'ასი'
|
|
15
|
+
* toWords(1000) // 'ათასი'
|
|
16
|
+
*/
|
|
17
|
+
export function toWords(value: number | string | bigint): string;
|