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

n2words-anycurrency

v6.1.2

Published

Convert numbers to words in 50 languages (72 regional variants) with zero dependencies. Supports BigInt, decimals, and browser/Node.js environments. Fork of n2words: every language can name any ISO 4217 currency via an explicit Intl.DisplayNames fallback.

Readme

n2words-anycurrency

CI npm version npm provenance npm downloads jsDelivr

Numbers to words. 50 languages, 72 regional variants. Zero dependencies.

Fork of n2words with one deliberate divergence: every language can name any ISO 4217 currency. Upstream throws RangeError for a currency a language has no native words for; this fork falls back to Intl.DisplayNames in the quoting language plus home-language subunit words (see docs/currency-vocab.md).

Try it live → — an interactive demo running this library in your browser.

Why n2words-anycurrency?

  • Pure Functions — Each language exports standalone functions. No classes, no configuration, no side effects.
  • Tree-Shakeable — Import only what you need. Unused exports are eliminated by modern bundlers.
  • Tiny Bundles — ~2.5-3.5 KB gzipped per language with all three forms, less per form. No bloat.
  • Multiple Forms — Cardinal ("forty-two"), ordinal ("forty-second"), and currency ("forty-two dollars")
  • 50 Languages, 72 Regional Variants — European, Asian, Middle Eastern, African, and more — most importable via a single bare-tag code (n2words-anycurrency/de), no region required
  • Zero Dependencies — Works everywhere: Node.js, browsers, Deno, Bun
  • BigInt Support — Accepts bigint (and numeric-string) input, so large values keep full precision
  • Type-Safe — Full TypeScript support with generated .d.ts declarations

Quick Start

npm install n2words-anycurrency
import { toCardinal } from 'n2words-anycurrency/en'
import { toCardinal as es } from 'n2words-anycurrency/es'

toCardinal(42)   // 'forty-two'
es(42)           // 'cuarenta y dos'

Forms

n2words-anycurrency converts numbers to words in multiple forms:

| Form | Function | Example | | -------- | ------------------------------------- | ----------------------------------- | | Cardinal | toCardinal(42) | "forty-two" | | Ordinal | toOrdinal(42) | "forty-second" | | Currency | toCurrency(42.50, { currency }) | "forty-two dollars and fifty cents" |

import { toCardinal, toOrdinal, toCurrency } from 'n2words-anycurrency/en'

toCardinal(1234)                        // 'one thousand two hundred thirty-four'
toOrdinal(1234)                         // 'one thousand two hundred thirty-fourth'
toCurrency(1234.56, { currency: 'USD' }) // 'one thousand two hundred thirty-four dollars and fifty-six cents'

toCurrency is the one form a bare tag won't guess at. en names a language, and a default currency belongs to a country, so n2words-anycurrency/en requires the currency to be named. Import the region-qualified code when you want its default:

import { toCurrency } from 'n2words-anycurrency/en'
import { toCurrency as usd } from 'n2words-anycurrency/en-US'

toCurrency(42.50)                      // throws TypeError — en is a language, not a locale
toCurrency(42.50, { currency: 'GBP' }) // 'forty-two pounds and fifty pence'
usd(42.50)                             // 'forty-two dollars and fifty cents'

A currency-supporting language can also name an amount in a currency other than its own default, via the currency option — any ISO 4217 code is accepted. A language first uses its own native currency words (so existing outputs are unchanged); past those it falls back to Intl.DisplayNames in the quoting language for the major unit plus home-language subunit words. Only a genuinely unknown code — or a subunit with no words yet while the amount has a nonzero minor part — throws RangeError. pt-BR is one of the few languages with no bare-tag entry point (Brazilian and European Portuguese use different numbering systems, not just different currencies — see LANGUAGES.md), so it's imported by its full code:

import { toCurrency } from 'n2words-anycurrency/pt-BR'

toCurrency(42.50)                      // 'quarenta e dois reais e cinquenta centavos' (BRL, the default)
toCurrency(42.50, { currency: 'EUR' }) // 'quarenta e dois euros e cinquenta centavos'
toCurrency(42.50, { currency: 'CAD' }) // fallback: DisplayNames major + home minor

Every currency-supporting language still exports its default, readable at runtime:

import { currencyDefaults, currencyValues } from 'n2words-anycurrency/pt-BR'

currencyDefaults.currency  // 'BRL'
currencyValues             // {} — open set on purpose (any ISO 4217 code accepted)

This generalizes: every form that takes options exports its defaults (cardinalDefaults, ordinalDefaults, currencyDefaults), and any option with a fixed set of allowed values — gender, but no longer currency — also exports that set as <form>Values. Boolean and free-string options have no such set, so they have no Values entry. LANGUAGES.md lists every option, type and default per language.

Each language implements one or more of these forms — see LANGUAGES.md for per-language coverage.

Range: each form spells values up to the largest scale word it knows, then throws a RangeError rather than inventing vocabulary — and the ceiling varies by language and form (e.g. es-ES cardinals reach 10^30 − 1, ordinals only 10^9 − 1). Cardinal and currency accept negatives and decimals; ordinal is positive integers only.

Usage

ESM (Node.js, modern bundlers):

import { toCardinal } from 'n2words-anycurrency/de'            // Single form, bare-tag entry point
import { toCardinal, toOrdinal } from 'n2words-anycurrency/de' // Multiple forms
import { toCardinal as fr } from 'n2words-anycurrency/fr'       // Aliased import

