@vielzeug/i18nit 2.0.0 → 2.1.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/dist/i18n.js CHANGED
@@ -1,178 +1,41 @@
1
- import { BoundView as e } from "./core.js";
2
- import { BoundedMap as t, deepMerge as n, isMessageValue as r, resolvePath as i } from "./helpers.js";
3
- import { formatDate as a, formatList as o, formatNumber as s, formatRelative as c, getPluralForm as l, makeIntlCaches as u } from "./intl.js";
4
- import { interpolate as d } from "./interpolate.js";
1
+ import { BoundedMap as e, deepMerge as t, isMessageValue as n, resolvePath as r } from "./helpers.js";
2
+ import { formatDate as i, formatList as a, formatNumber as o, formatRelative as s, getPluralForm as c, makeIntlCaches as l } from "./intl.js";
3
+ import { interpolate as u } from "./interpolate.js";
5
4
  //#region src/i18n.ts
6
- var f = class {
7
- #e;
8
- #t;
9
- #n = /* @__PURE__ */ new Map();
10
- #r = /* @__PURE__ */ new Map();
11
- #i = /* @__PURE__ */ new Map();
12
- #a = /* @__PURE__ */ new Set();
13
- #o = new t(128);
14
- #s;
15
- #c;
16
- #l = null;
17
- #u = null;
18
- #d = !1;
19
- #f = 0;
20
- #p = null;
21
- #m;
22
- #h = u();
23
- #g;
24
- constructor({ fallback: t, loaders: n, locale: l = "en", messages: u, onDiagnostic: d, onMissing: f } = {}) {
25
- if (this.#e = l, this.#t = Array.isArray(t) ? t : t ? [t] : [], this.#s = f, this.#c = d, u) for (let [e, t] of Object.entries(u)) this.#n.set(e, structuredClone(t));
26
- if (n) for (let [e, t] of Object.entries(n)) this.#r.set(e, t);
27
- this.#m = {
28
- checkOwn: (e, t) => {
29
- let n = this.#n.get(t);
30
- if (!n) return !1;
31
- let a = i(n, e);
32
- return a !== void 0 && r(a);
33
- },
34
- findMessage: (e, t) => this.#b(e, t),
35
- formatDate: (e, t, n) => a(this.#h, e, t, n),
36
- formatList: (e, t, n) => o(this.#h, e, t, n),
37
- formatNumber: (e, t, n) => s(this.#h, e, t, n),
38
- formatRelative: (e, t, n, r) => c(this.#h, e, t, n, r),
39
- getLocale: () => this.#e,
40
- translate: (e, t, n) => this.#S(e, t, n)
41
- }, this.#g = new e(this.#m, null);
42
- }
43
- get locale() {
44
- return this.#e;
45
- }
46
- get locales() {
47
- return this.#l ??= [...this.#n.keys()], this.#l;
48
- }
49
- set locale(e) {
50
- this.#e !== e && (this.#e = e, this.#y("locale-change"));
51
- }
52
- async setLocale(e) {
53
- e !== this.#e && (await this.load(e), this.locale = e);
54
- }
55
- add(e, t) {
56
- let r = this.#n.get(e) ?? {};
57
- this.#n.set(e, n(r, t)), this.#l = null, this.#x(this.#e).includes(e) && this.#y("catalog-update");
58
- }
59
- replace(e, t) {
60
- this.#n.set(e, structuredClone(t)), this.#l = null, this.#x(this.#e).includes(e) && this.#y("catalog-update");
61
- }
62
- has(e) {
63
- return this.#g.has(e);
64
- }
65
- hasOwn(e) {
66
- return this.#g.hasOwn(e);
67
- }
68
- hasLocale(e) {
69
- return this.#n.has(e);
70
- }
71
- async load(...e) {
72
- await Promise.all(e.map((e) => this.#C(e)));
73
- }
74
- async reload(e) {
75
- this.#r.has(e) && (this.#n.delete(e), this.#l = null, await this.#C(e));
76
- }
77
- registerLoader(e, t) {
78
- this.#r.set(e, t), this.#u = null;
79
- }
80
- get loadableLocales() {
81
- return this.#u ??= [...this.#r.keys()], this.#u;
82
- }
83
- t(e, t) {
84
- return this.#g.t(e, t);
85
- }
86
- number(e, t) {
87
- return this.#g.number(e, t);
88
- }
89
- date(e, t) {
90
- return this.#g.date(e, t);
91
- }
92
- list(e, t = "and") {
93
- return this.#g.list(e, t);
94
- }
95
- relative(e, t, n) {
96
- return this.#g.relative(e, t, n);
97
- }
98
- currency(e, t, n) {
99
- return this.#g.currency(e, t, n);
100
- }
101
- withLocale(e) {
102
- return this.#g.withLocale(e);
103
- }
104
- scope(e) {
105
- return this.#g.scope(e);
106
- }
107
- batch(e) {
108
- this.#f++;
109
- try {
110
- e();
111
- } finally {
112
- if (this.#f--, this.#f === 0 && this.#p !== null) {
113
- let e = this.#p;
114
- this.#p = null, this.#y(e);
115
- }
116
- }
117
- }
118
- subscribe(e, t) {
119
- if (this.#a.add(e), t) try {
120
- e({
121
- locale: this.#e,
122
- reason: "locale-change"
123
- });
124
- } catch (e) {
125
- this.#_(e);
126
- }
127
- return () => this.#a.delete(e);
128
- }
129
- dispose() {
130
- this.#d = !0, this.#a.clear(), this.#n.clear(), this.#r.clear(), this.#i.clear(), this.#o.clear(), this.#l = null, this.#u = null;
131
- }
132
- [Symbol.dispose]() {
133
- this.dispose();
134
- }
135
- async [Symbol.asyncDispose]() {
136
- await Promise.allSettled([...this.#i.values()]), this.dispose();
137
- }
138
- #_(e) {
139
- this.#c ? this.#c({
5
+ function d(d = {}) {
6
+ let f = d.locale ?? "en", p = d.switchMode ?? "strict", m = Array.isArray(d.fallback) ? d.fallback : d.fallback ? [d.fallback] : [], h = /* @__PURE__ */ new Map(), g = /* @__PURE__ */ new Map(), _ = /* @__PURE__ */ new Map(), v = /* @__PURE__ */ new Set(), y = new e(128), b = l(), x = null, S = null, C = !1, w = 0, T = null, E = d.onMissing, D = d.onDiagnostic;
7
+ if (d.messages) for (let [e, t] of Object.entries(d.messages)) h.set(e, structuredClone(t));
8
+ if (d.loaders) for (let [e, t] of Object.entries(d.loaders)) g.set(e, t);
9
+ function O(e) {
10
+ D ? D({
140
11
  error: e,
141
12
  kind: "subscriber-error"
142
13
  }) : console.error("[i18nit] Subscriber threw:", e);
143
14
  }
144
- #v(e, t) {
145
- this.#c ? this.#c({
15
+ function k(e, t) {
16
+ D ? D({
146
17
  error: e,
147
18
  kind: "loader-error",
148
19
  locale: t
149
20
  }) : console.warn("[i18nit] Loader error:", e);
150
21
  }
151
- #y(e) {
152
- if (this.#f > 0) {
153
- this.#p !== "locale-change" && (this.#p = e);
22
+ function A(e) {
23
+ if (w > 0) {
24
+ T !== "locale-change" && (T = e);
154
25
  return;
155
26
  }
156
27
  let t = {
157
- locale: this.#e,
28
+ locale: f,
158
29
  reason: e
159
30
  };
160
- for (let e of this.#a) try {
31
+ for (let e of v) try {
161
32
  e(t);
162
33
  } catch (e) {
163
- this.#_(e);
34
+ O(e);
164
35
  }
165
36
  }
166
- #b(e, t) {
167
- for (let n of this.#x(t)) {
168
- let t = this.#n.get(n);
169
- if (!t) continue;
170
- let a = i(t, e);
171
- if (a !== void 0 && r(a)) return a;
172
- }
173
- }
174
- #x(e) {
175
- let t = this.#o.get(e);
37
+ function j(e) {
38
+ let t = y.get(e);
176
39
  if (t) return t;
177
40
  let n = /* @__PURE__ */ new Set(), r = (e) => {
178
41
  n.add(e);
@@ -180,39 +43,139 @@ var f = class {
180
43
  for (let e = t.length - 1; e > 0; e--) n.add(t.slice(0, e).join("-"));
181
44
  };
182
45
  r(e);
183
- for (let e of this.#t) r(e);
46
+ for (let e of m) r(e);
184
47
  let i = [...n];
185
- return this.#o.set(e, i), i;
48
+ return y.set(e, i), i;
186
49
  }
187
- #S(e, t, n) {
188
- let r = this.#b(e, n);
189
- if (r === void 0) return this.#s?.(e, n) ?? e;
190
- if (typeof r == "string") return d(r, t ?? {}, n, this.#h);
191
- let i = t ?? {}, a = Number(i.count ?? 0);
192
- return d(r[a === 0 && r.zero !== void 0 ? "zero" : l(this.#h, n, a)] ?? r.other, i, n, this.#h);
50
+ function M(e, t) {
51
+ let i = h.get(t);
52
+ if (!i) return !1;
53
+ let a = r(i, e);
54
+ return a !== void 0 && n(a);
193
55
  }
194
- #C(e) {
195
- if (this.#i.has(e)) return this.#i.get(e);
196
- if (this.#n.has(e)) return Promise.resolve();
197
- let t = this.#r.get(e);
198
- if (!t) return Promise.resolve();
199
- let n = (async () => {
56
+ function N(e, t) {
57
+ for (let i of j(t)) {
58
+ let t = h.get(i);
59
+ if (!t) continue;
60
+ let a = r(t, e);
61
+ if (a !== void 0 && n(a)) return a;
62
+ }
63
+ }
64
+ function P(e, t, n) {
65
+ let r = N(e, n);
66
+ if (r === void 0) return E?.(e, n) ?? e;
67
+ if (typeof r == "string") return u(r, t ?? {}, n, b);
68
+ let i = t ?? {}, a = Number(i.count ?? 0);
69
+ return u(r[a === 0 && r.zero !== void 0 ? "zero" : c(b, n, a)] ?? r.other, i, n, b);
70
+ }
71
+ function F(e, t) {
72
+ if (_.has(e)) return _.get(e);
73
+ if (h.has(e)) return Promise.resolve();
74
+ let n = g.get(e);
75
+ if (!n) return t === "strict" ? Promise.reject(/* @__PURE__ */ Error(`[i18nit] Missing loader for locale "${e}".`)) : Promise.resolve();
76
+ let r = (async () => {
200
77
  try {
201
- let n = await t(e);
202
- this.#d || this.replace(e, n);
78
+ let t = await n(e);
79
+ C || R.replace(e, t);
203
80
  } catch (t) {
204
- throw this.#v(t, e), t;
81
+ throw k(t, e), t;
205
82
  } finally {
206
- this.#i.delete(e);
83
+ _.delete(e);
207
84
  }
208
85
  })();
209
- return this.#i.set(e, n), n;
86
+ return _.set(e, r), r;
87
+ }
88
+ function I(e, t) {
89
+ let n = () => e ?? f, r = (e) => t ? `${t}.${e}` : e;
90
+ return {
91
+ currency(e, t, r) {
92
+ return o(b, e, {
93
+ ...r,
94
+ currency: t,
95
+ style: "currency"
96
+ }, n());
97
+ },
98
+ date(e, t) {
99
+ return i(b, e, t, n());
100
+ },
101
+ has(e) {
102
+ return N(r(e), n()) !== void 0;
103
+ },
104
+ hasOwn(e) {
105
+ return M(r(e), n());
106
+ },
107
+ list(e, t = "and") {
108
+ return a(b, e, n(), t);
109
+ },
110
+ get locale() {
111
+ return n();
112
+ },
113
+ number(e, t) {
114
+ return o(b, e, t, n());
115
+ },
116
+ relative(e, t, r) {
117
+ return s(b, e, t, r, n());
118
+ },
119
+ scope(n) {
120
+ return I(e, t ? `${t}.${String(n)}` : String(n));
121
+ },
122
+ t: (e, t) => P(r(e), t, n()),
123
+ withLocale(e) {
124
+ return I(e, t);
125
+ }
126
+ };
210
127
  }
211
- };
212
- function p(e) {
213
- return new f(e);
128
+ let L = I(null), R = Object.create(L);
129
+ return R.add = (e, n) => {
130
+ let r = h.get(e) ?? {};
131
+ h.set(e, t(r, n)), x = null, j(f).includes(e) && A("catalog-update");
132
+ }, R.batch = (e) => {
133
+ w++;
134
+ try {
135
+ e();
136
+ } finally {
137
+ if (w--, w === 0 && T !== null) {
138
+ let e = T;
139
+ T = null, A(e);
140
+ }
141
+ }
142
+ }, R.dispose = () => {
143
+ C = !0, v.clear(), h.clear(), g.clear(), _.clear(), y.clear(), x = null, S = null;
144
+ }, R.ensureLocale = async (e, t = p) => {
145
+ await F(e, t);
146
+ }, R.hasLocale = (e) => h.has(e), R.isReady = (e) => h.has(e), Object.defineProperties(R, {
147
+ loadableLocales: { get() {
148
+ return S ??= [...g.keys()], S;
149
+ } },
150
+ locales: { get() {
151
+ return x ??= [...h.keys()], x;
152
+ } }
153
+ }), R.registerLoader = (e, t) => {
154
+ g.set(e, t), S = null;
155
+ }, R.reload = async (e) => {
156
+ if (!g.has(e)) throw Error(`[i18nit] Cannot reload locale "${e}" without a registered loader.`);
157
+ h.delete(e), x = null, await F(e, "strict");
158
+ }, R.replace = (e, t) => {
159
+ h.set(e, structuredClone(t)), x = null, j(f).includes(e) && A("catalog-update");
160
+ }, R.subscribe = (e, t) => {
161
+ if (v.add(e), t) try {
162
+ e({
163
+ locale: f,
164
+ reason: "locale-change"
165
+ });
166
+ } catch (e) {
167
+ O(e);
168
+ }
169
+ return () => v.delete(e);
170
+ }, R.switchLocale = async (e, t = p) => {
171
+ e !== f && (await F(e, t), f = e, A("locale-change"));
172
+ }, R[Symbol.asyncDispose] = async () => {
173
+ await Promise.allSettled([..._.values()]), R.dispose();
174
+ }, R[Symbol.dispose] = () => {
175
+ R.dispose();
176
+ }, R;
214
177
  }
215
178
  //#endregion
216
- export { f as I18n, p as createI18n };
179
+ export { d as createI18n };
217
180
 
218
181
  //# sourceMappingURL=i18n.js.map
package/dist/i18n.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.js","names":["#caches","#view","#locale","#fallbacks","#onMissing","#onDiagnostic","#catalogs","#loaders","#core","#findMessage","#translate","#localesCache","#notify","#getLocaleChain","#loadOne","#loadersCache","#batchDepth","#pendingNotify","#subscribers","#diagnoseSubscriber","#disposed","#loading","#chainCache","#diagnoseLoader"],"sources":["../src/i18n.ts"],"sourcesContent":["import type {\n BoundI18n,\n DiagnosticEvent,\n I18nOptions,\n Loader,\n Locale,\n LocaleChangeEvent,\n LocaleChangeReason,\n MessageValue,\n Messages,\n NamespaceKeys,\n TranslationKeyParam,\n Unsubscribe,\n Vars,\n} from './types';\n\nimport { BoundView, type I18nCore } from './core';\nimport { BoundedMap, deepMerge, isMessageValue, resolvePath } from './helpers';\nimport { interpolate } from './interpolate';\nimport {\n type IntlCaches,\n formatDate,\n formatList,\n formatNumber,\n formatRelative,\n getPluralForm,\n makeIntlCaches,\n} from './intl';\n\nexport class I18n<T extends Messages = Messages> implements BoundI18n<T> {\n #locale: Locale;\n #fallbacks: Locale[];\n #catalogs = new Map<Locale, Messages>();\n #loaders = new Map<Locale, Loader>();\n #loading = new Map<Locale, Promise<void>>();\n #subscribers = new Set<(event: LocaleChangeEvent) => void>();\n /** Bounded at 128 entries — prevents unbounded growth when locale tags come from user input. */\n #chainCache = new BoundedMap<Locale, Locale[]>(128);\n #onMissing?: (key: string, locale: Locale) => string | undefined;\n #onDiagnostic?: (event: DiagnosticEvent) => void;\n #localesCache: Locale[] | null = null;\n #loadersCache: Locale[] | null = null;\n #disposed = false;\n #batchDepth = 0;\n #pendingNotify: LocaleChangeReason | null = null;\n #core: I18nCore;\n\n // Instance-scoped Intl caches — GC'd with the instance (important for SSR with many locales).\n readonly #caches: IntlCaches = makeIntlCaches();\n\n /** Internal BoundView — I18n delegates its BoundI18n surface here to avoid duplicating every method. */\n readonly #view: BoundView<T>;\n\n constructor({ fallback, loaders, locale = 'en', messages, onDiagnostic, onMissing }: I18nOptions<T> = {}) {\n this.#locale = locale;\n this.#fallbacks = Array.isArray(fallback) ? fallback : fallback ? [fallback] : [];\n this.#onMissing = onMissing;\n this.#onDiagnostic = onDiagnostic;\n\n if (messages) {\n for (const [l, m] of Object.entries(messages)) {\n // Deep-clone at init so external mutations to the source object can't corrupt the catalog.\n this.#catalogs.set(l, structuredClone(m) as Messages);\n }\n }\n\n if (loaders) for (const [l, fn] of Object.entries(loaders)) this.#loaders.set(l, fn);\n\n this.#core = {\n checkOwn: (key: string, locale: Locale) => {\n const catalog = this.#catalogs.get(locale);\n\n if (!catalog) return false;\n\n const value = resolvePath(catalog, key);\n\n return value !== undefined && isMessageValue(value);\n },\n findMessage: (key: string, locale: Locale) => this.#findMessage(key, locale),\n formatDate: (value, options, locale) => formatDate(this.#caches, value, options, locale),\n formatList: (items, locale, type) => formatList(this.#caches, items, locale, type),\n formatNumber: (value, options, locale) => formatNumber(this.#caches, value, options, locale),\n formatRelative: (value, unit, options, locale) => formatRelative(this.#caches, value, unit, options, locale),\n getLocale: () => this.#locale,\n translate: (key, vars, locale) => this.#translate(key, vars, locale),\n };\n\n this.#view = new BoundView<T>(this.#core, null);\n }\n\n /* -------------------- Locale -------------------- */\n\n get locale(): Locale {\n return this.#locale;\n }\n\n get locales(): Locale[] {\n this.#localesCache ??= [...this.#catalogs.keys()];\n\n return this.#localesCache;\n }\n\n set locale(value: Locale) {\n if (this.#locale === value) return;\n\n if (import.meta.env?.DEV && !this.#catalogs.has(value) && this.#loaders.has(value)) {\n console.warn(\n `[i18nit] locale \"${value}\" has a registered loader but is not loaded. ` +\n 'Use setLocale() to load and switch atomically.',\n );\n }\n\n this.#locale = value;\n this.#notify('locale-change');\n }\n\n async setLocale(locale: Locale): Promise<void> {\n if (locale === this.#locale) return;\n\n await this.load(locale);\n this.locale = locale;\n }\n\n /* -------------------- Message Management -------------------- */\n\n /** Deep-merges messages into an existing locale catalog. */\n add(locale: Locale, messages: Messages): void {\n const existing = this.#catalogs.get(locale) ?? {};\n\n this.#catalogs.set(locale, deepMerge(existing, messages));\n this.#localesCache = null;\n\n if (this.#getLocaleChain(this.#locale).includes(locale)) this.#notify('catalog-update');\n }\n\n /** Replaces the entire locale catalog for `locale` with a deep clone of `messages`. */\n replace(locale: Locale, messages: Messages): void {\n this.#catalogs.set(locale, structuredClone(messages));\n this.#localesCache = null;\n\n if (this.#getLocaleChain(this.#locale).includes(locale)) this.#notify('catalog-update');\n }\n\n has(key: string): boolean {\n return this.#view.has(key);\n }\n\n /** Like `has()`, but only checks the exact locale without walking the fallback chain. */\n hasOwn(key: string): boolean {\n return this.#view.hasOwn(key);\n }\n\n hasLocale(locale: Locale): boolean {\n return this.#catalogs.has(locale);\n }\n\n /* -------------------- Async Loaders -------------------- */\n\n async load(...locales: Locale[]): Promise<void> {\n await Promise.all(locales.map((locale) => this.#loadOne(locale)));\n }\n\n /**\n * Force-reloads a locale catalog even if already populated. Useful for hot-reload and forced bundle refresh.\n * No-op (with a dev warning) when no loader is registered for the locale, to prevent silently clearing the catalog.\n */\n async reload(locale: Locale): Promise<void> {\n if (!this.#loaders.has(locale)) {\n if (import.meta.env?.DEV) {\n console.warn(`[i18nit] reload(\"${locale}\") skipped — no loader registered for this locale.`);\n }\n\n return;\n }\n\n this.#catalogs.delete(locale);\n this.#localesCache = null;\n await this.#loadOne(locale);\n }\n\n registerLoader(locale: Locale, loader: Loader): void {\n this.#loaders.set(locale, loader);\n this.#loadersCache = null;\n }\n\n /** Returns the locale keys for which a loader has been registered. */\n get loadableLocales(): Locale[] {\n this.#loadersCache ??= [...this.#loaders.keys()];\n\n return this.#loadersCache;\n }\n\n /* -------------------- BoundI18n surface (delegated to #view) -------------------- */\n\n /**\n * Translates a key with optional interpolation variables.\n * Locale must be loaded first via `load()` or provided via `messages` in config.\n * For a per-call locale override use `withLocale(locale).t(key, vars)`.\n */\n t(key: TranslationKeyParam<T>, vars?: Vars): string {\n return this.#view.t(key, vars);\n }\n\n number(value: number, options?: Intl.NumberFormatOptions): string {\n return this.#view.number(value, options);\n }\n\n date(value: Date | number, options?: Intl.DateTimeFormatOptions): string {\n return this.#view.date(value, options);\n }\n\n list(items: unknown[], type: 'and' | 'or' = 'and'): string {\n return this.#view.list(items, type);\n }\n\n relative(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string {\n return this.#view.relative(value, unit, options);\n }\n\n currency(value: number, currency: string, options?: Omit<Intl.NumberFormatOptions, 'style' | 'currency'>): string {\n return this.#view.currency(value, currency, options);\n }\n\n /**\n * Returns a bound interface that translates in the given locale without\n * changing the active locale on the instance. Useful for SSR and\n * multi-locale rendering in a single pass.\n */\n withLocale(locale: Locale): BoundI18n<T> {\n return this.#view.withLocale(locale);\n }\n\n /**\n * Returns a translator scoped to a key namespace prefix. Reacts to locale changes on the\n * instance. When `T` is a concrete message type, the returned `BoundI18n` is narrowed to\n * the subtree type so `t()` autocomplete works within the scope.\n * Only keys whose values are nested message objects are valid scope targets.\n */\n scope<K extends NamespaceKeys<T>>(ns: K): BoundI18n<T[K] & Messages> {\n return this.#view.scope(ns);\n }\n\n /* -------------------- Subscriptions -------------------- */\n\n /**\n * Executes `fn` while deferring subscriber notifications. A single notification fires\n * after `fn` completes, collapsing any number of `add()` / `replace()` calls made within.\n * Nested `batch()` calls are supported; notification fires when the outermost batch exits.\n * If both a locale change and a catalog update are triggered, `'locale-change'` takes priority.\n *\n * @remarks\n * `batch()` is synchronous. Async operations (e.g. `load()`) started inside `fn` complete\n * after the batch exits and will notify subscribers individually. To batch-load multiple\n * locales and notify once, await `load()` before entering the batch:\n * ```ts\n * await i18n.load('fr', 'de');\n * i18n.batch(() => { i18n.locale = 'fr'; });\n * ```\n */\n batch(fn: () => void): void {\n this.#batchDepth++;\n\n try {\n fn();\n } finally {\n this.#batchDepth--;\n\n if (this.#batchDepth === 0 && this.#pendingNotify !== null) {\n const reason = this.#pendingNotify;\n\n this.#pendingNotify = null;\n this.#notify(reason);\n }\n }\n }\n\n subscribe(listener: (event: LocaleChangeEvent) => void, immediate?: boolean): Unsubscribe {\n this.#subscribers.add(listener);\n\n if (immediate) {\n try {\n listener({ locale: this.#locale, reason: 'locale-change' });\n } catch (err) {\n this.#diagnoseSubscriber(err);\n }\n }\n\n return () => this.#subscribers.delete(listener);\n }\n\n /** Releases all resources held by this instance. */\n dispose(): void {\n this.#disposed = true;\n this.#subscribers.clear();\n this.#catalogs.clear();\n this.#loaders.clear();\n this.#loading.clear();\n this.#chainCache.clear();\n this.#localesCache = null;\n this.#loadersCache = null;\n }\n\n /** Enables `using i18n = createI18n(...)` for deterministic resource release. */\n [Symbol.dispose](): void {\n this.dispose();\n }\n\n /**\n * Awaits any in-flight `load()` calls and then releases all resources.\n * Enables `await using i18n = createI18n(...)` in environments that support `Symbol.asyncDispose`.\n */\n async [Symbol.asyncDispose](): Promise<void> {\n await Promise.allSettled([...this.#loading.values()]);\n this.dispose();\n }\n\n /* -------------------- Private -------------------- */\n\n #diagnoseSubscriber(error: unknown): void {\n if (this.#onDiagnostic) {\n this.#onDiagnostic({ error, kind: 'subscriber-error' });\n } else {\n console.error('[i18nit] Subscriber threw:', error);\n }\n }\n\n #diagnoseLoader(error: unknown, locale: Locale): void {\n if (this.#onDiagnostic) {\n this.#onDiagnostic({ error, kind: 'loader-error', locale });\n } else {\n console.warn('[i18nit] Loader error:', error);\n }\n }\n\n #notify(reason: LocaleChangeReason): void {\n if (this.#batchDepth > 0) {\n // 'locale-change' takes priority over 'catalog-update' if both occur in one batch\n if (this.#pendingNotify !== 'locale-change') this.#pendingNotify = reason;\n\n return;\n }\n\n const event: LocaleChangeEvent = { locale: this.#locale, reason };\n\n for (const listener of this.#subscribers) {\n try {\n listener(event);\n } catch (err) {\n this.#diagnoseSubscriber(err);\n }\n }\n }\n\n #findMessage(key: string, locale: Locale): MessageValue | undefined {\n for (const loc of this.#getLocaleChain(locale)) {\n const messages = this.#catalogs.get(loc);\n\n if (!messages) continue;\n\n const value = resolvePath(messages, key);\n\n if (value !== undefined && isMessageValue(value)) return value;\n }\n\n return undefined;\n }\n\n #getLocaleChain(locale: Locale): Locale[] {\n const cached = this.#chainCache.get(locale);\n\n if (cached) return cached;\n\n const seen = new Set<Locale>();\n const push = (l: Locale) => {\n seen.add(l);\n\n const parts = l.split('-');\n\n for (let i = parts.length - 1; i > 0; i--) {\n seen.add(parts.slice(0, i).join('-'));\n }\n };\n\n push(locale);\n for (const fallback of this.#fallbacks) push(fallback);\n\n const chain = [...seen];\n\n this.#chainCache.set(locale, chain);\n\n return chain;\n }\n\n #translate(key: string, vars: Vars | undefined, locale: Locale): string {\n const message = this.#findMessage(key, locale);\n\n if (message === undefined) return this.#onMissing?.(key, locale) ?? key;\n\n if (typeof message === 'string') return interpolate(message, vars ?? {}, locale, this.#caches);\n\n const v = vars ?? {};\n\n if (import.meta.env?.DEV && v.count === undefined) {\n console.warn(`[i18nit] Key \"${key}\" is a plural message but vars.count is missing. Defaulting to 0.`);\n }\n\n const count = Number(v.count ?? 0);\n const form = count === 0 && message.zero !== undefined ? 'zero' : getPluralForm(this.#caches, locale, count);\n\n return interpolate(message[form] ?? message.other, v, locale, this.#caches);\n }\n\n #loadOne(locale: Locale): Promise<void> {\n if (this.#loading.has(locale)) return this.#loading.get(locale)!;\n\n if (this.#catalogs.has(locale)) return Promise.resolve();\n\n const loader = this.#loaders.get(locale);\n\n if (!loader) return Promise.resolve();\n\n const promise = (async () => {\n try {\n const messages = await loader(locale);\n\n // Use replace() so the loader result is the authoritative catalog for this locale,\n // not merged on top of any pre-seeded static messages.\n if (!this.#disposed) this.replace(locale, messages);\n } catch (error) {\n this.#diagnoseLoader(error, locale);\n throw error;\n } finally {\n this.#loading.delete(locale);\n }\n })();\n\n this.#loading.set(locale, promise);\n\n return promise;\n }\n}\n\nexport function createI18n<T extends Messages = Messages>(config?: I18nOptions<T>): I18n<T> {\n return new I18n<T>(config);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"i18n.js","names":[],"sources":["../src/i18n.ts"],"sourcesContent":["import type {\n BoundI18n,\n I18n,\n I18nOptions,\n Loader,\n Locale,\n LocaleChangeEvent,\n LocaleChangeReason,\n Messages,\n MessageValue,\n NamespaceKeys,\n SwitchMode,\n Unsubscribe,\n Vars,\n} from './types';\n\nimport { BoundedMap, deepMerge, isMessageValue, resolvePath } from './helpers';\nimport { interpolate } from './interpolate';\nimport {\n formatDate,\n formatList,\n formatNumber,\n formatRelative,\n getPluralForm,\n type IntlCaches,\n makeIntlCaches,\n} from './intl';\n\nexport function createI18n<T extends Messages = Messages>(config: I18nOptions<T> = {}): I18n<T> {\n let locale = config.locale ?? 'en';\n const defaultSwitchMode: SwitchMode = config.switchMode ?? 'strict';\n const fallbacks = Array.isArray(config.fallback) ? config.fallback : config.fallback ? [config.fallback] : [];\n const catalogs = new Map<Locale, Messages>();\n const loaders = new Map<Locale, Loader>();\n const loading = new Map<Locale, Promise<void>>();\n const subscribers = new Set<(event: LocaleChangeEvent) => void>();\n const chainCache = new BoundedMap<Locale, Locale[]>(128);\n const caches: IntlCaches = makeIntlCaches();\n\n let localesCache: Locale[] | null = null;\n let loadersCache: Locale[] | null = null;\n let disposed = false;\n let batchDepth = 0;\n let pendingNotify: LocaleChangeReason | null = null;\n\n const onMissing = config.onMissing;\n const onDiagnostic = config.onDiagnostic;\n\n if (config.messages) {\n for (const [loc, messages] of Object.entries(config.messages)) {\n catalogs.set(loc, structuredClone(messages) as Messages);\n }\n }\n\n if (config.loaders) {\n for (const [loc, loader] of Object.entries(config.loaders)) {\n loaders.set(loc, loader);\n }\n }\n\n function diagnoseSubscriber(error: unknown): void {\n if (onDiagnostic) {\n onDiagnostic({ error, kind: 'subscriber-error' });\n } else {\n console.error('[i18nit] Subscriber threw:', error);\n }\n }\n\n function diagnoseLoader(error: unknown, loc: Locale): void {\n if (onDiagnostic) {\n onDiagnostic({ error, kind: 'loader-error', locale: loc });\n } else {\n console.warn('[i18nit] Loader error:', error);\n }\n }\n\n function notify(reason: LocaleChangeReason): void {\n if (batchDepth > 0) {\n if (pendingNotify !== 'locale-change') pendingNotify = reason;\n\n return;\n }\n\n const event: LocaleChangeEvent = { locale, reason };\n\n for (const listener of subscribers) {\n try {\n listener(event);\n } catch (error) {\n diagnoseSubscriber(error);\n }\n }\n }\n\n function getLocaleChain(loc: Locale): Locale[] {\n const cached = chainCache.get(loc);\n\n if (cached) return cached;\n\n const seen = new Set<Locale>();\n\n const push = (value: Locale) => {\n seen.add(value);\n\n const parts = value.split('-');\n\n for (let i = parts.length - 1; i > 0; i--) {\n seen.add(parts.slice(0, i).join('-'));\n }\n };\n\n push(loc);\n for (const fallback of fallbacks) push(fallback);\n\n const chain = [...seen];\n\n chainCache.set(loc, chain);\n\n return chain;\n }\n\n function checkOwn(key: string, loc: Locale): boolean {\n const catalog = catalogs.get(loc);\n\n if (!catalog) return false;\n\n const value = resolvePath(catalog, key);\n\n return value !== undefined && isMessageValue(value);\n }\n\n function findMessage(key: string, loc: Locale): MessageValue | undefined {\n for (const localeInChain of getLocaleChain(loc)) {\n const messages = catalogs.get(localeInChain);\n\n if (!messages) continue;\n\n const value = resolvePath(messages, key);\n\n if (value !== undefined && isMessageValue(value)) return value;\n }\n\n return undefined;\n }\n\n function translate(key: string, vars: Vars | undefined, loc: Locale): string {\n const message = findMessage(key, loc);\n\n if (message === undefined) return onMissing?.(key, loc) ?? key;\n\n if (typeof message === 'string') {\n return interpolate(message, vars ?? {}, loc, caches);\n }\n\n const context = vars ?? {};\n\n if (import.meta.env?.DEV && context.count === undefined) {\n console.warn(`[i18nit] Key \"${key}\" is a plural message but vars.count is missing. Defaulting to 0.`);\n }\n\n const count = Number(context.count ?? 0);\n const form = count === 0 && message.zero !== undefined ? 'zero' : getPluralForm(caches, loc, count);\n\n return interpolate(message[form] ?? message.other, context, loc, caches);\n }\n\n function loadOne(loc: Locale, mode: SwitchMode): Promise<void> {\n if (loading.has(loc)) return loading.get(loc)!;\n\n if (catalogs.has(loc)) return Promise.resolve();\n\n const loader = loaders.get(loc);\n\n if (!loader) {\n if (mode === 'strict') {\n return Promise.reject(new Error(`[i18nit] Missing loader for locale \"${loc}\".`));\n }\n\n return Promise.resolve();\n }\n\n const promise = (async () => {\n try {\n const messages = await loader(loc);\n\n if (!disposed) api.replace(loc, messages);\n } catch (error) {\n diagnoseLoader(error, loc);\n throw error;\n } finally {\n loading.delete(loc);\n }\n })();\n\n loading.set(loc, promise);\n\n return promise;\n }\n\n function createView<U extends Messages = Messages>(fixedLocale: Locale | null, prefix?: string): BoundI18n<U> {\n const activeLocale = (): Locale => fixedLocale ?? locale;\n const keyWithPrefix = (key: string): string => (prefix ? `${prefix}.${key}` : key);\n const t: BoundI18n<U>['t'] = (key: NamespaceKeys<U>, vars?: Record<string, unknown>) =>\n translate(keyWithPrefix(key as string), vars, activeLocale());\n\n const view = {\n currency(\n value: number,\n currency: string,\n options?: Omit<Intl.NumberFormatOptions, 'style' | 'currency'>,\n ): string {\n return formatNumber(caches, value, { ...options, currency, style: 'currency' }, activeLocale());\n },\n date(value: Date | number, options?: Intl.DateTimeFormatOptions): string {\n return formatDate(caches, value, options, activeLocale());\n },\n has(key: string): boolean {\n return findMessage(keyWithPrefix(key), activeLocale()) !== undefined;\n },\n hasOwn(key: string): boolean {\n return checkOwn(keyWithPrefix(key), activeLocale());\n },\n list(items: unknown[], type: 'and' | 'or' = 'and'): string {\n return formatList(caches, items, activeLocale(), type);\n },\n get locale(): Locale {\n return activeLocale();\n },\n number(value: number, options?: Intl.NumberFormatOptions): string {\n return formatNumber(caches, value, options, activeLocale());\n },\n relative(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string {\n return formatRelative(caches, value, unit, options, activeLocale());\n },\n scope<K extends NamespaceKeys<U>>(ns: K): BoundI18n<U[K] & Messages> {\n const nextPrefix = prefix ? `${prefix}.${String(ns)}` : String(ns);\n\n return createView<U[K] & Messages>(fixedLocale, nextPrefix);\n },\n t,\n withLocale(nextLocale: Locale): BoundI18n<U> {\n return createView<U>(nextLocale, prefix);\n },\n } satisfies BoundI18n<U>;\n\n return view;\n }\n\n const rootView = createView<T>(null);\n\n const api = Object.create(rootView) as I18n<T>;\n\n api.add = (loc: Locale, messages: Messages): void => {\n const existing = catalogs.get(loc) ?? {};\n\n catalogs.set(loc, deepMerge(existing, messages));\n localesCache = null;\n\n if (getLocaleChain(locale).includes(loc)) notify('catalog-update');\n };\n\n api.batch = (fn: () => void): void => {\n batchDepth++;\n\n try {\n fn();\n } finally {\n batchDepth--;\n\n if (batchDepth === 0 && pendingNotify !== null) {\n const reason = pendingNotify;\n\n pendingNotify = null;\n notify(reason);\n }\n }\n };\n\n api.dispose = (): void => {\n disposed = true;\n subscribers.clear();\n catalogs.clear();\n loaders.clear();\n loading.clear();\n chainCache.clear();\n localesCache = null;\n loadersCache = null;\n };\n\n api.ensureLocale = async (loc: Locale, mode: SwitchMode = defaultSwitchMode): Promise<void> => {\n await loadOne(loc, mode);\n };\n\n api.hasLocale = (loc: Locale): boolean => catalogs.has(loc);\n api.isReady = (loc: Locale): boolean => catalogs.has(loc);\n\n Object.defineProperties(api, {\n loadableLocales: {\n get(): Locale[] {\n loadersCache ??= [...loaders.keys()];\n\n return loadersCache;\n },\n },\n locales: {\n get(): Locale[] {\n localesCache ??= [...catalogs.keys()];\n\n return localesCache;\n },\n },\n });\n\n api.registerLoader = (loc: Locale, loader: Loader): void => {\n loaders.set(loc, loader);\n loadersCache = null;\n };\n\n api.reload = async (loc: Locale): Promise<void> => {\n if (!loaders.has(loc)) {\n throw new Error(`[i18nit] Cannot reload locale \"${loc}\" without a registered loader.`);\n }\n\n catalogs.delete(loc);\n localesCache = null;\n await loadOne(loc, 'strict');\n };\n\n api.replace = (loc: Locale, messages: Messages): void => {\n catalogs.set(loc, structuredClone(messages));\n localesCache = null;\n\n if (getLocaleChain(locale).includes(loc)) notify('catalog-update');\n };\n\n api.subscribe = (listener: (event: LocaleChangeEvent) => void, immediate?: boolean): Unsubscribe => {\n subscribers.add(listener);\n\n if (immediate) {\n try {\n listener({ locale, reason: 'locale-change' });\n } catch (error) {\n diagnoseSubscriber(error);\n }\n }\n\n return () => subscribers.delete(listener);\n };\n\n api.switchLocale = async (nextLocale: Locale, mode: SwitchMode = defaultSwitchMode): Promise<void> => {\n if (nextLocale === locale) return;\n\n await loadOne(nextLocale, mode);\n locale = nextLocale;\n notify('locale-change');\n };\n\n api[Symbol.asyncDispose] = async (): Promise<void> => {\n await Promise.allSettled([...loading.values()]);\n api.dispose();\n };\n\n api[Symbol.dispose] = (): void => {\n api.dispose();\n };\n\n return api;\n}\n\nexport type { I18n };\n"],"mappings":"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"}
package/dist/i18nit.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class e{#e;#t;#n;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}get locale(){return this.#t??this.#e.getLocale()}#r(e){return this.#n?`${this.#n}.${e}`:e}t(e,t){return this.#e.translate(this.#r(e),t,this.locale)}has(e){return this.#e.findMessage(this.#r(e),this.locale)!==void 0}hasOwn(e){return this.#e.checkOwn(this.#r(e),this.locale)}number(e,t){return this.#e.formatNumber(e,t,this.locale)}date(e,t){return this.#e.formatDate(e,t,this.locale)}list(e,t=`and`){return this.#e.formatList(e,this.locale,t)}relative(e,t,n){return this.#e.formatRelative(e,t,n,this.locale)}currency(e,t,n){return this.#e.formatNumber(e,{...n,currency:t,style:`currency`},this.locale)}scope(t){return new e(this.#e,this.#t,this.#n?`${this.#n}.${String(t)}`:String(t))}withLocale(t){return new e(this.#e,t,this.#n)}};function t(e,t){if(t in e)return e[t];let n=t.match(/[^.[\]]+/gu)??[],r=e;for(let e of n){if(typeof r!=`object`||!r)return;r=r[e]}return r}var n=new Set([`zero`,`one`,`two`,`few`,`many`,`other`]);function r(e){if(typeof e==`string`)return!0;if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=e;if(!(`other`in t))return!1;let r=Object.keys(t);return r.length>n.size?!1:r.every(e=>n.has(e))&&Object.values(t).every(e=>typeof e==`string`)}function i(e,t){let n={...e};for(let[e,a]of Object.entries(t)){let t=n[e];!r(a)&&!r(t)&&typeof t==`object`&&t?n[e]=i(t,a):n[e]=typeof a==`object`&&a?{...a}:a}return n}var a=class extends Map{#e;constructor(e){super(),this.#e=e}set(e,t){return!this.has(e)&&this.size>=this.#e&&this.delete(this.keys().next().value),super.set(e,t)}};function o(){return{dateFormat:new Map,listFormat:new Map,numberFormat:new Map,pluralRules:new Map,relativeTimeFormat:new Map}}function s(e,t,n){let r=e.get(t);return r||(r=n(),e.set(t,r)),r}function c(e,t){return t?`${e}:${JSON.stringify(t,Object.keys(t).sort())}`:e}function l(e,t,n,r){let i=c(r,n);try{return s(e.numberFormat,i,()=>new Intl.NumberFormat(r,n)).format(t)}catch{return String(t)}}function u(e,t,n,r){let i=typeof t==`number`?new Date(t):t,a=c(r,n);try{return s(e.dateFormat,a,()=>new Intl.DateTimeFormat(r,n)).format(i)}catch{return i.toString()}}function d(e,t,n,r,i){let a=c(i,r);try{return s(e.relativeTimeFormat,a,()=>new Intl.RelativeTimeFormat(i,r)).format(t,n)}catch{return String(t)}}function f(e,t,n,r){if(t.length===0)return``;let i=t.map(String),a=r===`and`?`conjunction`:`disjunction`;try{return s(e.listFormat,`${n}:${a}`,()=>new Intl.ListFormat(n,{style:`long`,type:a})).format(i)}catch{return i.length===1?i[0]:i.length===2?`${i[0]} ${r} ${i[1]}`:`${i.slice(0,-1).join(`, `)} ${r} ${i.at(-1)}`}}function p(e,t,n){let r=Math.floor(Math.abs(n));try{return s(e.pluralRules,t,()=>new Intl.PluralRules(t)).select(r)}catch{return r===1?`one`:`other`}}function m(e,t,n,r){return t==null?``:Array.isArray(t)?n===`and`?f(e,t,r,`and`):n===`or`?f(e,t,r,`or`):n===void 0?t.map(String).join(`, `):t.map(String).join(n):typeof t==`number`?l(e,t,void 0,r):String(t)}function h(e,n,r,i){return e.includes(`{`)?e.replace(/\{([\p{ID_Continue}\-.[\]]+)(?:\|([^}]+))?\}/gu,(e,a,o)=>m(i,t(n,a),o,r)):e}var g=class{#e;#t;#n=new Map;#r=new Map;#i=new Map;#a=new Set;#o=new a(128);#s;#c;#l=null;#u=null;#d=!1;#f=0;#p=null;#m;#h=o();#g;constructor({fallback:n,loaders:i,locale:a=`en`,messages:o,onDiagnostic:s,onMissing:c}={}){if(this.#e=a,this.#t=Array.isArray(n)?n:n?[n]:[],this.#s=c,this.#c=s,o)for(let[e,t]of Object.entries(o))this.#n.set(e,structuredClone(t));if(i)for(let[e,t]of Object.entries(i))this.#r.set(e,t);this.#m={checkOwn:(e,n)=>{let i=this.#n.get(n);if(!i)return!1;let a=t(i,e);return a!==void 0&&r(a)},findMessage:(e,t)=>this.#b(e,t),formatDate:(e,t,n)=>u(this.#h,e,t,n),formatList:(e,t,n)=>f(this.#h,e,t,n),formatNumber:(e,t,n)=>l(this.#h,e,t,n),formatRelative:(e,t,n,r)=>d(this.#h,e,t,n,r),getLocale:()=>this.#e,translate:(e,t,n)=>this.#S(e,t,n)},this.#g=new e(this.#m,null)}get locale(){return this.#e}get locales(){return this.#l??=[...this.#n.keys()],this.#l}set locale(e){this.#e!==e&&(this.#e=e,this.#y(`locale-change`))}async setLocale(e){e!==this.#e&&(await this.load(e),this.locale=e)}add(e,t){let n=this.#n.get(e)??{};this.#n.set(e,i(n,t)),this.#l=null,this.#x(this.#e).includes(e)&&this.#y(`catalog-update`)}replace(e,t){this.#n.set(e,structuredClone(t)),this.#l=null,this.#x(this.#e).includes(e)&&this.#y(`catalog-update`)}has(e){return this.#g.has(e)}hasOwn(e){return this.#g.hasOwn(e)}hasLocale(e){return this.#n.has(e)}async load(...e){await Promise.all(e.map(e=>this.#C(e)))}async reload(e){this.#r.has(e)&&(this.#n.delete(e),this.#l=null,await this.#C(e))}registerLoader(e,t){this.#r.set(e,t),this.#u=null}get loadableLocales(){return this.#u??=[...this.#r.keys()],this.#u}t(e,t){return this.#g.t(e,t)}number(e,t){return this.#g.number(e,t)}date(e,t){return this.#g.date(e,t)}list(e,t=`and`){return this.#g.list(e,t)}relative(e,t,n){return this.#g.relative(e,t,n)}currency(e,t,n){return this.#g.currency(e,t,n)}withLocale(e){return this.#g.withLocale(e)}scope(e){return this.#g.scope(e)}batch(e){this.#f++;try{e()}finally{if(this.#f--,this.#f===0&&this.#p!==null){let e=this.#p;this.#p=null,this.#y(e)}}}subscribe(e,t){if(this.#a.add(e),t)try{e({locale:this.#e,reason:`locale-change`})}catch(e){this.#_(e)}return()=>this.#a.delete(e)}dispose(){this.#d=!0,this.#a.clear(),this.#n.clear(),this.#r.clear(),this.#i.clear(),this.#o.clear(),this.#l=null,this.#u=null}[Symbol.dispose](){this.dispose()}async[Symbol.asyncDispose](){await Promise.allSettled([...this.#i.values()]),this.dispose()}#_(e){this.#c?this.#c({error:e,kind:`subscriber-error`}):console.error(`[i18nit] Subscriber threw:`,e)}#v(e,t){this.#c?this.#c({error:e,kind:`loader-error`,locale:t}):console.warn(`[i18nit] Loader error:`,e)}#y(e){if(this.#f>0){this.#p!==`locale-change`&&(this.#p=e);return}let t={locale:this.#e,reason:e};for(let e of this.#a)try{e(t)}catch(e){this.#_(e)}}#b(e,n){for(let i of this.#x(n)){let n=this.#n.get(i);if(!n)continue;let a=t(n,e);if(a!==void 0&&r(a))return a}}#x(e){let t=this.#o.get(e);if(t)return t;let n=new Set,r=e=>{n.add(e);let t=e.split(`-`);for(let e=t.length-1;e>0;e--)n.add(t.slice(0,e).join(`-`))};r(e);for(let e of this.#t)r(e);let i=[...n];return this.#o.set(e,i),i}#S(e,t,n){let r=this.#b(e,n);if(r===void 0)return this.#s?.(e,n)??e;if(typeof r==`string`)return h(r,t??{},n,this.#h);let i=t??{},a=Number(i.count??0);return h(r[a===0&&r.zero!==void 0?`zero`:p(this.#h,n,a)]??r.other,i,n,this.#h)}#C(e){if(this.#i.has(e))return this.#i.get(e);if(this.#n.has(e))return Promise.resolve();let t=this.#r.get(e);if(!t)return Promise.resolve();let n=(async()=>{try{let n=await t(e);this.#d||this.replace(e,n)}catch(t){throw this.#v(t,e),t}finally{this.#i.delete(e)}})();return this.#i.set(e,n),n}};function _(e){return new g(e)}exports.I18n=g,exports.createI18n=_;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e,t){if(Object.hasOwn(e,t))return e[t];let n=t.match(/[^.[\]]+/gu)??[],r=e;for(let e of n){if(typeof r!=`object`||!r||!Object.hasOwn(r,e))return;r=r[e]}return r}var t=new Set([`zero`,`one`,`two`,`few`,`many`,`other`]);function n(e){if(typeof e==`string`)return!0;if(typeof e!=`object`||!e||Array.isArray(e))return!1;let n=e;if(!(`other`in n))return!1;let r=Object.keys(n);return r.length>t.size?!1:r.every(e=>t.has(e))&&Object.values(n).every(e=>typeof e==`string`)}function r(e,t){let i={...e};for(let[e,a]of Object.entries(t)){let t=i[e];!n(a)&&!n(t)&&typeof t==`object`&&t?i[e]=r(t,a):i[e]=typeof a==`object`&&a?{...a}:a}return i}var i=class extends Map{#e;constructor(e){super(),this.#e=e}set(e,t){return!this.has(e)&&this.size>=this.#e&&this.delete(this.keys().next().value),super.set(e,t)}};function a(){return{dateFormat:new Map,listFormat:new Map,numberFormat:new Map,pluralRules:new Map,relativeTimeFormat:new Map}}function o(e,t,n){let r=e.get(t);return r||(r=n(),e.set(t,r)),r}function s(e,t){return t?`${e}:${JSON.stringify(t,Object.keys(t).sort())}`:e}function c(e,t,n,r){let i=s(r,n);try{return o(e.numberFormat,i,()=>new Intl.NumberFormat(r,n)).format(t)}catch{return String(t)}}function l(e,t,n,r){let i=typeof t==`number`?new Date(t):t,a=s(r,n);try{return o(e.dateFormat,a,()=>new Intl.DateTimeFormat(r,n)).format(i)}catch{return i.toString()}}function u(e,t,n,r,i){let a=s(i,r);try{return o(e.relativeTimeFormat,a,()=>new Intl.RelativeTimeFormat(i,r)).format(t,n)}catch{return String(t)}}function d(e,t,n,r){if(t.length===0)return``;let i=t.map(String),a=r===`and`?`conjunction`:`disjunction`;try{return o(e.listFormat,`${n}:${a}`,()=>new Intl.ListFormat(n,{style:`long`,type:a})).format(i)}catch{return i.length===1?i[0]:i.length===2?`${i[0]} ${r} ${i[1]}`:`${i.slice(0,-1).join(`, `)} ${r} ${i.at(-1)}`}}function f(e,t,n){let r=Math.floor(Math.abs(n));try{return o(e.pluralRules,t,()=>new Intl.PluralRules(t)).select(r)}catch{return r===1?`one`:`other`}}function p(e,t,n,r){return t==null?``:Array.isArray(t)?n===`and`?d(e,t,r,`and`):n===`or`?d(e,t,r,`or`):n===void 0?t.map(String).join(`, `):t.map(String).join(n):typeof t==`number`?c(e,t,void 0,r):String(t)}function m(t,n,r,i){return t.includes(`{`)?t.replace(/\{([\p{ID_Continue}\-.[\]]+)(?:\|([^}]+))?\}/gu,(t,a,o)=>p(i,e(n,a),o,r)):t}function h(t={}){let o=t.locale??`en`,s=t.switchMode??`strict`,p=Array.isArray(t.fallback)?t.fallback:t.fallback?[t.fallback]:[],h=new Map,g=new Map,_=new Map,v=new Set,y=new i(128),b=a(),x=null,S=null,C=!1,w=0,T=null,E=t.onMissing,D=t.onDiagnostic;if(t.messages)for(let[e,n]of Object.entries(t.messages))h.set(e,structuredClone(n));if(t.loaders)for(let[e,n]of Object.entries(t.loaders))g.set(e,n);function O(e){D?D({error:e,kind:`subscriber-error`}):console.error(`[i18nit] Subscriber threw:`,e)}function k(e,t){D?D({error:e,kind:`loader-error`,locale:t}):console.warn(`[i18nit] Loader error:`,e)}function A(e){if(w>0){T!==`locale-change`&&(T=e);return}let t={locale:o,reason:e};for(let e of v)try{e(t)}catch(e){O(e)}}function j(e){let t=y.get(e);if(t)return t;let n=new Set,r=e=>{n.add(e);let t=e.split(`-`);for(let e=t.length-1;e>0;e--)n.add(t.slice(0,e).join(`-`))};r(e);for(let e of p)r(e);let i=[...n];return y.set(e,i),i}function M(t,r){let i=h.get(r);if(!i)return!1;let a=e(i,t);return a!==void 0&&n(a)}function N(t,r){for(let i of j(r)){let r=h.get(i);if(!r)continue;let a=e(r,t);if(a!==void 0&&n(a))return a}}function P(e,t,n){let r=N(e,n);if(r===void 0)return E?.(e,n)??e;if(typeof r==`string`)return m(r,t??{},n,b);let i=t??{},a=Number(i.count??0);return m(r[a===0&&r.zero!==void 0?`zero`:f(b,n,a)]??r.other,i,n,b)}function F(e,t){if(_.has(e))return _.get(e);if(h.has(e))return Promise.resolve();let n=g.get(e);if(!n)return t===`strict`?Promise.reject(Error(`[i18nit] Missing loader for locale "${e}".`)):Promise.resolve();let r=(async()=>{try{let t=await n(e);C||R.replace(e,t)}catch(t){throw k(t,e),t}finally{_.delete(e)}})();return _.set(e,r),r}function I(e,t){let n=()=>e??o,r=e=>t?`${t}.${e}`:e;return{currency(e,t,r){return c(b,e,{...r,currency:t,style:`currency`},n())},date(e,t){return l(b,e,t,n())},has(e){return N(r(e),n())!==void 0},hasOwn(e){return M(r(e),n())},list(e,t=`and`){return d(b,e,n(),t)},get locale(){return n()},number(e,t){return c(b,e,t,n())},relative(e,t,r){return u(b,e,t,r,n())},scope(n){return I(e,t?`${t}.${String(n)}`:String(n))},t:(e,t)=>P(r(e),t,n()),withLocale(e){return I(e,t)}}}let L=I(null),R=Object.create(L);return R.add=(e,t)=>{let n=h.get(e)??{};h.set(e,r(n,t)),x=null,j(o).includes(e)&&A(`catalog-update`)},R.batch=e=>{w++;try{e()}finally{if(w--,w===0&&T!==null){let e=T;T=null,A(e)}}},R.dispose=()=>{C=!0,v.clear(),h.clear(),g.clear(),_.clear(),y.clear(),x=null,S=null},R.ensureLocale=async(e,t=s)=>{await F(e,t)},R.hasLocale=e=>h.has(e),R.isReady=e=>h.has(e),Object.defineProperties(R,{loadableLocales:{get(){return S??=[...g.keys()],S}},locales:{get(){return x??=[...h.keys()],x}}}),R.registerLoader=(e,t)=>{g.set(e,t),S=null},R.reload=async e=>{if(!g.has(e))throw Error(`[i18nit] Cannot reload locale "${e}" without a registered loader.`);h.delete(e),x=null,await F(e,`strict`)},R.replace=(e,t)=>{h.set(e,structuredClone(t)),x=null,j(o).includes(e)&&A(`catalog-update`)},R.subscribe=(e,t)=>{if(v.add(e),t)try{e({locale:o,reason:`locale-change`})}catch(e){O(e)}return()=>v.delete(e)},R.switchLocale=async(e,t=s)=>{e!==o&&(await F(e,t),o=e,A(`locale-change`))},R[Symbol.asyncDispose]=async()=>{await Promise.allSettled([..._.values()]),R.dispose()},R[Symbol.dispose]=()=>{R.dispose()},R}exports.createI18n=h;
2
2
  //# sourceMappingURL=i18nit.cjs.map