@pointnet/cldr-spellout
v48.2.0
Published
TypeScript implementation of ICU Rule-Based Number Formatting (RBNF) — converts numbers to words across 89 CLDR locales
Downloads
67
Maintainers
Readme
@pointnet/cldr-spellout
Documentation & live playground →
TypeScript implementation of ICU Rule-Based Number Formatting (RBNF) — converts numbers to words across 89 CLDR locales.
import { RuleBasedNumberFormat } from "@pointnet/cldr-spellout";
const fmt = RuleBasedNumberFormat.fromLocale("en");
fmt.format(123); // "one hundred twenty-three"
fmt.format(1, "%digits-ordinal"); // "1st"
fmt.parse("forty-two"); // 42Features
- 89 CLDR locales — all locales from
cldr-rbnfv48 (Unicode CLDR 48) - Spellout & ordinals — cardinal numbers, ordinals, and more depending on locale
- Parse — convert spelled-out text back to numbers
- Custom rules — construct a formatter from your own RBNF rule string
- Synchronous — no async, no dynamic imports; works in Node.js and browsers
- Dual ESM/CJS — ships both formats; tree-shakeable (
"sideEffects": false)
Installation
npm install @pointnet/cldr-spelloutRequires Node.js ≥ 24 (for native ESM JSON imports). Browser bundles are unaffected.
Usage
Load a built-in locale
import { RuleBasedNumberFormat } from "@pointnet/cldr-spellout";
const en = RuleBasedNumberFormat.fromLocale("en");
en.format(1000); // "one thousand"
en.format(0.5); // "zero point five"
en.format(-7); // "minus seven"
const fr = RuleBasedNumberFormat.fromLocale("fr");
fr.format(71); // "soixante-et-onze"
fr.format(80); // "quatre-vingts"
const de = RuleBasedNumberFormat.fromLocale("de");
de.format(42); // "zweiundvierzig"Ordinals and other rule sets
Each locale exposes one or more named rule sets. Pass a rule set name as the second argument to format():
const en = RuleBasedNumberFormat.fromLocale("en");
en.format(1, "%digits-ordinal"); // "1st"
en.format(2, "%digits-ordinal"); // "2nd"
en.format(13, "%digits-ordinal"); // "13th"
// List available rule sets
en.getRuleSetNames();
// → ["%spellout-numbering", "%spellout-numbering-year",
// "%spellout-cardinal", "%spellout-ordinal", "%digits-ordinal", ...]Parse words to a number
const en = RuleBasedNumberFormat.fromLocale("en");
en.parse("one hundred twenty-three"); // 123
en.parse("forty-two"); // 42Load from raw CLDR data
fromCldrData() accepts any object matching the IRBNFData shape — useful when you supply your own rule data rather than a locale file:
import { RuleBasedNumberFormat } from "@pointnet/cldr-spellout";
import type { IRBNFData } from "@pointnet/cldr-spellout";
const durationData: IRBNFData = {
rbnf: {
identity: { language: "en" },
rbnf: {
DurationRules: {
"%%min-sec": [["0", ":=00="]],
"%duration": [
["0", "=%%seconds="],
["60/60", "<0<>%%min-sec>"],
["3600/3600","<0<:>%%min-sec>"],
],
"%%seconds": [
["0", "0 sec."],
["1", "1 sec."],
["2", "=#,##0= sec."],
],
},
},
},
};
const fmt = RuleBasedNumberFormat.fromCldrData(durationData);
fmt.format(3662); // "1:01:02"Custom rule string
You can construct a formatter from any ICU RBNF rule description string:
import { RuleBasedNumberFormat } from "@pointnet/cldr-spellout";
// Custom duration rules (adapted from ICU test suite)
const rules = `
%%min-sec:
0: :=00=;
%duration:
0: =%%seconds=;
60/60: <0<>%%min-sec>;
3600/3600: <0<:>%%min-sec>;
%%seconds:
0: 0 sec.;
1: 1 sec.;
2: =#,##0= sec.;
`;
const fmt = new RuleBasedNumberFormat(rules, "en");
fmt.format(3662); // "1:01:02"API
Static methods
| Method | Description |
|--------|-------------|
| RuleBasedNumberFormat.fromLocale(locale) | Create a formatter from a built-in CLDR locale. Throws if the locale is unsupported. |
| RuleBasedNumberFormat.fromCldrData(data, locale?) | Create a formatter from a raw IRBNFData object. locale defaults to the identity in the JSON. |
| RuleBasedNumberFormat.getSupportedLocales() | Returns an array of all 89 supported BCP 47 locale tags. |
Constructor
new RuleBasedNumberFormat(rules: string, locale?: string)Construct from an ICU RBNF rule description string. locale is a BCP 47 tag used for Intl.NumberFormat / Intl.PluralRules (default: "en").
Instance methods
| Method | Description |
|--------|-------------|
| format(number, ruleSetName?) | Format a number to text. Uses the default rule set unless ruleSetName is given. |
| parse(text) | Parse spelled-out text back to a number. Tries all public rule sets and returns the best match. |
| getRuleSetNames() | Returns all public rule set names (e.g., ["%spellout-numbering", "%digits-ordinal"]). |
| getDefaultRuleSetName() | Returns the name of the active default rule set. |
| setDefaultRuleSet(name) | Change the default rule set used when format() is called without a rule set name. |
| getRules() | Returns the original rule description string. |
Types
import type { IRBNFData } from "@pointnet/cldr-spellout";IRBNFData is the shape of a cldr-rbnf locale JSON file, accepted by fromCldrData().
Supported Locales
89 BCP 47 locale tags:
af ak am ar az be bg bs ca ccp chr cs cy da de de-CH ee el en en-IN eo es es-419 et fa fa-AF ff fi fil fo fr fr-BE fr-CH ga gu he hi hr hu hy id is it ja ka kk kl km ko ky lb lo lrc lt lv mk ms mt my ne nl nn no pl pt pt-PT qu ro ru se sk sl sq sr sr-Latn su sv sw ta th tr uk und vec vi yue yue-Hans zh zh-Hant
Use RuleBasedNumberFormat.getSupportedLocales() at runtime to get the full list.
License
MIT © pointnet
