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/am-Latn.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Amharic Latin language converter - Functional Implementation
|
|
3
3
|
*
|
|
4
|
-
* Self-contained
|
|
4
|
+
* Self-contained module with its own input validation, ready for subpath exports.
|
|
5
5
|
* Latin/ASCII romanization of Amharic numerals.
|
|
6
6
|
*
|
|
7
7
|
* Key features:
|
|
@@ -64,11 +64,6 @@ function buildSegment (n) {
|
|
|
64
64
|
return parts.join(' ')
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const SEGMENTS = new Array(1000)
|
|
68
|
-
for (let i = 0; i < 1000; i++) {
|
|
69
|
-
SEGMENTS[i] = buildSegment(i)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
67
|
// ============================================================================
|
|
73
68
|
// Conversion Functions
|
|
74
69
|
// ============================================================================
|
|
@@ -77,7 +72,7 @@ function integerToWords (n) {
|
|
|
77
72
|
if (n === 0n) return ZERO
|
|
78
73
|
|
|
79
74
|
if (n < 1000n) {
|
|
80
|
-
return
|
|
75
|
+
return buildSegment(Number(n))
|
|
81
76
|
}
|
|
82
77
|
|
|
83
78
|
return buildLargeNumberWords(n)
|
|
@@ -111,9 +106,9 @@ function buildLargeNumberWords (n) {
|
|
|
111
106
|
const scaleWord = SCALE_WORDS[scaleIndex] || ''
|
|
112
107
|
|
|
113
108
|
if (scaleIndex === 0) {
|
|
114
|
-
parts.push(
|
|
109
|
+
parts.push(buildSegment(segment))
|
|
115
110
|
} else {
|
|
116
|
-
parts.push(
|
|
111
|
+
parts.push(buildSegment(segment) + ' ' + scaleWord)
|
|
117
112
|
}
|
|
118
113
|
}
|
|
119
114
|
|
package/lib/languages/am.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Amharic language converter - Functional Implementation
|
|
3
3
|
*
|
|
4
|
-
* Self-contained
|
|
4
|
+
* Self-contained module with its own input validation, ready for subpath exports.
|
|
5
5
|
* Native Ge'ez script (ግዕዝ) output.
|
|
6
6
|
*
|
|
7
7
|
* Key features:
|
|
@@ -64,11 +64,6 @@ function buildSegment (n) {
|
|
|
64
64
|
return parts.join(' ')
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const SEGMENTS = new Array(1000)
|
|
68
|
-
for (let i = 0; i < 1000; i++) {
|
|
69
|
-
SEGMENTS[i] = buildSegment(i)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
67
|
// ============================================================================
|
|
73
68
|
// Conversion Functions
|
|
74
69
|
// ============================================================================
|
|
@@ -77,7 +72,7 @@ function integerToWords (n) {
|
|
|
77
72
|
if (n === 0n) return ZERO
|
|
78
73
|
|
|
79
74
|
if (n < 1000n) {
|
|
80
|
-
return
|
|
75
|
+
return buildSegment(Number(n))
|
|
81
76
|
}
|
|
82
77
|
|
|
83
78
|
return buildLargeNumberWords(n)
|
|
@@ -111,9 +106,9 @@ function buildLargeNumberWords (n) {
|
|
|
111
106
|
const scaleWord = SCALE_WORDS[scaleIndex] || ''
|
|
112
107
|
|
|
113
108
|
if (scaleIndex === 0) {
|
|
114
|
-
parts.push(
|
|
109
|
+
parts.push(buildSegment(segment))
|
|
115
110
|
} else {
|
|
116
|
-
parts.push(
|
|
111
|
+
parts.push(buildSegment(segment) + ' ' + scaleWord)
|
|
117
112
|
}
|
|
118
113
|
}
|
|
119
114
|
|
package/lib/languages/az.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Azerbaijani 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
|
* - Turkic language patterns
|
|
@@ -64,11 +64,6 @@ function buildSegment (n) {
|
|
|
64
64
|
return parts.join(' ')
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const SEGMENTS = new Array(1000)
|
|
68
|
-
for (let i = 0; i < 1000; i++) {
|
|
69
|
-
SEGMENTS[i] = buildSegment(i)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
67
|
// ============================================================================
|
|
73
68
|
// Conversion Functions
|
|
74
69
|
// ============================================================================
|
|
@@ -77,7 +72,7 @@ function integerToWords (n) {
|
|
|
77
72
|
if (n === 0n) return ZERO
|
|
78
73
|
|
|
79
74
|
if (n < 1000n) {
|
|
80
|
-
return
|
|
75
|
+
return buildSegment(Number(n))
|
|
81
76
|
}
|
|
82
77
|
|
|
83
78
|
return buildLargeNumberWords(n)
|
|
@@ -111,12 +106,12 @@ function buildLargeNumberWords (n) {
|
|
|
111
106
|
const scaleWord = SCALE_WORDS[scaleIndex] || ''
|
|
112
107
|
|
|
113
108
|
if (scaleIndex === 0) {
|
|
114
|
-
parts.push(
|
|
109
|
+
parts.push(buildSegment(segment))
|
|
115
110
|
} else if (scaleIndex === 1 && segment === 1) {
|
|
116
111
|
// Omit "bir" before thousand
|
|
117
112
|
parts.push(scaleWord)
|
|
118
113
|
} else {
|
|
119
|
-
parts.push(
|
|
114
|
+
parts.push(buildSegment(segment) + ' ' + scaleWord)
|
|
120
115
|
}
|
|
121
116
|
}
|
|
122
117
|
|
package/lib/languages/bn.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bangla 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
|
* - Indian numbering system (হাজার, লাখ, কোটি)
|
|
@@ -38,26 +38,13 @@ const BELOW_HUNDRED = [
|
|
|
38
38
|
const SCALE_WORDS = ['', 'হাজার', 'লাখ', 'কোটি', 'আরব', 'খরব', 'নীল', 'পদ্ম', 'শঙ্খ']
|
|
39
39
|
|
|
40
40
|
// ============================================================================
|
|
41
|
-
// Segment
|
|
41
|
+
// Segment Building
|
|
42
42
|
// ============================================================================
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const segments = []
|
|
49
|
-
segments.unshift(Number(numStr.slice(-3)))
|
|
50
|
-
|
|
51
|
-
let remaining = numStr.slice(0, -3)
|
|
52
|
-
while (remaining.length > 0) {
|
|
53
|
-
segments.unshift(Number(remaining.slice(-2)))
|
|
54
|
-
remaining = remaining.slice(0, -2)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return segments
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function segmentToWords (n) {
|
|
44
|
+
/**
|
|
45
|
+
* Builds words for a 0-999 segment.
|
|
46
|
+
*/
|
|
47
|
+
function buildSegment (n) {
|
|
61
48
|
if (n === 0) return ''
|
|
62
49
|
if (n < 100) return BELOW_HUNDRED[n]
|
|
63
50
|
|
|
@@ -74,25 +61,59 @@ function segmentToWords (n) {
|
|
|
74
61
|
// Conversion Functions
|
|
75
62
|
// ============================================================================
|
|
76
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Converts a non-negative integer to Bengali words.
|
|
66
|
+
*
|
|
67
|
+
* Uses BigInt modulo for segment extraction (faster than string slicing).
|
|
68
|
+
* South Asian 3-2-2 grouping: first 3 digits, then groups of 2.
|
|
69
|
+
*
|
|
70
|
+
* @param {bigint} n - Non-negative integer to convert
|
|
71
|
+
* @returns {string} Bengali words
|
|
72
|
+
*/
|
|
77
73
|
function integerToWords (n) {
|
|
78
74
|
if (n === 0n) return ZERO
|
|
79
75
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
// Fast path: numbers < 1000 (direct lookup)
|
|
77
|
+
if (n < 1000n) {
|
|
78
|
+
return buildSegment(Number(n))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Extract segments using BigInt modulo
|
|
82
|
+
// First segment is 3 digits (thousands), rest are 2 digits (lakhs, crores, etc.)
|
|
83
|
+
// Segments stored least-significant first
|
|
84
|
+
const segments = []
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
// First segment: last 3 digits
|
|
87
|
+
segments.push(Number(n % 1000n))
|
|
88
|
+
let temp = n / 1000n
|
|
89
|
+
|
|
90
|
+
// Remaining segments: 2 digits each (lakh = 100k, crore = 10M, etc.)
|
|
91
|
+
while (temp > 0n) {
|
|
92
|
+
segments.push(Number(temp % 100n))
|
|
93
|
+
temp = temp / 100n
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Build result string (process from most-significant to least)
|
|
97
|
+
const words = []
|
|
98
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
99
|
+
const segment = segments[i]
|
|
100
|
+
if (segment === 0) continue
|
|
101
|
+
|
|
102
|
+
if (i === 0) {
|
|
103
|
+
// First segment (units place) can be 0-999
|
|
104
|
+
words.push(buildSegment(segment))
|
|
105
|
+
} else {
|
|
106
|
+
// Other segments are 0-99
|
|
107
|
+
words.push(BELOW_HUNDRED[segment])
|
|
108
|
+
}
|
|
87
109
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
words.push(SCALE_WORDS[scaleIndex])
|
|
110
|
+
// Add scale word if not the units segment
|
|
111
|
+
if (i > 0 && SCALE_WORDS[i]) {
|
|
112
|
+
words.push(SCALE_WORDS[i])
|
|
92
113
|
}
|
|
93
114
|
}
|
|
94
115
|
|
|
95
|
-
return words.join(' ')
|
|
116
|
+
return words.join(' ')
|
|
96
117
|
}
|
|
97
118
|
|
|
98
119
|
function decimalPartToWords (decimalPart) {
|
package/lib/languages/cs.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Czech 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
|
-
* Czech-specific rules (handled in precomputation):
|
|
6
|
+
* Czech-specific rules:
|
|
11
7
|
* - Three-form pluralization: 1 = singular, 2-4 = few, 5+ = many
|
|
12
8
|
* - Irregular hundreds: sto, dvě stě, tři sta, čtyři sta, pět set...
|
|
13
9
|
* - Gender: dva (masc) vs dvě (fem) for 2
|
|
@@ -50,12 +46,11 @@ const ZERO = 'nula'
|
|
|
50
46
|
const NEGATIVE = 'mínus'
|
|
51
47
|
|
|
52
48
|
// ============================================================================
|
|
53
|
-
//
|
|
49
|
+
// Segment Building
|
|
54
50
|
// ============================================================================
|
|
55
51
|
|
|
56
52
|
/**
|
|
57
53
|
* Builds segment word for 0-999 (masculine, default form).
|
|
58
|
-
* Only used during table construction.
|
|
59
54
|
*/
|
|
60
55
|
function buildSegment (n) {
|
|
61
56
|
if (n === 0) return ''
|
|
@@ -120,18 +115,6 @@ function buildSegmentWithHundreds (n) {
|
|
|
120
115
|
return parts.join(' ')
|
|
121
116
|
}
|
|
122
117
|
|
|
123
|
-
// Precompute all 1000 segment words (0-999) - masculine form
|
|
124
|
-
const SEGMENTS = new Array(1000)
|
|
125
|
-
for (let i = 0; i < 1000; i++) {
|
|
126
|
-
SEGMENTS[i] = buildSegment(i)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Precompute all 1000 segment words with hundreds (irregular hundreds forms)
|
|
130
|
-
const SEGMENTS_WITH_HUNDREDS = new Array(1000)
|
|
131
|
-
for (let i = 0; i < 1000; i++) {
|
|
132
|
-
SEGMENTS_WITH_HUNDREDS[i] = buildSegmentWithHundreds(i)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
118
|
// ============================================================================
|
|
136
119
|
// Helper Functions
|
|
137
120
|
// ============================================================================
|
|
@@ -185,9 +168,9 @@ function getDecimalSeparator (integerPart) {
|
|
|
185
168
|
function integerToWords (n) {
|
|
186
169
|
if (n === 0n) return ZERO
|
|
187
170
|
|
|
188
|
-
// Fast path: numbers < 1000
|
|
171
|
+
// Fast path: numbers < 1000
|
|
189
172
|
if (n < 1000n) {
|
|
190
|
-
return
|
|
173
|
+
return buildSegment(Number(n))
|
|
191
174
|
}
|
|
192
175
|
|
|
193
176
|
// Fast path: numbers < 1,000,000 (thousands)
|
|
@@ -202,12 +185,12 @@ function integerToWords (n) {
|
|
|
202
185
|
// Omit "one" before tisíc
|
|
203
186
|
result = scaleWord
|
|
204
187
|
} else {
|
|
205
|
-
result =
|
|
188
|
+
result = buildSegment(Number(thousands)) + ' ' + scaleWord
|
|
206
189
|
}
|
|
207
190
|
|
|
208
191
|
if (remainder > 0) {
|
|
209
192
|
// Use form with irregular hundreds (for "dvě stě" etc.)
|
|
210
|
-
result += ' ' +
|
|
193
|
+
result += ' ' + buildSegmentWithHundreds(remainder)
|
|
211
194
|
}
|
|
212
195
|
|
|
213
196
|
return result
|
|
@@ -245,7 +228,7 @@ function buildLargeNumberWords (n) {
|
|
|
245
228
|
|
|
246
229
|
if (i === 0) {
|
|
247
230
|
// Units segment (no scale word) - use form with irregular hundreds
|
|
248
|
-
result +=
|
|
231
|
+
result += buildSegmentWithHundreds(Number(segment))
|
|
249
232
|
} else {
|
|
250
233
|
// Scale word needed
|
|
251
234
|
const forms = PLURAL_FORMS[i]
|
|
@@ -257,11 +240,11 @@ function buildLargeNumberWords (n) {
|
|
|
257
240
|
result += scaleWord
|
|
258
241
|
} else {
|
|
259
242
|
// Use masculine form for multiplier before scale words
|
|
260
|
-
result +=
|
|
243
|
+
result += buildSegment(Number(segment)) + ' ' + scaleWord
|
|
261
244
|
}
|
|
262
245
|
} else {
|
|
263
246
|
// Fallback for very large scales without defined forms
|
|
264
|
-
result +=
|
|
247
|
+
result += buildSegment(Number(segment))
|
|
265
248
|
}
|
|
266
249
|
}
|
|
267
250
|
}
|
package/lib/languages/da.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Danish language converter - Functional Implementation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Self-contained module with its own input validation, ready for subpath exports.
|
|
5
5
|
*
|
|
6
6
|
* Key features:
|
|
7
7
|
* - Vigesimal (base-20) tens naming: halvtreds (50), treds (60), etc.
|
|
@@ -37,7 +37,7 @@ const DECIMAL_SEP = 'komma'
|
|
|
37
37
|
const SCALES = ['millioner', 'millarder', 'billioner', 'billarder', 'trillioner', 'trillarder', 'quadrillioner', 'quadrillarder']
|
|
38
38
|
|
|
39
39
|
// ============================================================================
|
|
40
|
-
//
|
|
40
|
+
// Segment Building
|
|
41
41
|
// ============================================================================
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -83,13 +83,6 @@ function buildSegment (n) {
|
|
|
83
83
|
return parts[0] || ''
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
// Precompute all 1000 segment words (0-999)
|
|
87
|
-
const SEGMENTS = new Array(1000)
|
|
88
|
-
|
|
89
|
-
for (let i = 0; i < 1000; i++) {
|
|
90
|
-
SEGMENTS[i] = buildSegment(i)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
86
|
// ============================================================================
|
|
94
87
|
// Conversion Functions
|
|
95
88
|
// ============================================================================
|
|
@@ -105,7 +98,7 @@ function integerToWords (n) {
|
|
|
105
98
|
|
|
106
99
|
// Fast path: numbers < 1000 (direct lookup)
|
|
107
100
|
if (n < 1000n) {
|
|
108
|
-
return
|
|
101
|
+
return buildSegment(Number(n))
|
|
109
102
|
}
|
|
110
103
|
|
|
111
104
|
// Fast path: numbers < 1,000,000 (thousands)
|
|
@@ -114,11 +107,11 @@ function integerToWords (n) {
|
|
|
114
107
|
const remainder = Number(n % 1000n)
|
|
115
108
|
|
|
116
109
|
// Compound thousands: "ettusind", "firetusind"
|
|
117
|
-
let result =
|
|
110
|
+
let result = buildSegment(thousands) + THOUSAND
|
|
118
111
|
|
|
119
112
|
if (remainder > 0) {
|
|
120
113
|
// Add 'e' suffix and " og " for remainder: "firetusinde og ..."
|
|
121
|
-
result += 'e og ' +
|
|
114
|
+
result += 'e og ' + buildSegment(remainder)
|
|
122
115
|
}
|
|
123
116
|
|
|
124
117
|
return result
|
|
@@ -162,7 +155,7 @@ function buildLargeNumberWords (n) {
|
|
|
162
155
|
const segment = segments[i]
|
|
163
156
|
|
|
164
157
|
if (segment !== 0) {
|
|
165
|
-
const segmentWord =
|
|
158
|
+
const segmentWord = buildSegment(segment)
|
|
166
159
|
|
|
167
160
|
if (scaleIndex === 0) {
|
|
168
161
|
// Units segment
|
package/lib/languages/de.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* German 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
|
-
* Key
|
|
8
|
-
* This eliminates all per-call string manipulation for segment conversion.
|
|
9
|
-
*
|
|
10
|
-
* German-specific rules (handled in precomputation):
|
|
6
|
+
* Key features:
|
|
11
7
|
* - Inverted tens-ones order: "einundzwanzig" (one-and-twenty) for 21-99
|
|
12
8
|
* - Compound words without spaces below million level
|
|
13
9
|
* - Three forms of 1: "eins" (standalone), "ein" (before hundert/tausend), "eine" (before Million+)
|
|
14
10
|
* - Scale pluralization: Million → Millionen, Milliarde → Milliarden
|
|
15
11
|
* - Spaces only around million+ scale words
|
|
12
|
+
* - BigInt modulo for efficient segment extraction
|
|
16
13
|
*/
|
|
17
14
|
|
|
18
15
|
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
@@ -45,20 +42,22 @@ const NEGATIVE = 'minus'
|
|
|
45
42
|
const DECIMAL_SEP = 'komma'
|
|
46
43
|
|
|
47
44
|
// ============================================================================
|
|
48
|
-
//
|
|
45
|
+
// Segment Building
|
|
49
46
|
// ============================================================================
|
|
50
47
|
|
|
51
48
|
/**
|
|
52
49
|
* Builds segment word for 0-999 (standalone form, uses "eins").
|
|
53
50
|
* German inverts ones and tens: "einundzwanzig" = one-and-twenty
|
|
54
|
-
*
|
|
51
|
+
*
|
|
52
|
+
* @param {number} n - Number 0-999
|
|
53
|
+
* @returns {string} German words for the segment
|
|
55
54
|
*/
|
|
56
55
|
function buildSegment (n) {
|
|
57
56
|
if (n === 0) return ''
|
|
58
57
|
|
|
59
58
|
const ones = n % 10
|
|
60
|
-
const tens = Math.
|
|
61
|
-
const hundreds = Math.
|
|
59
|
+
const tens = Math.trunc(n / 10) % 10
|
|
60
|
+
const hundreds = Math.trunc(n / 100)
|
|
62
61
|
|
|
63
62
|
let result = ''
|
|
64
63
|
|
|
@@ -90,14 +89,17 @@ function buildSegment (n) {
|
|
|
90
89
|
/**
|
|
91
90
|
* Builds segment word for use before "tausend".
|
|
92
91
|
* Uses "ein" instead of "eins" for 1.
|
|
92
|
+
*
|
|
93
|
+
* @param {number} n - Number 0-999
|
|
94
|
+
* @returns {string} German words for thousand context
|
|
93
95
|
*/
|
|
94
96
|
function buildSegmentForThousand (n) {
|
|
95
97
|
if (n === 0) return ''
|
|
96
98
|
if (n === 1) return EIN // "eintausend"
|
|
97
99
|
|
|
98
100
|
const ones = n % 10
|
|
99
|
-
const tens = Math.
|
|
100
|
-
const hundreds = Math.
|
|
101
|
+
const tens = Math.trunc(n / 10) % 10
|
|
102
|
+
const hundreds = Math.trunc(n / 100)
|
|
101
103
|
|
|
102
104
|
let result = ''
|
|
103
105
|
|
|
@@ -112,8 +114,6 @@ function buildSegmentForThousand (n) {
|
|
|
112
114
|
} else if (tens >= 2) {
|
|
113
115
|
result += TENS[tens]
|
|
114
116
|
} else if (ones > 0 && hundreds > 0) {
|
|
115
|
-
// After hundreds, ones 1 stays "eins" when not followed by scale
|
|
116
|
-
// But we're going to tausend, so this path won't hit for n=1
|
|
117
117
|
result += ONES[ones]
|
|
118
118
|
} else if (ones > 0) {
|
|
119
119
|
result += ONES[ones]
|
|
@@ -122,18 +122,6 @@ function buildSegmentForThousand (n) {
|
|
|
122
122
|
return result
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
// Precompute all 1000 segment words (0-999) - standalone form
|
|
126
|
-
const SEGMENTS = new Array(1000)
|
|
127
|
-
for (let i = 0; i < 1000; i++) {
|
|
128
|
-
SEGMENTS[i] = buildSegment(i)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Precompute all 1000 segment words for thousand context
|
|
132
|
-
const SEGMENTS_THOUSAND = new Array(1000)
|
|
133
|
-
for (let i = 0; i < 1000; i++) {
|
|
134
|
-
SEGMENTS_THOUSAND[i] = buildSegmentForThousand(i)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
125
|
// ============================================================================
|
|
138
126
|
// Conversion Functions
|
|
139
127
|
// ============================================================================
|
|
@@ -147,21 +135,21 @@ for (let i = 0; i < 1000; i++) {
|
|
|
147
135
|
function integerToWords (n) {
|
|
148
136
|
if (n === 0n) return ZERO
|
|
149
137
|
|
|
150
|
-
// Fast path: numbers < 1000
|
|
138
|
+
// Fast path: numbers < 1000
|
|
151
139
|
if (n < 1000n) {
|
|
152
|
-
return
|
|
140
|
+
return buildSegment(Number(n))
|
|
153
141
|
}
|
|
154
142
|
|
|
155
|
-
// Fast path: numbers < 1,000,000
|
|
143
|
+
// Fast path: numbers < 1,000,000
|
|
156
144
|
if (n < 1_000_000n) {
|
|
157
145
|
const thousands = Number(n / 1000n)
|
|
158
146
|
const remainder = Number(n % 1000n)
|
|
159
147
|
|
|
160
148
|
// Compound: "eintausendzweihundert" (no spaces)
|
|
161
|
-
let result =
|
|
149
|
+
let result = buildSegmentForThousand(thousands) + SCALES[0]
|
|
162
150
|
|
|
163
151
|
if (remainder > 0) {
|
|
164
|
-
result +=
|
|
152
|
+
result += buildSegment(remainder)
|
|
165
153
|
}
|
|
166
154
|
|
|
167
155
|
return result
|
|
@@ -206,10 +194,10 @@ function buildLargeNumberWords (n) {
|
|
|
206
194
|
if (segment !== 0) {
|
|
207
195
|
if (scaleIndex === 0) {
|
|
208
196
|
// Units segment (no scale word)
|
|
209
|
-
parts.push({ words:
|
|
197
|
+
parts.push({ words: buildSegment(segment), isScale: false, scaleLevel: 0 })
|
|
210
198
|
} else if (scaleIndex === 1) {
|
|
211
199
|
// Thousands: compound without space
|
|
212
|
-
const segWords =
|
|
200
|
+
const segWords = buildSegmentForThousand(segment)
|
|
213
201
|
parts.push({ words: segWords + SCALES[0], isScale: false, scaleLevel: 1 })
|
|
214
202
|
} else {
|
|
215
203
|
// Million+ : space around scale word
|
|
@@ -217,7 +205,7 @@ function buildLargeNumberWords (n) {
|
|
|
217
205
|
if (segment === 1) {
|
|
218
206
|
segWords = 'eine' // "eine Million"
|
|
219
207
|
} else {
|
|
220
|
-
segWords =
|
|
208
|
+
segWords = buildSegment(segment)
|
|
221
209
|
}
|
|
222
210
|
const scaleWord = segment === 1 ? SCALES[scaleIndex - 1] : SCALES_PLURAL[scaleIndex - 1]
|
|
223
211
|
parts.push({ words: segWords, isScale: false, scaleLevel: scaleIndex })
|
package/lib/languages/el.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Greek language converter - Functional Implementation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Self-contained module with its own input validation, ready for subpath exports.
|
|
5
5
|
*
|
|
6
6
|
* Key features:
|
|
7
7
|
* - Space-separated number composition
|
|
@@ -35,7 +35,7 @@ const DECIMAL_SEP = 'κόμμα'
|
|
|
35
35
|
const SCALES = ['εκατομμύριο', 'δισεκατομμύριο', 'τρισεκατομμύριο']
|
|
36
36
|
|
|
37
37
|
// ============================================================================
|
|
38
|
-
//
|
|
38
|
+
// Segment Building
|
|
39
39
|
// ============================================================================
|
|
40
40
|
|
|
41
41
|
/**
|
|
@@ -73,13 +73,6 @@ function buildSegment (n) {
|
|
|
73
73
|
return parts.join(' ')
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
// Precompute all 1000 segment words (0-999)
|
|
77
|
-
const SEGMENTS = new Array(1000)
|
|
78
|
-
|
|
79
|
-
for (let i = 0; i < 1000; i++) {
|
|
80
|
-
SEGMENTS[i] = buildSegment(i)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
76
|
// ============================================================================
|
|
84
77
|
// Conversion Functions
|
|
85
78
|
// ============================================================================
|
|
@@ -95,7 +88,7 @@ function integerToWords (n) {
|
|
|
95
88
|
|
|
96
89
|
// Fast path: numbers < 1000 (direct lookup)
|
|
97
90
|
if (n < 1000n) {
|
|
98
|
-
return
|
|
91
|
+
return buildSegment(Number(n))
|
|
99
92
|
}
|
|
100
93
|
|
|
101
94
|
// Fast path: numbers < 1,000,000 (thousands)
|
|
@@ -108,11 +101,11 @@ function integerToWords (n) {
|
|
|
108
101
|
if (thousands === 1) {
|
|
109
102
|
result = THOUSAND
|
|
110
103
|
} else {
|
|
111
|
-
result =
|
|
104
|
+
result = buildSegment(thousands) + ' ' + THOUSAND
|
|
112
105
|
}
|
|
113
106
|
|
|
114
107
|
if (remainder > 0) {
|
|
115
|
-
result += ' ' +
|
|
108
|
+
result += ' ' + buildSegment(remainder)
|
|
116
109
|
}
|
|
117
110
|
|
|
118
111
|
return result
|
|
@@ -155,7 +148,7 @@ function buildLargeNumberWords (n) {
|
|
|
155
148
|
const segment = segments[i]
|
|
156
149
|
|
|
157
150
|
if (segment !== 0) {
|
|
158
|
-
const segmentWord =
|
|
151
|
+
const segmentWord = buildSegment(segment)
|
|
159
152
|
|
|
160
153
|
if (scaleIndex === 0) {
|
|
161
154
|
// Units segment
|