Skip to content

mastermunj/to-words

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4,252 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

to-words

npm version npm downloads build OpenSSF Scorecard coverage minzipped size TypeScript license node Bun Deno CF Workers

Convert numbers and currency amounts into words across 116 locales with production-ready BigInt, ordinal, and TypeScript support.

🎮 Live Demo

Try it now: Open Interactive Demo

Test locale behavior, currency conversion, ordinals, and large number inputs in the browser.

🏆 Why to-words

  • 116 locale implementations with region-specific numbering and currency conventions
  • Built for real financial flows: amount in words, decimals, currency units, and negatives
  • Large number safe with BigInt and string input support
  • Run anywhere: Node.js, Deno, Bun, Cloudflare Workers, and all modern browsers
  • Functional APItoWords(42, { localeCode: 'en-US' }) for one-liners, or class-based for high-volume workloads
  • Strong developer ergonomics: TypeScript types, ESM/CJS/UMD, and per-locale imports
  • Performance focused for high-volume conversion workloads

📑 Table of Contents

💼 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

  • 116 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 — Up to 4.7M ops/sec (small integers; see benchmark section for full breakdown)
  • Functional APItoWords(), toOrdinal(), toCurrency() named exports for ergonomic one-liners
  • Auto Locale DetectiondetectLocale() reads navigator.language or Intl in any runtime
  • CLInpx to-words 12345 --locale en-US for shell scripts and quick conversions
  • Wide Compatibility — All modern browsers, Node.js 20+, Deno, Bun, and Cloudflare Workers

🚀 Quick Start

There are three ways to use to-words. Pick the one that fits your use case:

1. Class-based — best for high-volume workloads where you reuse one instance:

import { ToWords } from 'to-words';

const tw = new ToWords({ localeCode: 'en-US' });
tw.convert(12345); // "Twelve Thousand Three Hundred Forty Five"
tw.convert(100, { currency: true }); // "One Hundred Dollars Only"
tw.toOrdinal(3); // "Third"

2. Functional (full bundle) — one-liners with a localeCode option, all 116 locales available:

import { toWords, toOrdinal, toCurrency } from 'to-words';

toWords(12345, { localeCode: 'en-US' }); // "Twelve Thousand Three Hundred Forty Five"
toCurrency(100, { localeCode: 'en-US' }); // "One Hundred Dollars Only"
toOrdinal(3, { localeCode: 'en-US' }); // "Third"

3. Functional (per-locale import) — locale baked in, fully tree-shakeable, smallest bundle (~3.5 KB gzip):

import { toWords, toOrdinal, toCurrency } from 'to-words/en-US';

toWords(12345); // "Twelve Thousand Three Hundred Forty Five"
toCurrency(100); // "One Hundred Dollars Only"
toOrdinal(3); // "Third"

Default locale: When no localeCode is provided, the runtime locale is auto-detected via detectLocale() and falls back to en-IN if it cannot be matched.

📦 Installation

Runtime requirement: Node.js >= 20.

Moving from another library? See MIGRATION.md.

npm / yarn / pnpm

npm install to-words
# or
yarn add to-words
# or
pnpm add to-words

