npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@gks101/numtowords

v1.0.4

Published

Convert numbers to words in multiple languages and numeral systems (English, Indian, etc.)

Downloads

575

Readme

@gks101/numtowords

bundle size license tree shaking dependency count

Convert numbers to words in multiple languages and numeral systems — English, Indian, Hindi, German, French — with full TypeScript support, zero dependencies and < 3kb size.

Demo Documentation

registerAllBuiltInLocales()
convert(1_000_124)                       // "One million one hundred and twenty-four"
convert(12.34)                           // "Twelve point three four"
convert("150.50")                        // "One hundred and fifty point five zero"
convert(1_000_124, { locale: "in" })    // "Ten lakh one hundred twenty-four"
convert(1_000_124, { locale: "hi" })    // "दस लाख एक सौ चौबीस"
convert("12.34", { locale: "hi" })      // "बारह दशमलव तीन चार"
convert(1_000_124, { locale: "de" })    // "Eine Million einhundertvierundzwanzig"
convert(1_000_124, { locale: "fr" })    // "Un million cent vingt-quatre"

Features

  • 🌍 5 locales out of the boxen, in, hi, de, fr
  • 🇮🇳 Indian numbering system — lakh, crore, arab, kharab, neel, padma, shankh
  • 🔢 Decimal point support — converts decimal numbers and decimal strings digit by digit
  • 🔢 BigInt support — handles arbitrarily large numbers
  • 💱 Currency mode — locale-aware major/minor currency words
  • 🔌 Extensible — register your own locale in two lines
  • 📦 Three bundle formats — ESM, CJS, UMD (browser ready)
  • 🔷 Full TypeScript types included

Installation

npm install @gks101/numtowords

Quick Start

import { convert, registerAllBuiltInLocales } from '@gks101/numtowords';

registerAllBuiltInLocales();

convert(0); // "Zero"
convert(42); // "Forty-two"
convert(1_000_000); // "One million"
convert(12.34); // "Twelve point three four"
convert('150.50'); // "One hundred and fifty point five zero"

Register built-in locales

Built-in locales are explicit. Register them once during app startup.

import {
  registerBuiltInLocale,
  registerAllBuiltInLocales,
} from '@gks101/numtowords';

registerAllBuiltInLocales(); // registers en, in, hi, de, fr
// or registerBuiltInLocale('en'); // register only one locale

All examples below assume locales are registered.

Decimal numbers

Decimal values are supported with . as the decimal separator. The integer part is converted normally, and the fractional part is converted digit by digit using the selected locale.

convert(12.34); // "Twelve point three four"
convert('150.50'); // "One hundred and fifty point five zero"
convert('-0.25'); // "Negative zero point two five"
convert('12.34', { locale: 'in' }); // "Twelve point three four"
convert('12.34', { locale: 'hi' }); // "बारह दशमलव तीन चार"
convert('12.34', { locale: 'de' }); // "Zwölf komma drei vier"
convert('12.34', { locale: 'fr' }); // "Douze virgule trois quatre"

Use a string when trailing zeros are meaningful:

convert(150.5); // "One hundred and fifty point five"
convert('150.50'); // "One hundred and fifty point five zero"

Indian numbering system

convert(1_00_000, { locale: 'in' }); // "One lakh"
convert(10_00_000, { locale: 'in' }); // "Ten lakh"
convert(1_00_00_000, { locale: 'in' }); // "One crore"

Hindi (Devanagari)

convert(1_00_000, { locale: 'hi' }); // "एक लाख"
convert(1_00_00_000, { locale: 'hi' }); // "एक करोड़"

German

convert(21, { locale: 'de' }); // "Einundzwanzig"
convert(1_000_000, { locale: 'de' }); // "Eine Million"
convert(2_000_000, { locale: 'de' }); // "Zwei Millionen"

French

convert(70, { locale: 'fr' }); // "Soixante-dix"
convert(80, { locale: 'fr' }); // "Quatre-vingts"
convert(1_000_000, { locale: 'fr' }); // "Un million"

API

convert(input, options?)

| Parameter | Type | Description | | --------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | input | number \| bigint \| string | The number to convert. Negative numbers, decimals with ., string input with commas/underscores, and BigInts are all supported. | | options | ConvertOptions | Optional configuration (see below). |

Returns string

ConvertOptions

