# @suaveplan/i18n-icu-parser - ICU MessageFormat parser, {{var}} interpolation engine, and 9-language CLDR pluralization for the @suaveplan/i18n family. Universal leaf package — no formatter, backend, or core dependencies. Renders ICU placeholders through a FormatBridge DI seam so the package stays free of Intl wrappers. ## Summary ICU MessageFormat parser, `{{var}}` interpolation engine, and 9-language CLDR pluralization for the `@suaveplan/i18n` family. Universal leaf package — depends only on `@suaveplan/i18n-shared`, `@suaveplan/error`, `@suaveplan/types`, and the OTel API. No formatter, backend, detector, or core dependencies. ## Documentation tree - [interpolation](./src/interpolation/interpolation.md) — _(no submodules)_ - [escape-html](./src/interpolation/escape-html.md) — `escapeHtmlChars` converts the five HTML special characters (`&`, `<`, `>`, `"`, `'`) to their numeric / named entity e… - [escape-regex](./src/interpolation/escape-regex.md) — `escapeRegex` escapes every regular-expression metacharacter in a string so the string can be embedded literally inside… - [interpolation](./src/interpolation/interpolation.md) — _(no submodules)_ - [observability](./src/observability/metrics.md) — _(no submodules)_ - [metrics](./src/observability/metrics.md) — _(no submodules)_ - [format-bridge](./src/parser/format-bridge.md) — `FormatBridge` is the dependency-injection seam that lets `@suaveplan/i18n-icu-parser` render ICU format placeholders (… - [message-render](./src/parser/message-render.md) — `message-render` provides two standalone utility functions for `@suaveplan/i18n`: `extractVariables`, which collects al… - [message](./src/parser/message.md) — `MessageParser` converts ICU-like message strings into a typed AST (Abstract Syntax Tree), and provides utilities to re… - [pluralization](./src/pluralization/pluralization.md) — _(no submodules)_ - [pluralization](./src/pluralization/pluralization.md) — _(no submodules)_ - [escape-html](./src/interpolation/escape-html.md) — `escapeHtmlChars` converts the five HTML special characters (`&`, `<`, `>`, `"`, `'`) to their numeric / named entity e… - [escape-regex](./src/interpolation/escape-regex.md) — `escapeRegex` escapes every regular-expression metacharacter in a string so the string can be embedded literally inside… - [interpolation](./src/interpolation/interpolation.md) — _(no submodules)_- [observability](./src/observability/metrics.md) — _(no submodules)_- [metrics](./src/observability/metrics.md) — _(no submodules)_- [format-bridge](./src/parser/format-bridge.md) — `FormatBridge` is the dependency-injection seam that lets `@suaveplan/i18n-icu-parser` render ICU format placeholders (… - [message-render](./src/parser/message-render.md) — `message-render` provides two standalone utility functions for `@suaveplan/i18n`: `extractVariables`, which collects al… - [message](./src/parser/message.md) — `MessageParser` converts ICU-like message strings into a typed AST (Abstract Syntax Tree), and provides utilities to re… - [pluralization](./src/pluralization/pluralization.md) — _(no submodules)_- [pluralization](./src/pluralization/pluralization.md) — _(no submodules)_- [escape-html](./src/interpolation/escape-html.md) — `escapeHtmlChars` converts the five HTML special characters (`&`, `<`, `>`, `"`, `'`) to their numeric / named entity e… - [escape-regex](./src/interpolation/escape-regex.md) — `escapeRegex` escapes every regular-expression metacharacter in a string so the string can be embedded literally inside… - [interpolation](./src/interpolation/interpolation.md) — _(no submodules)_- [observability](./src/observability/metrics.md) — _(no submodules)_- [metrics](./src/observability/metrics.md) — _(no submodules)_- [format-bridge](./src/parser/format-bridge.md) — `FormatBridge` is the dependency-injection seam that lets `@suaveplan/i18n-icu-parser` render ICU format placeholders (… - [message-render](./src/parser/message-render.md) — `message-render` provides two standalone utility functions for `@suaveplan/i18n`: `extractVariables`, which collects al… - [message](./src/parser/message.md) — `MessageParser` converts ICU-like message strings into a typed AST (Abstract Syntax Tree), and provides utilities to re… - [pluralization](./src/pluralization/pluralization.md) — _(no submodules)_- [pluralization](./src/pluralization/pluralization.md) — _(no submodules)_- [escape-html](./src/interpolation/escape-html.md) — `escapeHtmlChars` converts the five HTML special characters (`&`, `<`, `>`, `"`, `'`) to their numeric / named entity e… - [escape-regex](./src/interpolation/escape-regex.md) — `escapeRegex` escapes every regular-expression metacharacter in a string so the string can be embedded literally inside… - [interpolation](./src/interpolation/interpolation.md) — _(no submodules)_- [observability](./src/observability/metrics.md) — _(no submodules)_- [metrics](./src/observability/metrics.md) — _(no submodules)_- [format-bridge](./src/parser/format-bridge.md) — `FormatBridge` is the dependency-injection seam that lets `@suaveplan/i18n-icu-parser` render ICU format placeholders (… - [message-render](./src/parser/message-render.md) — `message-render` provides two standalone utility functions for `@suaveplan/i18n`: `extractVariables`, which collects al… - [message](./src/parser/message.md) — `MessageParser` converts ICU-like message strings into a typed AST (Abstract Syntax Tree), and provides utilities to re… - [pluralization](./src/pluralization/pluralization.md) — _(no submodules)_- [pluralization](./src/pluralization/pluralization.md) — _(no submodules)_- [escape-html](./src/interpolation/escape-html.md) — `escapeHtmlChars` converts the five HTML special characters (`&`, `<`, `>`, `"`, `'`) to their numeric / named entity e… - [escape-regex](./src/interpolation/escape-regex.md) — `escapeRegex` escapes every regular-expression metacharacter in a string so the string can be embedded literally inside… - [interpolation](./src/interpolation/interpolation.md) — _(no submodules)_- [observability](./src/observability/metrics.md) — _(no submodules)_ - [metrics](./src/observability/metrics.md) — _(no submodules)_- [format-bridge](./src/parser/format-bridge.md) — `FormatBridge` is the dependency-injection seam that lets `@suaveplan/i18n-icu-parser` render ICU format placeholders (… - [message-render](./src/parser/message-render.md) — `message-render` provides two standalone utility functions for `@suaveplan/i18n`: `extractVariables`, which collects al… - [message](./src/parser/message.md) — `MessageParser` converts ICU-like message strings into a typed AST (Abstract Syntax Tree), and provides utilities to re… - [pluralization](./src/pluralization/pluralization.md) — _(no submodules)_ - [pluralization](./src/pluralization/pluralization.md) — _(no submodules)_ ## Why this exists The 14-package i18n modular split needs one home for the three pure-text-processing concerns that share the `MessageNode` AST, the `INTERPOLATION_ERROR` / `PLURAL_RULE_NOT_FOUND` / `NESTING_DEPTH_EXCEEDED` error codes, and the same renderer contract: ICU parsing, `{{var}}` interpolation, and CLDR pluralization. Splitting them further would fragment the seam without benefit; bundling them as `@suaveplan/i18n-icu-parser` keeps the AST internal and exposes a clean public surface (`MessageParser`, `InterpolationEngine`, `PluralizationEngine`, `renderNodes`) for the slim core to compose. ## The FormatBridge seam ICU format placeholders (`{value, number}`, `{amount, number, currency=USD}`, `{date, date, short}`, `{ts, time, long}`, `{delta, relativeTime}`) need locale-aware output. To stay free of the per-formatter packages, the renderer accepts a `FormatBridge` via `RenderOptions.formatBridge`. When the bridge (or a specific method on it) is undefined, the renderer falls back to `String(value)` and never throws — the parser remains fully usable in isolation, with no `Intl.*` runtime cost. The slim `@suaveplan/i18n` core (Wave 6) constructs a default bridge by importing the formatter packages and threads it through `createI18n({ ... })` so consumers who use the high-level façade get full ICU output with zero wiring. ```typescript import { MessageParser, type FormatBridge, } from "@suaveplan/i18n-icu-parser"; const bridge: FormatBridge = { number: (v, locale) => new Intl.NumberFormat(locale).format(v), currency: (v, currency, locale) => new Intl.NumberFormat(locale, { style: "currency", currency }).format(v), }; const parser = new MessageParser(); parser.format( "{amount, number, currency/USD} on {when, date, short}", { amount: 1234.5, when: new Date() }, { locale: "en-US", formatBridge: bridge } ); ``` ## Install ```bash bun add @suaveplan/i18n-icu-parser ``` ## Quick Start ```typescript import { InterpolationEngine, MessageParser, PluralizationEngine, } from "@suaveplan/i18n-icu-parser"; const parser = new MessageParser(); parser.format("{count, plural, one {1 item} other {# items}}", { count: 3 }); // "3 items" const engine = new InterpolationEngine({ escapeHtml: true }); engine.render("hello {{ name }}", { name: "Ada" }); // { text: "hello Ada", missingVariables: [] } const plural = new PluralizationEngine(); plural.resolve(0, { zero: "none", one: "one", other: "many" }, { locale: "ar" }); // { category: "zero", message: "none", count: 0 } ``` ## Modules | Subpath | Description | |---|---| | `@suaveplan/i18n-icu-parser` | Root barrel — every public symbol | | `@suaveplan/i18n-icu-parser/parser` | `MessageParser`, `renderNodes`, `extractVariables`, `FormatBridge`, `noopFormatBridge`, AST types | | `@suaveplan/i18n-icu-parser/parser/format-bridge` | `FormatBridge` interface + `noopFormatBridge` ONLY (single-file import for slim-core wiring) | | `@suaveplan/i18n-icu-parser/interpolation` | `InterpolationEngine`, `escapeHtmlChars`, `escapeRegex`, `InterpolationConfigSchema`, types | | `@suaveplan/i18n-icu-parser/pluralization` | `PluralizationEngine`, the 9 plural-rule functions, `defaultPluralRule`, `normalizeLocale`, `getBaseLocale`, types | ## Modules Documentation - [parser/format-bridge.md](src/parser/format-bridge.md) — DI seam between this package and the formatter packages - [parser/message.md](src/parser/message.md) — `MessageParser` class - [parser/message-render.md](src/parser/message-render.md) — `renderNodes` / `extractVariables` standalone helpers - [interpolation/interpolation.md](src/interpolation/interpolation.md) — `InterpolationEngine` - [interpolation/escape-html.md](src/interpolation/escape-html.md) — `escapeHtmlChars` standalone helper - [interpolation/escape-regex.md](src/interpolation/escape-regex.md) — `escapeRegex` standalone helper - [pluralization/pluralization.md](src/pluralization/pluralization.md) — `PluralizationEngine` + 9 plural-rule fns - [observability/metrics.md](src/observability/metrics.md) — OTel histogram recorders ## Public Surface (snapshot) | Export | Kind | Origin | |---|---|---| | `MessageParser` | class | `parser/message.ts` | | `extractVariables` | function | `parser/message-render.ts` | | `renderNodes` | function | `parser/message-render.ts` | | `renderToChunks` | function | `parser/message-render.ts` | | `preprocessIcuApostrophes` | function | `parser/message.ts` | | `findUnknownArgTypes` | function | `parser/message.ts` | | `FormatBridge` | type | `parser/format-bridge.ts` | | `noopFormatBridge` | const | `parser/format-bridge.ts` | | `MessageNode`, `ParsedMessage`, `ParserOptions`, `RenderOptions`, `RenderContext`, `ValidationIssue`, `ValidationResult` | types | `parser/*.types.ts` | | `InterpolationEngine` | class | `interpolation/interpolation.ts` | | `escapeHtmlChars` | function | `interpolation/escape-html.ts` | | `escapeRegex` | function | `interpolation/escape-regex.ts` | | `InterpolationConfigSchema` | const | `interpolation/interpolation.schemas.ts` | | `PluralizationEngine` | class | `pluralization/pluralization.ts` | | `arabicPlural` / `chinesePlural` / `englishPlural` / `frenchPlural` / `germanPlural` / `japanesePlural` / `polishPlural` / `russianPlural` / `welshPlural` / `spanishPlural` / `defaultPluralRule` | functions | `pluralization/pluralization.ts` | | `recordParseDuration` / `recordInterpolationDuration` / `recordPluralDuration` | functions | `observability/metrics.ts` | ## Versioning Part of the i18n modular-split rollout. Stays at `0.1.0` until the slim core (Wave 6) cuts the family `1.0.0` release. Test titles in `parser/message.test.ts`, `interpolation/interpolation.test.ts`, and `pluralization/pluralization.test.ts` mirror the pre-split monolith one-for-one so the core's conformance suite re-runs unchanged against the bridged surface. ## Wave 2 Scope Pure addition. `packages/i18n/i18n/src/` retains its copies of the parser, interpolation engine, and pluralization sources; both copies coexist during Waves 2–5 and the monolith remains green. The slim core (Wave 6) deletes the monolith copies and re-exports from this package.