CDN (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

// Class-based — ESM
import { ToWords } from 'to-words';

// Class-based — CommonJS
const { ToWords } = require('to-words');

// Functional helpers (full bundle) — ESM
import { toWords, toOrdinal, toCurrency, detectLocale } from 'to-words';

// Functional helpers (per-locale, tree-shakeable) — ESM
import { toWords, toOrdinal, toCurrency } from 'to-words/en-US';

// Per-locale class — ESM
import { ToWords } from 'to-words/en-US';

Basic Conversion

Class-based:

const tw = new ToWords({ localeCode: 'en-US' });

tw.convert(123); // "One Hundred Twenty Three"
tw.convert(123.45); // "One Hundred Twenty Three Point Four Five"
tw.convert(123.045); // "One Hundred Twenty Three Point Zero Four Five"

Functional (full bundle):

import { toWords } from 'to-words';

toWords(123, { localeCode: 'en-US' }); // "One Hundred Twenty Three"
toWords(123.45, { localeCode: 'en-US' }); // "One Hundred Twenty Three Point Four Five"

Functional (per-locale):

import { toWords } from 'to-words/en-US';

toWords(123); // "One Hundred Twenty Three"
toWords(123.45); // "One Hundred Twenty Three Point 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 One Hundred Ninety Nine Billion
//  Two Hundred Fifty Four Million Seven Hundred Forty Thousand Nine Hundred Ninety Three"

Currency Conversion

Class-based:

const tw = new ToWords({ localeCode: 'en-IN' });

tw.convert(452, { currency: true });
// "Four Hundred Fifty Two Rupees Only"

tw.convert(452.36, { currency: true });
// "Four Hundred Fifty Two Rupees And Thirty Six Paise Only"

// Without "Only" suffix
tw.convert(452, { currency: true, doNotAddOnly: true });
// "Four Hundred Fifty Two Rupees"

// Ignore decimal/fractional part
tw.convert(452.36, { currency: true, ignoreDecimal: true });
// "Four Hundred Fifty Two Rupees Only"

// Ignore zero currency
tw.convert(0.36, { currency: true, ignoreZeroCurrency: true });
// "Thirty Six Paise Only"

// Show fractional unit even when zero (string input preserves .00)
tw.convert('452.00', { currency: true, includeZeroFractional: true });
// "Four Hundred Fifty Two Rupees And Zero Paise Only"

Functional — toCurrency() shorthand:

import { toCurrency } from 'to-words';

toCurrency(452, { localeCode: 'en-IN' });
// "Four Hundred Fifty Two Rupees Only"

toCurrency(452.36, { localeCode: 'en-IN' });
// "Four Hundred Fifty Two Rupees And Thirty Six Paise Only"

toCurrency(452, { localeCode: 'en-IN', doNotAddOnly: true });
// "Four Hundred Fifty Two Rupees"

Functional per-locale (locale baked in, no localeCode needed):

import { toCurrency } from 'to-words/en-IN';

toCurrency(452); // "Four Hundred Fifty Two Rupees Only"
toCurrency(452.36); // "Four Hundred Fifty Two Rupees And 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.5);
// "One Hundred Euros And Fifty Cents Only"

Ordinal Numbers

Class-based:

const tw = new ToWords({ localeCode: 'en-US' });

tw.toOrdinal(1); // "First"
tw.toOrdinal(21); // "Twenty First"
tw.toOrdinal(100); // "One Hundredth"

Functional — toOrdinal() (full bundle):

import { toOrdinal } from 'to-words';

toOrdinal(1, { localeCode: 'en-US' }); // "First"
toOrdinal(21, { localeCode: 'en-US' }); // "Twenty First"
toOrdinal(100, { localeCode: 'en-US' }); // "One Hundredth"

Functional per-locale:

import { toOrdinal } from 'to-words/en-US';

toOrdinal(1); // "First"
toOrdinal(21); // "Twenty First"

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

Every locale entry point (to-words/<locale>) exports four things:

Export Type Description
ToWords class Full class API pre-configured for this locale
toWords function Convert number → words
toOrdinal function Convert number → ordinal words
toCurrency function Convert number → currency words
// Class-based (locale pre-configured, no localeCode needed)
import { ToWords } from 'to-words/en-US';
const tw = new ToWords();
tw.convert(12345); // "Twelve Thousand Three Hundred Forty Five"

// Functional helpers (locale baked in — smallest possible import)
import { toWords, toOrdinal, toCurrency } from 'to-words/en-US';
toWords(12345); // "Twelve Thousand Three Hundred Forty Five"
toOrdinal(3); // "Third"
toCurrency(100); // "One Hundred Dollars Only"

Individual imports are ~3.5 KB gzip vs ~58 KB for the full bundle.

Browser Usage (UMD)

<!-- Single locale (recommended, ~3.5 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 (~58 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>

Functional API

Two flavours depending on whether you need tree-shaking:

Full bundlelocaleCode is optional; omit it to use the auto-detected runtime locale:

import { toWords, toOrdinal, toCurrency } from 'to-words';

// Explicit locale
toWords(12345, { localeCode: 'en-US' });
// "Twelve Thousand Three Hundred Forty Five"

toOrdinal(21, { localeCode: 'en-US' });
// "Twenty First"

toCurrency(1234.56, { localeCode: 'en-IN' });
// "One Thousand Two Hundred Thirty Four Rupees And Fifty Six Paise Only"

// No localeCode — uses detectLocale() automatically
toWords(12345);
// result depends on the runtime locale (e.g. 'en-US' → "Twelve Thousand Three Hundred Forty Five")

toCurrency(1234.56, { doNotAddOnly: true });
// currency in the runtime locale, without "Only" suffix

Per-locale import — locale baked in, no localeCode argument at all, fully tree-shakeable (~3.5 KB gzip):

import { toWords, toOrdinal, toCurrency } from 'to-words/en-US';

toWords(12345); // "Twelve Thousand Three Hundred Forty Five"
toOrdinal(21); // "Twenty First"
toCurrency(1234.56); // "One Thousand Two Hundred Thirty Four Dollars And Fifty Six Cents Only"

Performance note: The functional API creates one ToWords instance per call. For high-frequency hot paths (invoice loops, real-time input), prefer a shared class instance.

Auto-Detect Locale

detectLocale() is automatically used by toWords(), toOrdinal(), and toCurrency() when no localeCode is provided — so in most cases you don't need to call it directly. It is useful when you want to read the runtime locale for other purposes, display it to the user, or pass it explicitly to a class instance.

import { detectLocale, toWords, ToWords } from 'to-words';

// Used implicitly — no localeCode needed
toWords(1000);
// On a browser with navigator.language = 'fr-FR': "Mille"
// In a Node.js process with fr-FR locale:         "Mille"
// Fallback when nothing can be detected:           "One Thousand" (en-IN)

// Used explicitly — read once, reuse across many calls
const locale = detectLocale('en-US'); // custom fallback if detection misses
const tw = new ToWords({ localeCode: locale });
tw.convert(1000);

Reads navigator.language in browsers, Intl.DateTimeFormat().resolvedOptions().locale in Node.js / Deno / Bun / CF Workers. Falls back to 'en-IN' (or your custom fallback) if the detected value cannot be matched to a supported locale.

🔄 Migration Guide

Migrating from number-to-words, written-number, num-words, or n2words?

  • See MIGRATION.md for side-by-side API mapping and migration recipes.
  • Includes package comparison, behavior notes, and a regression checklist.

🖥️ CLI

Run one-off conversions from the command line without installing:

npx to-words 12345
# Twelve Thousand Three Hundred Forty Five

npx to-words 12345 --locale en-US
# Twelve Thousand Three Hundred Forty Five

npx to-words 1234.56 --locale en-US --currency
# One Thousand Two Hundred Thirty Four Dollars And Fifty Six Cents Only

npx to-words 3 --locale en-US --ordinal
# Third

npx to-words --detect-locale
# en-US  (or whatever your system locale is)

Once installed globally (npm i -g to-words), the to-words command is available directly.

⚛️ Framework Integration

React

// Change the locale import to match your users' region (e.g. 'to-words/en-GB' for UK)
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>

Next.js

// Server Component (App Router) — locale from request headers or user profile
import { toWords } from 'to-words';

type Props = { amount: number; locale: string };

export default function AmountInWords({ amount, locale }: Props) {
  return <p>{toWords(amount, { localeCode: locale, currency: true })}</p>;
}
// Client Component — dynamic locale switching
'use client';

import { useState } from 'react';
import { toCurrency, detectLocale } from 'to-words';

export function CurrencyDisplay({ amount }: { amount: number }) {
  const [locale, setLocale] = useState(detectLocale('en-US'));
  return (
    <div>
      <select value={locale} onChange={(e) => setLocale(e.target.value)}>
        <option value="en-US">English (US)</option>
        <option value="fr-FR">French</option>
        <option value="hi-IN">Hindi</option>
        <option value="ar-AE">Arabic</option>
      </select>
      <p>{toCurrency(amount, { localeCode: locale })}</p>
    </div>
  );
}

Node.js / Express

import express from 'express';
import { toWords, toCurrency, detectLocale } from 'to-words';

const app = express();

app.get('/convert', (req, res) => {
  const number = String(req.query.number ?? '');
  const locale = String(req.query.locale ?? detectLocale());
  const currency = req.query.currency === 'true';

  try {
    const result = currency ? toCurrency(number, { localeCode: locale }) : toWords(number, { localeCode: locale });
    res.json({ result, locale });
  } catch (e) {
    res.status(400).json({ error: (e as Error).message });
  }
});

🌍 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
    includeZeroFractional?: boolean; // Default: false
    currencyOptions?: {
      name: string;
      plural: string;
      symbol: string;
      fractionalUnit: {
        name: string;
        plural: string;
        symbol: string;
      };
    };
  };
}

Class 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")

Functional Exports

All four functional helpers are available from the full bundle (to-words) and from every per-locale entry point (to-words/<locale>). When importing from to-words/<locale>, the locale is already baked in and localeCode is not accepted.

toWords(number, options?)

Converts a number to words.

  • number: number | bigint | string — The number to convert
  • options (full bundle): ConverterOptions & { localeCode?: string } — When localeCode is omitted, detectLocale() is called automatically
  • options (per-locale): ConverterOptions
  • returns: string
import { toWords } from 'to-words';
toWords(12345, { localeCode: 'en-US' }); // explicit locale
toWords(12345); // auto-detects runtime locale

import { toWords } from 'to-words/en-US';
toWords(12345); // locale baked in, no detection needed

toOrdinal(number, options?)

Converts a number to ordinal words.

  • number: number — Must be a non-negative integer
  • options (full bundle): OrdinalOptions & { localeCode?: string } — When localeCode is omitted, detectLocale() is called automatically
  • options (per-locale): OrdinalOptions
  • returns: string
import { toOrdinal } from 'to-words';
toOrdinal(21, { localeCode: 'en-US' }); // explicit locale
toOrdinal(21); // auto-detects runtime locale

import { toOrdinal } from 'to-words/en-US';
toOrdinal(21); // locale baked in

toCurrency(number, options?)

Shorthand for converting a number to currency words. Equivalent to toWords(number, { currency: true, ...options }).

  • number: number | bigint | string
  • options (full bundle): ConverterOptions & { localeCode?: string } — When localeCode is omitted, detectLocale() is called automatically
  • options (per-locale): ConverterOptions
  • returns: string
import { toCurrency } from 'to-words';
toCurrency(1234.56, { localeCode: 'en-US' }); // explicit locale
toCurrency(1234.56); // auto-detects runtime locale

import { toCurrency } from 'to-words/en-US';
toCurrency(1234.56); // locale baked in

detectLocale(fallback?)

Reads the current runtime locale.

  • In browsers: reads navigator.language

  • In Node.js / Deno / Bun / CF Workers: reads Intl.DateTimeFormat().resolvedOptions().locale

  • Normalises BCP 47 tags (e.g. zh-Hant-TWzh-TW) and falls back to a language-prefix match

  • fallback (optional): string — Returned when no supported locale can be matched. Default: 'en-IN'

  • returns: string — A supported locale code

import { detectLocale } from 'to-words';

detectLocale(); // e.g. 'en-US', 'fr-FR', 'ja-JP'
detectLocale('en-GB'); // custom fallback if detection fails

detectLocale is only available from the full bundle (to-words), not from per-locale entry points.

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
includeZeroFractional boolean false When input is a string like "123.00", include "And Zero Paise" even though the decimal is zero
currencyOptions object undefined Override locale's default currency settings

Common Options Example

const toWords = new ToWords({ localeCode: 'en-US' });

toWords.convert(1234.56, {
  currency: true,
  ignoreDecimal: false,
  doNotAddOnly: true,
});
// "One Thousand Two Hundred Thirty Four Dollars And Fifty Six Cents"

📏 Bundle Sizes

Import Method Raw Gzip
Full bundle (all locales) 654 KB 60 KB
Single locale (en-US) 12 KB 3.5 KB
Single locale (en-IN) 10 KB 3.4 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 67+
Firefox 68+
Safari 14+
Edge 79+
Opera 54+

BigInt Support: BigInt is required for full functionality. Internet Explorer is not supported.

Runtime Compatibility

Runtime Support
Node.js 20+
Deno 1.28+
Bun 1.0+
Cloudflare Workers

The library uses only standard ECMAScript features (BigInt, Intl, Map) with zero Node.js-specific APIs, making it compatible with any modern JavaScript runtime.

🗺️ Supported Locales

All 116 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-AT German Austria Euro Long
de-CH German Switzerland Franken 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-HK English Hong Kong Dollar Short
en-IE English Ireland Euro Short
en-IN English India Rupee Indian
en-JM English Jamaica Dollar Short
en-KE English Kenya Shilling Short
en-LK English Sri Lanka Rupee 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-TT English Trinidad and Tobago Dollar Short
en-TZ English Tanzania Shilling Short
en-UG English Uganda Shilling Short
en-US English USA Dollar Short
en-ZA English South Africa Rand Short
en-ZW English Zimbabwe Zimbabwe Gold Short
es-AR Spanish Argentina Peso Short
es-CL Spanish Chile Peso Short
es-CO Spanish Colombia 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-CA French Canada Dollar 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
km-KH Khmer Cambodia រៀល Khmer
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
ms-SG Malay Singapore Dolar Short
my-MM Burmese Myanmar ကျပ် Burmese
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-AO Portuguese Angola Kwanza Short
pt-BR Portuguese Brazil Real Short
pt-MZ Portuguese Mozambique Metical Short
pt-PT Portuguese Portugal Euro Short
ro-RO Romanian Romania Leu Short
ru-RU Russian Russia Рубль Short
si-LK Sinhala Sri Lanka රුපියල Indian
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
sw-TZ Swahili Tanzania 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
uz-UZ Uzbek Uzbekistan So'm Short
vi-VN Vietnamese Vietnam Đồng Short
yo-NG Yoruba Nigeria Naira Short
zh-CN Chinese China East Asian
zh-TW Chinese Taiwan East Asian
zu-ZA Zulu South Africa Rand Short

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 (万, 億, 兆, 京...)
  • Burmese — Burmese traditional scale (သောင်း=10k, သိန်း=100k, သန်း=1M)
  • Khmer — Khmer traditional scale (មុឺន=10k, សែន=100k, លាន=1M)

⚠️ 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

Contributions are welcome! Please read CONTRIBUTING.md for the full guide covering development setup, coding guidelines, how to add a new locale, commit message format, and the PR process.

For questions or ideas, open an issue or start a discussion.

This project follows the Contributor Covenant Code of Conduct.

❓ FAQ

How do I handle numbers larger than JavaScript's safe integer limit?

Use BigInt or pass the number as a string:

// Using BigInt
toWords.convert(9007199254740993n);

// Using string
toWords.convert('9007199254740993');
Why am I seeing scientific notation in my output?

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);
Can I use a custom currency?

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"
Does this work in the browser?

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>
Can I inject a custom locale (crypto, internal units, custom currency)?