| Option | Type | Default | Description | | ------------ | ------------------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------ | | locale | "en" \| "in" \| "hi" \| "de" \| "fr" \| "auto" | "en" | Target language / numeral system | | capitalize | boolean | true | Capitalise the first letter | | useAnd | boolean | true | Include "and" connector (English only) | | currency | boolean \| string \| CurrencyOptions | false | true = locale currency words, string = append label (legacy), object = full currency configuration | | ordinal | boolean | false | Convert integer to ordinal words |

Other exports

import {
  toWords,
  toOrdinalWords,
  toCurrencyWords,
  detectRuntimeLocale,
  registerBuiltInLocale,
  registerAllBuiltInLocales,
  availableLocales,
  registerLocale,
  getLocale,
} from '@gks101/numtowords';

registerBuiltInLocale('en');
registerBuiltInLocale('fr');
// or registerAllBuiltInLocales()

toWords(42); // "Forty-two"
toOrdinalWords(21, { locale: 'en' }); // "Twenty-first"
toCurrencyWords('12.34', { locale: 'en' }); // "Twelve dollars and thirty-four cents"
detectRuntimeLocale(); // e.g. "en"
availableLocales(); // e.g. ["en", "fr"]

Advanced Usage

BigInt (very large numbers)

convert(9_007_199_254_740_993n); // beyond Number.MAX_SAFE_INTEGER
convert(1_00_00_00_00_000n, { locale: 'in' }); // "One arab"

Disable capitalisation

convert(42, { capitalize: false }); // "forty-two"

Disable "and" connector

convert(101, { useAnd: false }); // "One hundred one"

Currency

convert('200.42', { locale: 'en', currency: true }); // "Two hundred dollars and forty-two cents"
convert('200.42', { locale: 'in', currency: true }); // "Two hundred rupees and forty-two paise"
convert('200.42', { locale: 'hi', currency: true }); // "दो सौ रुपये और बयालीस पैसे"
convert('200.42', { locale: 'de', currency: true }); // "Zweihundert euro und zweiundvierzig cent"
convert('200.42', { locale: 'fr', currency: true }); // "Deux cents euros et quarante-deux centimes"
convert(5, { currency: 'USD' }); // "Five USD" (legacy suffix mode)

convert('12.34', {
  locale: 'en',
  currency: {
    enabled: true,
    rules: {
      major: { one: 'credit', other: 'credits' },
      minor: { one: 'point', other: 'points' },
      joinWord: 'and',
      minorBase: 100,
    },
  },
}); // "Twelve credits and thirty-four points"

CurrencyOptions (configuration)

When currency is an object, you can fully control how currency values are rendered.

type CurrencyOptions = {
  enabled?: boolean;
  code?: string; // resolves locale currencyDefaults (e.g. "USD", "INR", "EUR")
  roundMinor?: 'truncate' | 'round';
  includeOnly?: 'both' | 'major' | 'minor';
  rules?: {
    major: { one: string; other: string };
    minor: { one: string; other: string };
    joinWord?: string;
    minorBase?: 10 | 100 | 1000;
    hideZeroMinor?: boolean;
  };
};

Examples:

// Resolve by currency code (uses locale defaults when available)
convert('1.01', { locale: 'en', currency: { enabled: true, code: 'USD' } }); // "One dollar and one cent"

// Round minor part (1.236 -> 1.24)
convert('1.236', {
  locale: 'en',
  currency: { enabled: true, roundMinor: 'round' },
}); // "One dollar and twenty-four cents"

// Only major / only minor
convert('12.34', {
  locale: 'en',
  currency: { enabled: true, includeOnly: 'major' },
}); // "Twelve dollars"
convert('12.34', {
  locale: 'en',
  currency: { enabled: true, includeOnly: 'minor' },
}); // "Thirty-four cents"

// Show zero minor part
convert('12.00', {
  locale: 'en',
  currency: {
    enabled: true,
    rules: {
      major: { one: 'dollar', other: 'dollars' },
      minor: { one: 'cent', other: 'cents' },
      hideZeroMinor: false,
    },
  },
}); // "Twelve dollars and zero cents"

Ordinals

toOrdinalWords(1, { locale: 'en' }); // "First"
toOrdinalWords(21, { locale: 'en' }); // "Twenty-first"
convert(3, { locale: 'en', ordinal: true }); // "Third"

Notes:

  • Ordinals are built-in for en and in. Other locales can support ordinals by implementing convertOrdinal.
  • Ordinal mode does not support decimals (e.g. "3.2").