Most languages are imported this way — a bare BCP 47 primary subtag (n2words-anycurrency/de, n2words-anycurrency/en, n2words-anycurrency/fr, ...), no region required. Each bare tag is a documented alias for one specific regional/script variant (see LANGUAGES.md's "Languages" table for the full list and each one's default). Import the full region/script-qualified code instead when you need a specific variant (n2words-anycurrency/en-GB, n2words-anycurrency/fr-BE) or when the language has no single safe default — a handful of languages whose variants genuinely diverge in script or core numbering grammar (Chinese, Portuguese, Serbian, Amharic) require the full code:

import { toCardinal } from 'n2words-anycurrency/zh-Hans-CN'

A bare tag forwards toCardinal and toOrdinal untouched — they're the same functions its target exports. The one difference is toCurrency, which requires an explicit currency because a language tag carries no country to take a default from (see docs/bare-tag-aliases.md).

Browser (CDN):

<!-- ESM (recommended) -->
<script type="module">
  import { toCardinal } from 'https://cdn.jsdelivr.net/npm/n2words-anycurrency/dist/en.js'
  console.log(toCardinal(42))  // 'forty-two'
</script>

<!-- UMD (legacy script tags) -->
<script src="https://cdn.jsdelivr.net/npm/n2words-anycurrency/dist/en.umd.js"></script>
<script>
  n2words.en(42)                                   // 'forty-two'
  n2words.ordinal.en(42)                           // 'forty-second'
  n2words.currency.en(42.50, { currency: 'USD' })  // 'forty-two dollars and fifty cents'
  // `en` is a language, so currency must be named — or load dist/en-US.umd.js
  // and call n2words.currency.enUS(42.50) for a locale's own default.
</script>

dist/{code}.js carries all three forms. A page that needs only one can fetch just that form from dist/{code}/{form}.jscardinal, ordinal or currency:

<script type="module">
  import { toCurrency } from 'https://cdn.jsdelivr.net/npm/n2words-anycurrency/dist/en-US/currency.js'
  console.log(toCurrency(42.50))  // 'forty-two dollars and fifty cents'
</script>

Under half the bytes for cardinal or ordinal, since the other two forms and everything only they reach are never in the file. Currency is the heaviest of the three: it carries the shared currency-name matrix plus the any-ISO fallback, so en-US can quote any ISO 4217 currency, not just USD.

|en-US|bytes| |-------|-----| |dist/en-US.js (all three forms)|9,227| |dist/en-US/cardinal.js|4,293| |dist/en-US/ordinal.js|3,815| |dist/en-US/currency.js|5,258|

This is for CDN consumers only. If you install from npm and use a bundler, import { toCurrency } from 'n2words-anycurrency/en-US' already drops the forms you don't import — the package is sideEffects-free and each form is an independent export, so there is nothing to opt into.

See LANGUAGES.md for all language codes and available forms.

CLI

The package ships an n2words-anycurrency command, so a number can be spelled without writing any code:

npx n2words-anycurrency 42 --lang en                   # forty-two
npx n2words-anycurrency 42 -l fr-FR --ordinal          # quarante-deuxième
npx n2words-anycurrency 42.50 -l en-US --currency EUR  # forty-two euro and fifty cents
npx n2words-anycurrency 101 -l es-ES --gender feminine # ciento una

--lang takes any entry point the library exports — a bare tag (en, de) or a region/script-qualified code (en-GB, zh-Hans-CN), in any casing. --cardinal (the default), --ordinal and --currency pick the form; --currency <CODE> names the currency and selects the form in one flag, while --form currency keeps the locale's own default.

Options are the language's own. Every flag past the built-ins is derived at runtime from the module you selected, so the CLI offers exactly what that language and form declare — --gender for Spanish, --formal for Chinese, --and/--no-and for English currency. Booleans also accept --flag=false, and --option key=value reaches any option by its declared name.

Two commands make the whole library explorable:

npx n2words-anycurrency --list               # every entry point, its kind, and the forms it exports
npx n2words-anycurrency --help --lang es-ES  # es-ES's forms, their ceilings, and its options

It composes in pipelines. With no values it reads stdin, one per line, streaming:

printf '1\n2\n3\n' | npx n2words-anycurrency -l de
seq 1 100 | npx n2words-anycurrency -l fr --json > numbers.jsonl

--json emits one record per line ({input, output, lang, form, options}), with failures reported as {input, error} rather than on stderr. Values are read as text and passed through untouched, so precision is never lost — n2words-anycurrency 12345678901234567890 -l en is exact.

Exit codes: 0 success, 1 a usage error (unknown flag, unknown language, missing --lang), 2 at least one value could not be converted — an out-of-range number, or an option value the language rejects. A bad line in a pipe doesn't stop the rest.

Supported Languages

See LANGUAGES.md for the complete list with codes and options.

Highlights: Arabic, Chinese (Simplified/Traditional), English, French, German, Hindi, Japanese, Korean, Portuguese, Russian, Spanish, and many more.

Compatibility

  • Node.js: 22+
  • Browsers: Chrome 67+, Firefox 68+, Safari 14+, Edge 79+ — any browser with BigInt support
  • Runtimes: Deno, Bun, Cloudflare Workers

Requires BigInt support (cannot be polyfilled).

Performance

n2words-anycurrency is optimized for both size and speed:

  • ~2.5-3.5 KB gzipped per language, all three forms included
  • Individual language imports enable tree-shaking
  • No runtime dependencies
  • BigInt modulo operations (no string manipulation)
  • Pure functions with no shared state
  • Minimal memory allocation per conversion

Run npm run bench to measure on your hardware.

Contributing

We welcome contributions! Add a new language or improve existing ones:

npm run lang:add -- <code>   # Scaffold a new language (BCP 47 code)
npm test                     # Run full test suite

Also welcome: bug reports, feature requests, and documentation improvements.

License

MIT © Wael TELLAT, Tyler Vigario & contributors