Yes. ToWordsCore exposes a setLocale() method that accepts any class implementing LocaleInterface ({ config: LocaleConfig }). No need to fork the package or submit a PR — your custom locale stays in your own codebase.

import { ToWordsCore } from 'to-words';
import type { LocaleInterface, LocaleConfig } from 'to-words';

class BitcoinLocale implements LocaleInterface {
  config: LocaleConfig = {
    currency: {
      name: 'Bitcoin',
      plural: 'Bitcoins',
      symbol: '₿',
      fractionalUnit: { name: 'Satoshi', plural: 'Satoshis', symbol: 'sat' },
    },
    texts: { and: 'And', minus: 'Minus', only: 'Only', point: 'Point' },
    numberWordsMapping: [
      { number: 1, value: 'One' },
      { number: 2, value: 'Two' },
      // ... rest of the mapping, same structure as any built-in locale
    ],
  };
}

const tw = new ToWordsCore();
tw.setLocale(BitcoinLocale);
console.log(tw.convert(2.1, { currency: true }));
// "Two Bitcoins And Ten Satoshis Only"

The easiest starting point is to copy the nearest built-in locale from src/locales/ and change only what differs.

How do I add support for a new locale?

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.

📄 License

MIT

About

Converts Numbers (including decimal points) into words. It also converts the numbers into words for currency.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors