@lssm/lib.translation-runtime 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +166 -0
  2. package/dist/adapters/i18next-export.d.ts +2 -0
  3. package/dist/adapters/i18next-helpers.d.ts +8 -0
  4. package/dist/adapters/i18next-types.d.ts +93 -0
  5. package/dist/adapters/i18next.d.ts +8 -0
  6. package/dist/adapters/i18next.test.d.ts +1 -0
  7. package/dist/browser/diagnostics.js +1 -0
  8. package/dist/browser/direction.js +1 -0
  9. package/dist/browser/fallback.js +1 -0
  10. package/dist/browser/formatter.js +0 -0
  11. package/dist/browser/formatters/intl-messageformat.js +1 -0
  12. package/dist/browser/i18next.js +1 -0
  13. package/dist/browser/index.js +28 -0
  14. package/dist/browser/loader.js +1 -0
  15. package/dist/browser/locale.js +1 -0
  16. package/dist/browser/overrides.js +1 -0
  17. package/dist/browser/precompile.js +28 -0
  18. package/dist/browser/preferences.js +1 -0
  19. package/dist/browser/snapshot.js +1 -0
  20. package/dist/diagnostics.d.ts +21 -0
  21. package/dist/diagnostics.js +2 -0
  22. package/dist/direction.d.ts +3 -0
  23. package/dist/direction.js +2 -0
  24. package/dist/fallback.d.ts +6 -0
  25. package/dist/fallback.js +2 -0
  26. package/dist/formatter.d.ts +32 -0
  27. package/dist/formatter.js +1 -0
  28. package/dist/formatters/intl-messageformat.d.ts +12 -0
  29. package/dist/formatters/intl-messageformat.js +2 -0
  30. package/dist/i18next.d.ts +1 -0
  31. package/dist/i18next.js +2 -0
  32. package/dist/index.d.ts +11 -0
  33. package/dist/index.js +29 -0
  34. package/dist/loader.d.ts +42 -0
  35. package/dist/loader.js +2 -0
  36. package/dist/loader.test.d.ts +1 -0
  37. package/dist/locale.d.ts +26 -0
  38. package/dist/locale.js +2 -0
  39. package/dist/node/diagnostics.js +1 -0
  40. package/dist/node/direction.js +1 -0
  41. package/dist/node/fallback.js +1 -0
  42. package/dist/node/formatter.js +0 -0
  43. package/dist/node/formatters/intl-messageformat.js +1 -0
  44. package/dist/node/i18next.js +1 -0
  45. package/dist/node/index.js +28 -0
  46. package/dist/node/loader.js +1 -0
  47. package/dist/node/locale.js +1 -0
  48. package/dist/node/overrides.js +1 -0
  49. package/dist/node/precompile.js +28 -0
  50. package/dist/node/preferences.js +1 -0
  51. package/dist/node/snapshot.js +1 -0
  52. package/dist/overrides.d.ts +10 -0
  53. package/dist/overrides.js +2 -0
  54. package/dist/precompile.d.ts +0 -0
  55. package/dist/precompile.js +29 -0
  56. package/dist/precompile.test.d.ts +1 -0
  57. package/dist/preferences.d.ts +17 -0
  58. package/dist/preferences.js +2 -0
  59. package/dist/snapshot.d.ts +38 -0
  60. package/dist/snapshot.js +2 -0
  61. package/package.json +368 -0
