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/ms.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Malay (Bahasa Melayu) 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 ALL singular scale units (seratus, seribu, sejuta, sebilion)
|
|
@@ -29,7 +29,7 @@ const NEGATIVE = 'minus'
|
|
|
29
29
|
const DECIMAL_SEP = 'perpuluhan'
|
|
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,12 +126,12 @@ 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
|
// Malay: "se-" prefix for ALL scale words when segment is 1
|
|
@@ -144,7 +139,7 @@ function buildLargeNumberWords (n) {
|
|
|
144
139
|
if (segment === 1) {
|
|
145
140
|
parts.push('se' + scaleWord)
|
|
146
141
|
} else {
|
|
147
|
-
parts.push(
|
|
142
|
+
parts.push(buildSegment(segment) + ' ' + scaleWord)
|
|
148
143
|
}
|
|
149
144
|
}
|
|
150
145
|
}
|
package/lib/languages/nb.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Norwegian Bokmål 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
|
* - Hyphenated tens+ones: "tjue-en" (21)
|
|
@@ -32,7 +32,7 @@ const DECIMAL_SEP = 'komma'
|
|
|
32
32
|
const SCALES = ['million', 'milliard', 'billion', 'billiard', 'kvintillion', 'sekstillion', 'septillion', 'oktillion']
|
|
33
33
|
|
|
34
34
|
// ============================================================================
|
|
35
|
-
//
|
|
35
|
+
// Segment Building
|
|
36
36
|
// ============================================================================
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -81,16 +81,6 @@ function buildSegment (n) {
|
|
|
81
81
|
return { word: parts[0] || '', hasHundred }
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
// Precompute all 1000 segment words (0-999)
|
|
85
|
-
const SEGMENTS = new Array(1000)
|
|
86
|
-
const SEGMENTS_HAS_HUNDRED = new Array(1000)
|
|
87
|
-
|
|
88
|
-
for (let i = 0; i < 1000; i++) {
|
|
89
|
-
const result = buildSegment(i)
|
|
90
|
-
SEGMENTS[i] = result.word
|
|
91
|
-
SEGMENTS_HAS_HUNDRED[i] = result.hasHundred
|
|
92
|
-
}
|
|
93
|
-
|
|
94
84
|
// ============================================================================
|
|
95
85
|
// Conversion Functions
|
|
96
86
|
// ============================================================================
|
|
@@ -104,9 +94,9 @@ for (let i = 0; i < 1000; i++) {
|
|
|
104
94
|
function integerToWords (n) {
|
|
105
95
|
if (n === 0n) return ZERO
|
|
106
96
|
|
|
107
|
-
// Fast path: numbers < 1000
|
|
97
|
+
// Fast path: numbers < 1000
|
|
108
98
|
if (n < 1000n) {
|
|
109
|
-
return
|
|
99
|
+
return buildSegment(Number(n)).word
|
|
110
100
|
}
|
|
111
101
|
|
|
112
102
|
// Fast path: numbers < 1,000,000 (thousands)
|
|
@@ -114,14 +104,15 @@ function integerToWords (n) {
|
|
|
114
104
|
const thousands = Number(n / 1000n)
|
|
115
105
|
const remainder = Number(n % 1000n)
|
|
116
106
|
|
|
117
|
-
let result =
|
|
107
|
+
let result = buildSegment(thousands).word + ' ' + THOUSAND
|
|
118
108
|
|
|
119
109
|
if (remainder > 0) {
|
|
110
|
+
const remainderResult = buildSegment(remainder)
|
|
120
111
|
// Comma before hundreds, " og " before small numbers
|
|
121
|
-
if (
|
|
122
|
-
result += ', ' +
|
|
112
|
+
if (remainderResult.hasHundred) {
|
|
113
|
+
result += ', ' + remainderResult.word
|
|
123
114
|
} else {
|
|
124
|
-
result += ' og ' +
|
|
115
|
+
result += ' og ' + remainderResult.word
|
|
125
116
|
}
|
|
126
117
|
}
|
|
127
118
|
|
|
@@ -165,19 +156,18 @@ function buildLargeNumberWords (n) {
|
|
|
165
156
|
const segment = segments[i]
|
|
166
157
|
|
|
167
158
|
if (segment !== 0) {
|
|
168
|
-
const
|
|
169
|
-
const hasHundred = SEGMENTS_HAS_HUNDRED[segment]
|
|
159
|
+
const segmentResult = buildSegment(segment)
|
|
170
160
|
|
|
171
161
|
if (scaleIndex === 0) {
|
|
172
162
|
// Units segment
|
|
173
|
-
parts.push({ word:
|
|
163
|
+
parts.push({ word: segmentResult.word, hasHundred: segmentResult.hasHundred, type: 'units' })
|
|
174
164
|
} else if (scaleIndex === 1) {
|
|
175
165
|
// Thousands
|
|
176
|
-
parts.push({ word:
|
|
166
|
+
parts.push({ word: segmentResult.word + ' ' + THOUSAND, hasHundred: false, type: 'thousand' })
|
|
177
167
|
} else {
|
|
178
168
|
// Millions+
|
|
179
169
|
const scaleWord = SCALES[scaleIndex - 2]
|
|
180
|
-
parts.push({ word:
|
|
170
|
+
parts.push({ word: segmentResult.word + ' ' + scaleWord, hasHundred: false, type: 'million' })
|
|
181
171
|
}
|
|
182
172
|
}
|
|
183
173
|
|
package/lib/languages/nl.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dutch 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
|
-
* Dutch-specific rules (handled in precomputation):
|
|
6
|
+
* Dutch-specific rules:
|
|
11
7
|
* - Inverted tens-ones: eenentwintig (one-and-twenty)
|
|
12
8
|
* - "ën" connector when ones ends in 'e' (twee, drie)
|
|
13
9
|
* - Compound words without spaces
|
|
@@ -38,7 +34,7 @@ const NEGATIVE = 'min'
|
|
|
38
34
|
const DECIMAL_SEP = 'komma'
|
|
39
35
|
|
|
40
36
|
// ============================================================================
|
|
41
|
-
//
|
|
37
|
+
// Segment Building
|
|
42
38
|
// ============================================================================
|
|
43
39
|
|
|
44
40
|
/**
|
|
@@ -98,18 +94,6 @@ function buildSegment (n, withAnd) {
|
|
|
98
94
|
return result
|
|
99
95
|
}
|
|
100
96
|
|
|
101
|
-
// Precompute all 1000 segment words (0-999) - standard form
|
|
102
|
-
const SEGMENTS = new Array(1000)
|
|
103
|
-
for (let i = 0; i < 1000; i++) {
|
|
104
|
-
SEGMENTS[i] = buildSegment(i, false)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Precompute all 1000 segment words (0-999) - with optional "en"
|
|
108
|
-
const SEGMENTS_WITH_AND = new Array(1000)
|
|
109
|
-
for (let i = 0; i < 1000; i++) {
|
|
110
|
-
SEGMENTS_WITH_AND[i] = buildSegment(i, true)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
97
|
// ============================================================================
|
|
114
98
|
// Conversion Functions
|
|
115
99
|
// ============================================================================
|
|
@@ -125,7 +109,6 @@ function integerToWords (n, options) {
|
|
|
125
109
|
if (n === 0n) return ZERO
|
|
126
110
|
|
|
127
111
|
const { accentOne, includeOptionalAnd, noHundredPairing } = options
|
|
128
|
-
const segments = includeOptionalAnd ? SEGMENTS_WITH_AND : SEGMENTS
|
|
129
112
|
|
|
130
113
|
// Apply één/een replacement
|
|
131
114
|
const applyAccent = (word) => {
|
|
@@ -135,9 +118,9 @@ function integerToWords (n, options) {
|
|
|
135
118
|
return word
|
|
136
119
|
}
|
|
137
120
|
|
|
138
|
-
// Fast path: numbers < 1000
|
|
121
|
+
// Fast path: numbers < 1000
|
|
139
122
|
if (n < 1000n) {
|
|
140
|
-
return applyAccent(
|
|
123
|
+
return applyAccent(buildSegment(Number(n), includeOptionalAnd))
|
|
141
124
|
}
|
|
142
125
|
|
|
143
126
|
// Hundred pairing for 1100-9999
|
|
@@ -147,9 +130,9 @@ function integerToWords (n, options) {
|
|
|
147
130
|
|
|
148
131
|
// Only use pairing when high is not a multiple of 10
|
|
149
132
|
if (high % 10 !== 0) {
|
|
150
|
-
let result =
|
|
133
|
+
let result = buildSegment(high, includeOptionalAnd) + HUNDRED
|
|
151
134
|
if (low > 0) {
|
|
152
|
-
const lowWord =
|
|
135
|
+
const lowWord = buildSegment(low, includeOptionalAnd)
|
|
153
136
|
if (includeOptionalAnd && low < 13) {
|
|
154
137
|
result += ' en ' + lowWord
|
|
155
138
|
} else {
|
|
@@ -171,11 +154,11 @@ function integerToWords (n, options) {
|
|
|
171
154
|
result = SCALES[0]
|
|
172
155
|
} else {
|
|
173
156
|
// Compound: "vijfduizend"
|
|
174
|
-
result =
|
|
157
|
+
result = buildSegment(thousands, includeOptionalAnd) + SCALES[0]
|
|
175
158
|
}
|
|
176
159
|
|
|
177
160
|
if (remainder > 0) {
|
|
178
|
-
const remainderWord =
|
|
161
|
+
const remainderWord = buildSegment(remainder, includeOptionalAnd)
|
|
179
162
|
if (includeOptionalAnd && remainder < 13) {
|
|
180
163
|
result += ' en ' + remainderWord
|
|
181
164
|
} else {
|
|
@@ -200,7 +183,6 @@ function integerToWords (n, options) {
|
|
|
200
183
|
*/
|
|
201
184
|
function buildLargeNumberWords (n, options) {
|
|
202
185
|
const { includeOptionalAnd } = options
|
|
203
|
-
const segmentLookup = includeOptionalAnd ? SEGMENTS_WITH_AND : SEGMENTS
|
|
204
186
|
|
|
205
187
|
// Extract segments using BigInt division (faster than string slicing)
|
|
206
188
|
// Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)
|
|
@@ -221,7 +203,7 @@ function buildLargeNumberWords (n, options) {
|
|
|
221
203
|
|
|
222
204
|
if (i === 0) {
|
|
223
205
|
// Units segment
|
|
224
|
-
const word =
|
|
206
|
+
const word = buildSegment(segment, includeOptionalAnd)
|
|
225
207
|
if (result) {
|
|
226
208
|
if (prevWasScale && includeOptionalAnd && segment < 13) {
|
|
227
209
|
result += ' en ' + word
|
|
@@ -238,7 +220,7 @@ function buildLargeNumberWords (n, options) {
|
|
|
238
220
|
if (segment === 1) {
|
|
239
221
|
result += SCALES[0]
|
|
240
222
|
} else {
|
|
241
|
-
result +=
|
|
223
|
+
result += buildSegment(segment, includeOptionalAnd) + SCALES[0]
|
|
242
224
|
}
|
|
243
225
|
prevWasScale = true
|
|
244
226
|
} else {
|
|
@@ -248,7 +230,7 @@ function buildLargeNumberWords (n, options) {
|
|
|
248
230
|
if (segment === 1) {
|
|
249
231
|
result += 'een ' + scaleWord
|
|
250
232
|
} else {
|
|
251
|
-
result +=
|
|
233
|
+
result += buildSegment(segment, includeOptionalAnd) + ' ' + scaleWord
|
|
252
234
|
}
|
|
253
235
|
prevWasScale = true
|
|
254
236
|
}
|
package/lib/languages/pa.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Punjabi 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,13 +38,13 @@ const BELOW_HUNDRED = [
|
|
|
38
38
|
const SCALE_WORDS = ['', 'ਹਜ਼ਾਰ', 'ਲੱਖ', 'ਕਰੋੜ', 'ਅਰਬ', 'ਖਰਬ', 'ਨੀਲ', 'ਪਦਮ', 'ਸ਼ੰਖ']
|
|
39
39
|
|
|
40
40
|
// ============================================================================
|
|
41
|
-
//
|
|
41
|
+
// Segment Building
|
|
42
42
|
// ============================================================================
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
45
|
+
* Builds words for a 0-999 segment.
|
|
46
46
|
*/
|
|
47
|
-
function
|
|
47
|
+
function buildSegment (n) {
|
|
48
48
|
if (n === 0) return ''
|
|
49
49
|
if (n < 100) return BELOW_HUNDRED[n]
|
|
50
50
|
|
|
@@ -57,9 +57,15 @@ function segmentToWords (n) {
|
|
|
57
57
|
return BELOW_HUNDRED[hundreds] + ' ' + HUNDRED + ' ' + BELOW_HUNDRED[remainder]
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// Conversion Functions
|
|
62
|
+
// ============================================================================
|
|
63
|
+
|
|
60
64
|
/**
|
|
61
65
|
* Converts a non-negative integer to Punjabi words.
|
|
62
|
-
*
|
|
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.
|
|
63
69
|
*
|
|
64
70
|
* @param {bigint} n - Non-negative integer to convert
|
|
65
71
|
* @returns {string} Punjabi words
|
|
@@ -69,48 +75,37 @@ function integerToWords (n) {
|
|
|
69
75
|
|
|
70
76
|
// Fast path: numbers < 1000 (direct lookup)
|
|
71
77
|
if (n < 1000n) {
|
|
72
|
-
return
|
|
78
|
+
return buildSegment(Number(n))
|
|
73
79
|
}
|
|
74
80
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
* Recursively builds words for numbers >= 1000.
|
|
80
|
-
* Indian grouping: first 3 digits, then 2-digit groups.
|
|
81
|
-
*
|
|
82
|
-
* @param {bigint} n - Number to convert
|
|
83
|
-
* @param {number} scale - Current scale index (0=units, 1=thousands, etc.)
|
|
84
|
-
* @returns {string} Punjabi words
|
|
85
|
-
*/
|
|
86
|
-
function buildLargeNumberWords (n, scale) {
|
|
87
|
-
if (n === 0n) return ''
|
|
88
|
-
|
|
89
|
-
// Determine divisor: 1000 for first split, 100 for rest
|
|
90
|
-
const divisor = scale === 0 ? 1000n : 100n
|
|
91
|
-
const segment = Number(n % divisor)
|
|
92
|
-
const rest = n / divisor
|
|
81
|
+
// Extract segments using BigInt modulo
|
|
82
|
+
const segments = []
|
|
83
|
+
segments.push(Number(n % 1000n))
|
|
84
|
+
let temp = n / 1000n
|
|
93
85
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
result = buildLargeNumberWords(rest, scale + 1)
|
|
86
|
+
while (temp > 0n) {
|
|
87
|
+
segments.push(Number(temp % 100n))
|
|
88
|
+
temp = temp / 100n
|
|
98
89
|
}
|
|
99
90
|
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
91
|
+
// Build result string (process from most-significant to least)
|
|
92
|
+
const words = []
|
|
93
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
94
|
+
const segment = segments[i]
|
|
95
|
+
if (segment === 0) continue
|
|
103
96
|
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
result += segmentToWords(segment)
|
|
97
|
+
if (i === 0) {
|
|
98
|
+
words.push(buildSegment(segment))
|
|
107
99
|
} else {
|
|
108
|
-
|
|
109
|
-
|
|
100
|
+
words.push(BELOW_HUNDRED[segment])
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (i > 0 && SCALE_WORDS[i]) {
|
|
104
|
+
words.push(SCALE_WORDS[i])
|
|
110
105
|
}
|
|
111
106
|
}
|
|
112
107
|
|
|
113
|
-
return
|
|
108
|
+
return words.join(' ')
|
|
114
109
|
}
|
|
115
110
|
|
|
116
111
|
function decimalPartToWords (decimalPart) {
|
package/lib/languages/pl.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Polish 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
|
-
* Polish-specific rules (handled in precomputation):
|
|
6
|
+
* Polish-specific rules:
|
|
11
7
|
* - Three-form pluralization: 1 = singular, 2-4 = few, 5+ = many
|
|
12
8
|
* - Gender agreement (masculine/feminine for numbers < 1000)
|
|
13
9
|
* - Omit "jeden" before scale words (tysiąc, milion, etc.)
|
|
@@ -51,7 +47,7 @@ const NEGATIVE = 'minus'
|
|
|
51
47
|
const DECIMAL_SEP = 'przecinek'
|
|
52
48
|
|
|
53
49
|
// ============================================================================
|
|
54
|
-
//
|
|
50
|
+
// Segment Building
|
|
55
51
|
// ============================================================================
|
|
56
52
|
|
|
57
53
|
/**
|
|
@@ -123,14 +119,6 @@ function buildSegmentFeminine (n) {
|
|
|
123
119
|
return parts.join(' ')
|
|
124
120
|
}
|
|
125
121
|
|
|
126
|
-
// Precompute all 1000 segment words (0-999)
|
|
127
|
-
const SEGMENTS_MASC = new Array(1000)
|
|
128
|
-
const SEGMENTS_FEM = new Array(1000)
|
|
129
|
-
for (let i = 0; i < 1000; i++) {
|
|
130
|
-
SEGMENTS_MASC[i] = buildSegment(i)
|
|
131
|
-
SEGMENTS_FEM[i] = buildSegmentFeminine(i)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
122
|
// ============================================================================
|
|
135
123
|
// Helper Functions
|
|
136
124
|
// ============================================================================
|
|
@@ -174,10 +162,9 @@ function pluralize (n, forms) {
|
|
|
174
162
|
function integerToWords (n, options = {}) {
|
|
175
163
|
if (n === 0n) return ZERO
|
|
176
164
|
|
|
177
|
-
// Fast path: numbers < 1000
|
|
165
|
+
// Fast path: numbers < 1000
|
|
178
166
|
if (n < 1000n) {
|
|
179
|
-
|
|
180
|
-
return segments[Number(n)]
|
|
167
|
+
return options.gender === 'feminine' ? buildSegmentFeminine(Number(n)) : buildSegment(Number(n))
|
|
181
168
|
}
|
|
182
169
|
|
|
183
170
|
// Fast path: numbers < 1,000,000 (thousands)
|
|
@@ -192,11 +179,11 @@ function integerToWords (n, options = {}) {
|
|
|
192
179
|
// Omit "jeden" before tysiąc
|
|
193
180
|
result = scaleWord
|
|
194
181
|
} else {
|
|
195
|
-
result =
|
|
182
|
+
result = buildSegment(thousands) + ' ' + scaleWord
|
|
196
183
|
}
|
|
197
184
|
|
|
198
185
|
if (remainder > 0) {
|
|
199
|
-
result += ' ' +
|
|
186
|
+
result += ' ' + buildSegment(remainder)
|
|
200
187
|
}
|
|
201
188
|
|
|
202
189
|
return result
|
|
@@ -231,7 +218,7 @@ function buildLargeNumberWords (n, options) {
|
|
|
231
218
|
const segment = segmentValues[i]
|
|
232
219
|
if (segment === 0n) continue
|
|
233
220
|
|
|
234
|
-
const segmentWord =
|
|
221
|
+
const segmentWord = buildSegment(Number(segment))
|
|
235
222
|
|
|
236
223
|
if (result) result += ' '
|
|
237
224
|
|
package/lib/languages/pt.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Portuguese 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
|
-
* Portuguese-specific rules (handled in precomputation):
|
|
6
|
+
* Portuguese-specific rules:
|
|
11
7
|
* - "e" conjunction everywhere: vinte e um, cento e um, mil e um
|
|
12
8
|
* - "cem" for exact 100, "cento" for 100+ remainder
|
|
13
9
|
* - Irregular hundreds: duzentos, trezentos, quatrocentos, etc.
|
|
@@ -34,7 +30,7 @@ const NEGATIVE = 'menos'
|
|
|
34
30
|
const DECIMAL_SEP = 'vírgula'
|
|
35
31
|
|
|
36
32
|
// ============================================================================
|
|
37
|
-
//
|
|
33
|
+
// Segment Building
|
|
38
34
|
// ============================================================================
|
|
39
35
|
|
|
40
36
|
/**
|
|
@@ -76,22 +72,11 @@ function buildSegment (n) {
|
|
|
76
72
|
// Join hundreds with "e": "cento e um", "duzentos e trinta e um"
|
|
77
73
|
const word = parts.join(' e ')
|
|
78
74
|
|
|
79
|
-
return { word, isExactHundred: hundreds > 0 && tens === 0 && ones === 0 }
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Precompute all 1000 segment words (0-999)
|
|
83
|
-
const SEGMENTS = new Array(1000)
|
|
84
|
-
const SEGMENTS_STARTS_WITH_HUNDREDS = new Array(1000)
|
|
85
|
-
|
|
86
|
-
for (let i = 0; i < 1000; i++) {
|
|
87
|
-
const result = buildSegment(i)
|
|
88
|
-
SEGMENTS[i] = result.word
|
|
89
|
-
// Precompute whether segment starts with hundreds (100-999)
|
|
90
|
-
SEGMENTS_STARTS_WITH_HUNDREDS[i] = i >= 100
|
|
75
|
+
return { word, isExactHundred: hundreds > 0 && tens === 0 && ones === 0, startsWithHundreds: n >= 100 }
|
|
91
76
|
}
|
|
92
77
|
|
|
93
78
|
// ============================================================================
|
|
94
|
-
// Scale Word Lookup
|
|
79
|
+
// Scale Word Lookup
|
|
95
80
|
// ============================================================================
|
|
96
81
|
|
|
97
82
|
// Precompute scale words for singular and plural forms
|
|
@@ -133,9 +118,9 @@ const SCALE_WORDS_PLURAL = [
|
|
|
133
118
|
function integerToWords (n) {
|
|
134
119
|
if (n === 0n) return ZERO
|
|
135
120
|
|
|
136
|
-
// Fast path: numbers < 1000
|
|
121
|
+
// Fast path: numbers < 1000
|
|
137
122
|
if (n < 1000n) {
|
|
138
|
-
return
|
|
123
|
+
return buildSegment(Number(n)).word
|
|
139
124
|
}
|
|
140
125
|
|
|
141
126
|
// Fast path: numbers < 1,000,000 (thousands)
|
|
@@ -148,15 +133,16 @@ function integerToWords (n) {
|
|
|
148
133
|
// "mil" not "um mil"
|
|
149
134
|
result = THOUSAND
|
|
150
135
|
} else {
|
|
151
|
-
result =
|
|
136
|
+
result = buildSegment(thousands).word + ' ' + THOUSAND
|
|
152
137
|
}
|
|
153
138
|
|
|
154
139
|
if (remainder > 0) {
|
|
140
|
+
const remainderResult = buildSegment(remainder)
|
|
155
141
|
// Insert "e" before remainder if it doesn't start with hundreds (< 100)
|
|
156
|
-
if (!
|
|
157
|
-
result += ' e ' +
|
|
142
|
+
if (!remainderResult.startsWithHundreds) {
|
|
143
|
+
result += ' e ' + remainderResult.word
|
|
158
144
|
} else {
|
|
159
|
-
result += ' ' +
|
|
145
|
+
result += ' ' + remainderResult.word
|
|
160
146
|
}
|
|
161
147
|
}
|
|
162
148
|
|
|
@@ -201,11 +187,11 @@ function buildLargeNumberWords (n) {
|
|
|
201
187
|
const segment = segments[i]
|
|
202
188
|
if (segment === 0) continue
|
|
203
189
|
|
|
204
|
-
const
|
|
190
|
+
const segmentResult = buildSegment(segment)
|
|
205
191
|
const isLastSegment = (i === firstNonZeroIdx)
|
|
206
192
|
|
|
207
193
|
// Add "e" before final segment if previous was scale and this doesn't start with hundreds
|
|
208
|
-
if (result && isLastSegment && prevWasScale && !
|
|
194
|
+
if (result && isLastSegment && prevWasScale && !segmentResult.startsWithHundreds) {
|
|
209
195
|
result += ' e'
|
|
210
196
|
}
|
|
211
197
|
|
|
@@ -213,23 +199,23 @@ function buildLargeNumberWords (n) {
|
|
|
213
199
|
|
|
214
200
|
if (i === 0) {
|
|
215
201
|
// Units segment
|
|
216
|
-
result +=
|
|
202
|
+
result += segmentResult.word
|
|
217
203
|
prevWasScale = false
|
|
218
204
|
} else if (i === 1) {
|
|
219
205
|
// Thousands
|
|
220
206
|
if (segment === 1) {
|
|
221
207
|
result += THOUSAND
|
|
222
208
|
} else {
|
|
223
|
-
result +=
|
|
209
|
+
result += segmentResult.word + ' ' + THOUSAND
|
|
224
210
|
}
|
|
225
211
|
prevWasScale = true
|
|
226
212
|
} else {
|
|
227
|
-
// Million and above - use
|
|
213
|
+
// Million and above - use scale arrays
|
|
228
214
|
const scaleWord = segment === 1 ? SCALE_WORDS_SINGULAR[i] : SCALE_WORDS_PLURAL[i]
|
|
229
215
|
if (segment === 1) {
|
|
230
216
|
result += 'um ' + scaleWord
|
|
231
217
|
} else {
|
|
232
|
-
result +=
|
|
218
|
+
result += segmentResult.word + ' ' + scaleWord
|
|
233
219
|
}
|
|
234
220
|
prevWasScale = true
|
|
235
221
|
}
|