numberstring 0.2.0 → 1.0.1

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.
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Portuguese number-to-words converter
3
+ * @module languages/pt
4
+ */
5
+
6
+ const PT_ONES = Object.freeze(['', 'um', 'dois', 'três', 'quatro', 'cinco', 'seis', 'sete', 'oito', 'nove']);
7
+ const PT_TENS = Object.freeze(['', '', 'vinte', 'trinta', 'quarenta', 'cinquenta', 'sessenta', 'setenta', 'oitenta', 'noventa']);
8
+ const PT_TEENS = Object.freeze(['dez', 'onze', 'doze', 'treze', 'catorze', 'quinze', 'dezesseis', 'dezessete', 'dezoito', 'dezenove']);
9
+ const PT_HUNDREDS = Object.freeze(['', 'cento', 'duzentos', 'trezentos', 'quatrocentos', 'quinhentos', 'seiscentos', 'setecentos', 'oitocentos', 'novecentos']);
10
+ const PT_ILLIONS = Object.freeze(['', 'mil', 'milhão', 'bilhão', 'trilhão', 'quatrilhão', 'quintilhão', 'sextilhão', 'septilhão', 'octilhão', 'nonilhão', 'decilhão']);
11
+ const PT_ILLIONS_PLURAL = Object.freeze(['', 'mil', 'milhões', 'bilhões', 'trilhões', 'quatrilhões', 'quintilhões', 'sextilhões', 'septilhões', 'octilhões', 'nonilhões', 'decilhões']);
12
+
13
+ const MAX_VALUE = 10n ** 36n - 1n;
14
+
15
+ const group = (n) => Math.ceil(n.toString().length / 3) - 1;
16
+ const power = (g) => 10n ** BigInt(g * 3);
17
+ const segment = (n, g) => n % power(g + 1);
18
+ const hundment = (n, g) => Number(segment(n, g) / power(g));
19
+
20
+ const hundredPt = (n) => {
21
+ if (n < 100 || n >= 1000) return '';
22
+ const h = Math.floor(n / 100);
23
+ const remainder = n % 100;
24
+ // "cem" when exactly 100, "cento" otherwise
25
+ if (h === 1 && remainder === 0) return 'cem';
26
+ return PT_HUNDREDS[h];
27
+ };
28
+
29
+ const tenPt = (n) => {
30
+ if (n === 0) return '';
31
+ if (n < 10) return PT_ONES[n];
32
+ if (n < 20) return PT_TEENS[n - 10];
33
+ const onesDigit = n % 10;
34
+ if (onesDigit) return `${PT_TENS[Math.floor(n / 10)]} e ${PT_ONES[onesDigit]}`;
35
+ return PT_TENS[Math.floor(n / 10)];
36
+ };
37
+
38
+ const cap = (str, style) => {
39
+ switch (style) {
40
+ case 'title':
41
+ return str.replace(/\w([^-\s]*)/g, (txt) =>
42
+ txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase()
43
+ );
44
+ case 'upper': return str.toUpperCase();
45
+ case 'lower': return str.toLowerCase();
46
+ default: return str;
47
+ }
48
+ };
49
+
50
+ /**
51
+ * Convert a number to Portuguese words
52
+ * @param {number|bigint} n - The number to convert
53
+ * @param {Object} [opt] - Options object
54
+ * @returns {string|false} The Portuguese word representation
55
+ *
56
+ * @example
57
+ * portuguese(42) // 'quarenta e dois'
58
+ * portuguese(1000) // 'mil'
59
+ */
60
+ const portuguese = (n, opt) => {
61
+ let num;
62
+
63
+ if (typeof n === 'bigint') {
64
+ if (n < 0n || n > MAX_VALUE) return false;
65
+ num = n;
66
+ } else if (typeof n === 'number') {
67
+ if (isNaN(n) || n < 0 || !Number.isInteger(n)) return false;
68
+ if (n > Number.MAX_SAFE_INTEGER) return false;
69
+ num = BigInt(n);
70
+ } else {
71
+ return false;
72
+ }
73
+
74
+ if (num === 0n) return 'zero';
75
+ if (num === 1n) return 'um';
76
+
77
+ const parts = [];
78
+
79
+ for (let i = group(num); i >= 0; i--) {
80
+ const h = hundment(num, i);
81
+ if (h > 0) {
82
+ let part = '';
83
+
84
+ const hundreds = Math.floor(h / 100);
85
+ const tens = h % 100;
86
+
87
+ if (hundreds > 0) {
88
+ part += hundredPt(h);
89
+ if (tens > 0) part += ' e ';
90
+ }
91
+
92
+ if (tens > 0) {
93
+ part += tenPt(tens);
94
+ }
95
+
96
+ if (i > 0) {
97
+ // Add scale word (mil, milhão, etc.)
98
+ if (i === 1) {
99
+ // "mil" doesn't change for plural and doesn't need "um" before it
100
+ if (h === 1) {
101
+ part = 'mil';
102
+ } else {
103
+ part += ' mil';
104
+ }
105
+ } else {
106
+ // milhão, bilhão, etc. - use singular for 1, plural for others
107
+ const scaleWord = h === 1 ? PT_ILLIONS[i] : PT_ILLIONS_PLURAL[i];
108
+ part += ` ${scaleWord}`;
109
+ }
110
+ }
111
+
112
+ parts.push(part);
113
+ }
114
+ }
115
+
116
+ // Join parts with appropriate connectors
117
+ let result = '';
118
+ for (let i = 0; i < parts.length; i++) {
119
+ if (i > 0) {
120
+ const prevPart = parts[i - 1];
121
+ const currPart = parts[i];
122
+
123
+ // Use "e" (and) for connecting in Portuguese when:
124
+ // 1. Current part has no scale word (final small numbers)
125
+ // 2. Previous part has a higher scale (milhão+) and current part has "mil"
126
+ const prevHasHigherScale = prevPart && (prevPart.includes('ilhão') || prevPart.includes('ilhões'));
127
+ const currHasMil = currPart && currPart.includes('mil');
128
+ const currHasNoScale = currPart && !currHasMil && !currPart.includes('ilhão') && !currPart.includes('ilhões');
129
+
130
+ if (currHasNoScale || (prevHasHigherScale && currHasMil)) {
131
+ result += ' e ';
132
+ } else {
133
+ result += ' ';
134
+ }
135
+ }
136
+ result += parts[i];
137
+ }
138
+
139
+ result = result.trim();
140
+ if (opt?.cap) result = cap(result, opt.cap);
141
+ return result;
142
+ };
143
+
144
+ export default portuguese;
145
+ export { portuguese };
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Russian number-to-words converter
3
+ * Handles Russian plural forms (1, 2-4, 5-20/0)
4
+ * @module languages/ru
5
+ */
6
+
7
+ const RU_ONES = Object.freeze(['', 'один', 'два', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять']);
8
+ const RU_ONES_FEM = Object.freeze(['', 'одна', 'две', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять']);
9
+ const RU_TENS = Object.freeze(['', '', 'двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят', 'семьдесят', 'восемьдесят', 'девяносто']);
10
+ const RU_TEENS = Object.freeze(['десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать', 'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать']);
11
+ const RU_HUNDREDS = Object.freeze(['', 'сто', 'двести', 'триста', 'четыреста', 'пятьсот', 'шестьсот', 'семьсот', 'восемьсот', 'девятьсот']);
12
+ const RU_ILLIONS = Object.freeze([
13
+ ['', '', ''], // ones
14
+ ['тысяча', 'тысячи', 'тысяч'], // thousands (feminine)
15
+ ['миллион', 'миллиона', 'миллионов'], // millions
16
+ ['миллиард', 'миллиарда', 'миллиардов'], // billions
17
+ ['триллион', 'триллиона', 'триллионов'], // trillions
18
+ ['квадриллион', 'квадриллиона', 'квадриллионов'], // quadrillions
19
+ ['квинтиллион', 'квинтиллиона', 'квинтиллионов'], // quintillions
20
+ ['секстиллион', 'секстиллиона', 'секстиллионов'], // sextillions
21
+ ['септиллион', 'септиллиона', 'септиллионов'], // septillions
22
+ ['октиллион', 'октиллиона', 'октиллионов'], // octillions
23
+ ['нониллион', 'нониллиона', 'нониллионов'], // nonillions
24
+ ['дециллион', 'дециллиона', 'дециллионов'] // decillions
25
+ ]);
26
+
27
+ const MAX_VALUE = 10n ** 36n - 1n;
28
+
29
+ const group = (n) => Math.ceil(n.toString().length / 3) - 1;
30
+ const power = (g) => 10n ** BigInt(g * 3);
31
+ const segment = (n, g) => n % power(g + 1);
32
+ const hundment = (n, g) => Number(segment(n, g) / power(g));
33
+ const tenment = (n, g) => hundment(n, g) % 100;
34
+
35
+ /**
36
+ * Get the correct Russian plural form based on number
37
+ * Russian has 3 forms: 1, 2-4, 5-20/0
38
+ */
39
+ const getRuPlural = (n, forms) => {
40
+ const abs = Math.abs(n);
41
+ const lastTwo = abs % 100;
42
+ const lastOne = abs % 10;
43
+
44
+ if (lastTwo >= 11 && lastTwo <= 19) {
45
+ return forms[2];
46
+ }
47
+ if (lastOne === 1) {
48
+ return forms[0];
49
+ }
50
+ if (lastOne >= 2 && lastOne <= 4) {
51
+ return forms[1];
52
+ }
53
+ return forms[2];
54
+ };
55
+
56
+ const tenRu = (n, feminine = false) => {
57
+ if (n === 0) return '';
58
+ if (n < 10) return feminine ? RU_ONES_FEM[n] : RU_ONES[n];
59
+ if (n < 20) return RU_TEENS[n - 10];
60
+ const onesDigit = n % 10;
61
+ const tensDigit = Math.floor(n / 10);
62
+ if (onesDigit === 0) return RU_TENS[tensDigit];
63
+ const oneWord = feminine ? RU_ONES_FEM[onesDigit] : RU_ONES[onesDigit];
64
+ return `${RU_TENS[tensDigit]} ${oneWord}`;
65
+ };
66
+
67
+ const hundredRu = (n) => {
68
+ if (n < 100 || n >= 1000) return '';
69
+ return RU_HUNDREDS[Math.floor(n / 100)];
70
+ };
71
+
72
+ /**
73
+ * Convert a number to Russian words
74
+ * @param {number|bigint} n - The number to convert
75
+ * @returns {string|false} The Russian word representation
76
+ *
77
+ * @example
78
+ * russian(42) // 'сорок два'
79
+ * russian(1000) // 'одна тысяча'
80
+ * russian(2000) // 'две тысячи'
81
+ */
82
+ const russian = (n) => {
83
+ let num;
84
+
85
+ if (typeof n === 'bigint') {
86
+ if (n < 0n || n > MAX_VALUE) return false;
87
+ num = n;
88
+ } else if (typeof n === 'number') {
89
+ if (isNaN(n) || n < 0 || !Number.isInteger(n)) return false;
90
+ if (n > Number.MAX_SAFE_INTEGER) return false;
91
+ num = BigInt(n);
92
+ } else {
93
+ return false;
94
+ }
95
+
96
+ if (num === 0n) return 'ноль';
97
+
98
+ let s = '';
99
+ for (let i = group(num); i >= 0; i--) {
100
+ const h = hundment(num, i);
101
+ if (h > 0) {
102
+ const isFeminine = i === 1;
103
+
104
+ const hund = hundredRu(h);
105
+ if (hund) s += hund + ' ';
106
+
107
+ const t = tenment(num, i);
108
+ const tenWord = tenRu(t, isFeminine);
109
+ if (tenWord) s += tenWord + ' ';
110
+
111
+ if (i > 0) {
112
+ const illionForms = RU_ILLIONS[i];
113
+ if (illionForms && illionForms[0]) {
114
+ const illion = getRuPlural(t || h, illionForms);
115
+ s += illion + ' ';
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ return s.trim().replace(/\s+/g, ' ');
122
+ };
123
+
124
+ export default russian;
125
+ export { russian };
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Swedish number-to-words converter
3
+ * Swedish uses long scale (miljon, miljard, biljon, etc.)
4
+ * @module languages/sv
5
+ */
6
+
7
+ const SV_ONES = Object.freeze(['', 'en', 'två', 'tre', 'fyra', 'fem', 'sex', 'sju', 'åtta', 'nio']);
8
+ const SV_TENS = Object.freeze(['', '', 'tjugo', 'trettio', 'fyrtio', 'femtio', 'sextio', 'sjuttio', 'åttio', 'nittio']);
9
+ const SV_TEENS = Object.freeze(['tio', 'elva', 'tolv', 'tretton', 'fjorton', 'femton', 'sexton', 'sjutton', 'arton', 'nitton']);
10
+ const SV_ILLIONS = Object.freeze(['', 'tusen', 'miljon', 'miljard', 'biljon', 'biljard', 'triljon', 'triljard', 'kvadriljon', 'kvadriljard', 'kvintiljon', 'kvintiljard']);
11
+ const SV_ILLIONS_PLURAL = Object.freeze(['', 'tusen', 'miljoner', 'miljarder', 'biljoner', 'biljarder', 'triljoner', 'triljarder', 'kvadriljoner', 'kvadriljarder', 'kvintiljoner', 'kvintiljarder']);
12
+
13
+ const MAX_VALUE = 10n ** 36n - 1n;
14
+
15
+ const group = (n) => Math.ceil(n.toString().length / 3) - 1;
16
+ const power = (g) => 10n ** BigInt(g * 3);
17
+ const segment = (n, g) => n % power(g + 1);
18
+ const hundment = (n, g) => Number(segment(n, g) / power(g));
19
+ const tenment = (n, g) => hundment(n, g) % 100;
20
+
21
+ const tenSv = (n) => {
22
+ if (n === 0) return '';
23
+ if (n < 10) return SV_ONES[n];
24
+ if (n < 20) return SV_TEENS[n - 10];
25
+ const onesDigit = n % 10;
26
+ const tensDigit = Math.floor(n / 10);
27
+ if (onesDigit === 0) return SV_TENS[tensDigit];
28
+ return `${SV_TENS[tensDigit]}${SV_ONES[onesDigit]}`;
29
+ };
30
+
31
+ const hundredSv = (n) => {
32
+ if (n < 100 || n >= 1000) return '';
33
+ const h = Math.floor(n / 100);
34
+ if (h === 1) return 'etthundra';
35
+ return `${SV_ONES[h]}hundra`;
36
+ };
37
+
38
+ /**
39
+ * Convert a number to Swedish words
40
+ * @param {number|bigint} n - The number to convert
41
+ * @returns {string|false} The Swedish word representation
42
+ *
43
+ * @example
44
+ * swedish(42) // 'fyrtiotvå'
45
+ * swedish(1000) // 'ettusen'
46
+ */
47
+ const swedish = (n) => {
48
+ let num;
49
+
50
+ if (typeof n === 'bigint') {
51
+ if (n < 0n || n > MAX_VALUE) return false;
52
+ num = n;
53
+ } else if (typeof n === 'number') {
54
+ if (isNaN(n) || n < 0 || !Number.isInteger(n)) return false;
55
+ if (n > Number.MAX_SAFE_INTEGER) return false;
56
+ num = BigInt(n);
57
+ } else {
58
+ return false;
59
+ }
60
+
61
+ if (num === 0n) return 'noll';
62
+ if (num === 1n) return 'en';
63
+
64
+ let s = '';
65
+ for (let i = group(num); i >= 0; i--) {
66
+ const h = hundment(num, i);
67
+ if (h > 0) {
68
+ if (i === 0) {
69
+ if (h >= 100) s += hundredSv(h) + ' ';
70
+ const t = tenment(num, i);
71
+ if (t > 0) s += tenSv(t);
72
+ } else if (i === 1) {
73
+ if (h === 1) {
74
+ s += 'ettusen ';
75
+ } else {
76
+ if (h >= 100) s += hundredSv(h) + ' ';
77
+ const t = tenment(num, i);
78
+ if (t > 0) s += tenSv(t);
79
+ s += 'tusen ';
80
+ }
81
+ } else {
82
+ if (h >= 100) s += hundredSv(h) + ' ';
83
+ const t = tenment(num, i);
84
+ if (t > 0) {
85
+ if (t === 1) {
86
+ s += 'en ';
87
+ } else {
88
+ s += tenSv(t) + ' ';
89
+ }
90
+ } else if (h < 100 && h >= 1 && h === 1) {
91
+ s += 'en ';
92
+ }
93
+ const illionWord = h === 1 ? SV_ILLIONS[i] : SV_ILLIONS_PLURAL[i];
94
+ s += `${illionWord} `;
95
+ }
96
+ }
97
+ }
98
+
99
+ return s.trim();
100
+ };
101
+
102
+ export default swedish;
103
+ export { swedish };
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Thai number-to-words converter
3
+ * Uses the lan (ล้าน) system for grouping by 1,000,000
4
+ * @module languages/th
5
+ */
6
+
7
+ const TH_ONES = Object.freeze(['', 'หนึ่ง', 'สอง', 'สาม', 'สี่', 'ห้า', 'หก', 'เจ็ด', 'แปด', 'เก้า']);
8
+ const TH_POSITIONS = Object.freeze(['', 'สิบ', 'ร้อย', 'พัน', 'หมื่น', 'แสน']);
9
+
10
+ /** Maximum supported value (10^36 - 1) */
11
+ const MAX_VALUE = 10n ** 36n - 1n;
12
+
13
+
14
+ /**
15
+ * Convert a group of up to 6 digits (0-999999) to Thai
16
+ * @param {number} grp - The group value (0-999999)
17
+ * @returns {string} The Thai representation
18
+ */
19
+ const groupToTh = (grp) => {
20
+ if (grp === 0) return '';
21
+
22
+ let result = '';
23
+ const digits = [];
24
+
25
+ // Extract digits from right to left (position 0 = ones, 5 = hundred-thousands)
26
+ let temp = grp;
27
+ for (let i = 0; i < 6; i++) {
28
+ digits[i] = temp % 10;
29
+ temp = Math.floor(temp / 10);
30
+ }
31
+
32
+ // Process from highest position to lowest
33
+ for (let pos = 5; pos >= 0; pos--) {
34
+ const d = digits[pos];
35
+ if (d === 0) continue;
36
+
37
+ if (pos === 0) {
38
+ // Ones place: use เอ็ด when tens digit or higher exists
39
+ if (d === 1 && grp > 1) {
40
+ result += 'เอ็ด';
41
+ } else {
42
+ result += TH_ONES[d];
43
+ }
44
+ } else if (pos === 1) {
45
+ // Tens place
46
+ if (d === 1) {
47
+ // 1 in tens place: just สิบ, not หนึ่งสิบ
48
+ result += 'สิบ';
49
+ } else if (d === 2) {
50
+ // 2 in tens place: ยี่สิบ, not สองสิบ
51
+ result += 'ยี่สิบ';
52
+ } else {
53
+ result += TH_ONES[d] + 'สิบ';
54
+ }
55
+ } else {
56
+ // Hundreds, thousands, ten-thousands, hundred-thousands
57
+ result += TH_ONES[d] + TH_POSITIONS[pos];
58
+ }
59
+ }
60
+
61
+ return result;
62
+ };
63
+
64
+ /**
65
+ * Convert a number to Thai words
66
+ * @param {number|bigint} n - The number to convert
67
+ * @returns {string|false} The Thai word representation
68
+ *
69
+ * @example
70
+ * thai(42) // 'สี่สิบสอง'
71
+ * thai(11) // 'สิบเอ็ด'
72
+ * thai(1000000) // 'หนึ่งล้าน'
73
+ */
74
+ const thai = (n) => {
75
+ let num;
76
+
77
+ if (typeof n === 'bigint') {
78
+ if (n < 0n || n > MAX_VALUE) return false;
79
+ num = n;
80
+ } else if (typeof n === 'number') {
81
+ if (isNaN(n) || n < 0 || !Number.isInteger(n)) return false;
82
+ if (n > Number.MAX_SAFE_INTEGER) return false;
83
+ num = BigInt(n);
84
+ } else {
85
+ return false;
86
+ }
87
+
88
+ if (num === 0n) return 'ศูนย์';
89
+
90
+ const str = num.toString();
91
+ const len = str.length;
92
+
93
+ // Split into groups of 6 from the right (ล้าน = million grouping)
94
+ const groups = [];
95
+ for (let i = len; i > 0; i -= 6) {
96
+ const start = Math.max(0, i - 6);
97
+ groups.unshift(str.slice(start, i));
98
+ }
99
+
100
+ let result = '';
101
+
102
+ for (let i = 0; i < groups.length; i++) {
103
+ const grp = parseInt(groups[i], 10);
104
+ const grpIdx = groups.length - 1 - i;
105
+
106
+ if (grp === 0) continue;
107
+
108
+ const grpStr = groupToTh(grp);
109
+ result += grpStr;
110
+
111
+ // Append ล้าน scale for each group above the lowest
112
+ if (grpIdx > 0) {
113
+ for (let j = 0; j < grpIdx; j++) {
114
+ result += 'ล้าน';
115
+ }
116
+ }
117
+ }
118
+
119
+ return result;
120
+ };
121
+
122
+ export default thai;
123
+ export { thai, TH_ONES, TH_POSITIONS, MAX_VALUE };
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Turkish number-to-words converter
3
+ * Turkish is very regular with simple concatenation rules
4
+ * @module languages/tr
5
+ */
6
+
7
+ const TR_ONES = Object.freeze(['', 'bir', 'iki', 'üç', 'dört', 'beş', 'altı', 'yedi', 'sekiz', 'dokuz']);
8
+ const TR_TENS = Object.freeze(['', 'on', 'yirmi', 'otuz', 'kırk', 'elli', 'altmış', 'yetmiş', 'seksen', 'doksan']);
9
+ const TR_ILLIONS = Object.freeze(['', 'bin', 'milyon', 'milyar', 'trilyon', 'katrilyon', 'kentilyon', 'sekstilyon', 'septilyon', 'oktilyon', 'nonilyon', 'desilyon']);
10
+
11
+ /** Maximum supported value (10^36 - 1, up to decillions) */
12
+ const MAX_VALUE = 10n ** 36n - 1n;
13
+
14
+ const group = (n) => Math.ceil(n.toString().length / 3) - 1;
15
+ const power = (g) => 10n ** BigInt(g * 3);
16
+ const segment = (n, g) => n % power(g + 1);
17
+ const hundment = (n, g) => Number(segment(n, g) / power(g));
18
+ const tenment = (n, g) => hundment(n, g) % 100;
19
+
20
+ const hundredTr = (n) => {
21
+ if (n < 100 || n >= 1000) return '';
22
+ const h = Math.floor(n / 100);
23
+ // 1 before yüz is omitted: just "yüz", not "bir yüz"
24
+ if (h === 1) return 'yüz';
25
+ return `${TR_ONES[h]} yüz`;
26
+ };
27
+
28
+ const tenTr = (n) => {
29
+ if (n === 0) return '';
30
+ if (n < 10) return TR_ONES[n];
31
+ const onesDigit = n % 10;
32
+ const tensDigit = Math.floor(n / 10);
33
+ if (onesDigit === 0) return TR_TENS[tensDigit];
34
+ return `${TR_TENS[tensDigit]} ${TR_ONES[onesDigit]}`;
35
+ };
36
+
37
+ /**
38
+ * Convert a number to Turkish words
39
+ * @param {number|bigint} n - The number to convert
40
+ * @returns {string|false} The Turkish word representation
41
+ *
42
+ * @example
43
+ * turkish(42) // 'kırk iki'
44
+ * turkish(1000) // 'bin'
45
+ * turkish(1000000) // 'bir milyon'
46
+ */
47
+ const turkish = (n) => {
48
+ let num;
49
+
50
+ if (typeof n === 'bigint') {
51
+ if (n < 0n || n > MAX_VALUE) return false;
52
+ num = n;
53
+ } else if (typeof n === 'number') {
54
+ if (isNaN(n) || n < 0 || !Number.isInteger(n)) return false;
55
+ if (n > Number.MAX_SAFE_INTEGER) return false;
56
+ num = BigInt(n);
57
+ } else {
58
+ return false;
59
+ }
60
+
61
+ if (num === 0n) return 'sıfır';
62
+
63
+ let s = '';
64
+ for (let i = group(num); i >= 0; i--) {
65
+ const h = hundment(num, i);
66
+ if (h === 0) continue;
67
+
68
+ if (i === 0) {
69
+ // Ones group: no scale word
70
+ const hund = hundredTr(h);
71
+ if (hund) s += hund + ' ';
72
+ const t = tenment(num, i);
73
+ const tenWord = tenTr(t);
74
+ if (tenWord) s += tenWord + ' ';
75
+ } else if (i === 1) {
76
+ // Thousands: 1 before bin is omitted
77
+ if (h === 1) {
78
+ s += 'bin ';
79
+ } else {
80
+ const hund = hundredTr(h);
81
+ if (hund) s += hund + ' ';
82
+ const t = tenment(num, i);
83
+ const tenWord = tenTr(t);
84
+ if (tenWord) s += tenWord + ' ';
85
+ s += 'bin ';
86
+ }
87
+ } else {
88
+ // Millions and above: 1 IS included ("bir milyon")
89
+ const hund = hundredTr(h);
90
+ if (hund) s += hund + ' ';
91
+ const t = tenment(num, i);
92
+ const tenWord = tenTr(t);
93
+ if (tenWord) s += tenWord + ' ';
94
+ s += `${TR_ILLIONS[i]} `;
95
+ }
96
+ }
97
+
98
+ return s.trim();
99
+ };
100
+
101
+ export default turkish;
102
+ export { turkish, TR_ONES, TR_TENS, TR_ILLIONS, MAX_VALUE };