@fanee/core 0.4.0 → 0.6.0

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.
package/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # @fanee/core
2
+
3
+ Core runtime for the OTB (Open Translation Bundle) format.
4
+
5
+ Provides `FaneeRuntime`, a plugin-based i18n runtime with MF2 MessageFormat support, and `i18n`, a pre-instantiated singleton.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @fanee/core
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { FaneeRuntime } from "@fanee/core";
17
+
18
+ const runtime = new FaneeRuntime().config({
19
+ defaultLocale: "en",
20
+ currentLocale: "en",
21
+ resources: {
22
+ "": {
23
+ en: { greeting: "Hello, {name}!" },
24
+ fr: { greeting: "Bonjour, {name}!" },
25
+ },
26
+ },
27
+ });
28
+
29
+ runtime.t("greeting"); // "Hello, {name}!"
30
+ runtime.t("greeting", { name: "World" }); // "Hello, World!"
31
+
32
+ runtime.setLocale("fr");
33
+ runtime.t("greeting", { name: "World" }); // "Bonjour, World!"
34
+ ```
35
+
36
+ Or use the global singleton:
37
+
38
+ ```typescript
39
+ import { i18n } from "@fanee/core";
40
+
41
+ i18n.config({
42
+ defaultLocale: "en",
43
+ currentLocale: "en",
44
+ resources: {
45
+ "": { en: { hello: "Hello, World!" } },
46
+ },
47
+ });
48
+
49
+ i18n.t("hello"); // "Hello, World!"
50
+ ```
51
+
52
+ ## API
53
+
54
+ ### `FaneeRuntime`
55
+
56
+ #### `config(patch)`
57
+
58
+ Shallow-merges a partial state patch into the runtime state. Returns `this` for chaining.
59
+
60
+ ```typescript
61
+ runtime.config({
62
+ defaultLocale: "en",
63
+ currentLocale: "en",
64
+ baseNamespace: "web",
65
+ resources: { /* ... */ },
66
+ });
67
+ ```
68
+
69
+ #### `use(fn)`
70
+
71
+ Registers a plugin function that transforms the runtime state. Plugins run sequentially in registration order. Returns `this` for chaining.
72
+
73
+ ```typescript
74
+ runtime.use(async (state) => {
75
+ const resources = await loadFromAPI();
76
+ return { ...state, resources };
77
+ });
78
+ ```
79
+
80
+ #### `ready()`
81
+
82
+ Returns a `Promise<void>` that resolves when all queued plugins have completed. The runtime is also thenable, so `await runtime` works as a shorthand.
83
+
84
+ ```typescript
85
+ const runtime = new FaneeRuntime()
86
+ .use(asyncPlugin)
87
+ .config({ defaultLocale: "en" });
88
+
89
+ await runtime.ready();
90
+ // or: await runtime;
91
+ ```
92
+
93
+ ### Translation Methods
94
+
95
+ #### `t(key, vars?)`
96
+
97
+ Translates a key using the current locale and base namespace. Returns a string.
98
+
99
+ ```typescript
100
+ runtime.t("key"); // looks up in base namespace with current locale
101
+ runtime.t("greeting", { name: "World" }); // with MF2 variable interpolation
102
+ ```
103
+
104
+ #### `getT(context?)`
105
+
106
+ Returns a translation function bound to the given context. The `context.namespace` appends to the base namespace with `:` as separator.
107
+
108
+ ```typescript
109
+ const t = runtime.getT();
110
+ t("key"); // looks up in base namespace
111
+
112
+ const tAuth = runtime.getT({ namespace: "auth" });
113
+ tAuth("login"); // looks up in "base:auth" (or "auth" if base namespace is "")
114
+
115
+ const tFr = runtime.getT({ locale: "fr" });
116
+ tFr("key"); // looks up with locale "fr"
117
+ ```
118
+
119
+ #### `tAll(key, vars?)`
120
+
121
+ Returns translations for a key in all available locales within the base namespace.
122
+
123
+ ```typescript
124
+ runtime.tAll("greeting");
125
+ // { en: "Hello", fr: "Bonjour", de: "Hallo" }
126
+ ```
127
+
128
+ #### `getLocale()`
129
+
130
+ Returns the current locale BCP 47 tag.
131
+
132
+ #### `getLocales()`
133
+
134
+ Returns a sorted array of all locales present in loaded resources.
135
+
136
+ #### `setLocale(locale)`
137
+
138
+ Sets the current locale. Subsequent `t()` calls use the new locale.
139
+
140
+ ```typescript
141
+ runtime.setLocale("fr");
142
+ runtime.t("greeting"); // "Bonjour"
143
+ ```
144
+
145
+ #### `setNamespace(ns)`
146
+
147
+ Sets the base namespace. Subsequent lookups resolve against this namespace.
148
+
149
+ ```typescript
150
+ runtime.setNamespace("admin");
151
+ runtime.t("dashboard_title");
152
+ ```
153
+
154
+ #### `getAllTranslations()`
155
+
156
+ Returns the full resource tree (`BundleResources`).
157
+
158
+ #### `getTranslationsForNamespace(ns)`
159
+
160
+ Returns locale-indexed messages for a namespace, or `undefined`.
161
+
162
+ #### `subscribe(callback)`
163
+
164
+ Subscribes to state changes. Returns an unsubscribe function.
165
+
166
+ ```typescript
167
+ const unsub = runtime.subscribe((state) => {
168
+ console.log("locale changed to", state.currentLocale);
169
+ });
170
+ unsub();
171
+ ```
172
+
173
+ ### `i18n` singleton
174
+
175
+ A pre-instantiated `FaneeRuntime` exported for convenience.
176
+
177
+ ```typescript
178
+ import { i18n } from "@fanee/core";
179
+ ```
180
+
181
+ ## Translation Format
182
+
183
+ By default, messages are formatted using MF2 MessageFormat with variable interpolation:
184
+
185
+ ```typescript
186
+ runtime.t("greeting", { name: "World" }); // "Hello, World!"
187
+ runtime.t("price", { amount: 1234.56 }); // "Total: $1,234.56"
188
+ runtime.t("date", { today: new Date("2024-02-02") }); // "Today is Feb 2, 2024"
189
+ runtime.t("items", { count: 1 }); // "a item"
190
+ runtime.t("items", { count: 5 }); // "5 items"
191
+ ```
192
+
193
+ Set `formatting` to `"identity"` to disable formatting.
194
+
195
+ ## Locale Fallback
196
+
197
+ When a key is missing in the current locale, the runtime falls back to the default locale. If missing in both, the key itself is returned.
198
+
199
+ ## License
200
+
201
+ MIT
@@ -0,0 +1,297 @@
1
+ //#region src/types.d.ts
2
+ /** A BCP 47 language tag, e.g. `"en"`, `"zh-CN"`. */
3
+ type Locale = string;
4
+ /** A named scope for grouping translations, e.g. `"common"`, `"errors"`. */
5
+ type Namespace = string;
6
+ /** A key that identifies a specific message within a locale. */
7
+ type MessageKey = string;
8
+ /**
9
+ * How a message string is formatted after variable interpolation.
10
+ * - `"mf2"` – MessageFormat 2.0
11
+ * - `"mf1"` – MessageFormat (legacy)
12
+ * - `"identity"` – simple string replacement
13
+ * - Custom string – a user-provided formatter name.
14
+ */
15
+ type MessageFormattingMode = "mf2" | "mf1" | "identity" | (string & {});
16
+ /** A flat map of message keys to their translated strings for a single locale. */
17
+ type LocaleMessages = Record<MessageKey, string>;
18
+ /** Messages for all locales within a single namespace. */
19
+ type NamespaceResources = Record<Locale, LocaleMessages>;
20
+ /** The complete resource tree: namespace → locale → messages. */
21
+ type BundleResources = Record<Namespace, NamespaceResources>;
22
+ /** Options passed to the low-level translate function. */
23
+ interface TranslateOptions {
24
+ /** Target locale for the translation. */
25
+ locale: string;
26
+ /** Variable bag for message interpolation. */
27
+ vars?: Record<string, unknown>;
28
+ /** Override the default formatting mode for this call. */
29
+ formatting?: MessageFormattingMode;
30
+ }
31
+ /** Scoping hints used when obtaining a bound {@link TranslateFunction}. */
32
+ interface TranslateContext {
33
+ /** Override the active namespace. */
34
+ namespace?: Namespace;
35
+ /** Override the active locale. */
36
+ locale?: Locale;
37
+ }
38
+ /** A pre-bound translate function tied to a specific locale and namespace. */
39
+ type TranslateFunction = (key: MessageKey, vars?: Record<string, unknown>) => string;
40
+ /** A map of locale → translated string, typically the result of {@link runtime.FaneeRuntime.tAll | tAll}. */
41
+ type TranslationsByLocale = Record<Locale, string>;
42
+ /** Metadata descriptor for an OTB (Open Translation Bundle) package. */
43
+ interface OTBManifest {
44
+ /** Must be `"otb"`. */
45
+ format: "otb";
46
+ /** The spec version the bundle conforms to. */
47
+ specVersion: string;
48
+ /** Version of this particular bundle. */
49
+ bundleVersion?: string;
50
+ /** Whether the bundle contains all its dependencies inline. */
51
+ standalone?: boolean;
52
+ /** The locale the source messages are authored in. */
53
+ sourceLocale?: string;
54
+ /** Locales that this bundle provides translations for. */
55
+ targetLocales?: Locale[];
56
+ /** Human-readable name for the bundle. */
57
+ name?: string;
58
+ /** Default formatting mode for messages in this bundle. */
59
+ formatting?: MessageFormattingMode;
60
+ /** Extension keys (must be prefixed with `x-`). */
61
+ [key: `x-${string}`]: unknown;
62
+ }
63
+ /** Mutable state container consumed by plugins and the runtime. */
64
+ interface FaneeState {
65
+ /** The full translation resource tree. */
66
+ resources: BundleResources;
67
+ /** Fallback locale when a key is missing in the current locale. */
68
+ defaultLocale: Locale;
69
+ /** The currently active locale. */
70
+ currentLocale: Locale;
71
+ /** The default namespace used for unqualified lookups. */
72
+ baseNamespace: Namespace;
73
+ /** The active message formatting mode. */
74
+ formatting: MessageFormattingMode;
75
+ /** Low-level translate function (locale-aware, may be swapped by plugins). */
76
+ translate: (msg: string, options?: TranslateOptions) => string;
77
+ }
78
+ //#endregion
79
+ //#region src/runtime.d.ts
80
+ /** Sequenced runtime that processes plugin setup before translations are available. */
81
+ declare class FaneeRuntime {
82
+ private state;
83
+ private queue;
84
+ private listeners;
85
+ constructor();
86
+ private notify;
87
+ /**
88
+ * Register a plugin function that transforms the runtime state.
89
+ *
90
+ * Plugins are executed sequentially (FIFO) in the order they are registered.
91
+ * Each plugin receives the current {@link FaneeState} and may return a new state
92
+ * (or a promise thereof). The entire chain must resolve before the runtime
93
+ * is considered "ready" (see {@link ready}).
94
+ *
95
+ * @param fn - A plugin function receiving the current state.
96
+ * @returns `this` for chaining.
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * runtime.use((state) => ({ ...state, currentLocale: "zh-CN" }));
101
+ * ```
102
+ */
103
+ use(fn: (state: FaneeState) => FaneeState | Promise<FaneeState>): this;
104
+ /**
105
+ * Shallow-merge a partial state patch into the current state.
106
+ *
107
+ * This operation is queued and runs after all previously registered plugins.
108
+ * It is equivalent to a plugin that destructures the patch over the state.
109
+ *
110
+ * @param patch - A partial {@link FaneeState} whose properties override the current state.
111
+ * @returns `this` for chaining.
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * runtime.config({ defaultLocale: "en", formatting: "mf2" });
116
+ * ```
117
+ */
118
+ config(patch: Partial<FaneeState>): this;
119
+ /**
120
+ * @internal
121
+ * Makes the runtime thenable so callers can `await` plugin setup before
122
+ * calling translation methods.
123
+ *
124
+ * @param onfulfilled - Handler for successful queue resolution.
125
+ * @param onrejected - Handler for queue rejection.
126
+ */
127
+ then<TResult1 = void, TResult2 = never>(onfulfilled?: // biome-ignore lint/suspicious/noConfusingVoidType: matches Promise<void> queue resolution
128
+ ((value: void) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
129
+ /** Resolve locale + namespace from an optional context, falling back to the current state. */
130
+ private resolveContext;
131
+ /**
132
+ * Look up a message in the resource tree and (if vars are provided)
133
+ * delegate to the configured formatter.
134
+ */
135
+ private localize;
136
+ /**
137
+ * Translate a message key in the current locale and base namespace.
138
+ *
139
+ * Falls back through:
140
+ * 1. {@link FaneeState.currentLocale Current locale}
141
+ * 2. The {@link FaneeState.defaultLocale default locale}
142
+ * 3. Returns the key itself if no translation is found
143
+ *
144
+ * @param key - The message key to translate.
145
+ * @param vars - Optional variables for interpolation.
146
+ * @returns The translated string, or `key` if no translation exists.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * runtime.t("hello"); // "你好"
151
+ * runtime.t("greeting", { name: "Alice" }); // "Hello, Alice"
152
+ * ```
153
+ */
154
+ t(key: MessageKey, vars?: Record<string, unknown>): string;
155
+ /**
156
+ * Return a bound translate function pinned to a specific locale/namespace.
157
+ *
158
+ * Unlike {@link t}, this method captures the locale and namespace at call time
159
+ * so the returned function can be passed around (e.g. as a prop to a component)
160
+ * without retaining a reference to the runtime.
161
+ *
162
+ * @param context - Optional overrides for locale and/or namespace.
163
+ * If omitted, the current runtime values are captured.
164
+ * @returns A function with the signature `(key, vars?) => string`.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * const t = runtime.getT({ locale: "ja", namespace: "errors" });
169
+ * t("not_found"); // "見つかりませんでした"
170
+ * ```
171
+ */
172
+ getT(context?: Partial<TranslateContext>): TranslateFunction;
173
+ /**
174
+ * Translate a message key into every available locale within the base namespace.
175
+ *
176
+ * Useful for generating locale-switching UIs, SEO hreflang tags, or
177
+ * pre-rendering all translations on the server.
178
+ *
179
+ * @param key - The message key to translate.
180
+ * @param vars - Optional variables for interpolation (applied to every locale).
181
+ * @returns A record mapping each locale to its translated string.
182
+ * Returns an empty object if the base namespace has no resources.
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * runtime.tAll("welcome");
187
+ * // { en: "Welcome", "es": "Bienvenido", "fr": "Bienvenue", "zh-CN": "欢迎", ja: "ようこそ" }
188
+ * ```
189
+ */
190
+ tAll(key: MessageKey, vars?: Record<string, unknown>): TranslationsByLocale;
191
+ /**
192
+ * Get the currently active locale.
193
+ *
194
+ * @returns The BCP 47 tag of the active locale (e.g. `"en"`, `"zh-CN"`).
195
+ */
196
+ getLocale(): Locale;
197
+ /**
198
+ * Collect every unique locale present across all loaded namespaces.
199
+ *
200
+ * @returns A sorted array of locale tags. Returns an empty array if no
201
+ * resources have been loaded.
202
+ */
203
+ getLocales(): Locale[];
204
+ /**
205
+ * Return the complete resource tree.
206
+ *
207
+ * @returns The full {@link BundleResources} object (namespace → locale → messages).
208
+ */
209
+ getAllTranslations(): BundleResources;
210
+ /**
211
+ * Return resources for a single namespace.
212
+ *
213
+ * @param ns - The namespace to look up.
214
+ * @returns The locale-indexed messages for that namespace, or `undefined`
215
+ * if the namespace has not been loaded.
216
+ */
217
+ getTranslationsForNamespace(ns: string): NamespaceResources | undefined;
218
+ /**
219
+ * Wait for all queued plugins to finish.
220
+ *
221
+ * Because plugin registration and configuration are queued as a promise chain,
222
+ * you must `await runtime.ready()` (or `await runtime` directly) before calling
223
+ * translation methods if any plugin is asynchronous.
224
+ *
225
+ * @returns A promise that resolves once the plugin queue is empty.
226
+ *
227
+ * @example
228
+ * ```ts
229
+ * const runtime = new FaneeRuntime()
230
+ * .use(asyncLoaderPlugin)
231
+ * .config({ defaultLocale: "en" });
232
+ *
233
+ * await runtime.ready();
234
+ * console.log(runtime.t("hello")); // safe
235
+ * ```
236
+ */
237
+ ready(): Promise<void>;
238
+ /**
239
+ * Switch the active locale at runtime.
240
+ *
241
+ * This is a synchronous operation that updates the current locale instantly.
242
+ * Subsequent calls to {@link t} and {@link getT} (without an explicit locale)
243
+ * will use this new locale.
244
+ *
245
+ * @param locale - The BCP 47 tag of the target locale.
246
+ *
247
+ * @example
248
+ * ```ts
249
+ * runtime.setLocale("fr");
250
+ * runtime.t("hello"); // "Bonjour"
251
+ * ```
252
+ */
253
+ setLocale(locale: Locale): void;
254
+ /**
255
+ * Switch the base namespace at runtime.
256
+ *
257
+ * Subsequent calls to {@link t} and related methods will resolve keys against
258
+ * this namespace. When a scoped namespace is set via {@link getT},
259
+ * it is prefixed with the base namespace using `:` as separator.
260
+ *
261
+ * @param ns - The namespace to set as the base.
262
+ *
263
+ * @example
264
+ * ```ts
265
+ * runtime.setNamespace("admin");
266
+ * runtime.t("dashboard_title"); // resolved from "admin" namespace
267
+ * ```
268
+ */
269
+ setNamespace(ns: string): void;
270
+ /**
271
+ * Subscribe to state changes.
272
+ *
273
+ * The callback is invoked synchronously whenever the runtime state is mutated
274
+ * (via {@link setLocale}, {@link setNamespace}, or after a queued
275
+ * {@link use} / {@link config} operation resolves).
276
+ *
277
+ * @param callback - Called with the current {@link FaneeState} on every change.
278
+ * @returns An unsubscribe function. Call it to stop receiving notifications.
279
+ *
280
+ * @example
281
+ * ```ts
282
+ * const unsub = runtime.subscribe((state) => {
283
+ * console.log("locale changed to", state.currentLocale);
284
+ * });
285
+ *
286
+ * runtime.setLocale("fr"); // logs "locale changed to fr"
287
+ * unsub();
288
+ * ```
289
+ */
290
+ subscribe(callback: (state: FaneeState) => void): () => void;
291
+ }
292
+ //#endregion
293
+ //#region src/i18n.d.ts
294
+ declare const i18n: FaneeRuntime;
295
+ //#endregion
296
+ export { type BundleResources, FaneeRuntime, type FaneeState, type Locale, type LocaleMessages, type MessageKey, type Namespace, type NamespaceResources, type OTBManifest, type TranslateContext, type TranslateFunction, type TranslateOptions, type TranslationsByLocale, i18n };
297
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/runtime.ts","../src/i18n.ts"],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ const e=/^[\u061c\u200e\u200f\u2066-\u2069]+/,t=/^[-.+0-9A-Z_a-z\u{a1}-\u{61b}\u{61d}-\u{167f}\u{1681}-\u{1fff}\u{200b}-\u{200d}\u{2010}-\u{2027}\u{2030}-\u{205e}\u{2060}-\u{2065}\u{206a}-\u{2fff}\u{3001}-\u{d7ff}\u{e000}-\u{fdcf}\u{fdf0}-\u{fffd}\u{10000}-\u{1fffd}\u{20000}-\u{2fffd}\u{30000}-\u{3fffd}\u{40000}-\u{4fffd}\u{50000}-\u{5fffd}\u{60000}-\u{6fffd}\u{70000}-\u{7fffd}\u{80000}-\u{8fffd}\u{90000}-\u{9fffd}\u{a0000}-\u{afffd}\u{b0000}-\u{bfffd}\u{c0000}-\u{cfffd}\u{d0000}-\u{dfffd}\u{e0000}-\u{efffd}\u{f0000}-\u{ffffd}\u{100000}-\u{10fffd}]+/u,n=/^[-.0-9]/;function r(r,i){let a=i,o=r.slice(a).match(e);o&&(a+=o[0].length);let s=r.slice(a).match(t);if(!s)return null;let c=s[0];if(n.test(c))return null;a+=c.length;let l=r.slice(a).match(e);return l&&(a+=l[0].length),{value:c.normalize(),end:a}}const i=(e,n)=>e.slice(n).match(t)?.[0]??``,a=Symbol.for(`CST`);var o=class extends Error{type;constructor(e,t){super(t),this.type=e}},s=class extends o{start;end;constructor(e,t,n,r){let i=r?`Missing ${r}`:e;t>=0&&(i+=` at ${t}`),super(e,i),this.start=t,this.end=n??t+1}},c=class extends s{constructor(e,t){let{start:n,end:r}=t[a]??{start:-1,end:-1};super(e,n,r)}},l=class extends o{source;cause;constructor(e,t,n,r){super(e,t),this.source=n,r!==void 0&&(this.cause=r)}},u=class extends o{source;cause;constructor(e,t){super(e,t),this.source=`�`}};const d=new Set(`؜‎‏⁦⁧⁨⁩`),f=new Set(`
2
+ \r  `);let p,m;const h=(e,t)=>new s(`missing-syntax`,e,e+t.length,t),g=(...e)=>new s(...e);function _(e,t){if(m.startsWith(e,p))t&&(p+=e.length);else throw h(p,e)}function ee(e){p=0,m=e;let t=re();if(m.startsWith(`.match`,p))return te(t);let n=t.length>0||m.startsWith(`{{`,p);!n&&p>0&&(p=0);let r=v(n);if(n&&(D(),p<m.length))throw g(`extra-content`,p,m.length);return{type:`message`,declarations:t,pattern:r}}function te(e){p+=6,D(!0);let t=[];for(;m[p]===`$`;)t.push(C()),D(!0);if(t.length===0)throw g(`empty-token`,p);let n=[];for(;p<m.length;)n.push(ne()),D();return{type:`select`,declarations:e,selectors:t,variants:n}}function ne(){let e=[];for(;p<m.length;){D(e.length?`{`:!1);let t=m[p];if(t===`{`)break;if(t===`*`)e.push({type:`*`}),p+=1;else{let t=w(!0);t.value=t.value.normalize(),e.push(t)}}return{keys:e,value:v(!0)}}function v(e){if(e)if(m.startsWith(`{{`,p))p+=2;else throw h(p,`{{`);let t=[];loop:for(;p<m.length;)switch(m[p]){case`{`:t.push(b(!0));break;case`}`:if(!e)throw g(`parse-error`,p);break loop;default:t.push(oe())}if(e)if(m.startsWith(`}}`,p))p+=2;else throw h(p,`}}`);return t}function re(){let e=[];D();loop:for(;m[p]===`.`;){switch(m.substr(p,6)){case`.input`:e.push(ie());break;case`.local`:e.push(y());break;case`.match`:break loop;default:throw g(`parse-error`,p)}D()}return e}function ie(){p+=6,D(),_(`{`,!1);let e=p,t=b(!1);if(t.type===`expression`&&t.arg?.type===`variable`)return{type:`input`,name:t.arg.name,value:t};throw g(`bad-input-expression`,e,p)}function y(){p+=6,D(!0),_(`$`,!0);let e=E();return D(),_(`=`,!0),D(),_(`{`,!1),{type:`local`,name:e,value:b(!1)}}function b(e){let t=p;p+=1,D();let n=S(!1);n&&D(`}`);let r=m[p],i,a;switch(r){case`@`:case`}`:break;case`:`:{p+=1,i={type:`function`,name:T()};let e=x();e&&(i.options=e);break}case`#`:case`/`:{if(n||!e)throw g(`parse-error`,p);p+=1,a={type:`markup`,kind:r===`#`?`open`:`close`,name:T()};let t=x();t&&(a.options=t);break}default:throw g(`parse-error`,p)}let o=ae();if(a?.kind===`open`&&m[p]===`/`&&(a.kind=`standalone`,p+=1),_(`}`,!0),i){let e=n?{type:`expression`,arg:n,functionRef:i}:{type:`expression`,functionRef:i};return o&&(e.attributes=o),e}if(a)return o&&(a.attributes=o),a;if(!n)throw g(`empty-token`,t,p);return o?{type:`expression`,arg:n,attributes:o}:{type:`expression`,arg:n}}function x(){D(`/}`);let e={},t=!0;for(;p<m.length;){let n=m[p];if(n===`@`||n===`/`||n===`}`)break;let r=p,i=T();if(Object.hasOwn(e,i))throw g(`duplicate-option-name`,r,p);D(),_(`=`,!0),D(),e[i]=S(!0),t=!1,D(`/}`)}return t?null:e}function ae(){let e={},t=!0;for(;m[p]===`@`;){let n=p;p+=1;let r=T();if(Object.hasOwn(e,r))throw g(`duplicate-attribute`,n,p);D(`=/}`),m[p]===`=`?(p+=1,D(),e[r]=w(!0),D(`/}`)):e[r]=!0,t=!1}return t?null:e}function oe(){let e=``,t=p;loop:for(;t<m.length;++t)switch(m[t]){case`\\`:{let n=m[t+1];if(!`\\{|}`.includes(n))throw g(`bad-escape`,t,t+2);e+=m.substring(p,t)+n,t+=1,p=t+1;break}case`{`:case`}`:break loop}return e+=m.substring(p,t),p=t,e}function S(e){return m[p]===`$`?C():w(e)}function C(){return p+=1,{type:`variable`,name:E()}}function w(e){if(m[p]===`|`)return se();let t=i(m,p);if(!t){if(e)throw g(`empty-token`,p);return}return p+=t.length,{type:`literal`,value:t}}function se(){p+=1;let e=``;for(let t=p;t<m.length;++t)switch(m[t]){case`\\`:{let n=m[t+1];if(!`\\{|}`.includes(n))throw g(`bad-escape`,t,t+2);e+=m.substring(p,t)+n,t+=1,p=t+1;break}case`|`:return e+=m.substring(p,t),p=t+1,{type:`literal`,value:e}}throw h(m.length,`|`)}function T(){let e=E();return m[p]===`:`?(p+=1,e+`:`+E()):e}function E(){let e=r(m,p);if(!e)throw g(`empty-token`,p);return p=e.end,e.value}function D(e=!1){let t=m[p],n=!1;if(e){for(;d.has(t);)t=m[++p];for(;f.has(t);)t=m[++p],n=!0}for(;d.has(t)||f.has(t);)t=m[++p];if(e&&!n&&(e===!0||!e.includes(m[p])))throw h(p,`' '`)}function ce(e,t){let{node:n,pattern:r}=t,{functionRef:i=n,attributes:a=null,declaration:o=n,expression:s=n,key:c=n,markup:l=n,options:u=null,value:d=n,variant:f=n}=t,p=(e,t)=>{if(e){let n=u?.(e,t);if(d)for(let n of Object.values(e))d(n,t,`option`);n?.()}},m=(e,t)=>{if(e){let n=a?.(e,t);if(d)for(let n of Object.values(e))n!==!0&&d(n,t,`attribute`);n?.()}},h=(e,t)=>{if(typeof e==`object`){let n;switch(e.type){case`expression`:if(n=s?.(e,t),e.arg&&d?.(e.arg,t,`arg`),e.functionRef){let n=i?.(e.functionRef,t,e.arg);p(e.functionRef.options,t),n?.()}m(e.attributes,t);break;case`markup`:n=l?.(e,t),p(e.options,t),m(e.attributes,t);break}n?.()}},g=e=>{let t=r?.(e);for(let t of e)h(t,`placeholder`);t?.()};for(let t of e.declarations){let e=o?.(t);t.value&&h(t.value,`declaration`),e?.()}if(e.type===`message`)g(e.pattern);else{if(d)for(let t of e.selectors)d(t,`selector`,`arg`);for(let t of e.variants){let e=f?.(t);c&&t.keys.forEach(c),g(t.value),e?.()}}}function le(e,t=(e,t)=>{throw new c(e,t)}){let n=0,r=null,i=new Set,a=new Set,o=new Set,s=new Set,l=new Set,u=new Set,d=!0;ce(e,{declaration(e){if(e.name)return(e.value.functionRef||e.type===`local`&&e.value.arg?.type===`variable`&&i.has(e.value.arg.name))&&i.add(e.name),e.type===`local`&&s.add(e.name),d=e.type===`local`,()=>{a.has(e.name)?t(`duplicate-declaration`,e):a.add(e.name)}},expression({functionRef:e}){e&&o.add(e.name)},value(e,o,s){if(e.type===`variable`)switch(l.add(e.name),o){case`declaration`:(s!==`arg`||d)&&a.add(e.name);break;case`selector`:n+=1,r=e,i.has(e.name)||t(`missing-selector-annotation`,e)}},variant(e){let{keys:i}=e;i.length!==n&&t(`key-mismatch`,e);let a=JSON.stringify(i.map(e=>e.type===`literal`?e.value:0));u.has(a)?t(`duplicate-variant`,e):u.add(a),r&&=i.every(e=>e.type===`*`)?null:e}}),r&&t(`missing-fallback`,r);for(let e of s)l.delete(e);return{functions:o,variables:l}}function O(e){if(e)try{typeof e==`string`&&(e=new Intl.Locale(e));let t=e.getTextInfo?.()??e.textInfo;if(t?.direction)return t.direction;let n=e.maximize().script;if(n)return`Adlm,Arab,Hebr,Mand,Nkoo,Rohg,Syrc,Thaa`.includes(n)?`rtl`:`ltr`}catch{}return`auto`}function ue(e){if(e&&typeof e==`object`&&(e=e.valueOf()),typeof e==`boolean`)return e;if(e&&typeof e==`object`&&(e=String(e)),e===`true`)return!0;if(e===`false`)return!1;throw RangeError(`Not a boolean`)}function k(e){if(e&&typeof e==`object`&&(e=e.valueOf()),e&&typeof e==`object`&&(e=String(e)),typeof e==`string`&&/^(0|[1-9][0-9]*)$/.test(e)&&(e=Number(e)),typeof e==`number`&&e>=0&&Number.isInteger(e))return e;throw RangeError(`Not a positive integer`)}function A(e){if(e&&typeof e==`object`&&(e=e.valueOf()),typeof e==`string`)return e;if(e&&typeof e==`object`)return String(e);throw RangeError(`Not a string`)}function j(e){let t;if(typeof e==`object`){let n=e?.valueOf;typeof n==`function`&&(t=e.options,e=n.call(e))}if(typeof e==`string`)try{e=JSON.parse(e)}catch{}if(typeof e!=`bigint`&&typeof e!=`number`)throw new u(`bad-operand`,`Input is not numeric`);return{value:e,options:t}}function M(e,t,n,r){let{dir:i,locales:a}=e;n.useGrouping===`never`&&(n.useGrouping=!1),r&&`select`in n&&!e.literalOptionKeys.has(`select`)&&(e.onError(`bad-option`,`The option select may only be set by a literal value`),r=!1);let o,s,c,l;return{type:`number`,get dir(){return i??=(o??=Intl.NumberFormat.supportedLocalesOf(a,n)[0],O(o)),i},get options(){return{...n}},selectKey:r?e=>{let r=t;n.style===`percent`&&(typeof r==`bigint`?r*=100n:r*=100);let i=String(r);if(e.has(i))return i;if(n.select===`exact`)return null;let o=n.select?{...n,select:void 0,type:n.select}:n;return c??=new Intl.PluralRules(a,o).select(Number(r)),e.has(c)?c:null}:void 0,toParts(){s??=new Intl.NumberFormat(a,n);let e=s.formatToParts(t);return o??=s.resolvedOptions().locale,i??=O(o),i===`ltr`||i===`rtl`?[{type:`number`,dir:i,locale:o,parts:e}]:[{type:`number`,locale:o,parts:e}]},toString(){return s??=new Intl.NumberFormat(a,n),l??=s.format(t),l},valueOf:()=>t}}function N(e,t,n){let r=j(n),i=r.value,a=Object.assign({},r.options,{localeMatcher:e.localeMatcher,style:`decimal`});for(let[n,r]of Object.entries(t))if(r!==void 0)try{switch(n){case`minimumIntegerDigits`:case`minimumFractionDigits`:case`maximumFractionDigits`:case`minimumSignificantDigits`:case`maximumSignificantDigits`:case`roundingIncrement`:a[n]=k(r);break;case`roundingMode`:case`roundingPriority`:case`select`:case`signDisplay`:case`trailingZeroDisplay`:case`useGrouping`:a[n]=A(r)}}catch{e.onError(`bad-option`,`Value ${r} is not valid for :number option ${n}`)}return M(e,i,a,!0)}function de(e,t,n){let r=j(n),i=Number.isFinite(r.value)?Math.round(r.value):r.value,a=Object.assign({},r.options,{maximumFractionDigits:0,minimumFractionDigits:void 0,minimumSignificantDigits:void 0,style:`decimal`});for(let[n,r]of Object.entries(t))if(r!==void 0)try{switch(n){case`minimumIntegerDigits`:case`maximumSignificantDigits`:a[n]=k(r);break;case`select`:case`signDisplay`:case`useGrouping`:a[n]=A(r)}}catch{e.onError(`bad-option`,`Value ${r} is not valid for :integer option ${n}`)}return M(e,i,a,!0)}function fe(e,t,n){let r=j(n),i=Object.assign({},r.options,{localeMatcher:e.localeMatcher,style:`currency`});for(let[n,r]of Object.entries(t))if(r!==void 0)try{switch(n){case`currency`:case`currencySign`:case`roundingMode`:case`roundingPriority`:case`trailingZeroDisplay`:case`useGrouping`:i[n]=A(r);break;case`minimumIntegerDigits`:case`minimumSignificantDigits`:case`maximumSignificantDigits`:case`roundingIncrement`:i[n]=k(r);break;case`currencyDisplay`:{let t=A(r);t===`never`?e.onError(`unsupported-operation`,`Currency display "never" is not yet supported`):i[n]=t;break}case`fractionDigits`:{let e=A(r);if(e===`auto`)i.minimumFractionDigits=void 0,i.maximumFractionDigits=void 0;else{let t=k(e);i.minimumFractionDigits=t,i.maximumFractionDigits=t}break}}}catch{e.onError(`bad-option`,`Value ${r} is not valid for :currency option ${n}`)}if(!i.currency)throw new u(`bad-operand`,`A currency code is required for :currency`);return M(e,r.value,i,!1)}const pe=new Set([`weekday`,`day-weekday`,`month-day`,`month-day-weekday`,`year-month-day`,`year-month-day-weekday`]),me=new Set([`long`,`medium`,`short`]),he=new Set([`hour`,`minute`,`second`]),ge=new Set([`long`,`short`]),_e=(e,t,n)=>P(`datetime`,e,t,n),ve=(e,t,n)=>P(`date`,e,t,n),ye=(e,t,n)=>P(`time`,e,t,n);function P(e,t,n,r){let i={localeMatcher:t.localeMatcher},a=r;if(typeof a==`object`&&a){let t=a.options;t&&(i.calendar=t.calendar,e!==`date`&&(i.hour12=t.hour12),i.timeZone=t.timeZone),typeof a.valueOf==`function`&&(a=a.valueOf())}switch(typeof a){case`number`:case`string`:a=new Date(a)}if(!(a instanceof Date)||isNaN(a.getTime()))throw new u(`bad-operand`,`Input is not a valid date`);if(n.calendar!==void 0)try{i.calendar=A(n.calendar)}catch{t.onError(`bad-option`,`Invalid :${e} calendar option value`)}if(n.hour12!==void 0&&e!==`date`)try{i.hour12=ue(n.hour12)}catch{t.onError(`bad-option`,`Invalid :${e} hour12 option value`)}if(n.timeZone!==void 0){let r;try{r=A(n.timeZone)}catch{t.onError(`bad-option`,`Invalid :${e} timeZone option value`)}if(r===`input`)i.timeZone===void 0&&t.onError(`bad-operand`,`Missing input timeZone value for :${e}`);else if(r!==void 0){if(i.timeZone!==void 0&&r!==i.timeZone)throw new u(`bad-option`,`Time zone conversion is not supported`);i.timeZone=r}}if(e!==`time`){let r=e===`date`?`fields`:`dateFields`,a=e===`date`?`length`:`dateLength`,o=F(t,n,r,pe)??`year-month-day`,s=F(t,n,a,me),c=new Set(o.split(`-`));c.has(`year`)&&(i.year=`numeric`),c.has(`month`)&&(i.month=s===`long`?`long`:s===`short`?`numeric`:`short`),c.has(`day`)&&(i.day=`numeric`),c.has(`weekday`)&&(i.weekday=s===`long`?`long`:`short`)}if(e!==`date`){switch(F(t,n,e===`time`?`precision`:`timePrecision`,he)){case`hour`:i.hour=`numeric`;break;case`second`:i.hour=`numeric`,i.minute=`numeric`,i.second=`numeric`;break;default:i.hour=`numeric`,i.minute=`numeric`}i.timeZoneName=F(t,n,`timeZoneStyle`,ge)}let o=new Intl.DateTimeFormat(t.locales,i),s=t.dir,c,l;return{type:`datetime`,get dir(){return s??=(c??=o.resolvedOptions().locale,O(c)),s},get options(){return{...i}},toParts(){let e=o.formatToParts(a);return c??=o.resolvedOptions().locale,s??=O(c),s===`ltr`||s===`rtl`?[{type:`datetime`,dir:s,locale:c,parts:e}]:[{type:`datetime`,locale:c,parts:e}]},toString(){return l??=o.format(a),l},valueOf:()=>a}}function F(e,t,n,r){let i=t[n];if(i!==void 0)try{let e=A(i);if(r&&!r.has(e))throw Error();return e}catch{e.onError(`bad-option`,`Invalid value for ${n} option`)}}function be(e,t,n){let{value:r,options:i}=j(n),a;try{a=`add`in t?k(t.add):-1}catch{throw new u(`bad-option`,`Value ${t.add} is not valid for :offset option add`)}let o;try{o=`subtract`in t?k(t.subtract):-1}catch{throw new u(`bad-option`,`Value ${t.subtract} is not valid for :offset option subtract`)}if(a<0==o<0)throw new u(`bad-option`,`Exactly one of "add" or "subtract" is required as an :offset option`);let s=a<0?-o:a;return typeof r==`number`?r+=s:r+=BigInt(s),N(e,{},{valueOf:()=>r,options:i})}function xe(e,t,n){let r=j(n),i=Object.assign({},r.options,{localeMatcher:e.localeMatcher,style:`percent`});for(let[n,r]of Object.entries(t))if(r!==void 0)try{switch(n){case`roundingMode`:case`roundingPriority`:case`signDisplay`:case`trailingZeroDisplay`:case`useGrouping`:i[n]=A(r);break;case`minimumFractionDigits`:case`maximumFractionDigits`:case`minimumSignificantDigits`:case`maximumSignificantDigits`:i[n]=k(r);break}}catch{e.onError(`bad-option`,`Value ${r} is not valid for :percent option ${n}`)}return M(e,r.value,i,!0)}function I(e,t,n){let r=n===void 0?``:String(n),i=r.normalize();return{type:`string`,dir:e.dir??`auto`,selectKey:e=>e.has(i)?i:null,toParts(){let{dir:t}=e,n=e.locales[0];return t===`ltr`||t===`rtl`?[{type:`string`,dir:t,locale:n,value:r}]:[{type:`string`,locale:n,value:r}]},toString:()=>r,valueOf:()=>r}}function L(e,t,n){let r=j(n),i=Object.assign({},r.options,{localeMatcher:e.localeMatcher,style:`unit`});for(let[n,r]of Object.entries(t))if(r!==void 0)try{switch(n){case`signDisplay`:case`roundingMode`:case`roundingPriority`:case`trailingZeroDisplay`:case`unit`:case`unitDisplay`:case`useGrouping`:i[n]=A(r);break;case`minimumIntegerDigits`:case`minimumFractionDigits`:case`maximumFractionDigits`:case`minimumSignificantDigits`:case`maximumSignificantDigits`:case`roundingIncrement`:i[n]=k(r);break}}catch(t){t instanceof o?e.onError(t):e.onError(`bad-option`,`Value ${r} is not valid for :currency option ${n}`)}if(!i.unit)throw new u(`bad-operand`,`A unit identifier is required for :unit`);return M(e,r.value,i,!1)}let R={integer:de,number:N,offset:be,string:I};R=Object.freeze(Object.assign(Object.create(null),R));let z={currency:fe,date:ve,datetime:_e,percent:xe,time:ye,unit:L};z=Object.freeze(Object.assign(Object.create(null),z));const B=Symbol(`bidi-isolate`),V=(e=`�`)=>({type:`fallback`,source:e,toParts:()=>[{type:`fallback`,source:e}],toString:()=>`{${e}}`}),Se=(e,t)=>({type:`unknown`,source:e,dir:`auto`,toParts:()=>[{type:`unknown`,value:t}],toString:()=>String(t),valueOf:()=>t});var H=class{#e;#t;#n;dir;id;constructor(e,t,n){this.#e=e,this.#n=t,this.dir=void 0;let r=n&&Object.hasOwn(n,`u:dir`)?n[`u:dir`]:void 0;if(r){let t=String(J(e,r));if(t===`ltr`||t===`rtl`||t===`auto`)this.dir=t;else if(t!==`inherit`){let t=new u(`bad-option`,`Unsupported value for u:dir option`);t.source=Y(r),e.onError(t)}}let i=n&&Object.hasOwn(n,`u:id`)?n[`u:id`]:void 0;if(this.id=i?String(J(e,i)):void 0,n){this.#t=new Set;for(let[e,t]of Object.entries(n))t.type===`literal`&&this.#t.add(e)}}get literalOptionKeys(){return new Set(this.#t)}get localeMatcher(){return this.#e.localeMatcher}get locales(){return this.#e.locales.map(String)}onError(e,t){let n;e instanceof u?n=e:typeof e==`string`&&typeof t==`string`?n=new u(e,t):(n=new u(`function-error`,String(e)),n.cause=e),n.source=this.#n,this.#e.onError(n)}};function Ce(e,t,{name:n,options:r}){let i=`:${n}`,a=Y(t)??i;try{let o=t?[J(e,t)]:[],s=e.functions[n];if(!s)throw new l(`unknown-function`,`Unknown function ${i}`,a);let c=new H(e,a,r),u=s(c,we(e,r),...o);if(typeof u!=`object`||!u||typeof u.type!=`string`)throw new l(`bad-function-result`,`Function ${i} did not return a MessageValue`,a);let d={source:a};return c.dir&&(d.dir=c.dir,d[B]=!0),c.id&&typeof u.toParts==`function`&&(d.toParts=()=>{let e=u.toParts();for(let t of e)t.id=c.id;return e}),{...u,...d}}catch(t){return e.onError(t instanceof o?t:new l(`bad-function-result`,String(t),a,t)),V(a)}}function we(e,t){let n=Object.create(null);if(t)for(let[r,i]of Object.entries(t))r.startsWith(`u:`)||(n[r]=J(e,i));return n}function U(e,{arg:t,functionRef:n}){if(n)return Ce(e,t,n);switch(t?.type){case`literal`:{let n=`|${t.value}|`,r=I(new H(e,n),{},t.value);return r.source=n,r}case`variable`:return q(e,t);default:throw Error(`Unsupported expression: ${t?.type}`)}}var W=class{expression;scope;constructor(e,t){this.expression=e,this.scope=t}};const Te=e=>e!==null&&(typeof e==`object`||typeof e==`function`);function G(e,t){if(Te(e)){if(t in e)return e[t];let n=t.split(`.`);for(let t=n.length-1;t>0;--t){let r=n.slice(0,t).join(`.`);if(r in e){let i=n.slice(t).join(`.`);return G(e[r],i)}}for(let[n,r]of Object.entries(e))if(n.normalize()===t)return r}}function K(e,{name:t}){let n=G(e.scope,t);if(n===void 0){let n=`$`+t,r=`Variable not available: ${n}`;e.onError(new l(`unresolved-variable`,r,n))}else if(n instanceof W){let r=U(n.scope?{...e,scope:n.scope}:e,n.expression);return e.scope[t]=r,e.localVars.add(r),r}return n}function q(e,t){let n=`$`+t.name,r=K(e,t);if(r===void 0)return V(n);let i=typeof r;if(i===`object`){let t=r;if(t.type===`fallback`)return V(n);if(e.localVars.has(t))return t.source=n,t;r instanceof Number?i=`number`:r instanceof String&&(i=`string`)}let a;switch(i){case`bigint`:case`number`:a=e.functions.number;break;case`string`:a=e.functions.string;break;default:return Se(n,r)}let o=new H(e,n),s=a(o,{},r);return s.source=n,s}function J(e,t){switch(t.type){case`literal`:return t.value;case`variable`:return K(e,t);default:throw Error(`Unsupported value: ${t.type}`)}}function Y(e){switch(e?.type){case`literal`:return`|`+e.value.replaceAll(`\\`,`\\\\`).replaceAll(`|`,`\\|`)+`|`;case`variable`:return`$`+e.name;default:return}}function X(e,{kind:t,name:n,options:r}){let i={type:`markup`,kind:t,name:n},a=r?Object.entries(r):null;if(a?.length){i.options={};for(let[t,n]of a)if(t===`u:dir`){let r=new u(`bad-option`,`The option ${t} is not valid for markup`);r.source=Y(n),e.onError(r)}else{let r=J(e,n);typeof r==`object`&&typeof r?.valueOf==`function`&&(r=r.valueOf()),t===`u:id`?i.id=String(r):i.options[t]=r}}return i}function Z(e,t){if(t.type===`message`)return t.pattern;let n=t.selectors.map(t=>{let n=q(e,t),r;return typeof n.selectKey==`function`?r=n.selectKey.bind(n):(e.onError(new l(`bad-selector`,`Selector does not support selection`,n.source)),r=()=>null),{selectKey:r,source:n.source,best:null,keys:null}}),r=t.variants;loop:for(let i=0;i<n.length;++i){let a=n[i];if(!a.keys){a.keys=new Set;for(let{keys:e}of r){let t=e[i];if(!t)break loop;t.type!==`*`&&a.keys.add(t.value)}}try{a.best=a.keys.size?a.selectKey(a.keys):null}catch(t){e.onError(new l(`bad-selector`,`Selection failed`,a.source,t)),a.selectKey=()=>null,a.best=null}if(r=r.filter(e=>{let t=e.keys[i];return t.type===`*`?a.best==null:a.best===t.value}),r.length===0){if(i===0)break;let e=n[i-1];e.best==null?e.keys?.clear():e.keys?.delete(e.best);for(let e=i;e<n.length;++e)n[e].keys=null;r=t.variants,i=-1}}let i=r[0];return i?i.value:(e.onError(new l(`no-match`,`No variant was selected!?`,`.match`)),[])}var Ee=class{#e;#t;#n;#r;#i;#a;constructor(e,t,n){this.#e=n?.bidiIsolation!==`none`,this.#n=n?.localeMatcher??`best fit`,this.#r=Array.isArray(e)?e.map(e=>new Intl.Locale(e)):e?[new Intl.Locale(e)]:[],this.#t=n?.dir??O(this.#r[0]),this.#i=typeof t==`string`?ee(t):t,le(this.#i),this.#a=n?.functions?Object.assign(Object.create(null),R,n.functions):R}format(e,t){let n=this.#o(e,t),r=``;for(let e of Z(n,this.#i))if(typeof e==`string`)r+=e;else if(e.type===`markup`)X(n,e);else{let t;try{if(t=U(n,e),typeof t.toString==`function`)if(this.#e&&(this.#t!==`ltr`||t.dir!==`ltr`||t[B])){let e=t.dir===`ltr`?`⁦`:t.dir===`rtl`?`⁧`:`⁨`;r+=e+t.toString()+`⁩`}else r+=t.toString();else{let e=new u(`not-formattable`,`Message part is not formattable`);throw e.source=t.source,e}}catch(e){n.onError(e);let i=`{${t?.source??`�`}}`;r+=this.#e?`⁨`+i+`⁩`:i}}return r}formatToParts(e,t){let n=this.#o(e,t),r=[];for(let e of Z(n,this.#i))if(typeof e==`string`)r.push({type:`text`,value:e});else if(e.type===`markup`)r.push(X(n,e));else{let t;try{if(t=U(n,e),typeof t.toParts==`function`){let e=t.toParts();if(this.#e&&(this.#t!==`ltr`||t.dir!==`ltr`||t[B])){let n=t.dir===`ltr`?`⁦`:t.dir===`rtl`?`⁧`:`⁨`;r.push({type:`bidiIsolation`,value:n},...e,{type:`bidiIsolation`,value:`⁩`})}else r.push(...e)}else{let e=new u(`not-formattable`,`Message part is not formattable`);throw e.source=t.source,e}}catch(e){n.onError(e);let i={type:`fallback`,source:t?.source??`�`};this.#e?r.push({type:`bidiIsolation`,value:`⁨`},i,{type:`bidiIsolation`,value:`⁩`}):r.push(i)}}return r}#o(e,t=e=>{try{process.emitWarning(e)}catch{console.warn(e)}}){let n={...e};for(let t of this.#i.declarations)n[t.name]=new W(t.value,t.type===`input`?e??{}:void 0);return{onError:t,localeMatcher:this.#n,locales:this.#r,localVars:new WeakSet,functions:this.#a,scope:n}}};const Q=new Map;function De(e,t,n){let r=Q.get(e);r||(r=new Map,Q.set(e,r));let i=r.get(t);return i||(i=new Ee([e],n,{functions:z}),r.set(t,i),i)}function Oe(e,t){if(!t?.vars||Object.keys(t.vars).length===0||t.formatting===`identity`)return e;let{locale:n,vars:r}=t;return De(n,e,e).format(r,e=>{console.warn(`[fanee] Failed to format message: ${e}`)})}function ke(){return{resources:{},defaultLocale:`en`,currentLocale:`en`,baseNamespace:``,formatting:`mf2`,translate:Oe}}var $=class{state;queue;listeners;constructor(){this.state=ke(),this.queue=Promise.resolve(),this.listeners=new Set}notify(){let e=this.state;for(let t of this.listeners)t(e)}use(e){return this.queue=this.queue.then(async()=>{this.state=await e(this.state),this.notify()}),this}config(e){return this.queue=this.queue.then(async()=>{this.state={...this.state,...e},this.notify()}),this}then(e,t){return this.queue.then(e,t)}resolveContext(e){let{currentLocale:t,baseNamespace:n}=this.state;return{locale:e?.locale??t,ns:e?.namespace?n?`${n}:${e.namespace}`:e.namespace:n}}localize(e,t,n,r){if(!e)return n;let i=e[t];if(!i&&(i=e[this.state.defaultLocale],!i))return n;let a=i[n];return a===void 0?n:r===void 0||Object.keys(r).length===0?a:this.state.translate(a,{locale:t,vars:r,formatting:this.state.formatting})}t(e,t){let{locale:n,ns:r}=this.resolveContext();return this.localize(this.state.resources[r],n,e,t)}getT(e){let{locale:t,ns:n}=this.resolveContext(e),r=this.state.resources[n];return(e,n)=>this.localize(r,t,e,n)}tAll(e,t){let{baseNamespace:n,resources:r}=this.state,i=r[n],a={};if(!i)return a;for(let n of Object.keys(i))a[n]=this.localize(i,n,e,t);return a}getLocale(){return this.state.currentLocale}getLocales(){let e=new Set;for(let t of Object.values(this.state.resources))for(let n of Object.keys(t))e.add(n);return Array.from(e).sort()}getAllTranslations(){return this.state.resources}getTranslationsForNamespace(e){return this.state.resources[e]}ready(){return this.queue}setLocale(e){this.state.currentLocale=e,this.notify()}setNamespace(e){this.state.baseNamespace=e,this.notify()}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}};const Ae=new $;export{$ as FaneeRuntime,Ae as i18n};
3
+ //# sourceMappingURL=index.js.map