Migration Guide
to-words is designed to be low-friction if you are coming from another number-to-words package. The core conversion methods remain consistent across releases, and the functional helpers give you a modern migration path when you want less boilerplate.
Upgrading from to-words v6
v7 corrects the Estonian and Nepali locale identifiers. Update both dynamic locale values and per-locale imports:
-const tw = new ToWords({ localeCode: 'ee-EE' });
-import { toWords } from 'to-words/ee-EE';
+const tw = new ToWords({ localeCode: 'et-EE' });
+import { toWords } from 'to-words/et-EE';
-const nepali = new ToWords({ localeCode: 'np-NP' });
-import { toCurrency } from 'to-words/np-NP';
+const nepali = new ToWords({ localeCode: 'ne-NP' });
+import { toCurrency } from 'to-words/ne-NP';This also applies to functional helper options, CLI --locale values, and manifest lookups. The former codes are not retained as aliases: ee identifies the separate Ewe language, while np is not a registered language subtag. Estonian and Nepali output is otherwise unchanged, and automatic detection now recognizes et/et-EE and ne/ne-NP.
Four other contracts are intentionally stricter:
new ToWords()now auto-detects the runtime locale like the functional helpers. Usenew ToWords({ localeCode: 'en-IN' })to preserve the old implicit class default.- Locale options use the generated
LocaleCodeunion. UseresolveLocale(dynamicString)for request, profile, or picker values; canonical variants such asEN_usresolve toen-US. LOCALESand both exported default objects are frozen, constructor options are snapshotted, and the fullToWords/ publicToWordsCorevalidate custom locale classes on first use. Per-locale entry points keep using their smaller prevalidated table.- Conversion defaults to
rangeMode: 'strict'and throwsNumberOutOfRangeErrorabove the form-specific ceiling. Use{ rangeMode: 'compose' }(or CLI--range-mode compose) only when legacy recursive composition is intentional.
The exact cardinal, ordinal, and currency ceilings are available from getLocaleMetadata(localeCode).range.maximumSupported.
Upgrading from to-words v5
v6 corrects several public behaviors that could silently produce the wrong locale or numeric result. Most applications need no code changes, but check these cases:
- Language-only and unknown-region locale tags now use explicit defaults instead of registry order:
en→en-US,es→es-ES,pt→pt-BR, andsw→sw-KE. Pass a fulllocaleCodeif your application needs another regional variant. getLocale().configis recursively frozen after initialization so mutation cannot invalidate internal lookup caches. Complete custom locale configuration before callingsetLocale()or performing a conversion.toFixed()now uses decimal-safe rounding,isFloat()recognizes valid numeric strings and BigInts, andisNumberZero()returnstrueonly for exact zero. Audit code that called these public utility methods directly.- The CLI now rejects unknown options, repeated
--locale, and conflicting conversion modes, and accepts an exact numeric string for ordinals. Use--before negative positional values when needed. setLocaleDetector()remains process-global. In SSR and APIs, pass{ localeCode }per request instead of changing the detector while requests are in flight.
The normal conversion API is unchanged. The release also preserves large integer strings, fixes decimal currency rounding and negative sub-unit signs, and adds Spanish ordinal gender handling.
Upgrading from to-words v4
If you already use the class API, the upgrade is additive rather than disruptive.
import { ToWords } from 'to-words';
const tw = new ToWords({ localeCode: 'en-IN' });
tw.convert(1234);
tw.toOrdinal(3);New in v5:
- Functional helpers:
toWords(),toOrdinal(),toCurrency() - Runtime locale detection:
detectLocale() - Application-wide/test locale override hook:
setLocaleDetector()(passlocaleCodeexplicitly per server request) - Per-locale functional exports for better tree-shaking
- BigInt and string-safe large-number handling
Migrating from number-to-words
// Before
const converter = require('number-to-words');
converter.toWords(21);
// After
import { toWords, toOrdinal } from 'to-words/en-US';
toWords(21);
toOrdinal(21);Main differences:
to-wordsreturns full ordinal words, not numeric suffixes- Output is title-cased instead of lower-case with hyphens
- Currency mode is built in
Migrating from written-number
// Before
writtenNumber(1234, { lang: 'es' });
// After
import { toWords } from 'to-words';
toWords(1234, { localeCode: 'es-ES' });Move from language shorthands like es to full BCP 47 locale codes such as es-ES and fr-FR.
Migrating from num-words
import { toWords } from 'to-words/en-IN';
toWords(123456);
toWords(9999999999999n);This is the best route when you want Indian numbering plus BigInt safety.
Migrating from n2words
import { toWords as en } from 'to-words/en-US';
import { toWords as es } from 'to-words/es-ES';
en(42);
es(42);Both packages support locale subpath imports. to-words differs by also supporting root imports with localeCode when the language is chosen at runtime.
Full Reference
For the full side-by-side comparison and regression checklist, see MIGRATION.md.