@gks101/numtowords
v1.0.4
Published
Convert numbers to words in multiple languages and numeral systems (English, Indian, etc.)
Downloads
575
Maintainers
Keywords
Readme
@gks101/numtowords
Convert numbers to words in multiple languages and numeral systems — English, Indian, Hindi, German, French — with full TypeScript support, zero dependencies and < 3kb size.
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 box —
en,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/numtowordsQuick 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 localeAll 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
enandin. Other locales can support ordinals by implementingconvertOrdinal. - Ordinal mode does not support decimals (e.g.
"3.2").
Auto locale
convert(5, { locale: 'auto' }); // uses navigator.language / LANG fallbackNegative 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
convertshould acceptn: bigintand return the words in lowercase (prefer returning canonical forms with proper accents); the main library applies capitalization whencapitalize: true. - You can optionally implement
convertOrdinal(n, opts)for native ordinal support in your locale. - You can optionally provide
currencyDefaultsanddefaultCurrencyCodefor locale-native currency rendering. - Set
decimalPointand optionallydecimalDigitsto localize decimal output. WithoutdecimalDigits, fractional digits fall back to calling your locale'sconvertfor 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 checkIndian 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
