@gks101/numtowords 1.0.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/README.md +248 -0
- package/dist/core/converter.js +75 -0
- package/dist/core/types.js +4 -0
- package/dist/index.cjs.js +547 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.esm.js +542 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.umd.js +553 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/locales/de.js +98 -0
- package/dist/locales/en.js +94 -0
- package/dist/locales/fr.js +116 -0
- package/dist/locales/hi.js +78 -0
- package/dist/locales/in.js +80 -0
- package/dist/types/core/converter.d.ts +21 -0
- package/dist/types/core/types.d.ts +31 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/locales/de.d.ts +3 -0
- package/dist/types/locales/en.d.ts +3 -0
- package/dist/types/locales/fr.d.ts +3 -0
- package/dist/types/locales/hi.d.ts +3 -0
- package/dist/types/locales/in.d.ts +3 -0
- package/package.json +50 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { registerLocale } from "../core/converter";
|
|
2
|
+
// ─── Word tables ──────────────────────────────────────────────────────────────
|
|
3
|
+
const ONES_DE = [
|
|
4
|
+
"", "ein", "zwei", "drei", "vier", "fünf",
|
|
5
|
+
"sechs", "sieben", "acht", "neun", "zehn",
|
|
6
|
+
"elf", "zwölf", "dreizehn", "vierzehn", "fünfzehn",
|
|
7
|
+
"sechzehn", "siebzehn", "achtzehn", "neunzehn",
|
|
8
|
+
];
|
|
9
|
+
const TENS_DE = [
|
|
10
|
+
"", "", "zwanzig", "dreißig", "vierzig", "fünfzig",
|
|
11
|
+
"sechzig", "siebzig", "achtzig", "neunzig",
|
|
12
|
+
];
|
|
13
|
+
/** German uses "und" between ones and tens: einundzwanzig */
|
|
14
|
+
function upToNinetyNine(n) {
|
|
15
|
+
if (n === 0)
|
|
16
|
+
return "";
|
|
17
|
+
if (n < 20)
|
|
18
|
+
return ONES_DE[n];
|
|
19
|
+
const t = Math.floor(n / 10);
|
|
20
|
+
const o = n % 10;
|
|
21
|
+
if (o === 0)
|
|
22
|
+
return TENS_DE[t];
|
|
23
|
+
return `${ONES_DE[o]}und${TENS_DE[t]}`;
|
|
24
|
+
}
|
|
25
|
+
function hundredsDE(n) {
|
|
26
|
+
if (n === 0)
|
|
27
|
+
return "";
|
|
28
|
+
const h = Math.floor(n / 100);
|
|
29
|
+
const rem = n % 100;
|
|
30
|
+
const parts = [];
|
|
31
|
+
if (h === 1)
|
|
32
|
+
parts.push("einhundert");
|
|
33
|
+
else if (h > 1)
|
|
34
|
+
parts.push(`${ONES_DE[h]}hundert`);
|
|
35
|
+
if (rem > 0)
|
|
36
|
+
parts.push(upToNinetyNine(rem));
|
|
37
|
+
return parts.join("");
|
|
38
|
+
}
|
|
39
|
+
const SCALES_DE = [
|
|
40
|
+
"tausend",
|
|
41
|
+
"Million",
|
|
42
|
+
"Milliarde",
|
|
43
|
+
"Billion",
|
|
44
|
+
"Billiarde",
|
|
45
|
+
"Trillion",
|
|
46
|
+
];
|
|
47
|
+
/** Scales that are grammatically feminine in German (require "eine" not "ein") */
|
|
48
|
+
const FEMININE_SCALES = new Set([1, 2, 4, 6]); // indices: Million, Milliarde, Billiarde, Trillion
|
|
49
|
+
const SCALES_DE_PLURAL = [
|
|
50
|
+
"tausend",
|
|
51
|
+
"Millionen",
|
|
52
|
+
"Milliarden",
|
|
53
|
+
"Billionen",
|
|
54
|
+
"Billiarden",
|
|
55
|
+
"Trillionen",
|
|
56
|
+
];
|
|
57
|
+
// ─── Locale definition ────────────────────────────────────────────────────────
|
|
58
|
+
const de = {
|
|
59
|
+
name: "German",
|
|
60
|
+
convert(n, _opts) {
|
|
61
|
+
if (n === 0n)
|
|
62
|
+
return "null";
|
|
63
|
+
const chunks = [];
|
|
64
|
+
let remaining = n;
|
|
65
|
+
let idx = 0;
|
|
66
|
+
while (remaining > 0n) {
|
|
67
|
+
chunks.unshift({ value: Number(remaining % 1000n), scale: idx });
|
|
68
|
+
remaining = remaining / 1000n;
|
|
69
|
+
idx++;
|
|
70
|
+
}
|
|
71
|
+
const parts = [];
|
|
72
|
+
for (const { value, scale } of chunks) {
|
|
73
|
+
if (value === 0)
|
|
74
|
+
continue;
|
|
75
|
+
const words = hundredsDE(value);
|
|
76
|
+
if (scale === 0) {
|
|
77
|
+
parts.push(words);
|
|
78
|
+
}
|
|
79
|
+
else if (scale === 1) {
|
|
80
|
+
// "tausend" compounds directly
|
|
81
|
+
parts.push(`${words}tausend`);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const scaleIdx = scale - 1;
|
|
85
|
+
const isFeminine = FEMININE_SCALES.has(scaleIdx);
|
|
86
|
+
// Replace trailing "ein" with "eine" for feminine scales
|
|
87
|
+
const adjustedWords = value === 1 && isFeminine
|
|
88
|
+
? words.replace(/ein$/, "eine")
|
|
89
|
+
: words;
|
|
90
|
+
const scaleName = value === 1 ? SCALES_DE[scaleIdx] : SCALES_DE_PLURAL[scaleIdx];
|
|
91
|
+
parts.push(`${adjustedWords} ${scaleName}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return parts.join(" ").trim();
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
registerLocale("de", de);
|
|
98
|
+
export default de;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { registerLocale } from "../core/converter";
|
|
2
|
+
// ─── Word tables ──────────────────────────────────────────────────────────────
|
|
3
|
+
const ONES = [
|
|
4
|
+
"", "one", "two", "three", "four", "five",
|
|
5
|
+
"six", "seven", "eight", "nine", "ten",
|
|
6
|
+
"eleven", "twelve", "thirteen", "fourteen", "fifteen",
|
|
7
|
+
"sixteen", "seventeen", "eighteen", "nineteen",
|
|
8
|
+
];
|
|
9
|
+
const TENS = [
|
|
10
|
+
"", "", "twenty", "thirty", "forty", "fifty",
|
|
11
|
+
"sixty", "seventy", "eighty", "ninety",
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Scale names in the short (American/modern British) scale.
|
|
15
|
+
* Index 0 = thousands, 1 = millions, …
|
|
16
|
+
*/
|
|
17
|
+
const SCALES = [
|
|
18
|
+
"thousand",
|
|
19
|
+
"million",
|
|
20
|
+
"billion",
|
|
21
|
+
"trillion",
|
|
22
|
+
"quadrillion",
|
|
23
|
+
"quintillion",
|
|
24
|
+
"sextillion",
|
|
25
|
+
"septillion",
|
|
26
|
+
"octillion",
|
|
27
|
+
"nonillion",
|
|
28
|
+
"decillion",
|
|
29
|
+
];
|
|
30
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
31
|
+
/** Convert 0–999 to words */
|
|
32
|
+
function hundredsToWords(n, useAnd) {
|
|
33
|
+
if (n === 0)
|
|
34
|
+
return "";
|
|
35
|
+
const parts = [];
|
|
36
|
+
const h = Math.floor(n / 100);
|
|
37
|
+
const remainder = n % 100;
|
|
38
|
+
if (h > 0) {
|
|
39
|
+
parts.push(`${ONES[h]} hundred`);
|
|
40
|
+
}
|
|
41
|
+
if (remainder === 0) {
|
|
42
|
+
// nothing more
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
if (h > 0 && useAnd)
|
|
46
|
+
parts.push("and");
|
|
47
|
+
if (remainder < 20) {
|
|
48
|
+
parts.push(ONES[remainder]);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
const t = Math.floor(remainder / 10);
|
|
52
|
+
const o = remainder % 10;
|
|
53
|
+
parts.push(o === 0 ? TENS[t] : `${TENS[t]}-${ONES[o]}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return parts.join(" ");
|
|
57
|
+
}
|
|
58
|
+
// ─── Locale definition ────────────────────────────────────────────────────────
|
|
59
|
+
const en = {
|
|
60
|
+
name: "English",
|
|
61
|
+
convert(n, opts) {
|
|
62
|
+
if (n === 0n)
|
|
63
|
+
return "zero";
|
|
64
|
+
const useAnd = opts.useAnd;
|
|
65
|
+
const chunks = [];
|
|
66
|
+
// Split number into groups of 3 from the right
|
|
67
|
+
let remaining = n;
|
|
68
|
+
const divisor = 1000n;
|
|
69
|
+
// chunk index: 0 = units, 1 = thousands, 2 = millions, …
|
|
70
|
+
let idx = 0;
|
|
71
|
+
while (remaining > 0n) {
|
|
72
|
+
chunks.unshift({ value: Number(remaining % divisor), scale: idx });
|
|
73
|
+
remaining = remaining / divisor;
|
|
74
|
+
idx++;
|
|
75
|
+
}
|
|
76
|
+
const parts = [];
|
|
77
|
+
for (const { value, scale } of chunks) {
|
|
78
|
+
if (value === 0)
|
|
79
|
+
continue;
|
|
80
|
+
// useAnd within the hundreds group (e.g. "one hundred and five")
|
|
81
|
+
const words = hundredsToWords(value, useAnd);
|
|
82
|
+
if (scale === 0) {
|
|
83
|
+
parts.push(words);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
const scaleName = SCALES[scale - 1];
|
|
87
|
+
parts.push(`${words} ${scaleName}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return parts.join(" ");
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
registerLocale("en", en);
|
|
94
|
+
export default en;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { registerLocale } from "../core/converter";
|
|
2
|
+
// ─── Word tables ──────────────────────────────────────────────────────────────
|
|
3
|
+
const ONES_FR = [
|
|
4
|
+
"", "un", "deux", "trois", "quatre", "cinq",
|
|
5
|
+
"six", "sept", "huit", "neuf", "dix",
|
|
6
|
+
"onze", "douze", "treize", "quatorze", "quinze",
|
|
7
|
+
"seize", "dix-sept", "dix-huit", "dix-neuf",
|
|
8
|
+
];
|
|
9
|
+
const TENS_FR = [
|
|
10
|
+
"", "", "vingt", "trente", "quarante", "cinquante",
|
|
11
|
+
"soixante", "soixante", "quatre-vingt", "quatre-vingt",
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* French has irregular 70s (soixante-dix + 10–19) and 90s (quatre-vingt-dix + 10–19).
|
|
15
|
+
*/
|
|
16
|
+
function upToNinetyNine(n) {
|
|
17
|
+
if (n === 0)
|
|
18
|
+
return "";
|
|
19
|
+
if (n < 20)
|
|
20
|
+
return ONES_FR[n];
|
|
21
|
+
const t = Math.floor(n / 10);
|
|
22
|
+
const o = n % 10;
|
|
23
|
+
if (t === 7) {
|
|
24
|
+
// soixante + (10 + o)
|
|
25
|
+
return o === 0 ? "soixante-dix" : `soixante-${ONES_FR[10 + o]}`;
|
|
26
|
+
}
|
|
27
|
+
if (t === 8) {
|
|
28
|
+
// quatre-vingt(s) + o
|
|
29
|
+
if (o === 0)
|
|
30
|
+
return "quatre-vingts";
|
|
31
|
+
return `quatre-vingt-${ONES_FR[o]}`;
|
|
32
|
+
}
|
|
33
|
+
if (t === 9) {
|
|
34
|
+
// quatre-vingt-dix + (0 + o)
|
|
35
|
+
return o === 0 ? "quatre-vingt-dix" : `quatre-vingt-${ONES_FR[10 + o]}`;
|
|
36
|
+
}
|
|
37
|
+
if (o === 0)
|
|
38
|
+
return TENS_FR[t];
|
|
39
|
+
if (o === 1 && t !== 8)
|
|
40
|
+
return `${TENS_FR[t]}-et-${ONES_FR[o]}`;
|
|
41
|
+
return `${TENS_FR[t]}-${ONES_FR[o]}`;
|
|
42
|
+
}
|
|
43
|
+
function hundredsFR(n) {
|
|
44
|
+
if (n === 0)
|
|
45
|
+
return "";
|
|
46
|
+
const h = Math.floor(n / 100);
|
|
47
|
+
const rem = n % 100;
|
|
48
|
+
const parts = [];
|
|
49
|
+
if (h === 1) {
|
|
50
|
+
parts.push("cent");
|
|
51
|
+
}
|
|
52
|
+
else if (h > 1) {
|
|
53
|
+
parts.push(`${ONES_FR[h]} cents`);
|
|
54
|
+
}
|
|
55
|
+
if (rem > 0) {
|
|
56
|
+
// remove trailing 's' from "cents" if followed by more words
|
|
57
|
+
if (h > 1 && parts.length > 0) {
|
|
58
|
+
parts[0] = `${ONES_FR[h]} cent`;
|
|
59
|
+
}
|
|
60
|
+
parts.push(upToNinetyNine(rem));
|
|
61
|
+
}
|
|
62
|
+
return parts.join(" ");
|
|
63
|
+
}
|
|
64
|
+
const SCALES_FR = [
|
|
65
|
+
"mille",
|
|
66
|
+
"million",
|
|
67
|
+
"milliard",
|
|
68
|
+
"billion",
|
|
69
|
+
"billiard",
|
|
70
|
+
"trillion",
|
|
71
|
+
];
|
|
72
|
+
const SCALES_FR_PLURAL = [
|
|
73
|
+
"mille",
|
|
74
|
+
"millions",
|
|
75
|
+
"milliards",
|
|
76
|
+
"billions",
|
|
77
|
+
"billiards",
|
|
78
|
+
"trillions",
|
|
79
|
+
];
|
|
80
|
+
// ─── Locale definition ────────────────────────────────────────────────────────
|
|
81
|
+
const fr = {
|
|
82
|
+
name: "French",
|
|
83
|
+
convert(n, _opts) {
|
|
84
|
+
if (n === 0n)
|
|
85
|
+
return "zéro";
|
|
86
|
+
const chunks = [];
|
|
87
|
+
let remaining = n;
|
|
88
|
+
let idx = 0;
|
|
89
|
+
while (remaining > 0n) {
|
|
90
|
+
chunks.unshift({ value: Number(remaining % 1000n), scale: idx });
|
|
91
|
+
remaining = remaining / 1000n;
|
|
92
|
+
idx++;
|
|
93
|
+
}
|
|
94
|
+
const parts = [];
|
|
95
|
+
for (const { value, scale } of chunks) {
|
|
96
|
+
if (value === 0)
|
|
97
|
+
continue;
|
|
98
|
+
const words = hundredsFR(value);
|
|
99
|
+
if (scale === 0) {
|
|
100
|
+
parts.push(words);
|
|
101
|
+
}
|
|
102
|
+
else if (scale === 1) {
|
|
103
|
+
// "mille" does not pluralise and drops "un" prefix
|
|
104
|
+
parts.push(value === 1 ? "mille" : `${words} mille`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
const scaleIdx = scale - 1;
|
|
108
|
+
const scaleName = value === 1 ? SCALES_FR[scaleIdx] : SCALES_FR_PLURAL[scaleIdx];
|
|
109
|
+
parts.push(`${words} ${scaleName}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return parts.join(" ").trim();
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
registerLocale("fr", fr);
|
|
116
|
+
export default fr;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { registerLocale } from "../core/converter";
|
|
2
|
+
// ─── Word tables ──────────────────────────────────────────────────────────────
|
|
3
|
+
/** 1–99 in Hindi */
|
|
4
|
+
const HINDI_ONES = {
|
|
5
|
+
1: "एक", 2: "दो", 3: "तीन", 4: "चार", 5: "पाँच",
|
|
6
|
+
6: "छह", 7: "सात", 8: "आठ", 9: "नौ", 10: "दस",
|
|
7
|
+
11: "ग्यारह", 12: "बारह", 13: "तेरह", 14: "चौदह", 15: "पन्द्रह",
|
|
8
|
+
16: "सोलह", 17: "सत्रह", 18: "अट्ठारह", 19: "उन्नीस", 20: "बीस",
|
|
9
|
+
21: "इक्कीस", 22: "बाईस", 23: "तेईस", 24: "चौबीस", 25: "पच्चीस",
|
|
10
|
+
26: "छब्बीस", 27: "सत्ताईस", 28: "अट्ठाईस", 29: "उनतीस", 30: "तीस",
|
|
11
|
+
31: "इकतीस", 32: "बत्तीस", 33: "तैंतीस", 34: "चौंतीस", 35: "पैंतीस",
|
|
12
|
+
36: "छत्तीस", 37: "सैंतीस", 38: "अड़तीस", 39: "उनतालीस", 40: "चालीस",
|
|
13
|
+
41: "इकतालीस", 42: "बयालीस", 43: "तैंतालीस", 44: "चौवालीस", 45: "पैंतालीस",
|
|
14
|
+
46: "छियालीस", 47: "सैंतालीस", 48: "अड़तालीस", 49: "उनचास", 50: "पचास",
|
|
15
|
+
51: "इक्यावन", 52: "बावन", 53: "तिरपन", 54: "चौवन", 55: "पचपन",
|
|
16
|
+
56: "छप्पन", 57: "सत्तावन", 58: "अट्ठावन", 59: "उनसठ", 60: "साठ",
|
|
17
|
+
61: "इकसठ", 62: "बासठ", 63: "तिरसठ", 64: "चौंसठ", 65: "पैंसठ",
|
|
18
|
+
66: "छियासठ", 67: "सड़सठ", 68: "अड़सठ", 69: "उनहत्तर", 70: "सत्तर",
|
|
19
|
+
71: "इकहत्तर", 72: "बहत्तर", 73: "तिहत्तर", 74: "चौहत्तर", 75: "पचहत्तर",
|
|
20
|
+
76: "छिहत्तर", 77: "सत्तहत्तर", 78: "अठहत्तर", 79: "उन्यासी", 80: "अस्सी",
|
|
21
|
+
81: "इक्यासी", 82: "बयासी", 83: "तिरासी", 84: "चौरासी", 85: "पचासी",
|
|
22
|
+
86: "छियासी", 87: "सत्तासी", 88: "अट्ठासी", 89: "नवासी", 90: "नब्बे",
|
|
23
|
+
91: "इक्यानबे", 92: "बानवे", 93: "तिरानवे", 94: "चौरानवे", 95: "पचानवे",
|
|
24
|
+
96: "छियानवे", 97: "सत्तानवे", 98: "अट्ठानवे", 99: "निन्यानवे",
|
|
25
|
+
};
|
|
26
|
+
const HINDI_HUNDRED = "सौ";
|
|
27
|
+
const INDIAN_SCALES_HI = [
|
|
28
|
+
[100000000000000000n, "शंख"], // 10^17
|
|
29
|
+
[1000000000000000n, "पद्म"], // 10^15
|
|
30
|
+
[10000000000000n, "नील"], // 10^13
|
|
31
|
+
[100000000000n, "खरब"], // 10^11
|
|
32
|
+
[1000000000n, "अरब"], // 10^9
|
|
33
|
+
[10000000n, "करोड़"], // 10^7
|
|
34
|
+
[100000n, "लाख"], // 10^5
|
|
35
|
+
[1000n, "हज़ार"], // 10^3
|
|
36
|
+
];
|
|
37
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
38
|
+
function upToNinetyNine(n) {
|
|
39
|
+
return HINDI_ONES[n] ?? "";
|
|
40
|
+
}
|
|
41
|
+
function hundredsHindi(n) {
|
|
42
|
+
if (n === 0)
|
|
43
|
+
return "";
|
|
44
|
+
const h = Math.floor(n / 100);
|
|
45
|
+
const rem = n % 100;
|
|
46
|
+
const parts = [];
|
|
47
|
+
if (h > 0) {
|
|
48
|
+
// Always include the digit before 'सौ' (e.g., 'एक सौ', 'तीन सौ')
|
|
49
|
+
parts.push(`${upToNinetyNine(h)} ${HINDI_HUNDRED}`);
|
|
50
|
+
}
|
|
51
|
+
if (rem > 0)
|
|
52
|
+
parts.push(upToNinetyNine(rem));
|
|
53
|
+
return parts.join(" ");
|
|
54
|
+
}
|
|
55
|
+
// ─── Locale definition ────────────────────────────────────────────────────────
|
|
56
|
+
const hi = {
|
|
57
|
+
name: "Hindi",
|
|
58
|
+
convert(n, _opts) {
|
|
59
|
+
if (n === 0n)
|
|
60
|
+
return "शून्य";
|
|
61
|
+
const parts = [];
|
|
62
|
+
let remaining = n;
|
|
63
|
+
for (const [scale, name] of INDIAN_SCALES_HI) {
|
|
64
|
+
if (remaining >= scale) {
|
|
65
|
+
const count = remaining / scale;
|
|
66
|
+
remaining = remaining % scale;
|
|
67
|
+
const countWords = hundredsHindi(Number(count));
|
|
68
|
+
parts.push(`${countWords} ${name}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (remaining > 0n) {
|
|
72
|
+
parts.push(hundredsHindi(Number(remaining)));
|
|
73
|
+
}
|
|
74
|
+
return parts.join(" ");
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
registerLocale("hi", hi);
|
|
78
|
+
export default hi;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { registerLocale } from "../core/converter";
|
|
2
|
+
// ─── Word tables ──────────────────────────────────────────────────────────────
|
|
3
|
+
const ONES = [
|
|
4
|
+
"", "one", "two", "three", "four", "five",
|
|
5
|
+
"six", "seven", "eight", "nine", "ten",
|
|
6
|
+
"eleven", "twelve", "thirteen", "fourteen", "fifteen",
|
|
7
|
+
"sixteen", "seventeen", "eighteen", "nineteen",
|
|
8
|
+
];
|
|
9
|
+
const TENS = [
|
|
10
|
+
"", "", "twenty", "thirty", "forty", "fifty",
|
|
11
|
+
"sixty", "seventy", "eighty", "ninety",
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Indian scale names (each 100× the previous after thousand):
|
|
15
|
+
* 1,000 = thousand
|
|
16
|
+
* 1,00,000 = lakh
|
|
17
|
+
* 1,00,00,000 = crore
|
|
18
|
+
* 1,00,00,00,000 = arab (not commonly used in English but included)
|
|
19
|
+
* …
|
|
20
|
+
*/
|
|
21
|
+
const INDIAN_SCALES = [
|
|
22
|
+
[100000000000000000n, "shankh"], // 10^17
|
|
23
|
+
[1000000000000000n, "padma"], // 10^15
|
|
24
|
+
[10000000000000n, "neel"], // 10^13
|
|
25
|
+
[100000000000n, "kharab"], // 10^11
|
|
26
|
+
[1000000000n, "arab"], // 10^9
|
|
27
|
+
[10000000n, "crore"], // 10^7
|
|
28
|
+
[100000n, "lakh"], // 10^5
|
|
29
|
+
[1000n, "thousand"],
|
|
30
|
+
];
|
|
31
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
32
|
+
function twoDigits(n) {
|
|
33
|
+
if (n === 0)
|
|
34
|
+
return "";
|
|
35
|
+
if (n < 20)
|
|
36
|
+
return ONES[n];
|
|
37
|
+
const t = Math.floor(n / 10);
|
|
38
|
+
const o = n % 10;
|
|
39
|
+
return o === 0 ? TENS[t] : `${TENS[t]}-${ONES[o]}`;
|
|
40
|
+
}
|
|
41
|
+
function hundredsToWords(n) {
|
|
42
|
+
if (n === 0)
|
|
43
|
+
return "";
|
|
44
|
+
const h = Math.floor(n / 100);
|
|
45
|
+
const rem = n % 100;
|
|
46
|
+
const parts = [];
|
|
47
|
+
if (h > 0)
|
|
48
|
+
parts.push(`${ONES[h]} hundred`);
|
|
49
|
+
const two = twoDigits(rem);
|
|
50
|
+
if (two)
|
|
51
|
+
parts.push(two);
|
|
52
|
+
return parts.join(" ");
|
|
53
|
+
}
|
|
54
|
+
// ─── Locale definition ────────────────────────────────────────────────────────
|
|
55
|
+
const indian = {
|
|
56
|
+
name: "Indian",
|
|
57
|
+
convert(n, _opts) {
|
|
58
|
+
if (n === 0n)
|
|
59
|
+
return "zero";
|
|
60
|
+
const parts = [];
|
|
61
|
+
let remaining = n;
|
|
62
|
+
for (const [scale, name] of INDIAN_SCALES) {
|
|
63
|
+
if (remaining >= scale) {
|
|
64
|
+
const count = remaining / scale;
|
|
65
|
+
remaining = remaining % scale;
|
|
66
|
+
// The count itself may need Indian-style words
|
|
67
|
+
// For counts < 1000 we use simple English words
|
|
68
|
+
const countWords = hundredsToWords(Number(count));
|
|
69
|
+
parts.push(`${countWords} ${name}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Anything left is < 1000
|
|
73
|
+
if (remaining > 0n) {
|
|
74
|
+
parts.push(hundredsToWords(Number(remaining)));
|
|
75
|
+
}
|
|
76
|
+
return parts.join(" ");
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
registerLocale("in", indian);
|
|
80
|
+
export default indian;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ConvertOptions, Locale, LocaleDefinition } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Register a locale definition.
|
|
4
|
+
* Called internally by each locale module.
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerLocale(id: Locale, def: LocaleDefinition): void;
|
|
7
|
+
/**
|
|
8
|
+
* Retrieve a locale definition or throw.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getLocale(id: Locale): LocaleDefinition;
|
|
11
|
+
/** List all registered locale IDs */
|
|
12
|
+
export declare function availableLocales(): Locale[];
|
|
13
|
+
/**
|
|
14
|
+
* Convert a number (integer or string) to its word representation.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* convert(1000124) // "One Million One Hundred Twenty Four"
|
|
18
|
+
* convert(1000124, { locale: "in" }) // "Ten Lakh One Hundred Twenty Four"
|
|
19
|
+
* convert(1000124, { locale: "hi" }) // "दस लाख एक सौ चौबीस"
|
|
20
|
+
*/
|
|
21
|
+
export declare function convert(input: number | bigint | string, options?: ConvertOptions): string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Supported locale identifiers */
|
|
2
|
+
export type Locale = "en" | "in" | "hi" | "de" | "fr" | (string & {});
|
|
3
|
+
/** Options passed to convert() */
|
|
4
|
+
export interface ConvertOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Target locale / numeral system.
|
|
7
|
+
* - `"en"` — English (International): 1,000,000 = One Million
|
|
8
|
+
* - `"in"` — Indian numbering system: 10,00,000 = Ten Lakh
|
|
9
|
+
* - `"hi"` — Hindi words (Indian system): दस लाख
|
|
10
|
+
* - `"de"` — German
|
|
11
|
+
* - `"fr"` — French
|
|
12
|
+
* @default "en"
|
|
13
|
+
*/
|
|
14
|
+
locale?: Locale;
|
|
15
|
+
/** Capitalise the first letter of the result. @default true */
|
|
16
|
+
capitalize?: boolean;
|
|
17
|
+
/** Include "and" connector where applicable (English only). @default true */
|
|
18
|
+
useAnd?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Currency mode — append currency labels.
|
|
21
|
+
* e.g. { currency: "USD" } → "One Million Dollars"
|
|
22
|
+
*/
|
|
23
|
+
currency?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Internal locale definition contract */
|
|
26
|
+
export interface LocaleDefinition {
|
|
27
|
+
/** Human-readable name */
|
|
28
|
+
name: string;
|
|
29
|
+
/** Convert an integer ≥ 0 to words */
|
|
30
|
+
convert(n: bigint, opts: Required<ConvertOptions>): string;
|
|
31
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import "./locales/en";
|
|
2
|
+
import "./locales/in";
|
|
3
|
+
import "./locales/hi";
|
|
4
|
+
import "./locales/de";
|
|
5
|
+
import "./locales/fr";
|
|
6
|
+
export { convert, registerLocale, getLocale, availableLocales } from "./core/converter";
|
|
7
|
+
export type { Locale, ConvertOptions, LocaleDefinition } from "./core/types";
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gks101/numtowords",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Convert numbers to words in multiple languages and numeral systems (English, Indian, etc.)",
|
|
5
|
+
"main": "dist/index.cjs.js",
|
|
6
|
+
"module": "dist/index.esm.js",
|
|
7
|
+
"browser": "dist/index.umd.js",
|
|
8
|
+
"types": "dist/types/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.esm.js",
|
|
12
|
+
"require": "./dist/index.cjs.js",
|
|
13
|
+
"types": "./dist/types/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "rm -rf dist && rollup -c rollup.config.mjs && tsc --target ES2020 --module ES2020 --downlevelIteration --skipLibCheck --noEmitOnError false --outDir dist --rootDir src src/locales/*.ts",
|
|
22
|
+
"test": "jest",
|
|
23
|
+
"lint": "tsc --noEmit",
|
|
24
|
+
"prepack": "npm run build",
|
|
25
|
+
"prepare": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"number-to-words",
|
|
29
|
+
"multilingual",
|
|
30
|
+
"indian-numbering",
|
|
31
|
+
"english",
|
|
32
|
+
"hindi",
|
|
33
|
+
"number-converter",
|
|
34
|
+
"number-to-word-converter",
|
|
35
|
+
"i18n"
|
|
36
|
+
],
|
|
37
|
+
"author": "",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
41
|
+
"@rollup/plugin-typescript": "^11.1.6",
|
|
42
|
+
"rollup": "^4.18.0",
|
|
43
|
+
"rollup-plugin-dts": "^6.1.1",
|
|
44
|
+
"tslib": "^2.6.3",
|
|
45
|
+
"typescript": "^5.5.3",
|
|
46
|
+
"jest": "^29.7.0",
|
|
47
|
+
"@types/jest": "^29.5.12",
|
|
48
|
+
"ts-jest": "^29.2.2"
|
|
49
|
+
}
|
|
50
|
+
}
|