to-words
v5.1.0
Published
Converts numbers (including decimal points) into words & currency.
Downloads
374,994
Maintainers
Readme
to-words
Convert numbers to words with comprehensive locale, currency, and ordinal support. Ideal for invoicing, e-commerce, financial apps, and educational tools.
📑 Table of Contents
- Use Cases
- Features
- Quick Start
- Installation
- Usage
- Framework Integration
- Numbering Systems
- API Reference
- Bundle Sizes
- Performance
- Browser Compatibility
- Supported Locales
- Error Handling
- Contributing
- FAQ
- Changelog
- License
💼 Use Cases
- Invoicing & Billing — Display amounts in words on invoices, receipts, and financial documents
- Check Printing — Banks and financial institutions require amounts in words for check validation
- E-commerce — Show order totals in words for clarity and accessibility
- Legal Documents — Contracts and agreements often require written-out amounts
- Educational Apps — Teach number pronunciation and spelling in different languages
- Accessibility — Screen readers benefit from properly formatted number-to-text conversion
- Localization — Support global users with region-specific number formatting
✨ Features
- 94 Locales — The most comprehensive locale coverage available
- BigInt Support — Handle numbers up to 10^63 (Vigintillion) and beyond
- Multiple Numbering Systems — Short scale, Long scale, Indian, and East Asian
- Currency Formatting — Locale-specific currency with fractional units
- Ordinal Numbers — First, Second, Third, etc.
- Tree-Shakeable — Import only the locales you need
- TypeScript Native — Full type definitions included
- Multiple Formats — ESM, CommonJS, and UMD browser bundles
- Zero Dependencies — Lightweight and self-contained
- High Performance — 4M+ conversions per second
- Wide Browser Support — All modern browsers + IE11
🚀 Quick Start
import { ToWords } from 'to-words';
const toWords = new ToWords();
toWords.convert(12345);
// "Twelve Thousand Three Hundred Forty Five"📦 Installation
npm / yarn / pnpm
npm install to-words
# or
yarn add to-words
# or
pnpm add to-wordsCDN (Browser)
<!-- Full bundle with all locales -->
<script src="https://cdn.jsdelivr.net/npm/to-words/dist/umd/to-words.min.js"></script>
<!-- Single locale bundle (smaller, recommended) -->
<script src="https://cdn.jsdelivr.net/npm/to-words/dist/umd/en-US.min.js"></script>📖 Usage
Importing
// ESM
import { ToWords } from 'to-words';
// CommonJS
const { ToWords } = require('to-words');Basic Conversion
const toWords = new ToWords({ localeCode: 'en-US' });
toWords.convert(123);
// "One Hundred Twenty Three"
toWords.convert(123.45);
// "One Hundred Twenty Three Point Four Five"
toWords.convert(123.045);
// "One Hundred Twenty Three Point Zero Four Five"Note: When the fractional part starts with zero, digits after the decimal point are converted individually.
BigInt & Large Numbers
Handle numbers beyond JavaScript's safe integer limit:
const toWords = new ToWords({ localeCode: 'en-US' });
// Using BigInt
toWords.convert(1000000000000000000n);
// "One Quintillion"
toWords.convert(1000000000000000000000000000000000000000000000000000000000000000n);
// "One Vigintillion"
// Using string for precision
toWords.convert('9007199254740993');
// "Nine Quadrillion Seven Trillion..."Currency Conversion
const toWords = new ToWords({ localeCode: 'en-IN' });
toWords.convert(452, { currency: true });
// "Four Hundred Fifty Two Rupees Only"
toWords.convert(452.36, { currency: true });
// "Four Hundred Fifty Two Rupees And Thirty Six Paise Only"
// Without "Only" suffix
toWords.convert(452, { currency: true, doNotAddOnly: true });
// "Four Hundred Fifty Two Rupees"
// Ignore decimal/fractional part
toWords.convert(452.36, { currency: true, ignoreDecimal: true });
// "Four Hundred Fifty Two Rupees Only"
// Ignore zero currency
toWords.convert(0.36, { currency: true, ignoreZeroCurrency: true });
// "Thirty Six Paise Only"Custom Currency
Override currency settings while keeping the locale's language:
const toWords = new ToWords({
localeCode: 'en-US',
converterOptions: {
currency: true,
currencyOptions: {
name: 'Euro',
plural: 'Euros',
symbol: '€',
fractionalUnit: {
name: 'Cent',
plural: 'Cents',
symbol: '',
},
},
},
});
toWords.convert(100.50);
// "One Hundred Euros And Fifty Cents Only"Ordinal Numbers
const toWords = new ToWords({ localeCode: 'en-US' });
toWords.toOrdinal(1); // "First"
toWords.toOrdinal(21); // "Twenty First"
toWords.toOrdinal(100); // "One Hundredth"Note: Full ordinal word mappings are available for English, Spanish, French, Portuguese, Turkish, and Dutch locales. Other locales use suffix-based ordinals.
Tree-Shakeable Imports
Import only the locales you need for smaller bundle sizes:
// Import specific locale directly (includes ToWords configured for that locale)
import { ToWords } from 'to-words/en-US';
const toWords = new ToWords();
toWords.convert(12345);
// "Twelve Thousand Three Hundred Forty Five"Browser Usage (UMD)
<!-- Single locale (recommended, ~3 KB gzip) -->
<script src="https://cdn.jsdelivr.net/npm/to-words/dist/umd/en-US.min.js"></script>
<script>
// ToWords is pre-configured for en-US
const toWords = new ToWords();
console.log(toWords.convert(12345));
// "Twelve Thousand Three Hundred Forty Five"
</script>
<!-- Full bundle with all locales (~54 KB gzip) -->
<script src="https://cdn.jsdelivr.net/npm/to-words/dist/umd/to-words.min.js"></script>
<script>
// Specify locale when using full bundle
const toWords = new ToWords({ localeCode: 'fr-FR' });
console.log(toWords.convert(12345));
// "Douze Mille Trois Cent Quarante-Cinq"
</script>⚛️ Framework Integration
React
import { ToWords } from 'to-words/en-US';
const toWords = new ToWords();
function PriceInWords({ amount }: { amount: number }) {
const words = toWords.convert(amount, { currency: true });
return <span className="price-words">{words}</span>;
}
// Usage: <PriceInWords amount={1234.56} />
// Renders: "One Thousand Two Hundred Thirty Four Dollars And Fifty Six Cents Only"Vue 3
<script setup lang="ts">
import { computed } from 'vue';
import { ToWords } from 'to-words/en-US';
const props = defineProps<{ amount: number }>();
const toWords = new ToWords();
const words = computed(() =>
toWords.convert(props.amount, { currency: true })
);
</script>
<template>
<span class="price-words">{{ words }}</span>
</template>Angular
import { Pipe, PipeTransform } from '@angular/core';
import { ToWords } from 'to-words/en-US';
@Pipe({ name: 'toWords', standalone: true })
export class ToWordsPipe implements PipeTransform {
private toWords = new ToWords();
transform(value: number, currency = false): string {
return this.toWords.convert(value, { currency });
}
}
// Usage: {{ 1234.56 | toWords:true }}Svelte
<script lang="ts">
import { ToWords } from 'to-words/en-US';
export let amount: number;
const toWords = new ToWords();
$: words = toWords.convert(amount, { currency: true });
</script>
<span class="price-words">{words}</span>🌍 Numbering Systems
Different regions use different numbering systems. This library supports all major systems:
Short Scale (Western)
Used in: USA, UK, Canada, Australia, and most English-speaking countries.
| Number | Name | |--------|------| | 10^6 | Million | | 10^9 | Billion | | 10^12 | Trillion | | 10^15 | Quadrillion | | ... | ... | | 10^63 | Vigintillion |
const toWords = new ToWords({ localeCode: 'en-US' });
toWords.convert(1000000000000000000n);
// "One Quintillion"Long Scale (European)
Used in: Germany, France, and many European countries.
| Number | German | French | |--------|--------|--------| | 10^6 | Million | Million | | 10^9 | Milliarde | Milliard | | 10^12 | Billion | Billion | | 10^15 | Billiarde | Billiard |
const toWords = new ToWords({ localeCode: 'de-DE' });
toWords.convert(1000000000);
// "Eins Milliarde"Indian System
Used in: India, Bangladesh, Nepal, Pakistan.
| Number | Name | |--------|------| | 10^5 | Lakh | | 10^7 | Crore | | 10^9 | Arab | | 10^11 | Kharab | | 10^13 | Neel | | 10^15 | Padma | | 10^17 | Shankh |
const toWords = new ToWords({ localeCode: 'en-IN' });
toWords.convert(100000000000000000n);
// "One Shankh"
const toWordsHindi = new ToWords({ localeCode: 'hi-IN' });
toWordsHindi.convert(100000000000000000n);
// "एक शंख"East Asian System
Used in: Japan, China, Korea.
| Number | Character | |--------|-----------| | 10^4 | 万 (Man/Wan) | | 10^8 | 億 (Oku/Yi) | | 10^12 | 兆 (Chō/Zhao) | | 10^16 | 京 (Kei/Jing) | | 10^20 | 垓 (Gai) |
const toWords = new ToWords({ localeCode: 'ja-JP' });
toWords.convert(100000000);
// "一 億"⚙️ API Reference
Constructor Options
interface ToWordsOptions {
localeCode?: string; // Default: 'en-IN'
converterOptions?: {
currency?: boolean; // Default: false
ignoreDecimal?: boolean; // Default: false
ignoreZeroCurrency?: boolean;// Default: false
doNotAddOnly?: boolean; // Default: false
currencyOptions?: {
name: string;
plural: string;
symbol: string;
fractionalUnit: {
name: string;
plural: string;
symbol: string;
};
};
};
}Methods
convert(number, options?)
Converts a number to words.
- number:
number | bigint | string— The number to convert - options:
ConverterOptions— Override instance options - returns:
string— The number in words
toOrdinal(number)
Converts a number to ordinal words.
- number:
number— The number to convert (must be non-negative integer) - returns:
string— The ordinal in words (e.g., "First", "Twenty Third")
Converter Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| currency | boolean | false | Convert as currency with locale-specific formatting |
| ignoreDecimal | boolean | false | Ignore fractional part when converting |
| ignoreZeroCurrency | boolean | false | Skip zero main currency (e.g., show only "Thirty Six Paise") |
| doNotAddOnly | boolean | false | Omit "Only" suffix in currency mode |
| currencyOptions | object | undefined | Override locale's default currency settings |
📏 Bundle Sizes
| Import Method | Raw | Gzip | |--------------|-----|------| | Full bundle (all 94 locales) | 564 KB | 54 KB | | Single locale (en-US) | 11.5 KB | 3.2 KB | | Single locale (en-IN) | 9.3 KB | 3.1 KB |
Tip: Use tree-shakeable imports or single-locale UMD bundles for the smallest bundle size.
⚡ Performance
Benchmarked on Apple M2 (Node.js 23):
| Operation | Throughput | |-----------|------------| | Small integers (42) | ~4.7M ops/sec | | Medium integers (12,345) | ~2.2M ops/sec | | Large integers (15 digits) | ~700K ops/sec | | Currency conversion | ~1M ops/sec | | BigInt (30+ digits) | ~225K ops/sec |
Run benchmarks locally:
npm run bench🌐 Browser Compatibility
| Browser | Version | |---------|--------| | Chrome | 49+ | | Firefox | 52+ | | Safari | 10+ | | Edge | 14+ | | Opera | 36+ | | IE | 11 (with polyfills) |
BigInt Support: Chrome 67+, Firefox 68+, Safari 14+, Edge 79+. For older browsers, pass large numbers as strings.
🗺️ Supported Locales
All 94 locales with their features:
| Locale | Language | Country | Currency | Scale | Ordinal | |--------|----------|---------|----------|-------|---------| | af-ZA | Afrikaans | South Africa | Rand | Short | ✓ | | am-ET | Amharic | Ethiopia | ብር | Short | ✓ | | ar-AE | Arabic | UAE | درهم | Short | ✓ | | ar-LB | Arabic | Lebanon | ليرة | Short | ✓ | | ar-MA | Arabic | Morocco | درهم | Short | ✓ | | ar-SA | Arabic | Saudi Arabia | ريال | Short | ✓ | | az-AZ | Azerbaijani | Azerbaijan | Manat | Short | ✓ | | be-BY | Belarusian | Belarus | Рубель | Short | ✓ | | bg-BG | Bulgarian | Bulgaria | Лев | Short | ✓ | | bn-IN | Bengali | India | টাকা | Short | ✓ | | ca-ES | Catalan | Spain | Euro | Short | ✓ | | cs-CZ | Czech | Czech Republic | Koruna | Short | ✓ | | da-DK | Danish | Denmark | Krone | Long | ✓ | | de-DE | German | Germany | Euro | Long | ✓ | | ee-EE | Estonian | Estonia | Euro | Short | ✓ | | el-GR | Greek | Greece | Ευρώ | Short | ✓ | | en-AE | English | UAE | Dirham | Short | ✓ | | en-AU | English | Australia | Dollar | Short | ✓ | | en-BD | English | Bangladesh | Taka | Indian | ✓ | | en-CA | English | Canada | Dollar | Short | ✓ | | en-GB | English | United Kingdom | Pound | Short | ✓ | | en-GH | English | Ghana | Cedi | Short | ✓ | | en-IE | English | Ireland | Euro | Short | ✓ | | en-IN | English | India | Rupee | Indian | ✓ | | en-KE | English | Kenya | Shilling | Short | ✓ | | en-MA | English | Morocco | Dirham | Short | ✓ | | en-MM | English | Myanmar | Kyat | Short | ✓ | | en-MU | English | Mauritius | Rupee | Indian | ✓ | | en-MY | English | Malaysia | Ringgit | Short | ✓ | | en-NG | English | Nigeria | Naira | Short | ✓ | | en-NP | English | Nepal | Rupee | Indian | ✓ | | en-NZ | English | New Zealand | Dollar | Short | ✓ | | en-OM | English | Oman | Rial | Short | ✓ | | en-PH | English | Philippines | Peso | Short | ✓ | | en-PK | English | Pakistan | Rupee | Indian | ✓ | | en-SA | English | Saudi Arabia | Riyal | Short | ✓ | | en-SG | English | Singapore | Dollar | Short | ✓ | | en-US | English | USA | Dollar | Short | ✓ | | en-ZA | English | South Africa | Rand | Short | ✓ | | es-AR | Spanish | Argentina | Peso | Short | ✓ | | es-ES | Spanish | Spain | Euro | Short | ✓ | | es-MX | Spanish | Mexico | Peso | Short | ✓ | | es-US | Spanish | USA | Dólar | Short | ✓ | | es-VE | Spanish | Venezuela | Bolívar | Short | ✓ | | fa-IR | Persian | Iran | تومان | Short | ✓ | | fi-FI | Finnish | Finland | Euro | Short | ✓ | | fil-PH | Filipino | Philippines | Piso | Short | ✓ | | fr-BE | French | Belgium | Euro | Long | ✓ | | fr-FR | French | France | Euro | Long | ✓ | | fr-MA | French | Morocco | Dirham | Long | ✓ | | fr-SA | French | Saudi Arabia | Riyal | Long | ✓ | | gu-IN | Gujarati | India | રૂપિયો | Short | ✓ | | ha-NG | Hausa | Nigeria | Naira | Short | ✓ | | hbo-IL | Biblical Hebrew | Israel | שקל | Short | ✓ | | he-IL | Hebrew | Israel | שקל | Short | ✓ | | hi-IN | Hindi | India | रुपया | Indian | ✓ | | hr-HR | Croatian | Croatia | Euro | Short | ✓ | | hu-HU | Hungarian | Hungary | Forint | Short | ✓ | | id-ID | Indonesian | Indonesia | Rupiah | Short | ✓ | | is-IS | Icelandic | Iceland | Króna | Short | ✓ | | it-IT | Italian | Italy | Euro | Short | ✓ | | ja-JP | Japanese | Japan | 円 | East Asian | ✓ | | ka-GE | Georgian | Georgia | ლარი | Short | ✓ | | kn-IN | Kannada | India | ರೂಪಾಯಿ | Short | ✓ | | ko-KR | Korean | South Korea | 원 | Short | ✓ | | lt-LT | Lithuanian | Lithuania | Euras | Short | ✓ | | lv-LV | Latvian | Latvia | Eiro | Short | ✓ | | mr-IN | Marathi | India | रुपया | Indian | ✓ | | ms-MY | Malay | Malaysia | Ringgit | Short | ✓ | | nb-NO | Norwegian | Norway | Krone | Long | ✓ | | nl-NL | Dutch | Netherlands | Euro | Short | ✓ | | nl-SR | Dutch | Suriname | Dollar | Short | ✓ | | np-NP | Nepali | Nepal | रुपैयाँ | Indian | ✓ | | pa-IN | Punjabi | India | ਰੁਪਇਆ | Short | ✓ | | pl-PL | Polish | Poland | Złoty | Short | ✓ | | pt-BR | Portuguese | Brazil | Real | Short | ✓ | | pt-PT | Portuguese | Portugal | Euro | Short | ✓ | | ro-RO | Romanian | Romania | Leu | Short | ✓ | | ru-RU | Russian | Russia | Рубль | Short | ✓ | | sk-SK | Slovak | Slovakia | Euro | Short | ✓ | | sl-SI | Slovenian | Slovenia | Euro | Short | ✓ | | sq-AL | Albanian | Albania | Lek | Short | ✓ | | sr-RS | Serbian | Serbia | Dinar | Short | ✓ | | sv-SE | Swedish | Sweden | Krona | Short | ✓ | | sw-KE | Swahili | Kenya | Shilingi | Short | ✓ | | ta-IN | Tamil | India | ரூபாய் | Short | ✓ | | te-IN | Telugu | India | రూపాయి | Short | ✓ | | th-TH | Thai | Thailand | บาท | Short | ✓ | | tr-TR | Turkish | Turkey | Lira | Short | ✓ | | uk-UA | Ukrainian | Ukraine | Гривня | Short | ✓ | | ur-PK | Urdu | Pakistan | روپیہ | Short | ✓ | | vi-VN | Vietnamese | Vietnam | Đồng | Short | ✓ | | yo-NG | Yoruba | Nigeria | Naira | Short | ✓ | | zh-CN | Chinese | China | 元 | East Asian | ✓ |
Scale Legend:
- Short — Western short scale (Million, Billion, Trillion...)
- Long — European long scale (Million, Milliard, Billion, Billiard...)
- Indian — Indian numbering (Lakh, Crore, Arab, Kharab...)
- East Asian — East Asian numbering (万, 億, 兆, 京...)
⚠️ Error Handling
The library throws descriptive errors for invalid inputs:
Invalid Number
toWords.convert('abc');
// Error: Invalid Number "abc"
toWords.convert(NaN);
// Error: Invalid Number "NaN"
toWords.convert(Infinity);
// Error: Invalid Number "Infinity"Unknown Locale
const toWords = new ToWords({ localeCode: 'xx-XX' });
toWords.convert(123);
// Error: Unknown Locale "xx-XX"Invalid Ordinal Input
toWords.toOrdinal(-5);
// Error: Ordinal numbers must be non-negative integers, got "-5"
toWords.toOrdinal(3.14);
// Error: Ordinal numbers must be non-negative integers, got "3.14"Handling Errors
try {
const words = toWords.convert(userInput);
console.log(words);
} catch (error) {
console.error('Conversion failed:', error.message);
}🤝 Contributing
Adding a New Locale
Create the locale file: Add
src/locales/<locale-code>.tsimplementingLocaleInterfacefromsrc/types.ts. Use an existing locale as a template.Register the locale: Import your class in
src/locales/index.tsand add it to theLOCALESmap.Add tests: Create
__tests__/<locale-code>.test.tscovering integers, negatives, decimals, and currency.Update documentation: Add the locale to the Supported Locales section above.
Build and test: Run
npm testandnpm run build, then submit your PR.
❓ FAQ
Use BigInt or pass the number as a string:
// Using BigInt
toWords.convert(9007199254740993n);
// Using string
toWords.convert('9007199254740993');JavaScript automatically converts large numbers to scientific notation. Pass them as strings or BigInt instead:
// ❌ This may give unexpected results
toWords.convert(123456789012345678901);
// ✅ Use string or BigInt
toWords.convert('123456789012345678901');
toWords.convert(123456789012345678901n);Yes! Override the currency options:
toWords.convert(1234.56, {
currency: true,
currencyOptions: {
name: 'Bitcoin',
plural: 'Bitcoins',
symbol: '₿',
fractionalUnit: { name: 'Satoshi', plural: 'Satoshis', symbol: 'sat' }
}
});
// "One Thousand Two Hundred Thirty Four Bitcoins And Fifty Six Satoshis Only"Yes! Use the UMD bundles via CDN:
<script src="https://cdn.jsdelivr.net/npm/to-words/dist/umd/en-US.min.js"></script>
<script>
const toWords = new ToWords();
console.log(toWords.convert(123));
</script>See the Contributing section above. You'll need to create a locale file implementing the LocaleInterface and add tests.
📋 Changelog
See CHANGELOG.md for a detailed history of changes.
