19 KiB
@suaveplan/i18n - Internationalization core: message catalog, ICU-like interpolation, pluralization, locale resolution, middleware, date/currency formatting.
Summary
Slim integration core for the i18n family. After the Wave 6 modular
split, this package owns the MessageCatalog, the createI18n /
I18nInstance plugin slot, the locale negotiation/resolver, the
server middleware, the observability state, the schemas, the
key/translation utilities, and the default FormatBridge that wires
the per-formatter packages into the universal ICU parser.
Everything else — constants, error codes, the ICU parser itself, the
interpolation engine, the pluralization engine, every formatter, the
locale detector chain, and the translation backends — lives in a
sibling package and is re-exported here for back-compat.
Works in Bun, Node.js, and modern browsers (zero DOM dependencies).
Documentation tree
- catalog — (no submodules)
- catalog — (no submodules)
- plugin — (no submodules)
- instance — (no submodules)
- negotiation — (no submodules)
- negotiation — (no submodules)
- resolver — (no submodules)
- resolver — (no submodules)
- server — (no submodules)
- server — (no submodules)
- backend-metrics — OpenTelemetry instruments for the
Backendplugin family. Every concrete backend (MemoryBackend,HttpBackend, `Sto… - detector-metrics — This module hosts the OTel instruments and an in-memory test ring for the locale-detector chain. The instruments live h…
- metrics —
@suaveplan/i18n/observability/metricsis the package's instrumentation surface. It exposes a lazily-built singleton h… - format-bridge-default — (no submodules)
- format-bridge-default — (no submodules)
- missing-key-reporter — A development-time plugin that subscribes to the
missingKeyevent on anI18nInstanceand forwards every miss to a l… - pseudo-localize — Layout-stress transform for translated strings. Three modes deliberately produce non-translated output that exercises f…
- schemas — (no submodules)
- i18n.schemas — (no submodules)
- utils — (no submodules)
- utils — (no submodules)
- detector-metrics — This module hosts the OTel instruments and an in-memory test ring for the locale-detector chain. The instruments live h…
- metrics —
@suaveplan/i18n/observability/metricsis the package's instrumentation surface. It exposes a lazily-built singleton h… - format-bridge-default — (no submodules)- format-bridge-default — (no submodules)- missing-key-reporter — A development-time plugin that subscribes to the
missingKeyevent on anI18nInstanceand forwards every miss to a l… - pseudo-localize — Layout-stress transform for translated strings. Three modes deliberately produce non-translated output that exercises f…
- schemas — (no submodules)- i18n.schemas — (no submodules)- utils — (no submodules)- utils — (no submodules)- catalog — (no submodules)- plugin — (no submodules)- instance — (no submodules)- negotiation — (no submodules)- negotiation — (no submodules)- resolver — (no submodules)- resolver — (no submodules)- server — (no submodules)- server — (no submodules)- backend-metrics — OpenTelemetry instruments for the
Backendplugin family. Every concrete backend (MemoryBackend,HttpBackend, `Sto… - detector-metrics — This module hosts the OTel instruments and an in-memory test ring for the locale-detector chain. The instruments live h…
- metrics —
@suaveplan/i18n/observability/metricsis the package's instrumentation surface. It exposes a lazily-built singleton h… - format-bridge-default — (no submodules)- format-bridge-default — (no submodules)- missing-key-reporter — A development-time plugin that subscribes to the
missingKeyevent on anI18nInstanceand forwards every miss to a l… - pseudo-localize — Layout-stress transform for translated strings. Three modes deliberately produce non-translated output that exercises f…
- schemas — (no submodules)- i18n.schemas — (no submodules)- utils — (no submodules)- utils — (no submodules)- catalog — (no submodules)- plugin — (no submodules)- instance — (no submodules)- negotiation — (no submodules)- negotiation — (no submodules)- resolver — (no submodules)- resolver — (no submodules)- server — (no submodules)- server — (no submodules)- backend-metrics — OpenTelemetry instruments for the
Backendplugin family. Every concrete backend (MemoryBackend,HttpBackend, `Sto… - detector-metrics — This module hosts the OTel instruments and an in-memory test ring for the locale-detector chain. The instruments live h…
- metrics —
@suaveplan/i18n/observability/metricsis the package's instrumentation surface. It exposes a lazily-built singleton h… - format-bridge-default — (no submodules)- format-bridge-default — (no submodules)- missing-key-reporter — A development-time plugin that subscribes to the
missingKeyevent on anI18nInstanceand forwards every miss to a l… - pseudo-localize — Layout-stress transform for translated strings. Three modes deliberately produce non-translated output that exercises f…
- schemas — (no submodules)- i18n.schemas — (no submodules)- utils — (no submodules)- utils — (no submodules)- catalog — (no submodules)- plugin — (no submodules) - instance — (no submodules)- negotiation — (no submodules) - negotiation — (no submodules)- resolver — (no submodules) - resolver — (no submodules)- server — (no submodules) - server — (no submodules)- backend-metrics — OpenTelemetry instruments for the
Backendplugin family. Every concrete backend (MemoryBackend,HttpBackend, `Sto… - detector-metrics — This module hosts the OTel instruments and an in-memory test ring for the locale-detector chain. The instruments live h…
- metrics —
@suaveplan/i18n/observability/metricsis the package's instrumentation surface. It exposes a lazily-built singleton h… - format-bridge-default — (no submodules) - format-bridge-default — (no submodules)- missing-key-reporter — A development-time plugin that subscribes to the
missingKeyevent on anI18nInstanceand forwards every miss to a l… - pseudo-localize — Layout-stress transform for translated strings. Three modes deliberately produce non-translated output that exercises f…
- schemas — (no submodules) - i18n.schemas — (no submodules)- utils — (no submodules) - utils — (no submodules)
Modules in the slim core
| Module | Path | Description |
|---|---|---|
| Catalog | src/core/catalog/ |
MessageCatalog, deepMerge — namespaced + lazy-loaded translations |
| Plugin | src/core/plugin/ |
createI18n, I18nInstance — facade with plugin slot + post-processors |
| Locale negotiation | src/locale/negotiation/ |
negotiateLocale, parseAcceptLanguage — RFC 4647 lookup |
| Locale resolver | src/locale/resolver/ |
LocaleResolver, extractLanguage, extractRegion, normalizeLocaleCode, isValidBcp47 — strategy-based resolver with BCP-47 validation |
| Server middleware | src/middleware/server/ |
createLocaleMiddleware, detectLocale, getDirection |
| Observability | src/observability/ |
Detector + backend metrics state + OTel adapters |
| Schemas | src/schemas/ |
CatalogOptionsSchema, InterpolationConfigSchema, ResolverConfigSchema, TranslationRecordSchema |
| Utilities | src/utils/ |
buildKey, countKeys, flattenRecord, getNestedValue, getTextDirection, hasKey, isRtl, parseKey, sanitize, unflattenRecord |
Default FormatBridge |
src/parser/format-bridge-default/ |
Wires the formatter packages into MessageParser.format |
| Subpath shims | src/subpaths/ |
Forwarders for the four kept back-compat subpaths |
Re-exports from sibling packages
Every symbol the pre-split monolith exposed continues to resolve from
@suaveplan/i18n. The source of each symbol is now a sibling package:
| Capability | Sibling package |
|---|---|
| Constants (locale descriptors, RTL set, BCP-47 map, defaults) | @suaveplan/i18n-shared |
Error registry (I18N_CODES, I18nError, I18nErrorCode) |
@suaveplan/i18n-shared |
ICU parser (MessageParser, renderNodes, AST node types) |
@suaveplan/i18n-icu-parser |
Interpolation (InterpolationEngine, escapeHtmlChars, escapeRegex) |
@suaveplan/i18n-icu-parser |
Pluralization (9 CLDR rules, PluralizationEngine, getBaseLocale) |
@suaveplan/i18n-icu-parser |
Number formatting (formatNumber, helpers, observability) |
@suaveplan/i18n-formatter-number |
Currency formatting (formatCurrency, formatCurrencyValue, info helpers) |
@suaveplan/i18n-formatter-currency |
Date / time formatting (formatDate, formatTime, formatDateOnly, formatDateRange, getDayNames, getMonthNames) |
@suaveplan/i18n-formatter-date |
Relative time (formatRelativeTime, selectRelativeUnit, helpers) |
@suaveplan/i18n-formatter-relative-time |
List formatting (formatList, formatConjunction, formatDisjunction) |
@suaveplan/i18n-formatter-list |
Display name formatting (formatDisplayName, language/region/script helpers) |
@suaveplan/i18n-formatter-display-name |
Locale detection chain (createDetectorChain, every detector factory, direction helpers) |
@suaveplan/i18n-detector |
| Memory backend | @suaveplan/i18n-backend-memory |
| HTTP backend | @suaveplan/i18n-backend-http |
| Storage-backed backend | @suaveplan/i18n-backend-storage |
| Chained backend | @suaveplan/i18n-backend-chained |
| Filesystem multi-file backend (server-tier — NOT re-exported at root) | @suaveplan/i18n-backend-fs-multifile |
Collator (@suaveplan/i18n-formatter-collator) and Segmenter
(@suaveplan/i18n-formatter-segmenter) are not currently re-exported
at the root — consumers import directly from the leaf packages. Add
them to src/index.ts if cross-package call-sites stabilise.
Subpaths
Four subpaths are kept indefinitely for back-compat with downstream
consumers (@suaveplan/react-i18n, react-ui, react-forms):
| Subpath | Forwards to |
|---|---|
@suaveplan/i18n/detector |
@suaveplan/i18n-detector |
@suaveplan/i18n/parser |
@suaveplan/i18n-icu-parser |
@suaveplan/i18n/plugin |
local core/plugin/instance.ts |
@suaveplan/i18n/errors |
@suaveplan/i18n-shared |
New consumers SHOULD import directly from the sibling package for tighter tree-shaking.
Quick start
import { createI18n, defaultFormatBridge, MessageParser } from "@suaveplan/i18n";
const i18n = createI18n({
defaultLocale: "en",
fallbackLocale: "en",
messages: {
en: { greeting: "Hello, {name}!" },
fr: { greeting: "Bonjour, {name} !" },
},
});
i18n.t("greeting", { values: { name: "world" } });
// "Hello, world!"
// ICU placeholders are rendered through the default FormatBridge
// when consumers reach for the parser directly:
const parser = new MessageParser();
const parsed = parser.parse("Total: {amount, number, currency/USD}");
const formatted = parsed.format(
{ amount: 49.99 },
{ locale: "en-US", formatBridge: defaultFormatBridge }
);
// "Total: $49.99"
Custom FormatBridge
The slim core's defaultFormatBridge covers every method on the
FormatBridge contract by delegating to the formatter packages.
Consumers MAY override individual methods (or build a no-Intl bridge
via noopFormatBridge):
import { defaultFormatBridge, noopFormatBridge } from "@suaveplan/i18n";
import type { FormatBridge } from "@suaveplan/i18n";
const customBridge: FormatBridge = {
...defaultFormatBridge,
relativeTime: () => "(some other language)",
};
Bundle size (post-slim, 2026-05-09)
dist/total: 592 KBdist/index.js: 4.76 KB (1.60 KB gzip) — pure barrel- Heaviest module:
dist/core/catalog/catalog.jsat 17.52 KB (5.48 KB gzip) - 15 modules in the build output (was ~120 pre-Wave-6)
Status
- 320 tests in the slim core (
bunx turbo test --filter=@suaveplan/i18n) — green - Coverage: 100/100/100/100 on the surviving modules (Istanbul)
- Downstream sweep (
bunx turbo test --filter='@suaveplan/react-*'): 40 tasks green, 1238 tests inreact-uialone