Pluralization
Leaves can be plural objects keyed by CLDR category (zero | one | two | few | many | other). Ilingo selects the matching form via Intl.PluralRules keyed by the resolved locale.
Two forms
Explicit (recommended)
Wrap the plural object in { "@plural": { ... } }:
{
"cart": {
"items": {
"@plural": {
"one": "{{count}} item",
"other": "{{count}} items"
}
}
}
}The marker disambiguates plurals from regular namespaces that happen to use CLDR-category keys. Use this form in JSON files.
Structural (back-compat)
A bare { one, other } object is also recognised, as long as every key is a CLDR category and other is present:
{
cart: {
items: {
one: '{{count}} item',
other: '{{count}} items',
},
},
}This is what existing codebases use. New code should prefer the explicit form.
TS/JS: definePlural
JSON cannot call functions, but TS/JS can — use definePlural to get autocomplete and a compile error on missing-other / non-CLDR keys:
import { defineCatalog, definePlural } from 'ilingo';
const catalog = defineCatalog({
en: {
cart: {
items: definePlural({
one: '{{count}} item',
other: '{{count}} items',
}),
},
},
});Runtime: definePlural returns { '@plural': leaf } — identical to the JSON form. Type system: the const generic preserves the literal types so Ilingo<typeof catalog> still treats items as a plural key requiring count.
Selection rules
await ilingo.get({ group: 'cart', key: 'items', count: 0 }); // 'other' (en has no 'zero')
await ilingo.get({ group: 'cart', key: 'items', count: 1 }); // 'one'
await ilingo.get({ group: 'cart', key: 'items', count: 5 }); // 'other'- If the selected category is absent,
otheris used. - If
otheris also missing on a structural form, the form is not recognised as a plural — the bare object is returned as-is. - The locale used by
Intl.PluralRulesis the resolved locale (the one that actually yielded the leaf), not the requested one. Useful whenen-US → enfalls back and you want the English plural rules to apply.
Caching
Intl.PluralRules instances are cached per locale on the Ilingo instance. Repeated calls do not reallocate.
Round-tripping
Plural leaves go through store.set() cleanly — StoreSetContext.value accepts string | PluralLeaf. FSStore.set persists them as JSON unchanged.