@marcobytes/digits-to-words 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 ADDED
@@ -0,0 +1,78 @@
1
+ # Convert Number to Words
2
+
3
+ Convert numbers to words easily. Currently supports **English (`en-SG`)** and **Khmer (Cambodia, `km-KH`)**.
4
+
5
+ > **Note:** Support for additional languages will be added in future releases.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install number-to-words
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import { toWords } from 'number-to-words';
17
+
18
+ // English
19
+ console.log(toWords(181035, 'en-SG')); // "One Hundred and Eighty-One Thousand and Thirty-Five"
20
+
21
+ // Khmer
22
+ console.log(toWords(181035, 'km-KH')); // "មួយរយប៉ែតសិបមួយពាន់សូន្យសាមសិបប្រាំ"
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Example with Countries (Asia)
28
+
29
+ | Country | Language Code | Example: `181035` |
30
+ | ---------------- | ------------- | --------------------------------------------------- |
31
+ | Cambodia 🇰🇭 | `km-KH` | មួយរយប៉ែតសិបមួយពាន់សូន្យសាមសិបប្រាំ |
32
+ | Vietnam 🇻🇳 | `vi-VN` | (not supported yet) |
33
+ | Laos 🇱🇦 | `lo-LA` | (not supported yet) |
34
+ | Myanmar 🇲🇲 | `my-MM` | (not supported yet) |
35
+ | Malaysia 🇲🇾 | `ms-MY` | (not supported yet) |
36
+ | Singapore 🇸🇬 | `en-SG` | One Hundred and Eighty-One Thousand and Thirty-Five |
37
+ | Indonesia 🇮🇩 | `id-ID` | (not supported yet) |
38
+ | Philippines 🇵🇭 | `en-PH` | One Hundred and Eighty-One Thousand and Thirty-Five |
39
+ | Japan 🇯🇵 | `ja-JP` | (not supported yet) |
40
+ | South Korea 🇰🇷 | `ko-KR` | (not supported yet) |
41
+ | Thailand 🇹🇭 | `th-TH` | (not supported yet) |
42
+
43
+
44
+ ---
45
+
46
+ ## Example with Real Data
47
+
48
+ ```ts
49
+ import { toWords } from 'number-to-words';
50
+
51
+ const populationCambodia = 181035; // Area in km²
52
+ console.log(`Area of Cambodia: ${toWords(populationCambodia, 'en-SG')} square kilometers`);
53
+ // "Area of Cambodia: One Hundred Eighty-One Thousand Thirty-Five square kilometers"
54
+
55
+ console.log(`ផ្ទៃដីប្រទេសកម្ពុជា: ${toWords(populationCambodia, 'km-KH')} គីឡូម៉ែត្រការេ`);
56
+ // "ផ្ទៃដីប្រទេសកម្ពុជា: មួយរយប៉ែតសិបមួយពាន់សូន្យសាមសិបប្រាំ គីឡូម៉ែត្រការេ"
57
+ ```
58
+
59
+ ---
60
+
61
+ ## API
62
+
63
+ ```ts
64
+ toWords(number: number, lang: string): string
65
+ ```
66
+
67
+ * `number` → The number you want to convert
68
+ * `lang` → Language code (`'en-SG'` or `'km-KH'`)
69
+
70
+ > Currently, only **English (`en-SG`)** and **Khmer (`km`)** are supported.
71
+
72
+ ---
73
+
74
+ ## Contribution
75
+
76
+ Contributions are welcome! If you want to help add **more Asian languages**, feel free to submit a PR.
77
+
78
+ ---
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Public API
3
+ */
4
+ export declare function toWords(number: number, lang?: string): string;
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toWords = toWords;
4
+ const en_SG_1 = require("./languages/en-SG");
5
+ const km_KH_1 = require("./languages/km-KH");
6
+ /**
7
+ * Map all localization codes to internal converters.
8
+ * You can easily add more later.
9
+ */
10
+ const LANGUAGE_ROUTER = {
11
+ // English variants
12
+ "en": en_SG_1.convertNumberToEnglishWords,
13
+ "en-SG": en_SG_1.convertNumberToEnglishWords,
14
+ "en-PH": en_SG_1.convertNumberToEnglishWords,
15
+ // Khmer variants
16
+ "km": km_KH_1.convertNumberToKhmerWords,
17
+ "km-KH": km_KH_1.convertNumberToKhmerWords,
18
+ };
19
+ /**
20
+ * Public API
21
+ */
22
+ function toWords(number, lang = "en-SG") {
23
+ const key = lang.trim();
24
+ const converter = LANGUAGE_ROUTER[key] || LANGUAGE_ROUTER[key.toLowerCase()] || null;
25
+ if (!converter) {
26
+ throw new Error(`Language "${lang}" is not supported yet. Supported languages: ${Object.keys(LANGUAGE_ROUTER).join(", ")}`);
27
+ }
28
+ return converter(number);
29
+ }
@@ -0,0 +1 @@
1
+ export declare function convertNumberToEnglishWords(number: number | string): string;
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertNumberToEnglishWords = convertNumberToEnglishWords;
4
+ function convertNumberToEnglishWords(number) {
5
+ const hyphen = " ";
6
+ const conjunction = " ";
7
+ const separator = " ";
8
+ const negative = "negative ";
9
+ const decimal = " point ";
10
+ const dictionary = {
11
+ 0: "zero",
12
+ 1: "one",
13
+ 2: "two",
14
+ 3: "three",
15
+ 4: "four",
16
+ 5: "five",
17
+ 6: "six",
18
+ 7: "seven",
19
+ 8: "eight",
20
+ 9: "nine",
21
+ 10: "ten",
22
+ 11: "eleven",
23
+ 12: "twelve",
24
+ 13: "thirteen",
25
+ 14: "fourteen",
26
+ 15: "fifteen",
27
+ 16: "sixteen",
28
+ 17: "seventeen",
29
+ 18: "eighteen",
30
+ 19: "nineteen",
31
+ 20: "twenty",
32
+ 30: "thirty",
33
+ 40: "forty",
34
+ 50: "fifty",
35
+ 60: "sixty",
36
+ 70: "seventy",
37
+ 80: "eighty",
38
+ 90: "ninety",
39
+ 100: "hundred",
40
+ 1000: "thousand",
41
+ 1000000: "million",
42
+ 1000000000: "billion",
43
+ 1000000000000: "trillion",
44
+ };
45
+ if (typeof number === "string")
46
+ number = parseFloat(number);
47
+ if (isNaN(number))
48
+ return "";
49
+ if (number < 0)
50
+ return negative + convertNumberToEnglishWords(Math.abs(number));
51
+ let string = "";
52
+ let fraction = null;
53
+ let numberStr = number.toString();
54
+ if (numberStr.includes(".")) {
55
+ [numberStr, fraction] = numberStr.split(".");
56
+ number = parseInt(numberStr);
57
+ }
58
+ if (number < 21) {
59
+ string = dictionary[number];
60
+ }
61
+ else if (number < 100) {
62
+ const tens = Math.floor(number / 10) * 10;
63
+ const units = number % 10;
64
+ string = dictionary[tens];
65
+ if (units)
66
+ string += hyphen + dictionary[units];
67
+ }
68
+ else if (number < 1000) {
69
+ const hundreds = Math.floor(number / 100);
70
+ const remainder = number % 100;
71
+ string = dictionary[hundreds] + " " + dictionary[100];
72
+ if (remainder)
73
+ string += conjunction + convertNumberToEnglishWords(remainder);
74
+ }
75
+ else {
76
+ const baseUnit = Math.pow(1000, Math.floor(Math.log(number) / Math.log(1000)));
77
+ const numBaseUnits = Math.floor(number / baseUnit);
78
+ const remainder = number % baseUnit;
79
+ string =
80
+ convertNumberToEnglishWords(numBaseUnits) +
81
+ " " +
82
+ dictionary[baseUnit];
83
+ if (remainder)
84
+ string +=
85
+ remainder < 100
86
+ ? conjunction
87
+ : separator + convertNumberToEnglishWords(remainder);
88
+ }
89
+ if (fraction) {
90
+ string +=
91
+ decimal +
92
+ fraction
93
+ .split("")
94
+ .map((d) => dictionary[parseInt(d)])
95
+ .join(" ");
96
+ }
97
+ return string;
98
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./km-KH";
2
+ export * from "./en-SG";
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./km-KH"), exports);
18
+ __exportStar(require("./en-SG"), exports);
@@ -0,0 +1 @@
1
+ export declare function convertNumberToKhmerWords(number: number | string): string;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertNumberToKhmerWords = convertNumberToKhmerWords;
4
+ function convertNumberToKhmerWords(number) {
5
+ const hyphen = "";
6
+ const conjunction = "";
7
+ const separator = "";
8
+ const negative = "negative ";
9
+ const decimal = "ចុច";
10
+ const dictionary = {
11
+ 0: "សូន្យ",
12
+ 1: "មួយ",
13
+ 2: "ពីរ",
14
+ 3: "បី",
15
+ 4: "បួន",
16
+ 5: "ប្រាំ",
17
+ 6: "ប្រាំមួយ",
18
+ 7: "ប្រាំពីរ",
19
+ 8: "ប្រាំបី",
20
+ 9: "ប្រាំបួន",
21
+ 10: "ដប់",
22
+ 11: "ដប់មួយ",
23
+ 12: "ដប់ពីរ",
24
+ 13: "ដប់បី",
25
+ 14: "ដប់បួន",
26
+ 15: "ដប់ប្រាំ",
27
+ 16: "ដប់ប្រាំមួយ",
28
+ 17: "ដប់ប្រាំពីរ",
29
+ 18: "ដប់ប្រាំបី",
30
+ 19: "ដប់ប្រាំបួន",
31
+ 20: "ម្ភៃ",
32
+ 30: "សាមសិប",
33
+ 40: "សែសិប",
34
+ 50: "ហាសិប",
35
+ 60: "ហុកសិប",
36
+ 70: "ចិតសិប",
37
+ 80: "ប៉ែតសិប",
38
+ 90: "កៅសិប",
39
+ 100: "រយ",
40
+ 1000: "ពាន់",
41
+ 1000000: "លាន"
42
+ };
43
+ if (typeof number === "string")
44
+ number = parseFloat(number);
45
+ if (isNaN(number))
46
+ return "";
47
+ if (number < 0)
48
+ return negative + convertNumberToKhmerWords(Math.abs(number));
49
+ let string = "";
50
+ let fraction = null;
51
+ let numberStr = number.toString();
52
+ if (numberStr.includes(".")) {
53
+ [numberStr, fraction] = numberStr.split(".");
54
+ number = parseInt(numberStr);
55
+ }
56
+ if (number < 21) {
57
+ string = dictionary[number];
58
+ }
59
+ else if (number < 100) {
60
+ const tens = Math.floor(number / 10) * 10;
61
+ const units = number % 10;
62
+ string = dictionary[tens];
63
+ if (units)
64
+ string += hyphen + dictionary[units];
65
+ }
66
+ else if (number < 1000) {
67
+ const hundreds = Math.floor(number / 100);
68
+ const remainder = number % 100;
69
+ string = dictionary[hundreds] + dictionary[100];
70
+ if (remainder)
71
+ string += conjunction + convertNumberToKhmerWords(remainder);
72
+ }
73
+ else {
74
+ const baseUnit = Math.pow(1000, Math.floor(Math.log(number) / Math.log(1000)));
75
+ const numBaseUnits = Math.floor(number / baseUnit);
76
+ const remainder = number % baseUnit;
77
+ string =
78
+ convertNumberToKhmerWords(numBaseUnits) +
79
+ dictionary[baseUnit];
80
+ if (remainder)
81
+ string +=
82
+ remainder < 100
83
+ ? conjunction
84
+ : separator + convertNumberToKhmerWords(remainder);
85
+ }
86
+ if (fraction) {
87
+ if (string === "")
88
+ string = "0";
89
+ string += decimal + convertNumberToKhmerWords(fraction);
90
+ }
91
+ return string;
92
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const languages_1 = require("../languages");
4
+ describe("Number to English Words", () => {
5
+ test('converts 0 to "zero"', () => {
6
+ expect((0, languages_1.convertNumberToEnglishWords)(0)).toBe("zero");
7
+ });
8
+ test("converts single digits", () => {
9
+ expect((0, languages_1.convertNumberToEnglishWords)(5)).toBe("five");
10
+ expect((0, languages_1.convertNumberToEnglishWords)(19)).toBe("nineteen");
11
+ });
12
+ test("converts tens correctly", () => {
13
+ expect((0, languages_1.convertNumberToEnglishWords)(20)).toBe("twenty");
14
+ expect((0, languages_1.convertNumberToEnglishWords)(42)).toBe("forty two");
15
+ expect((0, languages_1.convertNumberToEnglishWords)(99)).toBe("ninety nine");
16
+ });
17
+ test("converts hundreds correctly", () => {
18
+ expect((0, languages_1.convertNumberToEnglishWords)(100)).toBe("one hundred");
19
+ expect((0, languages_1.convertNumberToEnglishWords)(123)).toBe("one hundred twenty three");
20
+ expect((0, languages_1.convertNumberToEnglishWords)(999)).toBe("nine hundred ninety nine");
21
+ });
22
+ test("converts thousands and above", () => {
23
+ expect((0, languages_1.convertNumberToEnglishWords)(1000)).toBe("one thousand");
24
+ expect((0, languages_1.convertNumberToEnglishWords)(1234)).toBe("one thousand two hundred thirty four");
25
+ expect((0, languages_1.convertNumberToEnglishWords)(1000000)).toBe("one million");
26
+ });
27
+ test("converts negative numbers", () => {
28
+ expect((0, languages_1.convertNumberToEnglishWords)(-5)).toBe("negative five");
29
+ expect((0, languages_1.convertNumberToEnglishWords)(-1234)).toBe("negative one thousand two hundred thirty four");
30
+ });
31
+ test("converts decimal numbers", () => {
32
+ expect((0, languages_1.convertNumberToEnglishWords)(12.34)).toBe("twelve point three four");
33
+ expect((0, languages_1.convertNumberToEnglishWords)(-0.56)).toBe("negative zero point five six");
34
+ });
35
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const languages_1 = require("../languages");
4
+ describe("Number to English Words", () => {
5
+ test('converts 0 to "zero"', () => {
6
+ expect((0, languages_1.convertNumberToEnglishWords)(0)).toBe("zero");
7
+ });
8
+ test("converts single digits", () => {
9
+ expect((0, languages_1.convertNumberToEnglishWords)(5)).toBe("five");
10
+ expect((0, languages_1.convertNumberToEnglishWords)(19)).toBe("nineteen");
11
+ });
12
+ test("converts tens correctly", () => {
13
+ expect((0, languages_1.convertNumberToEnglishWords)(20)).toBe("twenty");
14
+ expect((0, languages_1.convertNumberToEnglishWords)(42)).toBe("forty two");
15
+ expect((0, languages_1.convertNumberToEnglishWords)(99)).toBe("ninety nine");
16
+ });
17
+ test("converts hundreds correctly", () => {
18
+ expect((0, languages_1.convertNumberToEnglishWords)(100)).toBe("one hundred");
19
+ expect((0, languages_1.convertNumberToEnglishWords)(123)).toBe("one hundred twenty three");
20
+ expect((0, languages_1.convertNumberToEnglishWords)(999)).toBe("nine hundred ninety nine");
21
+ });
22
+ test("converts thousands and above", () => {
23
+ expect((0, languages_1.convertNumberToEnglishWords)(1000)).toBe("one thousand");
24
+ expect((0, languages_1.convertNumberToEnglishWords)(1234)).toBe("one thousand two hundred thirty four");
25
+ expect((0, languages_1.convertNumberToEnglishWords)(1000000)).toBe("one million");
26
+ });
27
+ test("converts negative numbers", () => {
28
+ expect((0, languages_1.convertNumberToEnglishWords)(-5)).toBe("negative five");
29
+ expect((0, languages_1.convertNumberToEnglishWords)(-1234)).toBe("negative one thousand two hundred thirty four");
30
+ });
31
+ test("converts decimal numbers", () => {
32
+ expect((0, languages_1.convertNumberToEnglishWords)(12.34)).toBe("twelve point three four");
33
+ expect((0, languages_1.convertNumberToEnglishWords)(-0.56)).toBe("negative zero point five six");
34
+ });
35
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const languages_1 = require("../languages");
4
+ describe("Number to Khmer Words", () => {
5
+ test("converts 0 to empty string", () => {
6
+ expect((0, languages_1.convertNumberToKhmerWords)(0)).toBe("សូន្យ");
7
+ });
8
+ test("converts single digits", () => {
9
+ expect((0, languages_1.convertNumberToKhmerWords)(1)).toBe("មួយ");
10
+ expect((0, languages_1.convertNumberToKhmerWords)(19)).toBe("ដប់ប្រាំបួន");
11
+ });
12
+ test("converts tens correctly", () => {
13
+ expect((0, languages_1.convertNumberToKhmerWords)(20)).toBe("ម្ភៃ");
14
+ expect((0, languages_1.convertNumberToKhmerWords)(42)).toBe("សែសិបពីរ");
15
+ expect((0, languages_1.convertNumberToKhmerWords)(99)).toBe("កៅសិបប្រាំបួន");
16
+ });
17
+ test("converts hundreds correctly", () => {
18
+ expect((0, languages_1.convertNumberToKhmerWords)(100)).toBe("មួយរយ");
19
+ expect((0, languages_1.convertNumberToKhmerWords)(123)).toBe("មួយរយម្ភៃបី");
20
+ expect((0, languages_1.convertNumberToKhmerWords)(999)).toBe("ប្រាំបួនរយកៅសិបប្រាំបួន");
21
+ });
22
+ test("converts thousands and above", () => {
23
+ expect((0, languages_1.convertNumberToKhmerWords)(1000)).toBe("មួយពាន់");
24
+ expect((0, languages_1.convertNumberToKhmerWords)(1234)).toBe("មួយពាន់ពីររយសាមសិបបួន");
25
+ expect((0, languages_1.convertNumberToKhmerWords)(1000000)).toBe("មួយលាន");
26
+ });
27
+ test("converts decimal numbers", () => {
28
+ expect((0, languages_1.convertNumberToKhmerWords)(12.34)).toBe("ដប់ពីរចុចសាមសិបបួន");
29
+ expect((0, languages_1.convertNumberToKhmerWords)(0.56)).toBe("សូន្យចុចហាសិបប្រាំមួយ");
30
+ });
31
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const languages_1 = require("../languages");
4
+ describe("Number to Khmer Words", () => {
5
+ test("converts 0 to empty string", () => {
6
+ expect((0, languages_1.convertNumberToKhmerWords)(0)).toBe("សូន្យ");
7
+ });
8
+ test("converts single digits", () => {
9
+ expect((0, languages_1.convertNumberToKhmerWords)(1)).toBe("មួយ");
10
+ expect((0, languages_1.convertNumberToKhmerWords)(19)).toBe("ដប់ប្រាំបួន");
11
+ });
12
+ test("converts tens correctly", () => {
13
+ expect((0, languages_1.convertNumberToKhmerWords)(20)).toBe("ម្ភៃ");
14
+ expect((0, languages_1.convertNumberToKhmerWords)(42)).toBe("សែសិបពីរ");
15
+ expect((0, languages_1.convertNumberToKhmerWords)(99)).toBe("កៅសិបប្រាំបួន");
16
+ });
17
+ test("converts hundreds correctly", () => {
18
+ expect((0, languages_1.convertNumberToKhmerWords)(100)).toBe("មួយរយ");
19
+ expect((0, languages_1.convertNumberToKhmerWords)(123)).toBe("មួយរយម្ភៃបី");
20
+ expect((0, languages_1.convertNumberToKhmerWords)(999)).toBe("ប្រាំបួនរយកៅសិបប្រាំបួន");
21
+ });
22
+ test("converts thousands and above", () => {
23
+ expect((0, languages_1.convertNumberToKhmerWords)(1000)).toBe("មួយពាន់");
24
+ expect((0, languages_1.convertNumberToKhmerWords)(1234)).toBe("មួយពាន់ពីររយសាមសិបបួន");
25
+ expect((0, languages_1.convertNumberToKhmerWords)(1000000)).toBe("មួយលាន");
26
+ });
27
+ test("converts decimal numbers", () => {
28
+ expect((0, languages_1.convertNumberToKhmerWords)(12.34)).toBe("ដប់ពីរចុចសាមសិបបួន");
29
+ expect((0, languages_1.convertNumberToKhmerWords)(0.56)).toBe("សូន្យចុចហាសិបប្រាំមួយ");
30
+ });
31
+ });
@@ -0,0 +1,3 @@
1
+ export interface NumberToWords {
2
+ convert(number: number): string;
3
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export declare function splitNumber(number: number): number[];
package/dist/utils.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.splitNumber = splitNumber;
4
+ function splitNumber(number) {
5
+ return [];
6
+ }
package/jest.config.js ADDED
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ testMatch: ['**/test/**/*.test.ts'],
5
+ moduleFileExtensions: ['ts', 'js', 'json', 'node'],
6
+ };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@marcobytes/digits-to-words",
3
+ "version": "1.0.0",
4
+ "description": "Convert numbers to words in multiple languages (Khmer, English, etc.)",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "jest",
10
+ "test:watch": "jest --watch",
11
+ "prepare": "npm run build"
12
+ },
13
+ "keywords": [
14
+ "numbers",
15
+ "words",
16
+ "english",
17
+ "khmer",
18
+ "cambodia",
19
+ "convert",
20
+ "multi-language"
21
+ ],
22
+ "author": "Marco Bytes",
23
+ "license": "MIT",
24
+ "devDependencies": {
25
+ "@babel/core": "^7.28.5",
26
+ "@babel/preset-env": "^7.28.5",
27
+ "@types/jest": "^30.0.0",
28
+ "babel-jest": "^30.2.0",
29
+ "jest": "^30.2.0",
30
+ "ts-jest": "^29.4.6",
31
+ "typescript": "^5.9.3"
32
+ }
33
+ }
package/src/index.ts ADDED
@@ -0,0 +1,36 @@
1
+ import { convertNumberToEnglishWords } from "./languages/en-SG";
2
+ import { convertNumberToKhmerWords } from "./languages/km-KH";
3
+
4
+ /**
5
+ * Map all localization codes to internal converters.
6
+ * You can easily add more later.
7
+ */
8
+ const LANGUAGE_ROUTER: Record<string, (n: number) => string> = {
9
+ // English variants
10
+ "en": convertNumberToEnglishWords,
11
+ "en-SG": convertNumberToEnglishWords,
12
+ "en-PH": convertNumberToEnglishWords,
13
+
14
+ // Khmer variants
15
+ "km": convertNumberToKhmerWords,
16
+ "km-KH": convertNumberToKhmerWords,
17
+ };
18
+
19
+ /**
20
+ * Public API
21
+ */
22
+ export function toWords(number: number, lang: string = "en-SG"): string {
23
+ const key = lang.trim();
24
+
25
+ const converter = LANGUAGE_ROUTER[key] || LANGUAGE_ROUTER[key.toLowerCase()] || null;
26
+
27
+ if (!converter) {
28
+ throw new Error(
29
+ `Language "${lang}" is not supported yet. Supported languages: ${Object.keys(
30
+ LANGUAGE_ROUTER
31
+ ).join(", ")}`
32
+ );
33
+ }
34
+
35
+ return converter(number);
36
+ }
@@ -0,0 +1,101 @@
1
+ export function convertNumberToEnglishWords(number: number | string): string {
2
+ const hyphen = " ";
3
+ const conjunction = " ";
4
+ const separator = " ";
5
+ const negative = "negative ";
6
+ const decimal = " point ";
7
+
8
+ const dictionary: Record<number, string> = {
9
+ 0: "zero",
10
+ 1: "one",
11
+ 2: "two",
12
+ 3: "three",
13
+ 4: "four",
14
+ 5: "five",
15
+ 6: "six",
16
+ 7: "seven",
17
+ 8: "eight",
18
+ 9: "nine",
19
+ 10: "ten",
20
+ 11: "eleven",
21
+ 12: "twelve",
22
+ 13: "thirteen",
23
+ 14: "fourteen",
24
+ 15: "fifteen",
25
+ 16: "sixteen",
26
+ 17: "seventeen",
27
+ 18: "eighteen",
28
+ 19: "nineteen",
29
+ 20: "twenty",
30
+ 30: "thirty",
31
+ 40: "forty",
32
+ 50: "fifty",
33
+ 60: "sixty",
34
+ 70: "seventy",
35
+ 80: "eighty",
36
+ 90: "ninety",
37
+ 100: "hundred",
38
+ 1000: "thousand",
39
+ 1_000_000: "million",
40
+ 1_000_000_000: "billion",
41
+ 1_000_000_000_000: "trillion",
42
+ };
43
+
44
+ if (typeof number === "string") number = parseFloat(number);
45
+
46
+ if (isNaN(number)) return "";
47
+
48
+ if (number < 0)
49
+ return negative + convertNumberToEnglishWords(Math.abs(number));
50
+
51
+ let string = "";
52
+ let fraction: string | null = null;
53
+
54
+ let numberStr = number.toString();
55
+ if (numberStr.includes(".")) {
56
+ [numberStr, fraction] = numberStr.split(".");
57
+ number = parseInt(numberStr);
58
+ }
59
+
60
+ if (number < 21) {
61
+ string = dictionary[number]!;
62
+ } else if (number < 100) {
63
+ const tens = Math.floor(number / 10) * 10;
64
+ const units = number % 10;
65
+ string = dictionary[tens]!;
66
+ if (units) string += hyphen + dictionary[units];
67
+ } else if (number < 1000) {
68
+ const hundreds = Math.floor(number / 100);
69
+ const remainder = number % 100;
70
+ string = dictionary[hundreds]! + " " + dictionary[100];
71
+ if (remainder)
72
+ string += conjunction + convertNumberToEnglishWords(remainder);
73
+ } else {
74
+ const baseUnit = Math.pow(
75
+ 1000,
76
+ Math.floor(Math.log(number) / Math.log(1000))
77
+ );
78
+ const numBaseUnits = Math.floor(number / baseUnit);
79
+ const remainder = number % baseUnit;
80
+ string =
81
+ convertNumberToEnglishWords(numBaseUnits) +
82
+ " " +
83
+ dictionary[baseUnit as keyof typeof dictionary];
84
+ if (remainder)
85
+ string +=
86
+ remainder < 100
87
+ ? conjunction
88
+ : separator + convertNumberToEnglishWords(remainder);
89
+ }
90
+
91
+ if (fraction) {
92
+ string +=
93
+ decimal +
94
+ fraction
95
+ .split("")
96
+ .map((d) => dictionary[parseInt(d)])
97
+ .join(" ");
98
+ }
99
+
100
+ return string;
101
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./km-KH";
2
+ export * from "./en-SG";
@@ -0,0 +1,92 @@
1
+ export function convertNumberToKhmerWords(number: number | string): string {
2
+ const hyphen = "";
3
+ const conjunction = "";
4
+ const separator = "";
5
+ const negative = "negative ";
6
+ const decimal = "ចុច";
7
+
8
+ const dictionary: Record<number, string> = {
9
+ 0: "សូន្យ",
10
+ 1: "មួយ",
11
+ 2: "ពីរ",
12
+ 3: "បី",
13
+ 4: "បួន",
14
+ 5: "ប្រាំ",
15
+ 6: "ប្រាំមួយ",
16
+ 7: "ប្រាំពីរ",
17
+ 8: "ប្រាំបី",
18
+ 9: "ប្រាំបួន",
19
+ 10: "ដប់",
20
+ 11: "ដប់មួយ",
21
+ 12: "ដប់ពីរ",
22
+ 13: "ដប់បី",
23
+ 14: "ដប់បួន",
24
+ 15: "ដប់ប្រាំ",
25
+ 16: "ដប់ប្រាំមួយ",
26
+ 17: "ដប់ប្រាំពីរ",
27
+ 18: "ដប់ប្រាំបី",
28
+ 19: "ដប់ប្រាំបួន",
29
+ 20: "ម្ភៃ",
30
+ 30: "សាមសិប",
31
+ 40: "សែសិប",
32
+ 50: "ហាសិប",
33
+ 60: "ហុកសិប",
34
+ 70: "ចិតសិប",
35
+ 80: "ប៉ែតសិប",
36
+ 90: "កៅសិប",
37
+ 100: "រយ",
38
+ 1000: "ពាន់",
39
+ 1_000_000: "លាន"
40
+ };
41
+
42
+ if (typeof number === "string") number = parseFloat(number);
43
+
44
+ if (isNaN(number)) return "";
45
+
46
+ if (number < 0) return negative + convertNumberToKhmerWords(Math.abs(number));
47
+
48
+ let string = "";
49
+ let fraction: string | null = null;
50
+
51
+ let numberStr = number.toString();
52
+ if (numberStr.includes(".")) {
53
+ [numberStr, fraction] = numberStr.split(".");
54
+ number = parseInt(numberStr);
55
+ }
56
+
57
+ if (number < 21) {
58
+ string = dictionary[number]!;
59
+ } else if (number < 100) {
60
+ const tens = Math.floor(number / 10) * 10;
61
+ const units = number % 10;
62
+ string = dictionary[tens]!;
63
+ if (units) string += hyphen + dictionary[units];
64
+ } else if (number < 1000) {
65
+ const hundreds = Math.floor(number / 100);
66
+ const remainder = number % 100;
67
+ string = dictionary[hundreds]! + dictionary[100];
68
+ if (remainder) string += conjunction + convertNumberToKhmerWords(remainder);
69
+ } else {
70
+ const baseUnit = Math.pow(
71
+ 1000,
72
+ Math.floor(Math.log(number) / Math.log(1000))
73
+ );
74
+ const numBaseUnits = Math.floor(number / baseUnit);
75
+ const remainder = number % baseUnit;
76
+ string =
77
+ convertNumberToKhmerWords(numBaseUnits) +
78
+ dictionary[baseUnit as keyof typeof dictionary];
79
+ if (remainder)
80
+ string +=
81
+ remainder < 100
82
+ ? conjunction
83
+ : separator + convertNumberToKhmerWords(remainder);
84
+ }
85
+
86
+ if (fraction) {
87
+ if (string === "") string = "0";
88
+ string += decimal + convertNumberToKhmerWords(fraction);
89
+ }
90
+
91
+ return string;
92
+ }
@@ -0,0 +1,46 @@
1
+ import { convertNumberToEnglishWords } from "../languages";
2
+
3
+ describe("Number to English Words", () => {
4
+ test('converts 0 to "zero"', () => {
5
+ expect(convertNumberToEnglishWords(0)).toBe("zero");
6
+ });
7
+
8
+ test("converts single digits", () => {
9
+ expect(convertNumberToEnglishWords(5)).toBe("five");
10
+ expect(convertNumberToEnglishWords(19)).toBe("nineteen");
11
+ });
12
+
13
+ test("converts tens correctly", () => {
14
+ expect(convertNumberToEnglishWords(20)).toBe("twenty");
15
+ expect(convertNumberToEnglishWords(42)).toBe("forty two");
16
+ expect(convertNumberToEnglishWords(99)).toBe("ninety nine");
17
+ });
18
+
19
+ test("converts hundreds correctly", () => {
20
+ expect(convertNumberToEnglishWords(100)).toBe("one hundred");
21
+ expect(convertNumberToEnglishWords(123)).toBe("one hundred twenty three");
22
+ expect(convertNumberToEnglishWords(999)).toBe("nine hundred ninety nine");
23
+ });
24
+
25
+ test("converts thousands and above", () => {
26
+ expect(convertNumberToEnglishWords(1000)).toBe("one thousand");
27
+ expect(convertNumberToEnglishWords(1234)).toBe(
28
+ "one thousand two hundred thirty four"
29
+ );
30
+ expect(convertNumberToEnglishWords(1_000_000)).toBe("one million");
31
+ });
32
+
33
+ test("converts negative numbers", () => {
34
+ expect(convertNumberToEnglishWords(-5)).toBe("negative five");
35
+ expect(convertNumberToEnglishWords(-1234)).toBe(
36
+ "negative one thousand two hundred thirty four"
37
+ );
38
+ });
39
+
40
+ test("converts decimal numbers", () => {
41
+ expect(convertNumberToEnglishWords(12.34)).toBe("twelve point three four");
42
+ expect(convertNumberToEnglishWords(-0.56)).toBe(
43
+ "negative zero point five six"
44
+ );
45
+ });
46
+ });
@@ -0,0 +1,35 @@
1
+ import { convertNumberToKhmerWords } from "../languages";
2
+
3
+ describe("Number to Khmer Words", () => {
4
+ test("converts 0 to empty string", () => {
5
+ expect(convertNumberToKhmerWords(0)).toBe("សូន្យ");
6
+ });
7
+
8
+ test("converts single digits", () => {
9
+ expect(convertNumberToKhmerWords(1)).toBe("មួយ");
10
+ expect(convertNumberToKhmerWords(19)).toBe("ដប់ប្រាំបួន");
11
+ });
12
+
13
+ test("converts tens correctly", () => {
14
+ expect(convertNumberToKhmerWords(20)).toBe("ម្ភៃ");
15
+ expect(convertNumberToKhmerWords(42)).toBe("សែសិបពីរ");
16
+ expect(convertNumberToKhmerWords(99)).toBe("កៅសិបប្រាំបួន");
17
+ });
18
+
19
+ test("converts hundreds correctly", () => {
20
+ expect(convertNumberToKhmerWords(100)).toBe("មួយរយ");
21
+ expect(convertNumberToKhmerWords(123)).toBe("មួយរយម្ភៃបី");
22
+ expect(convertNumberToKhmerWords(999)).toBe("ប្រាំបួនរយកៅសិបប្រាំបួន");
23
+ });
24
+
25
+ test("converts thousands and above", () => {
26
+ expect(convertNumberToKhmerWords(1000)).toBe("មួយពាន់");
27
+ expect(convertNumberToKhmerWords(1234)).toBe("មួយពាន់ពីររយសាមសិបបួន");
28
+ expect(convertNumberToKhmerWords(1_000_000)).toBe("មួយលាន");
29
+ });
30
+
31
+ test("converts decimal numbers", () => {
32
+ expect(convertNumberToKhmerWords(12.34)).toBe("ដប់ពីរចុចសាមសិបបួន");
33
+ expect(convertNumberToKhmerWords(0.56)).toBe("សូន្យចុចហាសិបប្រាំមួយ");
34
+ });
35
+ });
package/src/types.ts ADDED
@@ -0,0 +1,3 @@
1
+ export interface NumberToWords {
2
+ convert(number: number): string;
3
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,3 @@
1
+ export function splitNumber(number: number): number[] {
2
+ return [];
3
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES6",
4
+ "module": "CommonJS",
5
+ "outDir": "./dist",
6
+ "rootDir": "./src",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "declaration": true
10
+ },
11
+ "include": ["src"]
12
+ }