global-currency-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/LICENSE +21 -0
- package/README.md +191 -0
- package/package.json +27 -0
- package/src/converter.js +93 -0
- package/src/currencies.js +358 -0
- package/src/index.d.ts +79 -0
- package/src/index.js +129 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shaik Esub
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# global-currency-words
|
|
2
|
+
|
|
3
|
+
> Convert numeric currency amounts to human-readable words.
|
|
4
|
+
> Supports **15-digit numbers**, **decimals/subunits**, **30+ countries**, and **currency codes**.
|
|
5
|
+
|
|
6
|
+
```js
|
|
7
|
+
currencyWords(1234.56, { country: "India" })
|
|
8
|
+
// => "One Thousand Two Hundred Thirty Four Rupees and Fifty Six Paise"
|
|
9
|
+
|
|
10
|
+
currencyWords(999999999999999, { country: "USA", includeCode: true })
|
|
11
|
+
// => "Nine Hundred Ninety Nine Trillion ... Nine Hundred Ninety Nine Dollars (USD)"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install global-currency-words
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
const { currencyWords } = require("global-currency-words");
|
|
28
|
+
|
|
29
|
+
// Default: United States (USD)
|
|
30
|
+
currencyWords(500);
|
|
31
|
+
// => "Five Hundred Dollars"
|
|
32
|
+
|
|
33
|
+
currencyWords(1050.75, { country: "India" });
|
|
34
|
+
// => "One Thousand Fifty Rupees and Seventy Five Paise"
|
|
35
|
+
|
|
36
|
+
currencyWords(99.01, { country: "UK" });
|
|
37
|
+
// => "Ninety Nine Pounds and One Penny"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## API
|
|
43
|
+
|
|
44
|
+
### `currencyWords(amount, options?)`
|
|
45
|
+
|
|
46
|
+
| Parameter | Type | Description |
|
|
47
|
+
|-----------|------|-------------|
|
|
48
|
+
| `amount` | `number \| string` | The monetary amount. Supports up to **15 digits** before the decimal. Can be negative. |
|
|
49
|
+
| `options` | `CurrencyWordsOptions` | Optional configuration (see below). |
|
|
50
|
+
|
|
51
|
+
**Returns:** `string`
|
|
52
|
+
|
|
53
|
+
#### Options
|
|
54
|
+
|
|
55
|
+
| Option | Type | Default | Description |
|
|
56
|
+
|--------|------|---------|-------------|
|
|
57
|
+
| `country` | `string` | `"United States"` | Country name **or** ISO 4217 currency code (e.g. `"India"`, `"INR"`, `"USD"`) |
|
|
58
|
+
| `includeDecimals` | `boolean` | `true` | Include subunit (cents/paise/etc.) in the output |
|
|
59
|
+
| `decimalPlaces` | `number` | `2` | How many decimal places to read (`1` or `2`) |
|
|
60
|
+
| `includeSymbol` | `boolean` | `false` | Prepend the currency symbol (e.g. `₹`, `$`) |
|
|
61
|
+
| `includeCode` | `boolean` | `false` | Append the ISO currency code (e.g. `(INR)`) |
|
|
62
|
+
| `andBeforeDecimals` | `boolean` | `true` | Use "and" between main amount and subunit |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
### `getCurrencyInfo(countryOrCode)`
|
|
67
|
+
|
|
68
|
+
Returns metadata for a given country or currency code.
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
const { getCurrencyInfo } = require("global-currency-words");
|
|
72
|
+
|
|
73
|
+
getCurrencyInfo("Japan");
|
|
74
|
+
// {
|
|
75
|
+
// currency: "Yen", currencyPlural: "Yen",
|
|
76
|
+
// subunit: "Sen", subunitPlural: "Sen",
|
|
77
|
+
// symbol: "¥", code: "JPY", subunitFactor: 100
|
|
78
|
+
// }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### `listCountries()`
|
|
84
|
+
|
|
85
|
+
Returns all supported country names as an array of strings.
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
const { listCountries } = require("global-currency-words");
|
|
89
|
+
listCountries();
|
|
90
|
+
// => ["India", "Pakistan", "United States", "United Kingdom", ...]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Examples
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
const { currencyWords } = require("global-currency-words");
|
|
99
|
+
|
|
100
|
+
// Basic usage
|
|
101
|
+
currencyWords(0);
|
|
102
|
+
// => "Zero Dollars"
|
|
103
|
+
|
|
104
|
+
// Large number (15 digits)
|
|
105
|
+
currencyWords("999999999999999");
|
|
106
|
+
// => "Nine Hundred Ninety Nine Trillion ... Nine Hundred Ninety Nine Dollars"
|
|
107
|
+
|
|
108
|
+
// With symbol and code
|
|
109
|
+
currencyWords(1500, { country: "India", includeSymbol: true, includeCode: true });
|
|
110
|
+
// => "₹ One Thousand Five Hundred Rupees (INR)"
|
|
111
|
+
|
|
112
|
+
// Without "and"
|
|
113
|
+
currencyWords(10.50, { andBeforeDecimals: false });
|
|
114
|
+
// => "Ten Dollars Fifty Cents"
|
|
115
|
+
|
|
116
|
+
// Only 1 decimal place
|
|
117
|
+
currencyWords(10.5, { decimalPlaces: 1 });
|
|
118
|
+
// => "Ten Dollars and Five Cents"
|
|
119
|
+
|
|
120
|
+
// Ignore decimal part
|
|
121
|
+
currencyWords(100.99, { includeDecimals: false });
|
|
122
|
+
// => "One Hundred Dollars"
|
|
123
|
+
|
|
124
|
+
// Negative amount
|
|
125
|
+
currencyWords(-250.50, { country: "UK" });
|
|
126
|
+
// => "Negative Two Hundred Fifty Pounds and Fifty Pence"
|
|
127
|
+
|
|
128
|
+
// Lookup by currency code
|
|
129
|
+
currencyWords(100, { country: "EUR" });
|
|
130
|
+
// => "One Hundred Euros"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Supported Countries
|
|
136
|
+
|
|
137
|
+
| Country | Code | Currency | Subunit |
|
|
138
|
+
|---------|------|----------|---------|
|
|
139
|
+
| India | INR | Rupee | Paisa |
|
|
140
|
+
| United States / USA | USD | Dollar | Cent |
|
|
141
|
+
| United Kingdom / UK | GBP | Pound | Penny/Pence |
|
|
142
|
+
| Euro Zone / Germany / France… | EUR | Euro | Cent |
|
|
143
|
+
| Japan | JPY | Yen | Sen |
|
|
144
|
+
| China | CNY | Yuan | Fen |
|
|
145
|
+
| Australia | AUD | Dollar | Cent |
|
|
146
|
+
| Canada | CAD | Dollar | Cent |
|
|
147
|
+
| UAE | AED | Dirham | Fils |
|
|
148
|
+
| Saudi Arabia | SAR | Riyal | Halala |
|
|
149
|
+
| Singapore | SGD | Dollar | Cent |
|
|
150
|
+
| South Africa | ZAR | Rand | Cent |
|
|
151
|
+
| Brazil | BRL | Real | Centavo |
|
|
152
|
+
| Russia | RUB | Ruble | Kopek |
|
|
153
|
+
| Pakistan | PKR | Rupee | Paisa |
|
|
154
|
+
| Bangladesh | BDT | Taka | Paisa |
|
|
155
|
+
| Indonesia | IDR | Rupiah | Sen |
|
|
156
|
+
| Malaysia | MYR | Ringgit | Sen |
|
|
157
|
+
| Thailand | THB | Baht | Satang |
|
|
158
|
+
| Switzerland | CHF | Franc | Centime |
|
|
159
|
+
| … and more | | | |
|
|
160
|
+
|
|
161
|
+
> Use `listCountries()` for the full list, or pass an ISO 4217 code directly.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Limits & Validation
|
|
166
|
+
|
|
167
|
+
- Maximum **15 digits** in the integer part (up to 999,999,999,999,999)
|
|
168
|
+
- Throws `RangeError` if the integer part exceeds 15 digits
|
|
169
|
+
- Throws `TypeError` for invalid or missing amounts
|
|
170
|
+
- Throws `Error` for unsupported country names
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## TypeScript
|
|
175
|
+
|
|
176
|
+
Full TypeScript definitions are included:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
import { currencyWords, getCurrencyInfo, listCountries, CurrencyWordsOptions } from "global-currency-words";
|
|
180
|
+
|
|
181
|
+
const result: string = currencyWords(1234.56, {
|
|
182
|
+
country: "India",
|
|
183
|
+
includeCode: true,
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "global-currency-words",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Convert numeric currency amounts to words. Supports 15-digit numbers, decimal points, and 30+ countries/currencies.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"types": "src/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "node test/index.test.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"currency",
|
|
12
|
+
"words",
|
|
13
|
+
"number-to-words",
|
|
14
|
+
"currency-converter",
|
|
15
|
+
"amount-in-words",
|
|
16
|
+
"inr",
|
|
17
|
+
"usd",
|
|
18
|
+
"eur",
|
|
19
|
+
"rupees",
|
|
20
|
+
"dollars"
|
|
21
|
+
],
|
|
22
|
+
"author": "codify",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=12.0.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/src/converter.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const ONES = [
|
|
4
|
+
"",
|
|
5
|
+
"One",
|
|
6
|
+
"Two",
|
|
7
|
+
"Three",
|
|
8
|
+
"Four",
|
|
9
|
+
"Five",
|
|
10
|
+
"Six",
|
|
11
|
+
"Seven",
|
|
12
|
+
"Eight",
|
|
13
|
+
"Nine",
|
|
14
|
+
"Ten",
|
|
15
|
+
"Eleven",
|
|
16
|
+
"Twelve",
|
|
17
|
+
"Thirteen",
|
|
18
|
+
"Fourteen",
|
|
19
|
+
"Fifteen",
|
|
20
|
+
"Sixteen",
|
|
21
|
+
"Seventeen",
|
|
22
|
+
"Eighteen",
|
|
23
|
+
"Nineteen",
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const TENS = [
|
|
27
|
+
"",
|
|
28
|
+
"",
|
|
29
|
+
"Twenty",
|
|
30
|
+
"Thirty",
|
|
31
|
+
"Forty",
|
|
32
|
+
"Fifty",
|
|
33
|
+
"Sixty",
|
|
34
|
+
"Seventy",
|
|
35
|
+
"Eighty",
|
|
36
|
+
"Ninety",
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Convert a number 0–999 to words.
|
|
41
|
+
*/
|
|
42
|
+
function convertHundreds(n) {
|
|
43
|
+
if (n === 0) return "";
|
|
44
|
+
if (n < 20) return ONES[n];
|
|
45
|
+
if (n < 100) {
|
|
46
|
+
const rem = n % 10;
|
|
47
|
+
return TENS[Math.floor(n / 10)] + (rem ? " " + ONES[rem] : "");
|
|
48
|
+
}
|
|
49
|
+
const rem = n % 100;
|
|
50
|
+
return (
|
|
51
|
+
ONES[Math.floor(n / 100)] +
|
|
52
|
+
" Hundred" +
|
|
53
|
+
(rem ? " " + convertHundreds(rem) : "")
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Convert an integer (BigInt-safe via string) up to 15 digits to words.
|
|
59
|
+
* Returns "" for 0.
|
|
60
|
+
*/
|
|
61
|
+
function integerToWords(numStr) {
|
|
62
|
+
// Remove leading zeros
|
|
63
|
+
numStr = String(numStr).replace(/^0+/, "") || "0";
|
|
64
|
+
|
|
65
|
+
if (numStr === "0") return "Zero";
|
|
66
|
+
if (numStr.length > 15) {
|
|
67
|
+
throw new Error("Number exceeds 15-digit limit (max: 999,999,999,999,999)");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Pad to 15 digits for slicing
|
|
71
|
+
const padded = numStr.padStart(15, "0");
|
|
72
|
+
|
|
73
|
+
// Groups (Indian system awareness + international)
|
|
74
|
+
// We split into: [trillions(3), billions(3), millions(3), thousands(3), hundreds(3)]
|
|
75
|
+
const groups = [
|
|
76
|
+
{ value: parseInt(padded.slice(0, 3), 10), label: "Trillion" },
|
|
77
|
+
{ value: parseInt(padded.slice(3, 6), 10), label: "Billion" },
|
|
78
|
+
{ value: parseInt(padded.slice(6, 9), 10), label: "Million" },
|
|
79
|
+
{ value: parseInt(padded.slice(9, 12), 10), label: "Thousand" },
|
|
80
|
+
{ value: parseInt(padded.slice(12, 15), 10), label: "" },
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
const parts = [];
|
|
84
|
+
for (const { value, label } of groups) {
|
|
85
|
+
if (value === 0) continue;
|
|
86
|
+
const words = convertHundreds(value);
|
|
87
|
+
parts.push(label ? words + " " + label : words);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return parts.join(" ");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = { integerToWords, convertHundreds };
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Currency definitions by country name (case-insensitive lookup)
|
|
5
|
+
* Each entry: { currency, subunit, symbol, code, subunitFactor }
|
|
6
|
+
*/
|
|
7
|
+
const CURRENCIES = {
|
|
8
|
+
// Asia
|
|
9
|
+
india: {
|
|
10
|
+
currency: "Rupee",
|
|
11
|
+
currencyPlural: "Rupees",
|
|
12
|
+
subunit: "Paisa",
|
|
13
|
+
subunitPlural: "Paise",
|
|
14
|
+
symbol: "₹",
|
|
15
|
+
code: "INR",
|
|
16
|
+
subunitFactor: 100,
|
|
17
|
+
},
|
|
18
|
+
pakistan: {
|
|
19
|
+
currency: "Rupee",
|
|
20
|
+
currencyPlural: "Rupees",
|
|
21
|
+
subunit: "Paisa",
|
|
22
|
+
subunitPlural: "Paise",
|
|
23
|
+
symbol: "₨",
|
|
24
|
+
code: "PKR",
|
|
25
|
+
subunitFactor: 100,
|
|
26
|
+
},
|
|
27
|
+
"sri lanka": {
|
|
28
|
+
currency: "Rupee",
|
|
29
|
+
currencyPlural: "Rupees",
|
|
30
|
+
subunit: "Cent",
|
|
31
|
+
subunitPlural: "Cents",
|
|
32
|
+
symbol: "Rs",
|
|
33
|
+
code: "LKR",
|
|
34
|
+
subunitFactor: 100,
|
|
35
|
+
},
|
|
36
|
+
nepal: {
|
|
37
|
+
currency: "Rupee",
|
|
38
|
+
currencyPlural: "Rupees",
|
|
39
|
+
subunit: "Paisa",
|
|
40
|
+
subunitPlural: "Paise",
|
|
41
|
+
symbol: "Rs",
|
|
42
|
+
code: "NPR",
|
|
43
|
+
subunitFactor: 100,
|
|
44
|
+
},
|
|
45
|
+
bangladesh: {
|
|
46
|
+
currency: "Taka",
|
|
47
|
+
currencyPlural: "Taka",
|
|
48
|
+
subunit: "Paisa",
|
|
49
|
+
subunitPlural: "Paise",
|
|
50
|
+
symbol: "৳",
|
|
51
|
+
code: "BDT",
|
|
52
|
+
subunitFactor: 100,
|
|
53
|
+
},
|
|
54
|
+
china: {
|
|
55
|
+
currency: "Yuan",
|
|
56
|
+
currencyPlural: "Yuan",
|
|
57
|
+
subunit: "Fen",
|
|
58
|
+
subunitPlural: "Fen",
|
|
59
|
+
symbol: "¥",
|
|
60
|
+
code: "CNY",
|
|
61
|
+
subunitFactor: 100,
|
|
62
|
+
},
|
|
63
|
+
japan: {
|
|
64
|
+
currency: "Yen",
|
|
65
|
+
currencyPlural: "Yen",
|
|
66
|
+
subunit: "Sen",
|
|
67
|
+
subunitPlural: "Sen",
|
|
68
|
+
symbol: "¥",
|
|
69
|
+
code: "JPY",
|
|
70
|
+
subunitFactor: 100,
|
|
71
|
+
},
|
|
72
|
+
"south korea": {
|
|
73
|
+
currency: "Won",
|
|
74
|
+
currencyPlural: "Won",
|
|
75
|
+
subunit: "Jeon",
|
|
76
|
+
subunitPlural: "Jeon",
|
|
77
|
+
symbol: "₩",
|
|
78
|
+
code: "KRW",
|
|
79
|
+
subunitFactor: 100,
|
|
80
|
+
},
|
|
81
|
+
indonesia: {
|
|
82
|
+
currency: "Rupiah",
|
|
83
|
+
currencyPlural: "Rupiah",
|
|
84
|
+
subunit: "Sen",
|
|
85
|
+
subunitPlural: "Sen",
|
|
86
|
+
symbol: "Rp",
|
|
87
|
+
code: "IDR",
|
|
88
|
+
subunitFactor: 100,
|
|
89
|
+
},
|
|
90
|
+
malaysia: {
|
|
91
|
+
currency: "Ringgit",
|
|
92
|
+
currencyPlural: "Ringgit",
|
|
93
|
+
subunit: "Sen",
|
|
94
|
+
subunitPlural: "Sen",
|
|
95
|
+
symbol: "RM",
|
|
96
|
+
code: "MYR",
|
|
97
|
+
subunitFactor: 100,
|
|
98
|
+
},
|
|
99
|
+
singapore: {
|
|
100
|
+
currency: "Dollar",
|
|
101
|
+
currencyPlural: "Dollars",
|
|
102
|
+
subunit: "Cent",
|
|
103
|
+
subunitPlural: "Cents",
|
|
104
|
+
symbol: "S$",
|
|
105
|
+
code: "SGD",
|
|
106
|
+
subunitFactor: 100,
|
|
107
|
+
},
|
|
108
|
+
thailand: {
|
|
109
|
+
currency: "Baht",
|
|
110
|
+
currencyPlural: "Baht",
|
|
111
|
+
subunit: "Satang",
|
|
112
|
+
subunitPlural: "Satang",
|
|
113
|
+
symbol: "฿",
|
|
114
|
+
code: "THB",
|
|
115
|
+
subunitFactor: 100,
|
|
116
|
+
},
|
|
117
|
+
"united arab emirates": {
|
|
118
|
+
currency: "Dirham",
|
|
119
|
+
currencyPlural: "Dirhams",
|
|
120
|
+
subunit: "Fils",
|
|
121
|
+
subunitPlural: "Fils",
|
|
122
|
+
symbol: "د.إ",
|
|
123
|
+
code: "AED",
|
|
124
|
+
subunitFactor: 100,
|
|
125
|
+
},
|
|
126
|
+
uae: {
|
|
127
|
+
currency: "Dirham",
|
|
128
|
+
currencyPlural: "Dirhams",
|
|
129
|
+
subunit: "Fils",
|
|
130
|
+
subunitPlural: "Fils",
|
|
131
|
+
symbol: "د.إ",
|
|
132
|
+
code: "AED",
|
|
133
|
+
subunitFactor: 100,
|
|
134
|
+
},
|
|
135
|
+
"saudi arabia": {
|
|
136
|
+
currency: "Riyal",
|
|
137
|
+
currencyPlural: "Riyals",
|
|
138
|
+
subunit: "Halala",
|
|
139
|
+
subunitPlural: "Halalas",
|
|
140
|
+
symbol: "﷼",
|
|
141
|
+
code: "SAR",
|
|
142
|
+
subunitFactor: 100,
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// Americas
|
|
146
|
+
"united states": {
|
|
147
|
+
currency: "Dollar",
|
|
148
|
+
currencyPlural: "Dollars",
|
|
149
|
+
subunit: "Cent",
|
|
150
|
+
subunitPlural: "Cents",
|
|
151
|
+
symbol: "$",
|
|
152
|
+
code: "USD",
|
|
153
|
+
subunitFactor: 100,
|
|
154
|
+
},
|
|
155
|
+
usa: {
|
|
156
|
+
currency: "Dollar",
|
|
157
|
+
currencyPlural: "Dollars",
|
|
158
|
+
subunit: "Cent",
|
|
159
|
+
subunitPlural: "Cents",
|
|
160
|
+
symbol: "$",
|
|
161
|
+
code: "USD",
|
|
162
|
+
subunitFactor: 100,
|
|
163
|
+
},
|
|
164
|
+
canada: {
|
|
165
|
+
currency: "Dollar",
|
|
166
|
+
currencyPlural: "Dollars",
|
|
167
|
+
subunit: "Cent",
|
|
168
|
+
subunitPlural: "Cents",
|
|
169
|
+
symbol: "CA$",
|
|
170
|
+
code: "CAD",
|
|
171
|
+
subunitFactor: 100,
|
|
172
|
+
},
|
|
173
|
+
mexico: {
|
|
174
|
+
currency: "Peso",
|
|
175
|
+
currencyPlural: "Pesos",
|
|
176
|
+
subunit: "Centavo",
|
|
177
|
+
subunitPlural: "Centavos",
|
|
178
|
+
symbol: "MX$",
|
|
179
|
+
code: "MXN",
|
|
180
|
+
subunitFactor: 100,
|
|
181
|
+
},
|
|
182
|
+
brazil: {
|
|
183
|
+
currency: "Real",
|
|
184
|
+
currencyPlural: "Reais",
|
|
185
|
+
subunit: "Centavo",
|
|
186
|
+
subunitPlural: "Centavos",
|
|
187
|
+
symbol: "R$",
|
|
188
|
+
code: "BRL",
|
|
189
|
+
subunitFactor: 100,
|
|
190
|
+
},
|
|
191
|
+
argentina: {
|
|
192
|
+
currency: "Peso",
|
|
193
|
+
currencyPlural: "Pesos",
|
|
194
|
+
subunit: "Centavo",
|
|
195
|
+
subunitPlural: "Centavos",
|
|
196
|
+
symbol: "AR$",
|
|
197
|
+
code: "ARS",
|
|
198
|
+
subunitFactor: 100,
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// Europe
|
|
202
|
+
"euro zone": {
|
|
203
|
+
currency: "Euro",
|
|
204
|
+
currencyPlural: "Euros",
|
|
205
|
+
subunit: "Cent",
|
|
206
|
+
subunitPlural: "Cents",
|
|
207
|
+
symbol: "€",
|
|
208
|
+
code: "EUR",
|
|
209
|
+
subunitFactor: 100,
|
|
210
|
+
},
|
|
211
|
+
germany: {
|
|
212
|
+
currency: "Euro",
|
|
213
|
+
currencyPlural: "Euros",
|
|
214
|
+
subunit: "Cent",
|
|
215
|
+
subunitPlural: "Cents",
|
|
216
|
+
symbol: "€",
|
|
217
|
+
code: "EUR",
|
|
218
|
+
subunitFactor: 100,
|
|
219
|
+
},
|
|
220
|
+
france: {
|
|
221
|
+
currency: "Euro",
|
|
222
|
+
currencyPlural: "Euros",
|
|
223
|
+
subunit: "Cent",
|
|
224
|
+
subunitPlural: "Cents",
|
|
225
|
+
symbol: "€",
|
|
226
|
+
code: "EUR",
|
|
227
|
+
subunitFactor: 100,
|
|
228
|
+
},
|
|
229
|
+
italy: {
|
|
230
|
+
currency: "Euro",
|
|
231
|
+
currencyPlural: "Euros",
|
|
232
|
+
subunit: "Cent",
|
|
233
|
+
subunitPlural: "Cents",
|
|
234
|
+
symbol: "€",
|
|
235
|
+
code: "EUR",
|
|
236
|
+
subunitFactor: 100,
|
|
237
|
+
},
|
|
238
|
+
spain: {
|
|
239
|
+
currency: "Euro",
|
|
240
|
+
currencyPlural: "Euros",
|
|
241
|
+
subunit: "Cent",
|
|
242
|
+
subunitPlural: "Cents",
|
|
243
|
+
symbol: "€",
|
|
244
|
+
code: "EUR",
|
|
245
|
+
subunitFactor: 100,
|
|
246
|
+
},
|
|
247
|
+
"united kingdom": {
|
|
248
|
+
currency: "Pound",
|
|
249
|
+
currencyPlural: "Pounds",
|
|
250
|
+
subunit: "Penny",
|
|
251
|
+
subunitPlural: "Pence",
|
|
252
|
+
symbol: "£",
|
|
253
|
+
code: "GBP",
|
|
254
|
+
subunitFactor: 100,
|
|
255
|
+
},
|
|
256
|
+
uk: {
|
|
257
|
+
currency: "Pound",
|
|
258
|
+
currencyPlural: "Pounds",
|
|
259
|
+
subunit: "Penny",
|
|
260
|
+
subunitPlural: "Pence",
|
|
261
|
+
symbol: "£",
|
|
262
|
+
code: "GBP",
|
|
263
|
+
subunitFactor: 100,
|
|
264
|
+
},
|
|
265
|
+
switzerland: {
|
|
266
|
+
currency: "Franc",
|
|
267
|
+
currencyPlural: "Francs",
|
|
268
|
+
subunit: "Centime",
|
|
269
|
+
subunitPlural: "Centimes",
|
|
270
|
+
symbol: "CHF",
|
|
271
|
+
code: "CHF",
|
|
272
|
+
subunitFactor: 100,
|
|
273
|
+
},
|
|
274
|
+
sweden: {
|
|
275
|
+
currency: "Krona",
|
|
276
|
+
currencyPlural: "Kronor",
|
|
277
|
+
subunit: "Öre",
|
|
278
|
+
subunitPlural: "Öre",
|
|
279
|
+
symbol: "kr",
|
|
280
|
+
code: "SEK",
|
|
281
|
+
subunitFactor: 100,
|
|
282
|
+
},
|
|
283
|
+
norway: {
|
|
284
|
+
currency: "Krone",
|
|
285
|
+
currencyPlural: "Kroner",
|
|
286
|
+
subunit: "Øre",
|
|
287
|
+
subunitPlural: "Øre",
|
|
288
|
+
symbol: "kr",
|
|
289
|
+
code: "NOK",
|
|
290
|
+
subunitFactor: 100,
|
|
291
|
+
},
|
|
292
|
+
russia: {
|
|
293
|
+
currency: "Ruble",
|
|
294
|
+
currencyPlural: "Rubles",
|
|
295
|
+
subunit: "Kopek",
|
|
296
|
+
subunitPlural: "Kopeks",
|
|
297
|
+
symbol: "₽",
|
|
298
|
+
code: "RUB",
|
|
299
|
+
subunitFactor: 100,
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
// Africa & Oceania
|
|
303
|
+
"south africa": {
|
|
304
|
+
currency: "Rand",
|
|
305
|
+
currencyPlural: "Rand",
|
|
306
|
+
subunit: "Cent",
|
|
307
|
+
subunitPlural: "Cents",
|
|
308
|
+
symbol: "R",
|
|
309
|
+
code: "ZAR",
|
|
310
|
+
subunitFactor: 100,
|
|
311
|
+
},
|
|
312
|
+
australia: {
|
|
313
|
+
currency: "Dollar",
|
|
314
|
+
currencyPlural: "Dollars",
|
|
315
|
+
subunit: "Cent",
|
|
316
|
+
subunitPlural: "Cents",
|
|
317
|
+
symbol: "A$",
|
|
318
|
+
code: "AUD",
|
|
319
|
+
subunitFactor: 100,
|
|
320
|
+
},
|
|
321
|
+
"new zealand": {
|
|
322
|
+
currency: "Dollar",
|
|
323
|
+
currencyPlural: "Dollars",
|
|
324
|
+
subunit: "Cent",
|
|
325
|
+
subunitPlural: "Cents",
|
|
326
|
+
symbol: "NZ$",
|
|
327
|
+
code: "NZD",
|
|
328
|
+
subunitFactor: 100,
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Lookup by country name (case-insensitive) or currency code (e.g. "USD")
|
|
334
|
+
*/
|
|
335
|
+
function getCurrency(countryOrCode) {
|
|
336
|
+
if (!countryOrCode) return CURRENCIES["united states"];
|
|
337
|
+
const key = countryOrCode.trim().toLowerCase();
|
|
338
|
+
if (CURRENCIES[key]) return CURRENCIES[key];
|
|
339
|
+
|
|
340
|
+
// Try matching by currency code
|
|
341
|
+
const byCode = Object.values(CURRENCIES).find(
|
|
342
|
+
(c) => c.code.toLowerCase() === key
|
|
343
|
+
);
|
|
344
|
+
if (byCode) return byCode;
|
|
345
|
+
|
|
346
|
+
throw new Error(
|
|
347
|
+
`Unsupported country or currency code: "${countryOrCode}". ` +
|
|
348
|
+
`Supported: ${getSupportedCountries().join(", ")}`
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function getSupportedCountries() {
|
|
353
|
+
return Object.keys(CURRENCIES).map((k) =>
|
|
354
|
+
k.replace(/\b\w/g, (c) => c.toUpperCase())
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
module.exports = { getCurrency, getSupportedCountries, CURRENCIES };
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export interface CurrencyInfo {
|
|
2
|
+
currency: string;
|
|
3
|
+
currencyPlural: string;
|
|
4
|
+
subunit: string;
|
|
5
|
+
subunitPlural: string;
|
|
6
|
+
symbol: string;
|
|
7
|
+
code: string;
|
|
8
|
+
subunitFactor: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface CurrencyWordsOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Country name (e.g. "India", "United States") or ISO 4217 currency code (e.g. "INR", "USD").
|
|
14
|
+
* @default "United States"
|
|
15
|
+
*/
|
|
16
|
+
country?: string;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Whether to include the decimal/subunit part in the output.
|
|
20
|
+
* @default true
|
|
21
|
+
*/
|
|
22
|
+
includeDecimals?: boolean;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Number of decimal places to read (1 or 2).
|
|
26
|
+
* @default 2
|
|
27
|
+
*/
|
|
28
|
+
decimalPlaces?: number;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Prepend the currency symbol to the result (e.g. "₹ One Hundred Rupees").
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
includeSymbol?: boolean;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Append the ISO currency code to the result (e.g. "One Hundred Dollars (USD)").
|
|
38
|
+
* @default false
|
|
39
|
+
*/
|
|
40
|
+
includeCode?: boolean;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Use "and" between the main amount and the subunit amount.
|
|
44
|
+
* @default true
|
|
45
|
+
*/
|
|
46
|
+
andBeforeDecimals?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Convert a numeric currency amount to words.
|
|
51
|
+
*
|
|
52
|
+
* Supports up to 15 digits in the integer part.
|
|
53
|
+
*
|
|
54
|
+
* @param amount - Numeric amount as a number or string
|
|
55
|
+
* @param options - Conversion options
|
|
56
|
+
* @returns Human-readable currency string
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* currencyWords(1050.75, { country: "India" })
|
|
60
|
+
* // => "One Thousand Fifty Rupees and Seventy Five Paise"
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* currencyWords("999999999999999", { country: "USA", includeCode: true })
|
|
64
|
+
* // => "Nine Hundred Ninety Nine Trillion Nine Hundred Ninety Nine Billion ... Dollars (USD)"
|
|
65
|
+
*/
|
|
66
|
+
export function currencyWords(
|
|
67
|
+
amount: number | string,
|
|
68
|
+
options?: CurrencyWordsOptions
|
|
69
|
+
): string;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get currency metadata for a given country or currency code.
|
|
73
|
+
*/
|
|
74
|
+
export function getCurrencyInfo(countryOrCode: string): CurrencyInfo;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* List all supported country names.
|
|
78
|
+
*/
|
|
79
|
+
export function listCountries(): string[];
|
package/src/index.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { getCurrency, getSupportedCountries } = require("./currencies");
|
|
4
|
+
const { integerToWords } = require("./converter");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} CurrencyWordsOptions
|
|
8
|
+
* @property {string} [country="United States"] Country name or currency code (e.g. "India", "USD")
|
|
9
|
+
* @property {boolean} [includeDecimals=true] Whether to include decimal/subunit words
|
|
10
|
+
* @property {number} [decimalPlaces=2] Decimal places to consider (1 or 2)
|
|
11
|
+
* @property {boolean} [includeSymbol=false] Prepend the currency symbol
|
|
12
|
+
* @property {boolean} [includeCode=false] Append the currency ISO code
|
|
13
|
+
* @property {boolean} [andBeforeDecimals=true] Add "and" before subunit part
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Convert a numeric amount to currency words.
|
|
18
|
+
*
|
|
19
|
+
* @param {number|string} amount - The amount (up to 15 digits before decimal)
|
|
20
|
+
* @param {CurrencyWordsOptions} options
|
|
21
|
+
* @returns {string}
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* currencyWords(1234.56, { country: "India" })
|
|
25
|
+
* // => "One Thousand Two Hundred Thirty Four Rupees and Fifty Six Paise"
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* currencyWords(500, { country: "USA" })
|
|
29
|
+
* // => "Five Hundred Dollars"
|
|
30
|
+
*/
|
|
31
|
+
function currencyWords(amount, options = {}) {
|
|
32
|
+
const {
|
|
33
|
+
country = "United States",
|
|
34
|
+
includeDecimals = true,
|
|
35
|
+
decimalPlaces = 2,
|
|
36
|
+
includeSymbol = false,
|
|
37
|
+
includeCode = false,
|
|
38
|
+
andBeforeDecimals = true,
|
|
39
|
+
} = options;
|
|
40
|
+
|
|
41
|
+
// ── Validate & parse amount ──────────────────────────────────────────────
|
|
42
|
+
if (amount === null || amount === undefined || amount === "") {
|
|
43
|
+
throw new TypeError("amount is required");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const amountStr = String(amount).trim();
|
|
47
|
+
if (!/^-?\d+(\.\d+)?$/.test(amountStr)) {
|
|
48
|
+
throw new TypeError(`Invalid amount: "${amount}". Must be a numeric value.`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const isNegative = amountStr.startsWith("-");
|
|
52
|
+
const absStr = isNegative ? amountStr.slice(1) : amountStr;
|
|
53
|
+
|
|
54
|
+
const dotIndex = absStr.indexOf(".");
|
|
55
|
+
const intPart = dotIndex === -1 ? absStr : absStr.slice(0, dotIndex);
|
|
56
|
+
const rawDecimalStr = dotIndex === -1 ? "" : absStr.slice(dotIndex + 1);
|
|
57
|
+
|
|
58
|
+
// Enforce 15-digit integer limit
|
|
59
|
+
const cleanInt = intPart.replace(/^0+/, "") || "0";
|
|
60
|
+
if (cleanInt.length > 15) {
|
|
61
|
+
throw new RangeError(
|
|
62
|
+
`Integer part exceeds 15 digits. Got ${cleanInt.length} digits.`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ── Resolve currency ─────────────────────────────────────────────────────
|
|
67
|
+
const currency = getCurrency(country);
|
|
68
|
+
|
|
69
|
+
// ── Integer words ────────────────────────────────────────────────────────
|
|
70
|
+
const intWords = integerToWords(cleanInt);
|
|
71
|
+
const intValue = parseInt(cleanInt, 10);
|
|
72
|
+
const currencyLabel =
|
|
73
|
+
intValue === 1 ? currency.currency : currency.currencyPlural;
|
|
74
|
+
|
|
75
|
+
let result = intWords + " " + currencyLabel;
|
|
76
|
+
|
|
77
|
+
// ── Decimal / subunit words ──────────────────────────────────────────────
|
|
78
|
+
if (includeDecimals && rawDecimalStr) {
|
|
79
|
+
const places = Math.min(Math.max(parseInt(decimalPlaces, 10) || 2, 1), 2);
|
|
80
|
+
|
|
81
|
+
// Pad or trim decimal string to `places` digits
|
|
82
|
+
let decStr = rawDecimalStr.padEnd(places, "0").slice(0, places);
|
|
83
|
+
const decValue = parseInt(decStr, 10);
|
|
84
|
+
|
|
85
|
+
if (decValue > 0) {
|
|
86
|
+
const decWords = integerToWords(String(decValue));
|
|
87
|
+
const subLabel =
|
|
88
|
+
decValue === 1 ? currency.subunit : currency.subunitPlural;
|
|
89
|
+
|
|
90
|
+
result +=
|
|
91
|
+
(andBeforeDecimals ? " and " : " ") + decWords + " " + subLabel;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── Negative ─────────────────────────────────────────────────────────────
|
|
96
|
+
if (isNegative) {
|
|
97
|
+
result = "Negative " + result;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ── Symbol / code affixes ─────────────────────────────────────────────────
|
|
101
|
+
if (includeSymbol) {
|
|
102
|
+
result = currency.symbol + " " + result;
|
|
103
|
+
}
|
|
104
|
+
if (includeCode) {
|
|
105
|
+
result = result + " (" + currency.code + ")";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get currency info for a country.
|
|
113
|
+
*
|
|
114
|
+
* @param {string} country
|
|
115
|
+
* @returns {{ currency, currencyPlural, subunit, subunitPlural, symbol, code, subunitFactor }}
|
|
116
|
+
*/
|
|
117
|
+
function getCurrencyInfo(country) {
|
|
118
|
+
return getCurrency(country);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* List all supported countries.
|
|
123
|
+
* @returns {string[]}
|
|
124
|
+
*/
|
|
125
|
+
function listCountries() {
|
|
126
|
+
return getSupportedCountries();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = { currencyWords, getCurrencyInfo, listCountries };
|