first commit
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
# PluralizationEngine
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## 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)
|
||||
- `PluralRuleRegistry` backed by a `Map` for 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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
- `normalizeLocale` strips 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 % 100` for 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
|
||||
- `chinesePlural` always returns `"other"` — Chinese, Japanese, and Korean use the same zero-variation plural system
|
||||
Reference in New Issue
Block a user