package/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # @lssm/lib.translation-runtime
2
+
3
+ Framework-independent runtime for ContractSpec translation contracts.
4
+
5
+ `@lssm/lib.contracts-spec` remains the canonical source of truth for `TranslationSpec` contracts, metadata, ownership, versions, locale variants, validation, fallback declarations, and override policy. This package consumes those specs at runtime to resolve, format, cache, load, diagnose, and serialize translations across server code, React, React Native, CLIs, and tests.
6
+
7
+ ## Boundaries
8
+
9
+ - **Spec layer:** `@lssm/lib.contracts-spec/translations`
10
+ - **Runtime layer:** this package
11
+ - **Formatter engine:** `MessageFormatter`, with `createIntlMessageFormatter()` backed by FormatJS/`intl-messageformat`
12
+ - **Adapters:** React/design-system/i18next integrations consume this runtime and are optional downstream layers
13
+
14
+ ## Example
15
+
16
+ ```ts
17
+ import { createTranslationRuntime } from "@lssm/lib.translation-runtime";
18
+ import { enMessages, frMessages } from "./catalogs";
19
+
20
+ const runtime = createTranslationRuntime({
21
+ defaultLocale: "en-US",
22
+ requestedLocales: ["fr-FR", "en-US"],
23
+ specs: [enMessages, frMessages],
24
+ });
25
+
26
+ runtime.t("cart.itemCount", { count: 3 });
27
+ ```
28
+
29
+ ## SSR and hydration — factory stack (preferred)
30
+
31
+ Use the factory-stack snapshot surface from `@lssm/lib.contracts-spec/translations` for new integrations. It produces a versioned, framework-neutral `I18nFactoryHydrationPayload` that never aliases live catalog references:
32
+
33
+ ```ts
34
+ import {
35
+ createI18nFactory,
36
+ createI18nFactoryFromHydrationPayload,
37
+ } from "@lssm/lib.contracts-spec/translations";
38
+ import { loadShardDelta, createStaticBundleLoader } from "@lssm/lib.translation-runtime/loader";
39
+
40
+ // Server (RSC / route handler)
41
+ const factory = createI18nFactory({
42
+ catalogs: [enMessages, frMessages, esMessages],
43
+ supportedLocales: ["en", "fr", "es"],
44
+ defaultLocale: "en",
45
+ });
46
+ const payload = factory.hydrationPayload(undefined, requestLocale);
47
+ // Transfer payload to the client (inline script, RSC stream, etc.)
48
+
49
+ // Client — rehydrate without refetching or renegotiating locale
50
+ const instance = createI18nFactoryFromHydrationPayload(payload);
51
+ instance.t("greeting", { name: "Alice" });
52
+
53
+ // Navigation: lazy-load only new shards
54
+ const loader = createStaticBundleLoader(allCatalogs);
55
+ await loadShardDelta({ loaded: sharedSpecKeys, required: pageSpecKeys, locales: [locale] }, loader);
56
+ ```
57
+
58
+ `validateI18nFactorySnapshot(snapshot)` returns human-readable error strings for partial or unknown snapshots before hydration.
59
+
60
+ ## SSR and hydration — deprecated runtime (compat only)
61
+
62
+ > **Deprecated.** `createTranslationRuntime` is dead-but-present. `createTranslationRuntimeFromPreferences` remains as a compat shim. Do not add new call sites to the active SSR render path. Engine deletion is a residual follow-up; see `.omc/plans/ralplan-translation-catalog-sharding-ssr.md`.
63
+
64
+ ```ts
65
+ import { createTranslationRuntime } from "@lssm/lib.translation-runtime";
66
+ // … (legacy pattern preserved for compat)
67
+ ```
68
+
69
+ `createTranslationSnapshotStreamChunk(snapshot)` returns a cloned payload safe to enqueue before later async catalog loads mutate the request runtime. `validateTranslationHydrationPayload(payload)` returns `invalid_snapshot` diagnostics for unsupported payloads before hydration.
70
+
71
+ ## Shard-scoped catalog loading
72
+
73
+ `src/loader.ts` accepts an optional `specKeys?` array on `TranslationBundleLoadRequest` to load a subset of catalogs per route. Use `loadShardDelta` to fetch only the incremental shards needed on navigation:
74
+
75
+ ```ts
76
+ import { computeShardDelta, loadShardDelta } from "@lssm/lib.translation-runtime/loader";
77
+
78
+ const delta = computeShardDelta(alreadyLoadedSpecKeys, requiredSpecKeys);
79
+ // delta = requiredSpecKeys not yet loaded
80
+
81
+ const newCatalogs = await loadShardDelta(
82
+ { loaded: alreadyLoadedSpecKeys, required: requiredSpecKeys, locales: [locale] },
83
+ bundleLoader,
84
+ );
85
+ ```
86
+
87
+ Absent `specKeys` ⇒ all keys pass (existing `{specKey, locales}` semantics unchanged).
88
+
89
+ ## Optimization surfaces (Model A — per-request inline payload)
90
+
91
+ > **Scope: per-request inline HTML reduction.** Catalogs remain bundled and cached in the JS bundle (shipped once per deploy). These optimizations reduce the per-request inline `<script>` hydration payload — NOT total transferred bytes. The ~90% total-bytes win requires Model B (catalog code-splitting + load-bearing inline payload). See `.omc/plans/ralplan-translation-catalog-sharding-ssr.md` § "Optimization Wave — ARCHITECTURE DECISION: Model A".
92
+
93
+ ### O2/O3 — Slim inline payload + value-pool dedup
94
+
95
+ Enable slim mode on your scoped hydration builder to strip spec metadata from each shard before serialization. The value-pool dedup step folds message values appearing ≥2× into a `values[]` pool; expansion is automatic on rehydration.
96
+
97
+ **Realized:** `/` en 96.6 KB → 37.8 KB (−61%); `/companyos/founder` en 93.9 KB → 35.6 KB (−62%); fr routes ~−60%. No-flash (AC1) re-proven through slim+dedup path. CI budget gates: single-locale ≤ 64 KB, with-fallback ≤ 128 KB.
98
+
99
+ O3 adds only ~2% over O2 (five lanes have disjoint key namespaces; ~53 pooled value strings). The projected ~41 KB en-fallback win requires a shared-base architecture follow-up.
100
+
101
+ ### O4 — Precompiled ICU AST fast-path (`./precompile` subpath)
102
+
103
+ At bundle build time, call `buildPrecompiledIcuCatalogFromSpecs(specs)` from `@lssm/lib.translation-runtime/precompile` over your `TranslationSpec[]` set. This produces a `PrecompiledIcuCatalog` with FormatJS IR AST entries for every message. Pass `createPrecompiledIcuLookup(catalog)` as the `precompiled` option to `createIntlMessageFormatter` to activate the parse-skip fast-path:
104
+
105
+ ```ts
106
+ // build step (runs once at bundle time, e.g. in your bundler plugin)
107
+ import {
108
+ buildPrecompiledIcuCatalogFromSpecs,
109
+ createPrecompiledIcuLookup,
110
+ serializePrecompiledIcuCatalog,
111
+ } from "@lssm/lib.translation-runtime/precompile";
112
+
113
+ const catalog = buildPrecompiledIcuCatalogFromSpecs([enMessages, frMessages]);
114
+ // optionally serialize and ship as a build asset
115
+ const serialized = serializePrecompiledIcuCatalog(catalog);
116
+
117
+ // runtime (wire the formatter with the precompiled lookup)
118
+ import { parsePrecompiledIcuCatalog, createPrecompiledIcuLookup } from "@lssm/lib.translation-runtime/precompile";
119
+ import { createIntlMessageFormatter } from "@lssm/lib.translation-runtime/formatters/intl-messageformat";
120
+
121
+ const precompiled = createPrecompiledIcuLookup(parsePrecompiledIcuCatalog(serialized));
122
+ const formatter = createIntlMessageFormatter({ precompiled });
123
+ // formatter skips ICU re-parsing for any message with a precompiled entry
124
+ ```
125
+
126
+ Additive opt-in — messages without a precompiled entry fall back to the standard parser transparently. **Scope: formatter-path latency win only.** Factory proof routes use `{placeholder}` interpolation (no ICU); O4 benefits apply to ICU-heavy catalogs (plural/select/date/number).
127
+
128
+ ### O5b (#17) — Scoped-hydration intersection (wired, currently no-op)
129
+
130
+ > **App-layer wiring — not a `translation-runtime` export.** `intersectScopedI18nHydration` and `isEffectiveReachabilityMap` are implemented in `web-application-monolith`'s scoped hydration path (`packages/apps/web-application-monolith/src/i18n/scoped-hydration.reachability.ts` and `reachability-map.loader.ts`). They compose with the loader's `loadShardDelta` but are not themselves library APIs. Do not import them from `@lssm/lib.translation-runtime`.
131
+
132
+ Wired into `getRequestI18n` and fail-safe (pruned keys resolve via the bundled combined runtime — no flash). Currently no-op (77/129 `t()` call sites use dynamic dispatch; scanner emits retain-all map). Auto-activates if a precise reachability map ships. Zero regression.
133
+
134
+ ### O5 (#14) — Dead-key audit tool (`@lssm/tool.i18n-prune`)
135
+
136
+ `@lssm/tool.i18n-prune` in `packages/tools/` walks `t('key')` call sites against a `RouteShardManifest` and reports dead keys per route. Use it in CI to surface dead-key drift. It does not drive the runtime inline payload under Model A.
137
+
138
+ ## Optional i18next adapter
139
+
140
+ i18next is an optional downstream adapter, not the canonical ContractSpec model. Import it through the dedicated subpath so core runtime consumers do not load i18next:
141
+
142
+ ```ts
143
+ import { createInstance } from "i18next";
144
+ import {
145
+ createI18nextInitOptions,
146
+ exportTranslationSnapshotToI18next,
147
+ } from "@lssm/lib.translation-runtime/i18next";
148
+
149
+ const exported = exportTranslationSnapshotToI18next(runtime.snapshot(), {
150
+ assumeIcuFormatter: true,
151
+ });
152
+ const { options, diagnostics } = createI18nextInitOptions(exported);
153
+ const i18next = createInstance();
154
+
155
+ await i18next.init(options);
156
+ ```
157
+
158
+ The adapter exports resources as `{ [locale]: { [namespace]: { [messageKey]: string } } }`. `TranslationSpec.locale` becomes the i18next language, while the namespace defaults to the stable `TranslationSpec.meta.key`. Flat ContractSpec keys such as `"cart.items"` remain flat because generated init options set `keySeparator: false`.
159
+
160
+ ContractSpec ICU messages are exported intact. i18next does not use ICU as its default JSON format, so apps that call `i18next.t()` for ICU plural/select/selectordinal messages must install and configure an ICU-capable i18next format plugin such as `i18next-icu`. The adapter reports `i18next_icu_plugin_required` unless you explicitly acknowledge that formatting layer.
161
+
162
+ For SSR, export from the same runtime snapshot used by the server render and reuse those resources/options on the client. Do not let client-only language detection renegotiate the first hydrated render. For React Native, pass the host-selected locale explicitly; the adapter does not use DOM APIs or browser storage.
163
+
164
+ ## React Native
165
+
166
+ The core runtime uses no DOM APIs. Locale detection is host-owned; pass device or user locales into `requestedLocales`. Ensure the host JS engine provides the required `Intl` APIs or install platform polyfills before using the default formatter.
@@ -0,0 +1,2 @@
1
+ import type { ContractSpecI18nextExport, ContractSpecI18nextExportOptions, ContractSpecI18nextSourceSpec } from './i18next-types.js';
2
+ export declare function buildI18nextExport(sources: readonly ContractSpecI18nextSourceSpec[], options: ContractSpecI18nextExportOptions): ContractSpecI18nextExport;
@@ -0,0 +1,8 @@
1
+ import type { TranslationSpec } from '@lssm/lib.contracts-spec/translations';
2
+ import type { ContractSpecI18nextDiagnostic, ContractSpecI18nextExportOptions, ContractSpecI18nextFallbackLng, ContractSpecI18nextNamespace, ContractSpecI18nextNamespaceStrategy } from './i18next-types.js';
3
+ export declare function namespaceForSpec(spec: TranslationSpec, strategy?: ContractSpecI18nextNamespaceStrategy): string;
4
+ export declare function buildSpecFallbackChain(spec: TranslationSpec, defaultLocale: string): string[];
5
+ export declare function canonicalLocale(locale: string, diagnostics: ContractSpecI18nextDiagnostic[], specKey?: string): string;
6
+ export declare function deriveFallbackLng(namespaces: readonly ContractSpecI18nextNamespace[], options: ContractSpecI18nextExportOptions, diagnostics: ContractSpecI18nextDiagnostic[]): ContractSpecI18nextFallbackLng | undefined;
7
+ export declare function hasIncompatibleFallbackChains(namespaces: readonly ContractSpecI18nextNamespace[]): boolean;
8
+ export declare function unique(values: readonly string[]): string[];
@@ -0,0 +1,93 @@
1
+ import type { Locale, MessageVariant, PlaceholderDef, PluralRuleSet, TranslationSpec } from '@lssm/lib.contracts-spec/translations';
2
+ import type { TextDirection } from '../direction.js';
3
+ import type { OverrideScope } from '../overrides.js';
4
+ export type I18nextResourceStore = Record<string, Record<string, Record<string, string>>>;
5
+ export type ContractSpecI18nextNamespaceStrategy = 'domain' | 'specKey' | ((spec: TranslationSpec) => string);
6
+ export type ContractSpecI18nextFallbackLng = false | string | string[] | Record<string, string[]>;
7
+ export interface ContractSpecI18nextExportOptions {
8
+ namespace?: ContractSpecI18nextNamespaceStrategy;
9
+ defaultLocale?: Locale;
10
+ lng?: Locale;
11
+ fallbackLng?: ContractSpecI18nextFallbackLng;
12
+ assumeIcuFormatter?: boolean;
13
+ includeFallbackLng?: boolean;
14
+ }
15
+ export interface ContractSpecI18nextInitOptions {
16
+ lng?: Locale;
17
+ fallbackLng?: ContractSpecI18nextFallbackLng;
18
+ partialBundledLanguages?: boolean;
19
+ keySeparator?: false | string;
20
+ defaultNS?: string;
21
+ }
22
+ export interface ContractSpecI18nextInstallOptions {
23
+ deep?: boolean;
24
+ overwrite?: boolean;
25
+ }
26
+ export interface I18nextResourceWriter {
27
+ addResourceBundle(lng: string, ns: string, resources: Record<string, string>, deep?: boolean, overwrite?: boolean): unknown;
28
+ }
29
+ export interface ContractSpecI18nextNamespace {
30
+ specKey: string;
31
+ version: string;
32
+ domain: string;
33
+ namespace: string;
34
+ locale: string;
35
+ direction: TextDirection;
36
+ fallbackChain: string[];
37
+ syntax: 'plain' | 'icu';
38
+ owners?: TranslationSpec['meta']['owners'];
39
+ tags?: TranslationSpec['meta']['tags'];
40
+ source?: string;
41
+ scope?: OverrideScope;
42
+ }
43
+ export interface ContractSpecI18nextMessageMetadata {
44
+ specKey: string;
45
+ version: string;
46
+ locale: string;
47
+ namespace: string;
48
+ messageKey: string;
49
+ description?: string;
50
+ context?: string;
51
+ placeholders?: PlaceholderDef[];
52
+ variants?: MessageVariant[];
53
+ maxLength?: number;
54
+ tags?: string[];
55
+ pluralRules?: PluralRuleSet[];
56
+ source?: string;
57
+ scope?: OverrideScope;
58
+ }
59
+ export type ContractSpecI18nextMessageMetadataMap = Record<string, Record<string, Record<string, ContractSpecI18nextMessageMetadata>>>;
60
+ export type ContractSpecI18nextDiagnosticCode = 'i18next_fallback_projection_lossy' | 'i18next_icu_plugin_required' | 'i18next_invalid_locale' | 'i18next_metadata_omitted' | 'i18next_namespace_collision' | 'i18next_resource_collision';
61
+ export interface ContractSpecI18nextDiagnostic {
62
+ code: ContractSpecI18nextDiagnosticCode;
63
+ level: 'info' | 'warning' | 'error';
64
+ message: string;
65
+ locale?: string;
66
+ namespace?: string;
67
+ specKey?: string;
68
+ messageKey?: string;
69
+ }
70
+ export interface ContractSpecI18nextManifest {
71
+ defaultLocale?: string;
72
+ locales: string[];
73
+ namespaces: ContractSpecI18nextNamespace[];
74
+ messages: ContractSpecI18nextMessageMetadataMap;
75
+ diagnostics: ContractSpecI18nextDiagnostic[];
76
+ }
77
+ export interface ContractSpecI18nextExport {
78
+ resources: I18nextResourceStore;
79
+ manifest: ContractSpecI18nextManifest;
80
+ ns: string[];
81
+ lng?: string;
82
+ defaultNS?: string;
83
+ fallbackLng?: ContractSpecI18nextFallbackLng;
84
+ }
85
+ export interface ContractSpecI18nextInitOptionsResult {
86
+ options: Record<string, unknown>;
87
+ diagnostics: ContractSpecI18nextDiagnostic[];
88
+ }
89
+ export interface ContractSpecI18nextSourceSpec {
90
+ spec: TranslationSpec;
91
+ source?: string;
92
+ scope?: OverrideScope;
93
+ }
@@ -0,0 +1,8 @@
1
+ import type { TranslationSpec } from '@lssm/lib.contracts-spec/translations';
2
+ import type { TranslationRuntimeSnapshot } from '../snapshot.js';
3
+ import type { ContractSpecI18nextExport, ContractSpecI18nextExportOptions, ContractSpecI18nextInitOptions, ContractSpecI18nextInitOptionsResult, ContractSpecI18nextInstallOptions, I18nextResourceWriter } from './i18next-types.js';
4
+ export type * from './i18next-types.js';
5
+ export declare function exportContractSpecToI18next(specs: readonly TranslationSpec[], options?: ContractSpecI18nextExportOptions): ContractSpecI18nextExport;
6
+ export declare function exportTranslationSnapshotToI18next(snapshot: TranslationRuntimeSnapshot, options?: ContractSpecI18nextExportOptions): ContractSpecI18nextExport;
7
+ export declare function createI18nextInitOptions(exported: ContractSpecI18nextExport, options?: ContractSpecI18nextInitOptions): ContractSpecI18nextInitOptionsResult;
8
+ export declare function addContractSpecResourceBundles(instance: I18nextResourceWriter, exported: ContractSpecI18nextExport, options?: ContractSpecI18nextInstallOptions): void;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ class i{items=[];report(e){this.items.push(e)}list(){return[...this.items]}clear(){this.items.length=0}}export{i as TranslationDiagnosticsCollector};
@@ -0,0 +1 @@
1
+ var r=new Set(["ar","arc","dv","fa","ha","he","ks","ku","ps","ur","yi"]);function a(t){let e=n(t);return r.has(e)?"rtl":"ltr"}function n(t){try{return new Intl.Locale(t).language.toLowerCase()}catch{return t.split("-")[0]?.toLowerCase()??t.toLowerCase()}}export{n as getLanguageSubtag,a as getTextDirection};
@@ -0,0 +1 @@
1
+ var g=new Set(["ar","arc","dv","fa","ha","he","ks","ku","ps","ur","yi"]);function L(e){let o=p(e);return g.has(o)?"rtl":"ltr"}function p(e){try{return new Intl.Locale(e).language.toLowerCase()}catch{return e.split("-")[0]?.toLowerCase()??e.toLowerCase()}}function r(e){let o=e.trim();if(!o)return{input:e,valid:!1,error:"Locale is empty"};try{let t=Intl.getCanonicalLocales(o)[0];return t?{input:e,locale:t,valid:!0}:{input:e,valid:!1,error:"Locale did not canonicalize"}}catch(t){return{input:e,valid:!1,error:t instanceof Error?t.message:"Invalid locale"}}}function s(e=[]){let o=[];for(let t of e){let a=r(t);if(a.locale&&!o.includes(a.locale))o.push(a.locale)}return o}function y(e){let o=r(e.defaultLocale).locale??e.defaultLocale,t=s(e.requestedLocales),a=s(e.supportedLocales),c=s(e.fallbackLocales),l=a.length?a:[o];for(let n of t){let u=i(n,o,c).find((d)=>l.includes(d));if(u)return f(u,t,l,o,c)}return f(o,t,l,o,c)}function i(e,o,t=[]){let a=[],c=(l)=>{if(!l)return;let n=r(l).locale??l;if(!a.includes(n))a.push(n)};c(e);for(let l of h(e))c(l);for(let l of t)c(l);return c(o),a}function h(e){let t=(r(e).locale??e).split("-"),a=[];while(t.length>1)t.pop(),a.push(t.join("-"));return a}function f(e,o,t,a,c){return{locale:e,requestedLocales:o,supportedLocales:t,fallbackChain:i(e,a,c),direction:L(e),defaultLocale:a}}function v(e,o){return i(e,o.defaultLocale,o.locales??[])}export{v as resolveFallbackChain};
File without changes
@@ -0,0 +1 @@
1
+ import{parse as f,TYPE as a}from"@formatjs/icu-messageformat-parser";import M from"intl-messageformat";function w(e={}){let o=e.cache??new Map,l=e.precompiled;return{compile({id:n,locale:s,message:i,ast:c}){let t=`${s}\x00${n}\x00${i}`,r=o.get(t);if(r)return r;let m=c??l?.({id:n,locale:s,message:i}),d=new M(m??i,s),u={format(g){let p=d.format(g);return Array.isArray(p)?p.join(""):String(p)}};return o.set(t,u),u},validate(n){try{let s=f(n);return F(s)}catch(s){return{valid:!1,error:s instanceof Error?s.message:"Invalid ICU message",arguments:[],plurals:[],selects:[],selectOrdinals:[]}}}}}function F(e){let o=new Set,l=new Set,n=new Set,s=new Set,i=(c)=>{for(let t of c){let r="value"in t?String(t.value):void 0;if(r&&v(t.type))o.add(r);if(r&&t.type===a.select)n.add(r);if(r&&t.type===a.plural)if(o.add(r),"pluralType"in t&&t.pluralType==="ordinal")s.add(r);else l.add(r);if("options"in t)for(let m of Object.values(t.options))i(m.value)}};return i(e),{valid:!0,arguments:[...o],plurals:[...l],selects:[...n],selectOrdinals:[...s]}}function v(e){return e===a.argument||e===a.number||e===a.date||e===a.time||e===a.select||e===a.plural||e===a.pound||e===a.tag}export{w as createIntlMessageFormatter};
@@ -0,0 +1 @@
1
+ var v=new Set(["ar","arc","dv","fa","ha","he","ks","ku","ps","ur","yi"]);function x(e){let t=O(e);return v.has(t)?"rtl":"ltr"}function O(e){try{return new Intl.Locale(e).language.toLowerCase()}catch{return e.split("-")[0]?.toLowerCase()??e.toLowerCase()}}function g(e){let t=e.trim();if(!t)return{input:e,valid:!1,error:"Locale is empty"};try{let n=Intl.getCanonicalLocales(t)[0];return n?{input:e,locale:n,valid:!0}:{input:e,valid:!1,error:"Locale did not canonicalize"}}catch(n){return{input:e,valid:!1,error:n instanceof Error?n.message:"Invalid locale"}}}function S(e=[]){let t=[];for(let n of e){let a=g(n);if(a.locale&&!t.includes(a.locale))t.push(a.locale)}return t}function j(e){let t=g(e.defaultLocale).locale??e.defaultLocale,n=S(e.requestedLocales),a=S(e.supportedLocales),o=S(e.fallbackLocales),c=a.length?a:[t];for(let s of n){let r=d(s,t,o).find((l)=>c.includes(l));if(r)return I(r,n,c,t,o)}return I(t,n,c,t,o)}function d(e,t,n=[]){let a=[],o=(c)=>{if(!c)return;let s=g(c).locale??c;if(!a.includes(s))a.push(s)};o(e);for(let c of M(e))o(c);for(let c of n)o(c);return o(t),a}function M(e){let n=(g(e).locale??e).split("-"),a=[];while(n.length>1)n.pop(),a.push(n.join("-"));return a}function I(e,t,n,a,o){return{locale:e,requestedLocales:t,supportedLocales:n,fallbackChain:d(e,a,o),direction:x(e),defaultLocale:a}}function C(e,t="specKey"){if(typeof t==="function")return t(e);return t==="domain"?e.meta.domain:e.meta.key}function b(e,t){let n=e.fallbacks??(e.fallback?[e.fallback]:[]);return d(e.locale,t,n)}function m(e,t,n){let a=g(e);if(a.locale)return a.locale;return t.push({code:"i18next_invalid_locale",level:"warning",message:a.error??`Invalid locale ${e}`,locale:e,specKey:n}),e}function y(e,t,n){if(t.fallbackLng!==void 0)return t.fallbackLng;if(t.includeFallbackLng===!1)return;if(!e.length)return;let a=new Map;for(let c of e){let s=c.fallbackChain.filter((r)=>r!==c.locale),i=a.get(c.locale);if(i&&i.join("\x00")!==s.join("\x00")){n.push({code:"i18next_fallback_projection_lossy",level:"warning",message:`Multiple fallback chains exist for locale ${c.locale}; keeping authoritative chains in the ContractSpec manifest.`,locale:c.locale,namespace:c.namespace,specKey:c.specKey});return}a.set(c.locale,s)}let o=Object.fromEntries([...a.entries()].filter(([,c])=>c.length>0));if(!Object.keys(o).length)return;return o}function k(e){let t=new Map;for(let n of e){let a=n.fallbackChain.filter((c)=>c!==n.locale).join("\x00"),o=t.get(n.locale);if(o&&o!==a)return!0;t.set(n.locale,a)}return!1}function h(e){return[...new Set(e)]}function L(e,t){let n={},a={},o=[],c=[],s=new Map;for(let i of e){let{spec:r}=i,l=m(r.locale,c,r.meta.key),p=t.defaultLocale??r.defaultLocale??r.fallback??l,u=C(r,t.namespace),f=r.formatter?.syntax??"icu";R(s,u,r,c),E(n,a,l,u),o.push({specKey:r.meta.key,version:r.meta.version,domain:r.meta.domain,namespace:u,locale:l,direction:r.direction??x(l),fallbackChain:b(r,p),syntax:f,owners:r.meta.owners,tags:r.meta.tags,source:i.source,scope:i.scope}),w(f,t,l,u,r.meta.key,c),N(n,a,l,u,i,c)}return D(n,a,o,t,c)}function R(e,t,n,a){let o=e.get(t);if(o&&o!==n.meta.key)a.push({code:"i18next_namespace_collision",level:"warning",message:`Namespace ${t} is shared by ${o} and ${n.meta.key}.`,namespace:t,specKey:n.meta.key});else e.set(t,n.meta.key)}function w(e,t,n,a,o,c){if(e!=="icu"||t.assumeIcuFormatter)return;c.push({code:"i18next_icu_plugin_required",level:"warning",message:"ContractSpec ICU message exported to i18next; use an ICU-capable i18next format plugin for runtime formatting parity.",locale:n,namespace:a,specKey:o})}function E(e,t,n,a){e[n]??={},e[n][a]??={},t[n]??={},t[n][a]??={}}function N(e,t,n,a,o,c){let{spec:s}=o,i=e[n]?.[a],r=t[n]?.[a];if(!i||!r)return;for(let[l,p]of Object.entries(s.messages)){let u=i[l],f=r[l];if(u!==void 0&&(u!==p.value||f?.version!==s.meta.version||f?.source!==o.source||f?.scope!==o.scope)){c.push({code:"i18next_resource_collision",level:"error",message:`Message ${l} collides in ${n}/${a}.`,locale:n,namespace:a,specKey:s.meta.key,messageKey:l});continue}i[l]=p.value,r[l]={specKey:s.meta.key,version:s.meta.version,locale:n,namespace:a,messageKey:l,description:p.description,context:p.context,placeholders:p.placeholders,variants:p.variants,maxLength:p.maxLength,tags:p.tags,pluralRules:s.pluralRules,source:o.source,scope:o.scope}}}function D(e,t,n,a,o){let c=Object.keys(e).sort(),s=h(n.map((r)=>r.namespace)).sort(),i=y(n,a,o);return{resources:e,manifest:{defaultLocale:a.defaultLocale?m(a.defaultLocale,o):void 0,locales:c,namespaces:n,messages:t,diagnostics:o},ns:s,lng:a.lng?m(a.lng,o):void 0,defaultNS:s[0],fallbackLng:i}}function P(e,t={}){return L(e.map((n)=>({spec:n})),t)}function H(e,t={}){let n=e.sources?.length?e.sources.map(({scope:a,source:o,spec:c})=>({scope:a,source:o,spec:c})):e.specs.map((a)=>({spec:a}));return L(n,{defaultLocale:e.defaultLocale,lng:e.locale,fallbackLng:e.fallbackChain,...t})}function J(e,t={}){let n=[],a=t.fallbackLng??e.fallbackLng;if(!a&&k(e.manifest.namespaces))n.push({code:"i18next_fallback_projection_lossy",level:"warning",message:"ContractSpec has per-bundle fallback chains that cannot be represented as one i18next fallbackLng value."});let o={resources:e.resources,ns:e.ns,keySeparator:t.keySeparator??!1},c=t.defaultNS??e.defaultNS;if(c)o.defaultNS=c;let s=t.lng??e.lng??e.manifest.defaultLocale;if(s)o.lng=s;if(a)o.fallbackLng=a;if(t.partialBundledLanguages!==void 0)o.partialBundledLanguages=t.partialBundledLanguages;return{options:o,diagnostics:n}}function Q(e,t,n={}){let a=n.deep??!0,o=n.overwrite??!0;for(let[c,s]of Object.entries(t.resources))for(let[i,r]of Object.entries(s))e.addResourceBundle(c,i,r,a,o)}export{Q as addContractSpecResourceBundles,J as createI18nextInitOptions,P as exportContractSpecToI18next,H as exportTranslationSnapshotToI18next};
@@ -0,0 +1,28 @@
1
+ class x{items=[];report(e){this.items.push(e)}list(){return[...this.items]}clear(){this.items.length=0}}var C=new Set(["ar","arc","dv","fa","ha","he","ks","ku","ps","ur","yi"]);function h(e){let t=b(e);return C.has(t)?"rtl":"ltr"}function b(e){try{return new Intl.Locale(e).language.toLowerCase()}catch{return e.split("-")[0]?.toLowerCase()??e.toLowerCase()}}function u(e){let t=e.trim();if(!t)return{input:e,valid:!1,error:"Locale is empty"};try{let a=Intl.getCanonicalLocales(t)[0];return a?{input:e,locale:a,valid:!0}:{input:e,valid:!1,error:"Locale did not canonicalize"}}catch(a){return{input:e,valid:!1,error:a instanceof Error?a.message:"Invalid locale"}}}function f(e=[]){let t=[];for(let a of e){let o=u(a);if(o.locale&&!t.includes(o.locale))t.push(o.locale)}return t}function X(e){let t=u(e.defaultLocale).locale??e.defaultLocale,a=f(e.requestedLocales),o=f(e.supportedLocales),r=f(e.fallbackLocales),n=o.length?o:[t];for(let l of a){let s=g(l,t,r).find((d)=>n.includes(d));if(s)return R(s,a,n,t,r)}return R(t,a,n,t,r)}function g(e,t,a=[]){let o=[],r=(n)=>{if(!n)return;let l=u(n).locale??n;if(!o.includes(l))o.push(l)};r(e);for(let n of k(e))r(n);for(let n of a)r(n);return r(t),o}function k(e){let a=(u(e).locale??e).split("-"),o=[];while(a.length>1)a.pop(),o.push(a.join("-"));return o}function R(e,t,a,o,r){return{locale:e,requestedLocales:t,supportedLocales:a,fallbackChain:g(e,o,r),direction:h(e),defaultLocale:o}}function te(e,t){return g(e,t.defaultLocale,t.locales??[])}import{parse as N,TYPE as p}from"@formatjs/icu-messageformat-parser";import O from"intl-messageformat";function ne(e={}){let t=e.cache??new Map,a=e.precompiled;return{compile({id:o,locale:r,message:n,ast:l}){let i=`${r}\x00${o}\x00${n}`,s=t.get(i);if(s)return s;let d=l??a?.({id:o,locale:r,message:n}),c=new O(d??n,r),m={format(P){let L=c.format(P);return Array.isArray(L)?L.join(""):String(L)}};return t.set(i,m),m},validate(o){try{let r=N(o);return M(r)}catch(r){return{valid:!1,error:r instanceof Error?r.message:"Invalid ICU message",arguments:[],plurals:[],selects:[],selectOrdinals:[]}}}}}function M(e){let t=new Set,a=new Set,o=new Set,r=new Set,n=(l)=>{for(let i of l){let s="value"in i?String(i.value):void 0;if(s&&D(i.type))t.add(s);if(s&&i.type===p.select)o.add(s);if(s&&i.type===p.plural)if(t.add(s),"pluralType"in i&&i.pluralType==="ordinal")r.add(s);else a.add(s);if("options"in i)for(let d of Object.values(i.options))n(d.value)}};return n(e),{valid:!0,arguments:[...t],plurals:[...a],selects:[...o],selectOrdinals:[...r]}}function D(e){return e===p.argument||e===p.number||e===p.date||e===p.time||e===p.select||e===p.plural||e===p.pound||e===p.tag}function ie(e){return{async load(t){return e.filter((a)=>E(a.meta.key,t)&&t.locales.includes(a.locale))}}}function E(e,t){let a=t.specKey!==void 0,o=t.specKeys!==void 0&&t.specKeys.length>0;if(!a&&!o)return!0;if(a&&e===t.specKey)return!0;if(o&&t.specKeys?.includes(e))return!0;return!1}function _(e,t){let a=new Set(e),o=[],r=new Set;for(let n of t){if(a.has(n)||r.has(n))continue;r.add(n),o.push(n)}return o}async function le(e,t){let a=_(t.loaded,t.required);if(a.length===0)return[];return e.load({specKeys:a,locales:t.locales})}function pe(e){return[...e].sort((t,a)=>(t.priority??I(t.scope))-(a.priority??I(a.scope)))}function I(e){switch(e){case"base":return 0;case"package":return 10;case"project":return 20;case"tenant":return 30;case"user":return 40;case"request":return 50}}import A from"intl-messageformat";var y="contractspec.icu.precompiled",T=1;function S(e,t){return`${e}\x00${t}`}function F(e,t){return new A(e,t).getAst()}function w(e){let t={};for(let{locale:a,message:o}of e){let r=S(a,o);if(t[r])continue;t[r]={locale:a,message:o,ast:F(o,a)}}return{kind:y,version:T,entries:t}}function me(e){let t=[];for(let a of e)for(let o of Object.values(a.messages))t.push({locale:a.locale,message:o.value});return w(t)}function fe(e){return JSON.stringify(e)}function ge(e){let t=JSON.parse(e),a=q(t);if(a.length>0)throw Error(a.join("; "));return t}function q(e){let t=[];if(!e||e.kind!==y)t.push("Precompiled ICU catalog kind is invalid.");if(e?.version!==T)t.push("Precompiled ICU catalog version is unsupported.");if(!e?.entries||typeof e.entries!=="object")t.push("Precompiled ICU catalog entries must be an object.");return t}function Le(e){return({locale:t,message:a})=>e.entries[S(t,a)]?.ast}var ye={id:"docs.tech.translation-runtime.icu-precompile",title:"Precompiled ICU AST fast-path",summary:"Build-time ICU AST precompilation with an additive runtime parse-skip fast-path.",kind:"reference",visibility:"public",route:"/docs/tech/translation-runtime/icu-precompile",tags:["i18n","icu","performance","translation-runtime"],body:`# Precompiled ICU AST fast-path
2
+
3
+ Precompiles ICU message strings into the exact \`MessageFormatElement[]\` AST that
4
+ \`intl-messageformat\` produces, so the runtime can skip the per-call parse.
5
+
6
+ - \`precompileIcuMessage(message, locale)\` returns the AST via
7
+ \`IntlMessageFormat#getAst()\` — identical to the on-the-fly string path, so
8
+ formatted output is byte-identical (verified by golden tests across plural,
9
+ select, selectordinal, number, currency, date, and time for en/fr/es/ar-EG).
10
+ - \`buildPrecompiledIcuCatalog\` / \`buildPrecompiledIcuCatalogFromSpecs\` produce a
11
+ serializable \`PrecompiledIcuCatalog\` (\`kind\` =
12
+ \`${y}\`, \`version\` = \`${T}\`) as a
13
+ build-time asset.
14
+ - \`createPrecompiledIcuLookup(catalog)\` yields a lookup the formatter consumes.
15
+
16
+ Wire the lookup via \`createIntlMessageFormatter({ precompiled })\`, or pass a
17
+ per-call \`ast\` on \`compile()\`. When neither is present the formatter parses the
18
+ message string exactly as before — the surface is **ADDITIVE**.
19
+
20
+ Scope: this precompiles the ICU MessageFormat argument grammar
21
+ (plural/select/selectordinal/number/date/time + number skeletons such as
22
+ currency). List and relative-time rendering is performed by \`Intl.ListFormat\` /
23
+ \`Intl.RelativeTimeFormat\` outside the message AST and has no per-call message
24
+ parse to precompile. The \`createI18nFactory\` stack uses simple \`{key}\`
25
+ interpolation (no ICU parse) and is therefore unaffected by this fast-path.
26
+
27
+ Compatibility classification: **ADDITIVE**.
28
+ `};var K=["app","workspace","team","user","request"],U={app:"appLocale",workspace:"workspaceLocale",team:"teamLocale",user:"userLocale",request:"requestLocale"};function Re(e={},t={}){let a=u(t.fallbackLocale??e.defaultLocale??e.appLocale??"en").locale??"en",o=u(e.defaultLocale??e.appLocale??a).locale??a,r={},n=[];for(let c of K){let m=H(e[U[c]]);if(!m)continue;r[c]=m,n.push({source:c,locale:m,metadata:e.sources?.[c]})}let l=f(e.requestedLocales??[]),i=f(t.additionalRequestedLocales??[]),s=[...n].reverse().map((c)=>c.locale),d=V([...l,...i,...s,o]);return{defaultLocale:o,requestedLocales:d,preferenceLocales:r,sources:[...l.map((c)=>({source:"requested",locale:c})),...n]}}function H(e){if(!e)return;return u(e).locale}function V(e){return[...new Set(e)]}var Se="contractspec.translation-runtime.hydration",ve=1;function j(e){return JSON.stringify(e)}function B(e){return JSON.parse(e)}function v(e){return{kind:"contractspec.translation-runtime.hydration",version:1,snapshot:J(e)}}function Pe(e){return JSON.stringify(e)}function xe(e){let t=JSON.parse(e),a=z(t);if(a.length>0)throw Error(a.map((o)=>o.message).join("; "));return v(t.snapshot)}function Ce(e){return{type:"translation-runtime-snapshot",payload:v(e)}}function z(e){let t=[];if(!e||e.kind!=="contractspec.translation-runtime.hydration")t.push({code:"invalid_snapshot",level:"error",message:"Translation hydration payload kind is invalid."});if(e?.version!==1)t.push({code:"invalid_snapshot",level:"error",message:"Translation hydration payload version is unsupported.",context:{version:e?.version}});return t.push(...Y(e?.snapshot)),t}function Y(e){let t=[];if(!e)return[{code:"invalid_snapshot",level:"error",message:"Translation runtime snapshot is missing."}];if(!e.locale)t.push({code:"invalid_snapshot",level:"error",message:"Translation runtime snapshot locale is missing."});if(!e.defaultLocale)t.push({code:"invalid_snapshot",level:"error",message:"Translation runtime snapshot defaultLocale is missing."});for(let[a,o]of[["fallbackChain",e.fallbackChain],["supportedLocales",e.supportedLocales],["specs",e.specs]])if(!Array.isArray(o))t.push({code:"invalid_snapshot",level:"error",message:`Translation runtime snapshot ${a} must be an array.`,context:{field:a}});return t}function J(e){return B(j(e))}export{y as PRECOMPILED_ICU_KIND,T as PRECOMPILED_ICU_VERSION,ye as PrecompiledIcuDocBlock,Se as TRANSLATION_RUNTIME_HYDRATION_KIND,ve as TRANSLATION_RUNTIME_HYDRATION_VERSION,x as TranslationDiagnosticsCollector,g as buildLocaleFallbackChain,w as buildPrecompiledIcuCatalog,me as buildPrecompiledIcuCatalogFromSpecs,u as canonicalizeLocale,f as canonicalizeLocales,_ as computeShardDelta,ne as createIntlMessageFormatter,Le as createPrecompiledIcuLookup,ie as createStaticBundleLoader,v as createTranslationHydrationPayload,Ce as createTranslationSnapshotStreamChunk,I as defaultOverridePriority,b as getLanguageSubtag,h as getTextDirection,le as loadShardDelta,X as negotiateLocale,ge as parsePrecompiledIcuCatalog,xe as parseTranslationHydrationPayload,B as parseTranslationSnapshot,F as precompileIcuMessage,te as resolveFallbackChain,Re as resolveTranslationPreferenceContext,fe as serializePrecompiledIcuCatalog,Pe as serializeTranslationHydrationPayload,j as serializeTranslationSnapshot,pe as sortOverrideLayers,q as validatePrecompiledIcuCatalog,z as validateTranslationHydrationPayload,Y as validateTranslationSnapshot};
@@ -0,0 +1 @@
1
+ function c(a){return{async load(e){return a.filter((n)=>s(n.meta.key,e)&&e.locales.includes(n.locale))}}}function s(a,e){let n=e.specKey!==void 0,t=e.specKeys!==void 0&&e.specKeys.length>0;if(!n&&!t)return!0;if(n&&a===e.specKey)return!0;if(t&&e.specKeys?.includes(a))return!0;return!1}function l(a,e){let n=new Set(a),t=[],o=new Set;for(let r of e){if(n.has(r)||o.has(r))continue;o.add(r),t.push(r)}return t}async function d(a,e){let n=l(e.loaded,e.required);if(n.length===0)return[];return a.load({specKeys:n,locales:e.locales})}export{l as computeShardDelta,c as createStaticBundleLoader,d as loadShardDelta};
@@ -0,0 +1 @@
1
+ var g=new Set(["ar","arc","dv","fa","ha","he","ks","ku","ps","ur","yi"]);function u(e){let t=p(e);return g.has(t)?"rtl":"ltr"}function p(e){try{return new Intl.Locale(e).language.toLowerCase()}catch{return e.split("-")[0]?.toLowerCase()??e.toLowerCase()}}function r(e){let t=e.trim();if(!t)return{input:e,valid:!1,error:"Locale is empty"};try{let o=Intl.getCanonicalLocales(t)[0];return o?{input:e,locale:o,valid:!0}:{input:e,valid:!1,error:"Locale did not canonicalize"}}catch(o){return{input:e,valid:!1,error:o instanceof Error?o.message:"Invalid locale"}}}function i(e=[]){let t=[];for(let o of e){let a=r(o);if(a.locale&&!t.includes(a.locale))t.push(a.locale)}return t}function m(e){let t=r(e.defaultLocale).locale??e.defaultLocale,o=i(e.requestedLocales),a=i(e.supportedLocales),c=i(e.fallbackLocales),n=a.length?a:[t];for(let l of o){let s=f(l,t,c).find((d)=>n.includes(d));if(s)return L(s,o,n,t,c)}return L(t,o,n,t,c)}function f(e,t,o=[]){let a=[],c=(n)=>{if(!n)return;let l=r(n).locale??n;if(!a.includes(l))a.push(l)};c(e);for(let n of h(e))c(n);for(let n of o)c(n);return c(t),a}function h(e){let o=(r(e).locale??e).split("-"),a=[];while(o.length>1)o.pop(),a.push(o.join("-"));return a}function L(e,t,o,a,c){return{locale:e,requestedLocales:t,supportedLocales:o,fallbackChain:f(e,a,c),direction:u(e),defaultLocale:a}}export{f as buildLocaleFallbackChain,r as canonicalizeLocale,i as canonicalizeLocales,m as negotiateLocale};
@@ -0,0 +1 @@
1
+ function a(e){return[...e].sort((r,t)=>(r.priority??s(r.scope))-(t.priority??s(t.scope)))}function s(e){switch(e){case"base":return 0;case"package":return 10;case"project":return 20;case"tenant":return 30;case"user":return 40;case"request":return 50}}export{s as defaultOverridePriority,a as sortOverrideLayers};
@@ -0,0 +1,28 @@
1
+ import n from"intl-messageformat";var s="contractspec.icu.precompiled",a=1;function c(t,e){return`${t}\x00${e}`}function l(t,e){return new n(t,e).getAst()}function p(t){let e={};for(let{locale:r,message:o}of t){let i=c(r,o);if(e[i])continue;e[i]={locale:r,message:o,ast:l(o,r)}}return{kind:s,version:a,entries:e}}function d(t){let e=[];for(let r of t)for(let o of Object.values(r.messages))e.push({locale:r.locale,message:o.value});return p(e)}function g(t){return JSON.stringify(t)}function I(t){let e=JSON.parse(t),r=m(e);if(r.length>0)throw Error(r.join("; "));return e}function m(t){let e=[];if(!t||t.kind!==s)e.push("Precompiled ICU catalog kind is invalid.");if(t?.version!==a)e.push("Precompiled ICU catalog version is unsupported.");if(!t?.entries||typeof t.entries!=="object")e.push("Precompiled ICU catalog entries must be an object.");return e}function f(t){return({locale:e,message:r})=>t.entries[c(e,r)]?.ast}var P={id:"docs.tech.translation-runtime.icu-precompile",title:"Precompiled ICU AST fast-path",summary:"Build-time ICU AST precompilation with an additive runtime parse-skip fast-path.",kind:"reference",visibility:"public",route:"/docs/tech/translation-runtime/icu-precompile",tags:["i18n","icu","performance","translation-runtime"],body:`# Precompiled ICU AST fast-path
2
+
3
+ Precompiles ICU message strings into the exact \`MessageFormatElement[]\` AST that
4
+ \`intl-messageformat\` produces, so the runtime can skip the per-call parse.
5
+
6
+ - \`precompileIcuMessage(message, locale)\` returns the AST via
7
+ \`IntlMessageFormat#getAst()\` — identical to the on-the-fly string path, so
8
+ formatted output is byte-identical (verified by golden tests across plural,
9
+ select, selectordinal, number, currency, date, and time for en/fr/es/ar-EG).
10
+ - \`buildPrecompiledIcuCatalog\` / \`buildPrecompiledIcuCatalogFromSpecs\` produce a
11
+ serializable \`PrecompiledIcuCatalog\` (\`kind\` =
12
+ \`${s}\`, \`version\` = \`${a}\`) as a
13
+ build-time asset.
14
+ - \`createPrecompiledIcuLookup(catalog)\` yields a lookup the formatter consumes.
15
+
16
+ Wire the lookup via \`createIntlMessageFormatter({ precompiled })\`, or pass a
17
+ per-call \`ast\` on \`compile()\`. When neither is present the formatter parses the
18
+ message string exactly as before — the surface is **ADDITIVE**.
19
+
20
+ Scope: this precompiles the ICU MessageFormat argument grammar
21
+ (plural/select/selectordinal/number/date/time + number skeletons such as
22
+ currency). List and relative-time rendering is performed by \`Intl.ListFormat\` /
23
+ \`Intl.RelativeTimeFormat\` outside the message AST and has no per-call message
24
+ parse to precompile. The \`createI18nFactory\` stack uses simple \`{key}\`
25
+ interpolation (no ICU parse) and is therefore unaffected by this fast-path.
26
+
27
+ Compatibility classification: **ADDITIVE**.
28
+ `};export{s as PRECOMPILED_ICU_KIND,a as PRECOMPILED_ICU_VERSION,P as PrecompiledIcuDocBlock,p as buildPrecompiledIcuCatalog,d as buildPrecompiledIcuCatalogFromSpecs,f as createPrecompiledIcuLookup,I as parsePrecompiledIcuCatalog,l as precompileIcuMessage,g as serializePrecompiledIcuCatalog,m as validatePrecompiledIcuCatalog};
@@ -0,0 +1 @@
1
+ var m=new Set(["ar","arc","dv","fa","ha","he","ks","ku","ps","ur","yi"]);function p(e){let o=R(e);return m.has(o)?"rtl":"ltr"}function R(e){try{return new Intl.Locale(e).language.toLowerCase()}catch{return e.split("-")[0]?.toLowerCase()??e.toLowerCase()}}function l(e){let o=e.trim();if(!o)return{input:e,valid:!1,error:"Locale is empty"};try{let a=Intl.getCanonicalLocales(o)[0];return a?{input:e,locale:a,valid:!0}:{input:e,valid:!1,error:"Locale did not canonicalize"}}catch(a){return{input:e,valid:!1,error:a instanceof Error?a.message:"Invalid locale"}}}function i(e=[]){let o=[];for(let a of e){let n=l(a);if(n.locale&&!o.includes(n.locale))o.push(n.locale)}return o}function C(e){let o=l(e.defaultLocale).locale??e.defaultLocale,a=i(e.requestedLocales),n=i(e.supportedLocales),t=i(e.fallbackLocales),r=n.length?n:[o];for(let s of a){let u=T(s,o,t).find((f)=>r.includes(f));if(u)return g(u,a,r,o,t)}return g(o,a,r,o,t)}function T(e,o,a=[]){let n=[],t=(r)=>{if(!r)return;let s=l(r).locale??r;if(!n.includes(s))n.push(s)};t(e);for(let r of P(e))t(r);for(let r of a)t(r);return t(o),n}function P(e){let a=(l(e).locale??e).split("-"),n=[];while(a.length>1)a.pop(),n.push(a.join("-"));return n}function g(e,o,a,n,t){return{locale:e,requestedLocales:o,supportedLocales:a,fallbackChain:T(e,n,t),direction:p(e),defaultLocale:n}}var h=["app","workspace","team","user","request"],x={app:"appLocale",workspace:"workspaceLocale",team:"teamLocale",user:"userLocale",request:"requestLocale"};function E(e={},o={}){let a=l(o.fallbackLocale??e.defaultLocale??e.appLocale??"en").locale??"en",n=l(e.defaultLocale??e.appLocale??a).locale??a,t={},r=[];for(let c of h){let L=q(e[x[c]]);if(!L)continue;t[c]=L,r.push({source:c,locale:L,metadata:e.sources?.[c]})}let s=i(e.requestedLocales??[]),d=i(o.additionalRequestedLocales??[]),u=[...r].reverse().map((c)=>c.locale),f=y([...s,...d,...u,n]);return{defaultLocale:n,requestedLocales:f,preferenceLocales:t,sources:[...s.map((c)=>({source:"requested",locale:c})),...r]}}function q(e){if(!e)return;return l(e).locale}function y(e){return[...new Set(e)]}export{E as resolveTranslationPreferenceContext};
@@ -0,0 +1 @@
1
+ var c="contractspec.translation-runtime.hydration",T=1;function s(n){return JSON.stringify(n)}function e(n){return JSON.parse(n)}function i(n){return{kind:"contractspec.translation-runtime.hydration",version:1,snapshot:p(n)}}function u(n){return JSON.stringify(n)}function d(n){let a=JSON.parse(n),t=r(a);if(t.length>0)throw Error(t.map((o)=>o.message).join("; "));return i(a.snapshot)}function m(n){return{type:"translation-runtime-snapshot",payload:i(n)}}function r(n){let a=[];if(!n||n.kind!=="contractspec.translation-runtime.hydration")a.push({code:"invalid_snapshot",level:"error",message:"Translation hydration payload kind is invalid."});if(n?.version!==1)a.push({code:"invalid_snapshot",level:"error",message:"Translation hydration payload version is unsupported.",context:{version:n?.version}});return a.push(...l(n?.snapshot)),a}function l(n){let a=[];if(!n)return[{code:"invalid_snapshot",level:"error",message:"Translation runtime snapshot is missing."}];if(!n.locale)a.push({code:"invalid_snapshot",level:"error",message:"Translation runtime snapshot locale is missing."});if(!n.defaultLocale)a.push({code:"invalid_snapshot",level:"error",message:"Translation runtime snapshot defaultLocale is missing."});for(let[t,o]of[["fallbackChain",n.fallbackChain],["supportedLocales",n.supportedLocales],["specs",n.specs]])if(!Array.isArray(o))a.push({code:"invalid_snapshot",level:"error",message:`Translation runtime snapshot ${t} must be an array.`,context:{field:t}});return a}function p(n){return e(s(n))}export{c as TRANSLATION_RUNTIME_HYDRATION_KIND,T as TRANSLATION_RUNTIME_HYDRATION_VERSION,i as createTranslationHydrationPayload,m as createTranslationSnapshotStreamChunk,d as parseTranslationHydrationPayload,e as parseTranslationSnapshot,u as serializeTranslationHydrationPayload,s as serializeTranslationSnapshot,r as validateTranslationHydrationPayload,l as validateTranslationSnapshot};
@@ -0,0 +1,21 @@
1
+ import type { Locale, MessageKey } from '@lssm/lib.contracts-spec/translations';
2
+ export type TranslationDiagnosticLevel = 'info' | 'warning' | 'error';
3
+ export type TranslationDiagnosticCode = 'fallback_used' | 'formatter_error' | 'invalid_locale' | 'invalid_snapshot' | 'loader_error' | 'missing_message' | 'missing_spec' | 'override_used';
4
+ export interface TranslationDiagnostic {
5
+ code: TranslationDiagnosticCode;
6
+ level: TranslationDiagnosticLevel;
7
+ message: string;
8
+ specKey?: string;
9
+ messageKey?: MessageKey;
10
+ locale?: Locale;
11
+ fallbackLocale?: Locale;
12
+ source?: string;
13
+ context?: Record<string, unknown>;
14
+ }
15
+ export type TranslationDiagnosticReporter = (diagnostic: TranslationDiagnostic) => void;
16
+ export declare class TranslationDiagnosticsCollector {
17
+ private readonly items;
18
+ report(diagnostic: TranslationDiagnostic): void;
19
+ list(): TranslationDiagnostic[];
20
+ clear(): void;
21
+ }
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ class i{items=[];report(e){this.items.push(e)}list(){return[...this.items]}clear(){this.items.length=0}}export{i as TranslationDiagnosticsCollector};
@@ -0,0 +1,3 @@
1
+ export type TextDirection = 'ltr' | 'rtl';
2
+ export declare function getTextDirection(locale: string): TextDirection;
3
+ export declare function getLanguageSubtag(locale: string): string;
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ var r=new Set(["ar","arc","dv","fa","ha","he","ks","ku","ps","ur","yi"]);function a(t){let e=n(t);return r.has(e)?"rtl":"ltr"}function n(t){try{return new Intl.Locale(t).language.toLowerCase()}catch{return t.split("-")[0]?.toLowerCase()??t.toLowerCase()}}export{n as getLanguageSubtag,a as getTextDirection};
@@ -0,0 +1,6 @@
1
+ import type { Locale } from '@lssm/lib.contracts-spec/translations';
2
+ export interface FallbackPolicy {
3
+ defaultLocale: Locale;
4
+ locales?: readonly Locale[];
5
+ }
6
+ export declare function resolveFallbackChain(locale: Locale, policy: FallbackPolicy): Locale[];
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ var g=new Set(["ar","arc","dv","fa","ha","he","ks","ku","ps","ur","yi"]);function L(e){let o=p(e);return g.has(o)?"rtl":"ltr"}function p(e){try{return new Intl.Locale(e).language.toLowerCase()}catch{return e.split("-")[0]?.toLowerCase()??e.toLowerCase()}}function r(e){let o=e.trim();if(!o)return{input:e,valid:!1,error:"Locale is empty"};try{let t=Intl.getCanonicalLocales(o)[0];return t?{input:e,locale:t,valid:!0}:{input:e,valid:!1,error:"Locale did not canonicalize"}}catch(t){return{input:e,valid:!1,error:t instanceof Error?t.message:"Invalid locale"}}}function s(e=[]){let o=[];for(let t of e){let a=r(t);if(a.locale&&!o.includes(a.locale))o.push(a.locale)}return o}function y(e){let o=r(e.defaultLocale).locale??e.defaultLocale,t=s(e.requestedLocales),a=s(e.supportedLocales),c=s(e.fallbackLocales),l=a.length?a:[o];for(let n of t){let u=i(n,o,c).find((d)=>l.includes(d));if(u)return f(u,t,l,o,c)}return f(o,t,l,o,c)}function i(e,o,t=[]){let a=[],c=(l)=>{if(!l)return;let n=r(l).locale??l;if(!a.includes(n))a.push(n)};c(e);for(let l of h(e))c(l);for(let l of t)c(l);return c(o),a}function h(e){let t=(r(e).locale??e).split("-"),a=[];while(t.length>1)t.pop(),a.push(t.join("-"));return a}function f(e,o,t,a,c){return{locale:e,requestedLocales:o,supportedLocales:t,fallbackChain:i(e,a,c),direction:L(e),defaultLocale:a}}function v(e,o){return i(e,o.defaultLocale,o.locales??[])}export{v as resolveFallbackChain};
@@ -0,0 +1,32 @@
1
+ import type { MessageFormatElement } from '@formatjs/icu-messageformat-parser';
2
+ import type { Locale } from '@lssm/lib.contracts-spec/translations';
3
+ export type RuntimePrimitive = string | number | boolean | Date | null | undefined;
4
+ export type RuntimeValue = RuntimePrimitive | ((chunks: string[]) => string);
5
+ export type RuntimeValues = Record<string, RuntimeValue>;
6
+ export interface CompiledMessage {
7
+ format(values?: RuntimeValues): string;
8
+ }
9
+ export interface MessageFormatValidationResult {
10
+ valid: boolean;
11
+ error?: string;
12
+ arguments: string[];
13
+ plurals: string[];
14
+ selects: string[];
15
+ selectOrdinals: string[];
16
+ }
17
+ export interface CompileMessageOptions {
18
+ id: string;
19
+ locale: Locale;
20
+ message: string;
21
+ /**
22
+ * Optional precompiled AST for `message` at `locale`.
23
+ *
24
+ * Additive fast-path: when provided, formatters may construct from the AST
25
+ * directly and skip parsing `message`. When omitted, behavior is unchanged.
26
+ */
27
+ ast?: MessageFormatElement[];
28
+ }
29
+ export interface MessageFormatter {
30
+ compile(options: CompileMessageOptions): CompiledMessage;
31
+ validate(message: string): MessageFormatValidationResult;
32
+ }
@@ -0,0 +1 @@
1
+ // @bun
@@ -0,0 +1,12 @@
1
+ import type { CompiledMessage, MessageFormatter } from '../formatter.js';
2
+ import type { PrecompiledIcuLookup } from '../precompile.js';
3
+ export interface IntlMessageFormatterOptions {
4
+ cache?: Map<string, CompiledMessage>;
5
+ /**
6
+ * Optional precompiled-AST lookup. When it resolves an AST for a message,
7
+ * the formatter constructs from the AST and skips parsing the string.
8
+ * Additive: when absent or returning `undefined`, behavior is unchanged.
9
+ */
10
+ precompiled?: PrecompiledIcuLookup;
11
+ }
12
+ export declare function createIntlMessageFormatter(options?: IntlMessageFormatterOptions): MessageFormatter;
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{parse as f,TYPE as a}from"@formatjs/icu-messageformat-parser";import M from"intl-messageformat";function w(e={}){let o=e.cache??new Map,l=e.precompiled;return{compile({id:n,locale:s,message:i,ast:c}){let t=`${s}\x00${n}\x00${i}`,r=o.get(t);if(r)return r;let m=c??l?.({id:n,locale:s,message:i}),d=new M(m??i,s),u={format(g){let p=d.format(g);return Array.isArray(p)?p.join(""):String(p)}};return o.set(t,u),u},validate(n){try{let s=f(n);return F(s)}catch(s){return{valid:!1,error:s instanceof Error?s.message:"Invalid ICU message",arguments:[],plurals:[],selects:[],selectOrdinals:[]}}}}}function F(e){let o=new Set,l=new Set,n=new Set,s=new Set,i=(c)=>{for(let t of c){let r="value"in t?String(t.value):void 0;if(r&&v(t.type))o.add(r);if(r&&t.type===a.select)n.add(r);if(r&&t.type===a.plural)if(o.add(r),"pluralType"in t&&t.pluralType==="ordinal")s.add(r);else l.add(r);if("options"in t)for(let m of Object.values(t.options))i(m.value)}};return i(e),{valid:!0,arguments:[...o],plurals:[...l],selects:[...n],selectOrdinals:[...s]}}function v(e){return e===a.argument||e===a.number||e===a.date||e===a.time||e===a.select||e===a.plural||e===a.pound||e===a.tag}export{w as createIntlMessageFormatter};
@@ -0,0 +1 @@
1
+ export * from './adapters/i18next.js';
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ var v=new Set(["ar","arc","dv","fa","ha","he","ks","ku","ps","ur","yi"]);function x(e){let t=O(e);return v.has(t)?"rtl":"ltr"}function O(e){try{return new Intl.Locale(e).language.toLowerCase()}catch{return e.split("-")[0]?.toLowerCase()??e.toLowerCase()}}function g(e){let t=e.trim();if(!t)return{input:e,valid:!1,error:"Locale is empty"};try{let n=Intl.getCanonicalLocales(t)[0];return n?{input:e,locale:n,valid:!0}:{input:e,valid:!1,error:"Locale did not canonicalize"}}catch(n){return{input:e,valid:!1,error:n instanceof Error?n.message:"Invalid locale"}}}function S(e=[]){let t=[];for(let n of e){let a=g(n);if(a.locale&&!t.includes(a.locale))t.push(a.locale)}return t}function j(e){let t=g(e.defaultLocale).locale??e.defaultLocale,n=S(e.requestedLocales),a=S(e.supportedLocales),o=S(e.fallbackLocales),c=a.length?a:[t];for(let s of n){let r=d(s,t,o).find((l)=>c.includes(l));if(r)return I(r,n,c,t,o)}return I(t,n,c,t,o)}function d(e,t,n=[]){let a=[],o=(c)=>{if(!c)return;let s=g(c).locale??c;if(!a.includes(s))a.push(s)};o(e);for(let c of M(e))o(c);for(let c of n)o(c);return o(t),a}function M(e){let n=(g(e).locale??e).split("-"),a=[];while(n.length>1)n.pop(),a.push(n.join("-"));return a}function I(e,t,n,a,o){return{locale:e,requestedLocales:t,supportedLocales:n,fallbackChain:d(e,a,o),direction:x(e),defaultLocale:a}}function C(e,t="specKey"){if(typeof t==="function")return t(e);return t==="domain"?e.meta.domain:e.meta.key}function b(e,t){let n=e.fallbacks??(e.fallback?[e.fallback]:[]);return d(e.locale,t,n)}function m(e,t,n){let a=g(e);if(a.locale)return a.locale;return t.push({code:"i18next_invalid_locale",level:"warning",message:a.error??`Invalid locale ${e}`,locale:e,specKey:n}),e}function y(e,t,n){if(t.fallbackLng!==void 0)return t.fallbackLng;if(t.includeFallbackLng===!1)return;if(!e.length)return;let a=new Map;for(let c of e){let s=c.fallbackChain.filter((r)=>r!==c.locale),i=a.get(c.locale);if(i&&i.join("\x00")!==s.join("\x00")){n.push({code:"i18next_fallback_projection_lossy",level:"warning",message:`Multiple fallback chains exist for locale ${c.locale}; keeping authoritative chains in the ContractSpec manifest.`,locale:c.locale,namespace:c.namespace,specKey:c.specKey});return}a.set(c.locale,s)}let o=Object.fromEntries([...a.entries()].filter(([,c])=>c.length>0));if(!Object.keys(o).length)return;return o}function k(e){let t=new Map;for(let n of e){let a=n.fallbackChain.filter((c)=>c!==n.locale).join("\x00"),o=t.get(n.locale);if(o&&o!==a)return!0;t.set(n.locale,a)}return!1}function h(e){return[...new Set(e)]}function L(e,t){let n={},a={},o=[],c=[],s=new Map;for(let i of e){let{spec:r}=i,l=m(r.locale,c,r.meta.key),p=t.defaultLocale??r.defaultLocale??r.fallback??l,u=C(r,t.namespace),f=r.formatter?.syntax??"icu";R(s,u,r,c),E(n,a,l,u),o.push({specKey:r.meta.key,version:r.meta.version,domain:r.meta.domain,namespace:u,locale:l,direction:r.direction??x(l),fallbackChain:b(r,p),syntax:f,owners:r.meta.owners,tags:r.meta.tags,source:i.source,scope:i.scope}),w(f,t,l,u,r.meta.key,c),N(n,a,l,u,i,c)}return D(n,a,o,t,c)}function R(e,t,n,a){let o=e.get(t);if(o&&o!==n.meta.key)a.push({code:"i18next_namespace_collision",level:"warning",message:`Namespace ${t} is shared by ${o} and ${n.meta.key}.`,namespace:t,specKey:n.meta.key});else e.set(t,n.meta.key)}function w(e,t,n,a,o,c){if(e!=="icu"||t.assumeIcuFormatter)return;c.push({code:"i18next_icu_plugin_required",level:"warning",message:"ContractSpec ICU message exported to i18next; use an ICU-capable i18next format plugin for runtime formatting parity.",locale:n,namespace:a,specKey:o})}function E(e,t,n,a){e[n]??={},e[n][a]??={},t[n]??={},t[n][a]??={}}function N(e,t,n,a,o,c){let{spec:s}=o,i=e[n]?.[a],r=t[n]?.[a];if(!i||!r)return;for(let[l,p]of Object.entries(s.messages)){let u=i[l],f=r[l];if(u!==void 0&&(u!==p.value||f?.version!==s.meta.version||f?.source!==o.source||f?.scope!==o.scope)){c.push({code:"i18next_resource_collision",level:"error",message:`Message ${l} collides in ${n}/${a}.`,locale:n,namespace:a,specKey:s.meta.key,messageKey:l});continue}i[l]=p.value,r[l]={specKey:s.meta.key,version:s.meta.version,locale:n,namespace:a,messageKey:l,description:p.description,context:p.context,placeholders:p.placeholders,variants:p.variants,maxLength:p.maxLength,tags:p.tags,pluralRules:s.pluralRules,source:o.source,scope:o.scope}}}function D(e,t,n,a,o){let c=Object.keys(e).sort(),s=h(n.map((r)=>r.namespace)).sort(),i=y(n,a,o);return{resources:e,manifest:{defaultLocale:a.defaultLocale?m(a.defaultLocale,o):void 0,locales:c,namespaces:n,messages:t,diagnostics:o},ns:s,lng:a.lng?m(a.lng,o):void 0,defaultNS:s[0],fallbackLng:i}}function P(e,t={}){return L(e.map((n)=>({spec:n})),t)}function H(e,t={}){let n=e.sources?.length?e.sources.map(({scope:a,source:o,spec:c})=>({scope:a,source:o,spec:c})):e.specs.map((a)=>({spec:a}));return L(n,{defaultLocale:e.defaultLocale,lng:e.locale,fallbackLng:e.fallbackChain,...t})}function J(e,t={}){let n=[],a=t.fallbackLng??e.fallbackLng;if(!a&&k(e.manifest.namespaces))n.push({code:"i18next_fallback_projection_lossy",level:"warning",message:"ContractSpec has per-bundle fallback chains that cannot be represented as one i18next fallbackLng value."});let o={resources:e.resources,ns:e.ns,keySeparator:t.keySeparator??!1},c=t.defaultNS??e.defaultNS;if(c)o.defaultNS=c;let s=t.lng??e.lng??e.manifest.defaultLocale;if(s)o.lng=s;if(a)o.fallbackLng=a;if(t.partialBundledLanguages!==void 0)o.partialBundledLanguages=t.partialBundledLanguages;return{options:o,diagnostics:n}}function Q(e,t,n={}){let a=n.deep??!0,o=n.overwrite??!0;for(let[c,s]of Object.entries(t.resources))for(let[i,r]of Object.entries(s))e.addResourceBundle(c,i,r,a,o)}export{Q as addContractSpecResourceBundles,J as createI18nextInitOptions,P as exportContractSpecToI18next,H as exportTranslationSnapshotToI18next};
@@ -0,0 +1,11 @@
1
+ export * from './diagnostics.js';
2
+ export * from './direction.js';
3
+ export * from './fallback.js';
4
+ export * from './formatter.js';
5
+ export * from './formatters/intl-messageformat.js';
6
+ export * from './loader.js';
7
+ export * from './locale.js';
8
+ export * from './overrides.js';
9
+ export * from './precompile.js';
10
+ export * from './preferences.js';
11
+ export * from './snapshot.js';