Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
14 KiB
@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 — (no submodules)
- escape-html —
escapeHtmlCharsconverts the five HTML special characters (&,<,>,",') to their numeric / named entity e… - escape-regex —
escapeRegexescapes every regular-expression metacharacter in a string so the string can be embedded literally inside… - interpolation — (no submodules)
- escape-html —
- observability — (no submodules)
- metrics — (no submodules)
- format-bridge —
FormatBridgeis the dependency-injection seam that lets@suaveplan/i18n-icu-parserrender ICU format placeholders (… - message-render —
message-renderprovides two standalone utility functions for@suaveplan/i18n:extractVariables, which collects al… - message —
MessageParserconverts ICU-like message strings into a typed AST (Abstract Syntax Tree), and provides utilities to re… - pluralization — (no submodules)
- pluralization — (no submodules)
- escape-regex —
escapeRegexescapes every regular-expression metacharacter in a string so the string can be embedded literally inside… - interpolation — (no submodules)- observability — (no submodules)- metrics — (no submodules)- format-bridge —
FormatBridgeis the dependency-injection seam that lets@suaveplan/i18n-icu-parserrender ICU format placeholders (… - message-render —
message-renderprovides two standalone utility functions for@suaveplan/i18n:extractVariables, which collects al… - message —
MessageParserconverts ICU-like message strings into a typed AST (Abstract Syntax Tree), and provides utilities to re… - pluralization — (no submodules)- pluralization — (no submodules)- escape-html —
escapeHtmlCharsconverts the five HTML special characters (&,<,>,",') to their numeric / named entity e… - escape-regex —
escapeRegexescapes every regular-expression metacharacter in a string so the string can be embedded literally inside… - interpolation — (no submodules)- observability — (no submodules)- metrics — (no submodules)- format-bridge —
FormatBridgeis the dependency-injection seam that lets@suaveplan/i18n-icu-parserrender ICU format placeholders (… - message-render —
message-renderprovides two standalone utility functions for@suaveplan/i18n:extractVariables, which collects al… - message —
MessageParserconverts ICU-like message strings into a typed AST (Abstract Syntax Tree), and provides utilities to re… - pluralization — (no submodules)- pluralization — (no submodules)- escape-html —
escapeHtmlCharsconverts the five HTML special characters (&,<,>,",') to their numeric / named entity e… - escape-regex —
escapeRegexescapes every regular-expression metacharacter in a string so the string can be embedded literally inside… - interpolation — (no submodules)- observability — (no submodules)- metrics — (no submodules)- format-bridge —
FormatBridgeis the dependency-injection seam that lets@suaveplan/i18n-icu-parserrender ICU format placeholders (… - message-render —
message-renderprovides two standalone utility functions for@suaveplan/i18n:extractVariables, which collects al… - message —
MessageParserconverts ICU-like message strings into a typed AST (Abstract Syntax Tree), and provides utilities to re… - pluralization — (no submodules)- pluralization — (no submodules)- escape-html —
escapeHtmlCharsconverts the five HTML special characters (&,<,>,",') to their numeric / named entity e… - escape-regex —
escapeRegexescapes every regular-expression metacharacter in a string so the string can be embedded literally inside… - interpolation — (no submodules)- observability — (no submodules) - metrics — (no submodules)- format-bridge —
FormatBridgeis the dependency-injection seam that lets@suaveplan/i18n-icu-parserrender ICU format placeholders (… - message-render —
message-renderprovides two standalone utility functions for@suaveplan/i18n:extractVariables, which collects al… - message —
MessageParserconverts ICU-like message strings into a typed AST (Abstract Syntax Tree), and provides utilities to re… - pluralization — (no submodules) - pluralization — (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.
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
bun add @suaveplan/i18n-icu-parser
Quick Start
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 — DI seam between this package and the formatter packages
- parser/message.md —
MessageParserclass - parser/message-render.md —
renderNodes/extractVariablesstandalone helpers - interpolation/interpolation.md —
InterpolationEngine - interpolation/escape-html.md —
escapeHtmlCharsstandalone helper - interpolation/escape-regex.md —
escapeRegexstandalone helper - pluralization/pluralization.md —
PluralizationEngine+ 9 plural-rule fns - 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.