Auto locale

convert(5, { locale: 'auto' }); // uses navigator.language / LANG fallback

Negative numbers

convert(-1_500); // "Negative one thousand five hundred"

Registering a Custom Locale

You can add locales at runtime by calling registerLocale() with a LocaleDefinition. The convert function receives a bigint and the resolved options — return a string (do not capitalise; the library handles optional capitalization).

Minimal stub:

import { registerLocale, convert } from '@gks101/numtowords';

registerLocale('es', {
  name: 'Spanish',
  decimalPoint: 'punto',
  decimalDigits: [
    'cero',
    'uno',
    'dos',
    'tres',
    'cuatro',
    'cinco',
    'seis',
    'siete',
    'ocho',
    'nueve',
  ],
  convert(n, _opts) {
    // minimal implementation — handle zero and fall back to a placeholder
    if (n === 0n) return 'cero';
    return 'número';
  },
  defaultCurrencyCode: 'EUR',
  currencyDefaults: {
    EUR: {
      major: { one: 'euro', other: 'euros' },
      minor: { one: 'centimo', other: 'centimos' },
      joinWord: 'y',
      minorBase: 100,
      hideZeroMinor: true,
    },
  },
  convertOrdinal(n, opts) {
    // Optional: implement real Spanish ordinals as needed; this is a placeholder.
    return `${this.convert(n, opts)}o`;
  },
});

convert(5, { locale: 'es' }); // "Número" (capitalisation applied by library)
convert('5.25', { locale: 'es' }); // "Número punto dos cinco"

Practical Spanish examples

// Basic numbers
convert(0, { locale: 'es' }); // "Cero"
convert(1, { locale: 'es' }); // "Uno"
// Hundreds and thousands
convert(100, { locale: 'es' }); // "Cien"
convert(101, { locale: 'es' }); // "Ciento uno"

// Millions, accents and plurals
convert(1_000_000, { locale: 'es' }); // "Un millón"
convert(2_000_000, { locale: 'es' }); // "Dos millones"

// Negative and currency examples
convert(-5, { locale: 'es' }); // "Negativo cinco"
convert(1, { locale: 'es', currency: 'EUR' }); // "Uno EUR" (legacy suffix mode)

Notes

  • Your locale's convert should accept n: bigint and return the words in lowercase (prefer returning canonical forms with proper accents); the main library applies capitalization when capitalize: true.
  • You can optionally implement convertOrdinal(n, opts) for native ordinal support in your locale.
  • You can optionally provide currencyDefaults and defaultCurrencyCode for locale-native currency rendering.
  • Set decimalPoint and optionally decimalDigits to localize decimal output. Without decimalDigits, fractional digits fall back to calling your locale's convert for each digit.
  • Implementations may use BigInt arithmetic and should avoid throwing for valid numeric inputs.
  • For full correctness in Spanish, handle special forms (e.g., "cien" vs "ciento"), accents (dieciséis, veintidós), and pluralisation ("millón" → "millones").

Bundle Formats

| File | Format | Use case | | ------------------- | --------- | -------------------------------- | | dist/index.esm.js | ES Module | Bundlers (Vite, webpack, Rollup) | | dist/index.cjs.js | CommonJS | Node.js (require) | | dist/index.umd.js | UMD | Browser <script> tags |

Browser via CDN

<script src="dist/index.umd.js"></script>
<script>
  console.log(NumToWords.convert(1000000)); // "One million"
</script>

Building from Source

npm install
npm run build   # Rollup → dist/
npm test        # Jest test suite
npm run test:coverage  # Jest coverage report (coverage/lcov-report/index.html)
npm run lint    # TypeScript type check

Indian Numeral Scale Reference

| Value | Indian Name | English equivalent | | -------------------------- | ----------- | ------------------- | | 1,000 | Thousand | Thousand | | 1,00,000 | Lakh | Hundred Thousand | | 1,00,00,000 | Crore | Ten Million | | 1,00,00,00,000 | Arab | Billion | | 1,00,00,00,00,000 | Kharab | Hundred Billion | | 1,00,00,00,00,00,000 | Neel | Ten Trillion | | 1,00,00,00,00,00,00,000 | Padma | Quadrillion | | 1,00,00,00,00,00,00,00,000 | Shankh | Hundred Quadrillion |


License

MIT