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/ta.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tamil 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 (ஆயிரம், லட்சம், கோடி)
|
|
8
8
|
* - Tamil script
|
|
9
9
|
* - 3-2-2 grouping pattern
|
|
10
10
|
* - Complete word forms for 0-99
|
|
11
|
-
* - Special hundred word transformations
|
|
11
|
+
* - Special hundred word transformations (connected vs standalone)
|
|
12
12
|
* - Per-digit decimal reading
|
|
13
|
+
* - BigInt modulo for efficient segment extraction
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
@@ -48,35 +49,16 @@ const ONES = ['ஒன்று', 'இரண்டு', 'மூன்று', '
|
|
|
48
49
|
const SCALE_WORDS = ['', 'ஆயிரம்', 'லட்சம்', 'கோடி', 'அரபு', 'கராபு', 'நீல்', 'பத்ம', 'சங்கு']
|
|
49
50
|
|
|
50
51
|
// ============================================================================
|
|
51
|
-
// Segment
|
|
52
|
+
// Segment Building
|
|
52
53
|
// ============================================================================
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const segments = []
|
|
62
|
-
const last3 = numStr.slice(-3)
|
|
63
|
-
segments.unshift(Number(last3))
|
|
64
|
-
|
|
65
|
-
let remaining = numStr.slice(0, -3)
|
|
66
|
-
while (remaining.length > 0) {
|
|
67
|
-
const segment = remaining.slice(-2)
|
|
68
|
-
segments.unshift(Number(segment))
|
|
69
|
-
remaining = remaining.slice(0, -2)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return segments
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// ============================================================================
|
|
76
|
-
// Conversion Functions
|
|
77
|
-
// ============================================================================
|
|
78
|
-
|
|
79
|
-
function convertBelowThousand (n) {
|
|
55
|
+
/**
|
|
56
|
+
* Builds words for a 0-999 segment.
|
|
57
|
+
*
|
|
58
|
+
* @param {number} n - Number 0-999
|
|
59
|
+
* @returns {string} Tamil words for the segment
|
|
60
|
+
*/
|
|
61
|
+
function buildSegment (n) {
|
|
80
62
|
if (n === 0) return ''
|
|
81
63
|
if (n < 100) return BELOW_HUNDRED[n]
|
|
82
64
|
|
|
@@ -87,30 +69,61 @@ function convertBelowThousand (n) {
|
|
|
87
69
|
return HUNDREDS[hundreds]
|
|
88
70
|
}
|
|
89
71
|
|
|
90
|
-
// Use
|
|
72
|
+
// Use connected form when followed by remainder
|
|
91
73
|
return HUNDREDS_CONNECTED[hundreds] + ' ' + BELOW_HUNDRED[remainder]
|
|
92
74
|
}
|
|
93
75
|
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// Conversion Functions
|
|
78
|
+
// ============================================================================
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Converts a non-negative integer to Tamil words.
|
|
82
|
+
*
|
|
83
|
+
* Uses BigInt modulo for segment extraction (faster than string slicing).
|
|
84
|
+
* South Asian 3-2-2 grouping: first 3 digits, then groups of 2.
|
|
85
|
+
*
|
|
86
|
+
* @param {bigint} n - Non-negative integer to convert
|
|
87
|
+
* @returns {string} Tamil words
|
|
88
|
+
*/
|
|
94
89
|
function integerToWords (n) {
|
|
95
90
|
if (n === 0n) return ZERO
|
|
96
91
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
// Fast path: numbers < 1000
|
|
93
|
+
if (n < 1000n) {
|
|
94
|
+
return buildSegment(Number(n))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Extract segments using BigInt modulo
|
|
98
|
+
const segments = []
|
|
99
|
+
segments.push(Number(n % 1000n))
|
|
100
|
+
let temp = n / 1000n
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
while (temp > 0n) {
|
|
103
|
+
segments.push(Number(temp % 100n))
|
|
104
|
+
temp = temp / 100n
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Build result string (process from most-significant to least)
|
|
108
|
+
const words = []
|
|
109
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
110
|
+
const segment = segments[i]
|
|
111
|
+
if (segment === 0) continue
|
|
112
|
+
|
|
113
|
+
if (i === 0) {
|
|
114
|
+
words.push(buildSegment(segment))
|
|
115
|
+
} else {
|
|
116
|
+
// Use 'ஒரு' for 1 at scale positions
|
|
117
|
+
const groupWords = (segment === 1) ? 'ஒரு' : BELOW_HUNDRED[segment]
|
|
118
|
+
words.push(groupWords)
|
|
119
|
+
}
|
|
104
120
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
words.push(groupWords)
|
|
108
|
-
if (scaleIndex > 0 && SCALE_WORDS[scaleIndex]) {
|
|
109
|
-
words.push(SCALE_WORDS[scaleIndex])
|
|
121
|
+
if (i > 0 && SCALE_WORDS[i]) {
|
|
122
|
+
words.push(SCALE_WORDS[i])
|
|
110
123
|
}
|
|
111
124
|
}
|
|
112
125
|
|
|
113
|
-
return words.join(' ')
|
|
126
|
+
return words.join(' ')
|
|
114
127
|
}
|
|
115
128
|
|
|
116
129
|
function decimalPartToWords (decimalPart) {
|
package/lib/languages/te.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telugu 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 (వెయ్యి, లక్ష, కోటి)
|
|
@@ -34,10 +34,6 @@ const BELOW_HUNDRED = [
|
|
|
34
34
|
'తొంభై', 'తొంభై ఒకటి', 'తొంభై రెండు', 'తొంభై మూడు', 'తొంభై నాలుగు', 'తొంభై ఐదు', 'తొంభై ఆరు', 'తొంభై ఏడు', 'తొంభై ఎనిమిది', 'తొంభై తొమ్మిది'
|
|
35
35
|
]
|
|
36
36
|
|
|
37
|
-
// ============================================================================
|
|
38
|
-
// Vocabulary (continued)
|
|
39
|
-
// ============================================================================
|
|
40
|
-
|
|
41
37
|
const HUNDREDS = ['', 'వంద', 'రెండు వందలు', 'మూడు వందలు', 'నాలుగు వందలు', 'ఐదు వందలు', 'ఆరు వందలు', 'ఏడు వందలు', 'ఎనిమిది వందలు', 'తొమ్మిది వందలు']
|
|
42
38
|
|
|
43
39
|
// Ones for decimal reading
|
|
@@ -47,13 +43,13 @@ const ONES = ['ఒకటి', 'రెండు', 'మూడు', 'నాలు
|
|
|
47
43
|
const SCALE_WORDS = ['', 'వెయ్యి', 'లక్ష', 'కోటి', 'అరబ్', 'ఖరబ్', 'నిల్', 'పడ్మ', 'శంకు']
|
|
48
44
|
|
|
49
45
|
// ============================================================================
|
|
50
|
-
//
|
|
46
|
+
// Segment Building
|
|
51
47
|
// ============================================================================
|
|
52
48
|
|
|
53
49
|
/**
|
|
54
|
-
*
|
|
50
|
+
* Builds words for a 0-999 segment.
|
|
55
51
|
*/
|
|
56
|
-
function
|
|
52
|
+
function buildSegment (n) {
|
|
57
53
|
if (n === 0) return ''
|
|
58
54
|
if (n < 100) return BELOW_HUNDRED[n]
|
|
59
55
|
|
|
@@ -66,9 +62,15 @@ function convertBelowThousand (n) {
|
|
|
66
62
|
return HUNDREDS[hundreds] + ' ' + BELOW_HUNDRED[remainder]
|
|
67
63
|
}
|
|
68
64
|
|
|
65
|
+
// ============================================================================
|
|
66
|
+
// Conversion Functions
|
|
67
|
+
// ============================================================================
|
|
68
|
+
|
|
69
69
|
/**
|
|
70
70
|
* Converts a non-negative integer to Telugu words.
|
|
71
|
-
*
|
|
71
|
+
*
|
|
72
|
+
* Uses BigInt modulo for segment extraction (faster than string slicing).
|
|
73
|
+
* South Asian 3-2-2 grouping: first 3 digits, then groups of 2.
|
|
72
74
|
*
|
|
73
75
|
* @param {bigint} n - Non-negative integer to convert
|
|
74
76
|
* @returns {string} Telugu words
|
|
@@ -78,49 +80,39 @@ function integerToWords (n) {
|
|
|
78
80
|
|
|
79
81
|
// Fast path: numbers < 1000 (direct lookup)
|
|
80
82
|
if (n < 1000n) {
|
|
81
|
-
return
|
|
83
|
+
return buildSegment(Number(n))
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
* Recursively builds words for numbers >= 1000.
|
|
89
|
-
* Indian grouping: first 3 digits, then 2-digit groups.
|
|
90
|
-
*
|
|
91
|
-
* @param {bigint} n - Number to convert
|
|
92
|
-
* @param {number} scale - Current scale index (0=units, 1=thousands, etc.)
|
|
93
|
-
* @returns {string} Telugu words
|
|
94
|
-
*/
|
|
95
|
-
function buildLargeNumberWords (n, scale) {
|
|
96
|
-
if (n === 0n) return ''
|
|
97
|
-
|
|
98
|
-
// Determine divisor: 1000 for first split, 100 for rest
|
|
99
|
-
const divisor = scale === 0 ? 1000n : 100n
|
|
100
|
-
const segment = Number(n % divisor)
|
|
101
|
-
const rest = n / divisor
|
|
86
|
+
// Extract segments using BigInt modulo
|
|
87
|
+
const segments = []
|
|
88
|
+
segments.push(Number(n % 1000n))
|
|
89
|
+
let temp = n / 1000n
|
|
102
90
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
result = buildLargeNumberWords(rest, scale + 1)
|
|
91
|
+
while (temp > 0n) {
|
|
92
|
+
segments.push(Number(temp % 100n))
|
|
93
|
+
temp = temp / 100n
|
|
107
94
|
}
|
|
108
95
|
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
112
101
|
|
|
113
|
-
if (
|
|
114
|
-
|
|
115
|
-
result += convertBelowThousand(segment)
|
|
102
|
+
if (i === 0) {
|
|
103
|
+
words.push(buildSegment(segment))
|
|
116
104
|
} else {
|
|
117
|
-
//
|
|
105
|
+
// Use 'ఒక' for 1 at scale positions
|
|
118
106
|
const groupWords = (segment === 1) ? 'ఒక' : BELOW_HUNDRED[segment]
|
|
119
|
-
|
|
107
|
+
words.push(groupWords)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (i > 0 && SCALE_WORDS[i]) {
|
|
111
|
+
words.push(SCALE_WORDS[i])
|
|
120
112
|
}
|
|
121
113
|
}
|
|
122
114
|
|
|
123
|
-
return
|
|
115
|
+
return words.join(' ')
|
|
124
116
|
}
|
|
125
117
|
|
|
126
118
|
function decimalPartToWords (decimalPart) {
|
package/lib/languages/tr.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Turkish 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
|
* - Omits 'bir' (one) before hundreds and thousands
|
|
@@ -32,7 +32,7 @@ const DECIMAL_SEP = 'virgül'
|
|
|
32
32
|
const SCALES = ['milyon', 'milyar', 'trilyon', 'katrilyon', 'kentilyon']
|
|
33
33
|
|
|
34
34
|
// ============================================================================
|
|
35
|
-
//
|
|
35
|
+
// Segment Building
|
|
36
36
|
// ============================================================================
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -75,15 +75,6 @@ function buildSegment (n, separator = ' ') {
|
|
|
75
75
|
return parts.join(separator)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
// Precompute all 1000 segment words (0-999) with space separator
|
|
79
|
-
const SEGMENTS = new Array(1000)
|
|
80
|
-
const SEGMENTS_NO_SPACE = new Array(1000)
|
|
81
|
-
|
|
82
|
-
for (let i = 0; i < 1000; i++) {
|
|
83
|
-
SEGMENTS[i] = buildSegment(i, ' ')
|
|
84
|
-
SEGMENTS_NO_SPACE[i] = buildSegment(i, '')
|
|
85
|
-
}
|
|
86
|
-
|
|
87
78
|
// ============================================================================
|
|
88
79
|
// Conversion Functions
|
|
89
80
|
// ============================================================================
|
|
@@ -99,11 +90,10 @@ function integerToWords (n, options = {}) {
|
|
|
99
90
|
if (n === 0n) return ZERO
|
|
100
91
|
|
|
101
92
|
const sep = options.dropSpaces ? '' : ' '
|
|
102
|
-
const segments = options.dropSpaces ? SEGMENTS_NO_SPACE : SEGMENTS
|
|
103
93
|
|
|
104
|
-
// Fast path: numbers < 1000
|
|
94
|
+
// Fast path: numbers < 1000
|
|
105
95
|
if (n < 1000n) {
|
|
106
|
-
return
|
|
96
|
+
return buildSegment(Number(n), sep)
|
|
107
97
|
}
|
|
108
98
|
|
|
109
99
|
// Fast path: numbers < 1,000,000 (thousands)
|
|
@@ -116,11 +106,11 @@ function integerToWords (n, options = {}) {
|
|
|
116
106
|
if (thousands === 1) {
|
|
117
107
|
result = THOUSAND
|
|
118
108
|
} else {
|
|
119
|
-
result =
|
|
109
|
+
result = buildSegment(thousands, sep) + sep + THOUSAND
|
|
120
110
|
}
|
|
121
111
|
|
|
122
112
|
if (remainder > 0) {
|
|
123
|
-
result += sep +
|
|
113
|
+
result += sep + buildSegment(remainder, sep)
|
|
124
114
|
}
|
|
125
115
|
|
|
126
116
|
return result
|
|
@@ -139,7 +129,6 @@ function integerToWords (n, options = {}) {
|
|
|
139
129
|
*/
|
|
140
130
|
function buildLargeNumberWords (n, options) {
|
|
141
131
|
const sep = options.dropSpaces ? '' : ' '
|
|
142
|
-
const segmentsArr = options.dropSpaces ? SEGMENTS_NO_SPACE : SEGMENTS
|
|
143
132
|
|
|
144
133
|
const numStr = n.toString()
|
|
145
134
|
const len = numStr.length
|
|
@@ -167,7 +156,7 @@ function buildLargeNumberWords (n, options) {
|
|
|
167
156
|
const segment = segments[i]
|
|
168
157
|
|
|
169
158
|
if (segment !== 0) {
|
|
170
|
-
const segmentWord =
|
|
159
|
+
const segmentWord = buildSegment(segment, sep)
|
|
171
160
|
|
|
172
161
|
if (scaleIndex === 0) {
|
|
173
162
|
// Units segment
|
package/lib/languages/uk.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Ukrainian 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
|
* - Three-form pluralization (one/few/many)
|
|
@@ -14,7 +14,38 @@ import { parseNumericValue } from '../utils/parse-numeric.js'
|
|
|
14
14
|
import { validateOptions } from '../utils/validate-options.js'
|
|
15
15
|
|
|
16
16
|
// ============================================================================
|
|
17
|
-
//
|
|
17
|
+
// Vocabulary
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
const ONES_MASC = ['', 'один', 'два', 'три', 'чотири', 'п\'ять', 'шiсть', 'сiм', 'вiсiм', 'дев\'ять']
|
|
21
|
+
const ONES_FEM = ['', 'одна', 'двi', 'три', 'чотири', 'п\'ять', 'шiсть', 'сiм', 'вiсiм', 'дев\'ять']
|
|
22
|
+
|
|
23
|
+
const TEENS = ['десять', 'одинадцять', 'дванадцять', 'тринадцять', 'чотирнадцять', 'п\'ятнадцять', 'шiстнадцять', 'сiмнадцять', 'вiсiмнадцять', 'дев\'ятнадцять']
|
|
24
|
+
const TENS = ['', '', 'двадцять', 'тридцять', 'сорок', 'п\'ятдесят', 'шiстдесят', 'сiмдесят', 'вiсiмдесят', 'дев\'яносто']
|
|
25
|
+
|
|
26
|
+
// Irregular hundreds
|
|
27
|
+
const HUNDREDS = ['', 'сто', 'двiстi', 'триста', 'чотириста', 'п\'ятсот', 'шiстсот', 'сiмсот', 'вiсiмсот', 'дев\'ятсот']
|
|
28
|
+
|
|
29
|
+
const ZERO = 'нуль'
|
|
30
|
+
const NEGATIVE = 'мiнус'
|
|
31
|
+
const DECIMAL_SEP = 'кома'
|
|
32
|
+
|
|
33
|
+
// Scale words: [singular, few, many]
|
|
34
|
+
// Thousands (index 0) are feminine, rest are masculine
|
|
35
|
+
const SCALE_FORMS = [
|
|
36
|
+
['тисяча', 'тисячi', 'тисяч'],
|
|
37
|
+
['мiльйон', 'мiльйони', 'мiльйонiв'],
|
|
38
|
+
['мiльярд', 'мiльярди', 'мiльярдiв'],
|
|
39
|
+
['трильйон', 'трильйони', 'трильйонiв'],
|
|
40
|
+
['квадрильйон', 'квадрильйони', 'квадрильйонiв'],
|
|
41
|
+
['квiнтильйон', 'квiнтильйони', 'квiнтильйонiв'],
|
|
42
|
+
['секстильйон', 'секстильйони', 'секстильйонiв'],
|
|
43
|
+
['септильйон', 'септильйони', 'септильйонiв'],
|
|
44
|
+
['октильйон', 'октильйони', 'октильйонiв']
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Segment Building
|
|
18
49
|
// ============================================================================
|
|
19
50
|
|
|
20
51
|
function pluralize (n, forms) {
|
|
@@ -31,19 +62,7 @@ function pluralize (n, forms) {
|
|
|
31
62
|
return forms[2]
|
|
32
63
|
}
|
|
33
64
|
|
|
34
|
-
function
|
|
35
|
-
const masc = new Array(1000)
|
|
36
|
-
const fem = new Array(1000)
|
|
37
|
-
|
|
38
|
-
for (let i = 0; i < 1000; i++) {
|
|
39
|
-
masc[i] = buildSegment(i, onesMasc, teens, tens, hundreds)
|
|
40
|
-
fem[i] = buildSegment(i, onesFem, teens, tens, hundreds)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return { masc, fem }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function buildSegment (n, ones, teens, tens, hundreds) {
|
|
65
|
+
function buildSegmentMasc (n) {
|
|
47
66
|
if (n === 0) return ''
|
|
48
67
|
|
|
49
68
|
const onesDigit = n % 10
|
|
@@ -53,58 +72,47 @@ function buildSegment (n, ones, teens, tens, hundreds) {
|
|
|
53
72
|
const parts = []
|
|
54
73
|
|
|
55
74
|
if (hundredsDigit > 0) {
|
|
56
|
-
parts.push(
|
|
75
|
+
parts.push(HUNDREDS[hundredsDigit])
|
|
57
76
|
}
|
|
58
77
|
|
|
59
78
|
if (tensDigit > 1) {
|
|
60
|
-
parts.push(
|
|
79
|
+
parts.push(TENS[tensDigit])
|
|
61
80
|
}
|
|
62
81
|
|
|
63
82
|
if (tensDigit === 1) {
|
|
64
|
-
parts.push(
|
|
83
|
+
parts.push(TEENS[onesDigit])
|
|
65
84
|
} else if (onesDigit > 0) {
|
|
66
|
-
parts.push(
|
|
85
|
+
parts.push(ONES_MASC[onesDigit])
|
|
67
86
|
}
|
|
68
87
|
|
|
69
88
|
return parts.join(' ')
|
|
70
89
|
}
|
|
71
90
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// ============================================================================
|
|
75
|
-
|
|
76
|
-
const ONES_MASC = ['', 'один', 'два', 'три', 'чотири', 'п\'ять', 'шiсть', 'сiм', 'вiсiм', 'дев\'ять']
|
|
77
|
-
const ONES_FEM = ['', 'одна', 'двi', 'три', 'чотири', 'п\'ять', 'шiсть', 'сiм', 'вiсiм', 'дев\'ять']
|
|
91
|
+
function buildSegmentFem (n) {
|
|
92
|
+
if (n === 0) return ''
|
|
78
93
|
|
|
79
|
-
const
|
|
80
|
-
const
|
|
94
|
+
const onesDigit = n % 10
|
|
95
|
+
const tensDigit = Math.floor(n / 10) % 10
|
|
96
|
+
const hundredsDigit = Math.floor(n / 100)
|
|
81
97
|
|
|
82
|
-
|
|
83
|
-
const HUNDREDS = ['', 'сто', 'двiстi', 'триста', 'чотириста', 'п\'ятсот', 'шiстсот', 'сiмсот', 'вiсiмсот', 'дев\'ятсот']
|
|
98
|
+
const parts = []
|
|
84
99
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
100
|
+
if (hundredsDigit > 0) {
|
|
101
|
+
parts.push(HUNDREDS[hundredsDigit])
|
|
102
|
+
}
|
|
88
103
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
['тисяча', 'тисячi', 'тисяч'],
|
|
93
|
-
['мiльйон', 'мiльйони', 'мiльйонiв'],
|
|
94
|
-
['мiльярд', 'мiльярди', 'мiльярдiв'],
|
|
95
|
-
['трильйон', 'трильйони', 'трильйонiв'],
|
|
96
|
-
['квадрильйон', 'квадрильйони', 'квадрильйонiв'],
|
|
97
|
-
['квiнтильйон', 'квiнтильйони', 'квiнтильйонiв'],
|
|
98
|
-
['секстильйон', 'секстильйони', 'секстильйонiв'],
|
|
99
|
-
['септильйон', 'септильйони', 'септильйонiв'],
|
|
100
|
-
['октильйон', 'октильйони', 'октильйонiв']
|
|
101
|
-
]
|
|
104
|
+
if (tensDigit > 1) {
|
|
105
|
+
parts.push(TENS[tensDigit])
|
|
106
|
+
}
|
|
102
107
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
if (tensDigit === 1) {
|
|
109
|
+
parts.push(TEENS[onesDigit])
|
|
110
|
+
} else if (onesDigit > 0) {
|
|
111
|
+
parts.push(ONES_FEM[onesDigit])
|
|
112
|
+
}
|
|
106
113
|
|
|
107
|
-
|
|
114
|
+
return parts.join(' ')
|
|
115
|
+
}
|
|
108
116
|
|
|
109
117
|
// ============================================================================
|
|
110
118
|
// Conversion Functions
|
|
@@ -114,8 +122,7 @@ function integerToWords (n, options = {}) {
|
|
|
114
122
|
if (n === 0n) return ZERO
|
|
115
123
|
|
|
116
124
|
if (n < 1000n) {
|
|
117
|
-
|
|
118
|
-
return segments[Number(n)]
|
|
125
|
+
return options.gender === 'feminine' ? buildSegmentFem(Number(n)) : buildSegmentMasc(Number(n))
|
|
119
126
|
}
|
|
120
127
|
|
|
121
128
|
return buildLargeNumberWords(n, options)
|
|
@@ -147,15 +154,14 @@ function buildLargeNumberWords (n, options) {
|
|
|
147
154
|
|
|
148
155
|
if (segment !== 0) {
|
|
149
156
|
if (scaleIndex === 0) {
|
|
150
|
-
|
|
151
|
-
parts.push(segmentWords[segment])
|
|
157
|
+
parts.push(options.gender === 'feminine' ? buildSegmentFem(segment) : buildSegmentMasc(segment))
|
|
152
158
|
} else {
|
|
153
159
|
const scaleForms = SCALE_FORMS[scaleIndex - 1]
|
|
154
160
|
const scaleWord = pluralize(segment, scaleForms)
|
|
155
161
|
// Thousands (scaleIndex=1) are feminine, others masculine
|
|
156
162
|
const isFeminine = scaleIndex === 1
|
|
157
|
-
const
|
|
158
|
-
parts.push(
|
|
163
|
+
const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
|
|
164
|
+
parts.push(segmentWord + ' ' + scaleWord)
|
|
159
165
|
}
|
|
160
166
|
}
|
|
161
167
|
|
package/lib/languages/ur.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Urdu 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 Urdu 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} Urdu 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} Urdu 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) {
|