4.2 KiB
PluralizationEngine
(no submodules)
Purpose
PluralizationEngine selects the correct plural form of a message for a given count and locale, following the Unicode CLDR plural rules. Rather than requiring callers to know the intricacies of each language's plural grammar (Russian has four forms; Arabic six; Welsh six with unique triggers), the engine encapsulates that knowledge and exposes a simple resolve(count, messages, options) API.
The engine is pre-loaded with built-in rules for 14 languages and supports runtime extension via registerRule. The rule functions are exported as standalone named exports so they can be tree-shaken or used directly without the class.
Features
- Pre-built rules for Arabic, Chinese, English, French, German, Italian, Japanese, Korean, Dutch, Polish, Portuguese, Russian, Spanish, Welsh
- Cardinal and ordinal plural support (English ordinal: 1st, 2nd, 3rd)
PluralRuleRegistrybacked by aMapfor O(1) locale lookup- Locale normalisation:
"en-GB"→"en","ZH"→"zh" getCategories(locale)— introspects which CLDR categories a locale uses- All rule functions exported for standalone use
Usage: Basic Example
import { PluralizationEngine } from "@suaveplan/i18n-icu-parser/pluralization";
const engine = new PluralizationEngine();
const result = engine.resolve(3, { one: "1 item", other: "# items" }, { locale: "en" });
// result === { category: "other", message: "# items", count: 3 }
Usage: Advanced Example
import { PluralizationEngine, arabicPlural } from "@suaveplan/i18n-icu-parser/pluralization";
const engine = new PluralizationEngine({
// override Welsh with a custom rule
cy: (n) => (n === 0 ? "zero" : n === 1 ? "one" : "other"),
});
// Arabic: 0/1/2/few(3-10)/many(11-99)/other(100+)
const ar0 = engine.resolve(0, { zero: "لا عناصر", one: "عنصر", other: "عناصر" }, { locale: "ar" });
// { category: "zero", message: "لا عناصر", count: 0 }
// English ordinal
const { category } = engine.resolve(3, {}, { locale: "en", ordinal: true });
// category === "few" (3rd)
// Fallback to "other" when category absent
const r = engine.resolve(5, { one: "one thing" }, { locale: "en" });
// { category: "other", message: undefined, count: 5 }
API Reference
new PluralizationEngine(customRules?: Record<string, PluralRuleFunction>)
customRules is merged over the built-in rule set after registration.
.resolve(count, messages, options?): PluralResolveResult
| Param | Type | Description |
|---|---|---|
count |
number |
Numeric quantity |
messages |
PluralMessages |
Partial record of category → message string |
options.locale |
string? |
BCP-47 locale (default "en") |
options.ordinal |
boolean? |
Use ordinal rules (default false) |
Returns { category: PluralCategory; message: string | undefined; count: number }.
.getCategory(count, locale, ordinal?): PluralCategory
Returns the CLDR plural category without resolving a message string.
.registerRule(locale, rule): void
Registers or overrides a rule for a locale code. The locale is normalised before storage.
.hasRule(locale): boolean
Returns true if a rule is registered for the locale or its base language.
.getRule(locale): PluralRuleFunction
Returns the rule for the locale, its base language, or defaultPluralRule.
.getCategories(locale): PluralCategory[]
Returns all CLDR categories reachable for a locale by probing values [0,1,2,3,6,11,100].
Implementation Notes
normalizeLocalestrips the region subtag:"ru-RU"→"ru". Registering"ru"covers all regional variants- Russian and Polish rules use modulo 10 and modulo 100 together; the order of checks matters — always test the teens exception (
mod100 >= 11 && mod100 <= 14) before the single-digit rule - Arabic plurals use
n % 100for the few/many boundary, which correctly handles values ≥ 100 - Welsh plural rules are exact-value based (n === 0/1/2/3/6) rather than modulo based
chinesePluralalways returns"other"— Chinese, Japanese, and Korean use the same zero-variation plural system