inline-i18n-multi 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/dist/index.js CHANGED
@@ -10,6 +10,156 @@ function setLocale(locale) {
10
10
  function getLocale() {
11
11
  return currentLocale;
12
12
  }
13
+
14
+ // src/config.ts
15
+ function defaultWarningHandler(warning) {
16
+ const parts = [`[inline-i18n] Missing translation for locale "${warning.requestedLocale}"`];
17
+ if (warning.key) {
18
+ parts.push(`key: "${warning.key}"`);
19
+ }
20
+ parts.push(`Available: [${warning.availableLocales.join(", ")}]`);
21
+ if (warning.fallbackUsed) {
22
+ parts.push(`Using fallback: "${warning.fallbackUsed}"`);
23
+ }
24
+ console.warn(parts.join(" | "));
25
+ }
26
+ function isDevMode() {
27
+ try {
28
+ if (typeof globalThis !== "undefined" && "process" in globalThis) {
29
+ const proc = globalThis.process;
30
+ return proc?.env?.NODE_ENV !== "production";
31
+ }
32
+ return false;
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+ var defaultConfig = {
38
+ defaultLocale: "en",
39
+ fallbackLocale: "en",
40
+ autoParentLocale: true,
41
+ fallbackChain: {},
42
+ warnOnMissing: isDevMode(),
43
+ onMissingTranslation: defaultWarningHandler,
44
+ debugMode: false,
45
+ loader: void 0
46
+ };
47
+ var config = { ...defaultConfig };
48
+ function configure(options) {
49
+ config = { ...config, ...options };
50
+ }
51
+ function getConfig() {
52
+ return {
53
+ ...defaultConfig,
54
+ ...config
55
+ };
56
+ }
57
+ function resetConfig() {
58
+ config = { ...defaultConfig };
59
+ }
60
+ function getParentLocale(locale) {
61
+ const dashIndex = locale.indexOf("-");
62
+ if (dashIndex > 0) {
63
+ return locale.substring(0, dashIndex);
64
+ }
65
+ return void 0;
66
+ }
67
+ function buildFallbackChain(locale) {
68
+ const cfg = getConfig();
69
+ if (cfg.fallbackChain[locale]) {
70
+ return [locale, ...cfg.fallbackChain[locale]];
71
+ }
72
+ const chain = [locale];
73
+ if (cfg.autoParentLocale) {
74
+ let current = locale;
75
+ while (true) {
76
+ const parent = getParentLocale(current);
77
+ if (parent && !chain.includes(parent)) {
78
+ chain.push(parent);
79
+ current = parent;
80
+ } else {
81
+ break;
82
+ }
83
+ }
84
+ }
85
+ const finalFallback = cfg.fallbackLocale;
86
+ if (finalFallback && !chain.includes(finalFallback)) {
87
+ chain.push(finalFallback);
88
+ }
89
+ return chain;
90
+ }
91
+ function emitWarning(warning) {
92
+ const cfg = getConfig();
93
+ if (cfg.warnOnMissing && cfg.onMissingTranslation) {
94
+ cfg.onMissingTranslation(warning);
95
+ }
96
+ }
97
+ function applyDebugFormat(output, debugInfo) {
98
+ const cfg = getConfig();
99
+ if (!cfg.debugMode) {
100
+ return output;
101
+ }
102
+ const options = typeof cfg.debugMode === "object" ? cfg.debugMode : { showMissingPrefix: true, showFallbackPrefix: true };
103
+ if (debugInfo.isMissing && options.showMissingPrefix !== false) {
104
+ const prefix = options.missingPrefixFormat ? options.missingPrefixFormat(debugInfo.requestedLocale, debugInfo.key) : `[MISSING: ${debugInfo.requestedLocale}] `;
105
+ return prefix + output;
106
+ }
107
+ if (debugInfo.isFallback && options.showFallbackPrefix !== false && debugInfo.usedLocale) {
108
+ const prefix = options.fallbackPrefixFormat ? options.fallbackPrefixFormat(debugInfo.requestedLocale, debugInfo.usedLocale, debugInfo.key) : `[${debugInfo.requestedLocale} -> ${debugInfo.usedLocale}] `;
109
+ return prefix + output;
110
+ }
111
+ return output;
112
+ }
113
+ function handleMissingVar(varName, locale) {
114
+ const cfg = getConfig();
115
+ if (cfg.missingVarHandler) {
116
+ return cfg.missingVarHandler(varName, locale);
117
+ }
118
+ return `{${varName}}`;
119
+ }
120
+ var customFormatters = /* @__PURE__ */ new Map();
121
+ var RESERVED_FORMATTER_NAMES = /* @__PURE__ */ new Set([
122
+ "plural",
123
+ "select",
124
+ "selectordinal",
125
+ "number",
126
+ "date",
127
+ "time",
128
+ "relativeTime",
129
+ "list",
130
+ "currency"
131
+ ]);
132
+ function registerFormatter(name, formatter) {
133
+ if (RESERVED_FORMATTER_NAMES.has(name)) {
134
+ throw new Error(`Cannot register formatter "${name}": reserved ICU type name`);
135
+ }
136
+ customFormatters.set(name, formatter);
137
+ }
138
+ function clearFormatters() {
139
+ customFormatters.clear();
140
+ }
141
+ function buildCustomFormatterPattern() {
142
+ if (customFormatters.size === 0) return null;
143
+ const names = [...customFormatters.keys()].map((n) => n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
144
+ return new RegExp(`\\{(\\w+),\\s*(${names})(?:,\\s*(\\w+))?\\}`, "g");
145
+ }
146
+ function preprocessCustomFormatters(template) {
147
+ const replacements = /* @__PURE__ */ new Map();
148
+ const pattern = buildCustomFormatterPattern();
149
+ if (!pattern) return { processed: template, replacements };
150
+ let counter = 0;
151
+ const processed = template.replace(pattern, (_, variable, formatterName, style) => {
152
+ const placeholder = `__CUSTOM_${counter++}__`;
153
+ replacements.set(placeholder, { variable, formatterName, style });
154
+ return `{${placeholder}}`;
155
+ });
156
+ return { processed, replacements };
157
+ }
158
+ function hasCustomFormatter(template) {
159
+ const pattern = buildCustomFormatterPattern();
160
+ if (!pattern) return false;
161
+ return pattern.test(template);
162
+ }
13
163
  var DATE_STYLES = {
14
164
  short: { dateStyle: "short" },
15
165
  medium: { dateStyle: "medium" },
@@ -51,11 +201,11 @@ function toDate(value) {
51
201
  function formatNumberElement(el, vars, locale) {
52
202
  const value = vars[el.value];
53
203
  if (value === void 0) {
54
- return `{${el.value}}`;
204
+ return handleMissingVar(el.value, locale);
55
205
  }
56
206
  const num = typeof value === "number" ? value : Number(value);
57
207
  if (isNaN(num)) {
58
- return `{${el.value}}`;
208
+ return handleMissingVar(el.value, locale);
59
209
  }
60
210
  let options = {};
61
211
  if (el.style) {
@@ -78,7 +228,7 @@ function formatNumberElement(el, vars, locale) {
78
228
  function formatDateElement(el, vars, locale) {
79
229
  const value = vars[el.value];
80
230
  if (value === void 0) {
81
- return `{${el.value}}`;
231
+ return handleMissingVar(el.value, locale);
82
232
  }
83
233
  let options = {};
84
234
  if (el.style) {
@@ -92,13 +242,13 @@ function formatDateElement(el, vars, locale) {
92
242
  const date = toDate(value);
93
243
  return new Intl.DateTimeFormat(locale, options).format(date);
94
244
  } catch {
95
- return `{${el.value}}`;
245
+ return handleMissingVar(el.value, locale);
96
246
  }
97
247
  }
98
248
  function formatTimeElement(el, vars, locale) {
99
249
  const value = vars[el.value];
100
250
  if (value === void 0) {
101
- return `{${el.value}}`;
251
+ return handleMissingVar(el.value, locale);
102
252
  }
103
253
  let options = {};
104
254
  if (el.style) {
@@ -112,7 +262,68 @@ function formatTimeElement(el, vars, locale) {
112
262
  const date = toDate(value);
113
263
  return new Intl.DateTimeFormat(locale, options).format(date);
114
264
  } catch {
115
- return `{${el.value}}`;
265
+ return handleMissingVar(el.value, locale);
266
+ }
267
+ }
268
+ var CURRENCY_PATTERN = /\{(\w+),\s*currency(?:,\s*(\w+))?\}/g;
269
+ function preprocessCurrency(template) {
270
+ const replacements = /* @__PURE__ */ new Map();
271
+ let counter = 0;
272
+ const processed = template.replace(CURRENCY_PATTERN, (_, variable, currencyCode) => {
273
+ const placeholder = `__CURRENCY_${counter++}__`;
274
+ replacements.set(placeholder, { variable, currencyCode: currencyCode || "USD" });
275
+ return `{${placeholder}}`;
276
+ });
277
+ return { processed, replacements };
278
+ }
279
+ function formatCurrencyValue(variableName, currencyCode, vars, locale) {
280
+ const value = vars[variableName];
281
+ if (value === void 0) {
282
+ return handleMissingVar(variableName, locale);
283
+ }
284
+ const num = typeof value === "number" ? value : Number(value);
285
+ if (isNaN(num)) {
286
+ return handleMissingVar(variableName, locale);
287
+ }
288
+ try {
289
+ return new Intl.NumberFormat(locale, {
290
+ style: "currency",
291
+ currency: currencyCode
292
+ }).format(num);
293
+ } catch {
294
+ return String(num);
295
+ }
296
+ }
297
+ var COMPACT_NUMBER_PATTERN = /\{(\w+),\s*number,\s*(compact|compactLong)\}/g;
298
+ function preprocessCompactNumber(template) {
299
+ const replacements = /* @__PURE__ */ new Map();
300
+ let counter = 0;
301
+ const processed = template.replace(COMPACT_NUMBER_PATTERN, (_, variable, style) => {
302
+ const placeholder = `__COMPACT_${counter++}__`;
303
+ replacements.set(placeholder, {
304
+ variable,
305
+ display: style === "compactLong" ? "long" : "short"
306
+ });
307
+ return `{${placeholder}}`;
308
+ });
309
+ return { processed, replacements };
310
+ }
311
+ function formatCompactNumber(variableName, display, vars, locale) {
312
+ const value = vars[variableName];
313
+ if (value === void 0) {
314
+ return handleMissingVar(variableName, locale);
315
+ }
316
+ const num = typeof value === "number" ? value : Number(value);
317
+ if (isNaN(num)) {
318
+ return handleMissingVar(variableName, locale);
319
+ }
320
+ try {
321
+ return new Intl.NumberFormat(locale, {
322
+ notation: "compact",
323
+ compactDisplay: display
324
+ }).format(num);
325
+ } catch {
326
+ return String(num);
116
327
  }
117
328
  }
118
329
  function getRelativeTimeUnit(date, now = /* @__PURE__ */ new Date()) {
@@ -146,7 +357,7 @@ function preprocessRelativeTime(template) {
146
357
  function formatRelativeTimeValue(variableName, style, vars, locale) {
147
358
  const value = vars[variableName];
148
359
  if (value === void 0) {
149
- return `{${variableName}}`;
360
+ return handleMissingVar(variableName, locale);
150
361
  }
151
362
  try {
152
363
  const date = toDate(value);
@@ -154,7 +365,7 @@ function formatRelativeTimeValue(variableName, style, vars, locale) {
154
365
  const options = style && RELATIVE_TIME_STYLES[style] || RELATIVE_TIME_STYLES.long;
155
366
  return new Intl.RelativeTimeFormat(locale, options).format(relValue, unit);
156
367
  } catch {
157
- return `{${variableName}}`;
368
+ return handleMissingVar(variableName, locale);
158
369
  }
159
370
  }
160
371
  var LIST_PATTERN = /\{(\w+),\s*list(?:,\s*(\w+))?(?:,\s*(\w+))?\}/g;
@@ -181,7 +392,7 @@ function preprocessList(template) {
181
392
  function formatListValue(variableName, type, style, vars, locale) {
182
393
  const value = vars[variableName];
183
394
  if (value === void 0 || !Array.isArray(value)) {
184
- return `{${variableName}}`;
395
+ return handleMissingVar(variableName, locale);
185
396
  }
186
397
  const options = {
187
398
  type: type || "conjunction",
@@ -194,10 +405,32 @@ function formatListValue(variableName, type, style, vars, locale) {
194
405
  }
195
406
  }
196
407
  function interpolateICU(template, vars, locale) {
197
- const { processed: afterRelTime, replacements: relTimeReplacements } = preprocessRelativeTime(template);
408
+ const { processed: afterCustom, replacements: customReplacements } = preprocessCustomFormatters(template);
409
+ const { processed: afterCurrency, replacements: currencyReplacements } = preprocessCurrency(afterCustom);
410
+ const { processed: afterCompact, replacements: compactReplacements } = preprocessCompactNumber(afterCurrency);
411
+ const { processed: afterRelTime, replacements: relTimeReplacements } = preprocessRelativeTime(afterCompact);
198
412
  const { processed: afterList, replacements: listReplacements } = preprocessList(afterRelTime);
199
413
  const ast = icuMessageformatParser.parse(afterList);
200
414
  let result = formatElements(ast, vars, locale, null);
415
+ for (const [placeholder, { variable, formatterName, style }] of customReplacements) {
416
+ const value = vars[variable];
417
+ let formatted;
418
+ if (value === void 0) {
419
+ formatted = handleMissingVar(variable, locale);
420
+ } else {
421
+ const formatter = customFormatters.get(formatterName);
422
+ formatted = formatter ? formatter(value, locale, style) : String(value);
423
+ }
424
+ result = result.replace(`{${placeholder}}`, formatted);
425
+ }
426
+ for (const [placeholder, { variable, currencyCode }] of currencyReplacements) {
427
+ const formatted = formatCurrencyValue(variable, currencyCode, vars, locale);
428
+ result = result.replace(`{${placeholder}}`, formatted);
429
+ }
430
+ for (const [placeholder, { variable, display }] of compactReplacements) {
431
+ const formatted = formatCompactNumber(variable, display, vars, locale);
432
+ result = result.replace(`{${placeholder}}`, formatted);
433
+ }
201
434
  for (const [placeholder, { variable, style }] of relTimeReplacements) {
202
435
  const formatted = formatRelativeTimeValue(variable, style, vars, locale);
203
436
  result = result.replace(`{${placeholder}}`, formatted);
@@ -217,7 +450,11 @@ function formatElement(el, vars, locale, currentPluralValue) {
217
450
  }
218
451
  if (icuMessageformatParser.isArgumentElement(el)) {
219
452
  const value = vars[el.value];
220
- return value !== void 0 ? String(value) : `{${el.value}}`;
453
+ if (value !== void 0) return String(value);
454
+ if (el.value.startsWith("__") && el.value.endsWith("__")) {
455
+ return `{${el.value}}`;
456
+ }
457
+ return handleMissingVar(el.value, locale);
221
458
  }
222
459
  if (icuMessageformatParser.isPoundElement(el)) {
223
460
  return currentPluralValue !== null ? String(currentPluralValue) : "#";
@@ -242,7 +479,7 @@ function formatElement(el, vars, locale, currentPluralValue) {
242
479
  function formatPlural(el, vars, locale) {
243
480
  const value = vars[el.value];
244
481
  if (typeof value !== "number") {
245
- return `{${el.value}}`;
482
+ return handleMissingVar(el.value, locale);
246
483
  }
247
484
  const adjustedValue = value - el.offset;
248
485
  const pluralRules = new Intl.PluralRules(locale, { type: el.pluralType });
@@ -257,7 +494,7 @@ function formatPlural(el, vars, locale) {
257
494
  if (el.options.other) {
258
495
  return formatElements(el.options.other.value, vars, locale, adjustedValue);
259
496
  }
260
- return `{${el.value}}`;
497
+ return handleMissingVar(el.value, locale);
261
498
  }
262
499
  function formatSelect(el, vars, locale) {
263
500
  const value = vars[el.value];
@@ -268,9 +505,9 @@ function formatSelect(el, vars, locale) {
268
505
  if (el.options.other) {
269
506
  return formatElements(el.options.other.value, vars, locale, null);
270
507
  }
271
- return `{${el.value}}`;
508
+ return handleMissingVar(el.value, locale);
272
509
  }
273
- var ICU_PATTERN = /\{[^}]+,\s*(plural|select|selectordinal|number|date|time|relativeTime|list)\s*[,}]/;
510
+ var ICU_PATTERN = /\{[^}]+,\s*(plural|select|selectordinal|number|date|time|relativeTime|list|currency)\s*[,}]/;
274
511
  function hasICUPattern(template) {
275
512
  return ICU_PATTERN.test(template);
276
513
  }
@@ -278,113 +515,35 @@ function hasICUPattern(template) {
278
515
  // src/interpolation.ts
279
516
  var VARIABLE_PATTERN = /\{(\w+)\}/g;
280
517
  function interpolate(template, vars, locale) {
281
- if (!vars) return template;
282
- if (hasICUPattern(template)) {
283
- return interpolateICU(template, vars, locale || "en");
284
- }
285
- return template.replace(VARIABLE_PATTERN, (_, key) => {
286
- const value = vars[key];
287
- return value !== void 0 ? String(value) : `{${key}}`;
288
- });
289
- }
290
-
291
- // src/config.ts
292
- function defaultWarningHandler(warning) {
293
- const parts = [`[inline-i18n] Missing translation for locale "${warning.requestedLocale}"`];
294
- if (warning.key) {
295
- parts.push(`key: "${warning.key}"`);
296
- }
297
- parts.push(`Available: [${warning.availableLocales.join(", ")}]`);
298
- if (warning.fallbackUsed) {
299
- parts.push(`Using fallback: "${warning.fallbackUsed}"`);
300
- }
301
- console.warn(parts.join(" | "));
302
- }
303
- function isDevMode() {
304
- try {
305
- if (typeof globalThis !== "undefined" && "process" in globalThis) {
306
- const proc = globalThis.process;
307
- return proc?.env?.NODE_ENV !== "production";
308
- }
309
- return false;
310
- } catch {
311
- return false;
312
- }
313
- }
314
- var defaultConfig = {
315
- defaultLocale: "en",
316
- fallbackLocale: "en",
317
- autoParentLocale: true,
318
- fallbackChain: {},
319
- warnOnMissing: isDevMode(),
320
- onMissingTranslation: defaultWarningHandler,
321
- debugMode: false
322
- };
323
- var config = { ...defaultConfig };
324
- function configure(options) {
325
- config = { ...config, ...options };
326
- }
327
- function getConfig() {
328
- return {
329
- ...defaultConfig,
330
- ...config
331
- };
332
- }
333
- function resetConfig() {
334
- config = { ...defaultConfig };
335
- }
336
- function getParentLocale(locale) {
337
- const dashIndex = locale.indexOf("-");
338
- if (dashIndex > 0) {
339
- return locale.substring(0, dashIndex);
340
- }
341
- return void 0;
342
- }
343
- function buildFallbackChain(locale) {
344
- const cfg = getConfig();
345
- if (cfg.fallbackChain[locale]) {
346
- return [locale, ...cfg.fallbackChain[locale]];
347
- }
348
- const chain = [locale];
349
- if (cfg.autoParentLocale) {
350
- let current = locale;
351
- while (true) {
352
- const parent = getParentLocale(current);
353
- if (parent && !chain.includes(parent)) {
354
- chain.push(parent);
355
- current = parent;
356
- } else {
357
- break;
518
+ const resolvedLocale = locale || "en";
519
+ if (hasICUPattern(template) || hasCustomFormatter(template)) {
520
+ if (!vars) {
521
+ const cfg = getConfig();
522
+ if (cfg.missingVarHandler) {
523
+ return interpolateICU(template, {}, resolvedLocale);
358
524
  }
525
+ return template;
359
526
  }
527
+ return interpolateICU(template, vars, resolvedLocale);
360
528
  }
361
- const finalFallback = cfg.fallbackLocale;
362
- if (finalFallback && !chain.includes(finalFallback)) {
363
- chain.push(finalFallback);
364
- }
365
- return chain;
366
- }
367
- function emitWarning(warning) {
368
- const cfg = getConfig();
369
- if (cfg.warnOnMissing && cfg.onMissingTranslation) {
370
- cfg.onMissingTranslation(warning);
371
- }
372
- }
373
- function applyDebugFormat(output, debugInfo) {
374
- const cfg = getConfig();
375
- if (!cfg.debugMode) {
376
- return output;
377
- }
378
- const options = typeof cfg.debugMode === "object" ? cfg.debugMode : { showMissingPrefix: true, showFallbackPrefix: true };
379
- if (debugInfo.isMissing && options.showMissingPrefix !== false) {
380
- const prefix = options.missingPrefixFormat ? options.missingPrefixFormat(debugInfo.requestedLocale, debugInfo.key) : `[MISSING: ${debugInfo.requestedLocale}] `;
381
- return prefix + output;
382
- }
383
- if (debugInfo.isFallback && options.showFallbackPrefix !== false && debugInfo.usedLocale) {
384
- const prefix = options.fallbackPrefixFormat ? options.fallbackPrefixFormat(debugInfo.requestedLocale, debugInfo.usedLocale, debugInfo.key) : `[${debugInfo.requestedLocale} -> ${debugInfo.usedLocale}] `;
385
- return prefix + output;
529
+ if (!vars) {
530
+ const cfg = getConfig();
531
+ if (cfg.missingVarHandler) {
532
+ return template.replace(VARIABLE_PATTERN, (_, key) => {
533
+ return cfg.missingVarHandler(key, resolvedLocale);
534
+ });
535
+ }
536
+ return template;
386
537
  }
387
- return output;
538
+ return template.replace(VARIABLE_PATTERN, (_, key) => {
539
+ const value = vars[key];
540
+ if (value !== void 0) return String(value);
541
+ const cfg = getConfig();
542
+ if (cfg.missingVarHandler) {
543
+ return cfg.missingVarHandler(key, resolvedLocale);
544
+ }
545
+ return `{${key}}`;
546
+ });
388
547
  }
389
548
 
390
549
  // src/translate.ts
@@ -521,6 +680,11 @@ var zh_es = createPair("zh", "es");
521
680
  var DEFAULT_NAMESPACE = "default";
522
681
  var NAMESPACE_SEPARATOR = ":";
523
682
  var namespacedDictionaries = {};
683
+ var loadingState = {};
684
+ var loadingPromises = /* @__PURE__ */ new Map();
685
+ function getLoadingKey(locale, namespace) {
686
+ return `${namespace}:${locale}`;
687
+ }
524
688
  function parseKey(fullKey) {
525
689
  const separatorIndex = fullKey.indexOf(NAMESPACE_SEPARATOR);
526
690
  if (separatorIndex > 0) {
@@ -557,10 +721,50 @@ function loadDictionary(locale, dict, namespace) {
557
721
  function clearDictionaries(namespace) {
558
722
  if (namespace) {
559
723
  delete namespacedDictionaries[namespace];
724
+ for (const key of Object.keys(loadingState)) {
725
+ if (key.startsWith(`${namespace}:`)) {
726
+ delete loadingState[key];
727
+ loadingPromises.delete(key);
728
+ }
729
+ }
560
730
  } else {
561
731
  namespacedDictionaries = {};
732
+ loadingState = {};
733
+ loadingPromises.clear();
562
734
  }
563
735
  }
736
+ async function loadAsync(locale, namespace) {
737
+ const ns = namespace || DEFAULT_NAMESPACE;
738
+ const cfg = getConfig();
739
+ if (!cfg.loader) {
740
+ throw new Error("No loader configured. Call configure({ loader: ... }) first.");
741
+ }
742
+ const key = getLoadingKey(locale, ns);
743
+ if (loadingState[key] === "loaded") return;
744
+ if (loadingPromises.has(key)) {
745
+ return loadingPromises.get(key);
746
+ }
747
+ const promise = (async () => {
748
+ loadingState[key] = "loading";
749
+ try {
750
+ const dict = await cfg.loader(locale, ns);
751
+ loadDictionary(locale, dict, ns);
752
+ loadingState[key] = "loaded";
753
+ } catch (error) {
754
+ loadingState[key] = "error";
755
+ throw error;
756
+ } finally {
757
+ loadingPromises.delete(key);
758
+ }
759
+ })();
760
+ loadingPromises.set(key, promise);
761
+ return promise;
762
+ }
763
+ function isLoaded(locale, namespace) {
764
+ const ns = namespace || DEFAULT_NAMESPACE;
765
+ const key = getLoadingKey(locale, ns);
766
+ return loadingState[key] === "loaded";
767
+ }
564
768
  function getNestedValue(dict, key) {
565
769
  const parts = key.split(".");
566
770
  let current = dict;
@@ -670,9 +874,134 @@ function getLoadedNamespaces() {
670
874
  return Object.keys(namespacedDictionaries);
671
875
  }
672
876
 
877
+ // src/detect.ts
878
+ function matchLocale(candidate, supportedLocales) {
879
+ const normalized = candidate.trim().toLowerCase();
880
+ const exact = supportedLocales.find((l) => l.toLowerCase() === normalized);
881
+ if (exact) return exact;
882
+ const parent = getParentLocale(candidate);
883
+ if (parent) {
884
+ const parentMatch = supportedLocales.find((l) => l.toLowerCase() === parent.toLowerCase());
885
+ if (parentMatch) return parentMatch;
886
+ }
887
+ return void 0;
888
+ }
889
+ function detectFromNavigator(supportedLocales) {
890
+ if (typeof globalThis === "undefined") return void 0;
891
+ const nav = globalThis.navigator;
892
+ if (!nav) return void 0;
893
+ const languages = nav.languages || (nav.language ? [nav.language] : []);
894
+ for (const lang of languages) {
895
+ const match = matchLocale(lang, supportedLocales);
896
+ if (match) return match;
897
+ }
898
+ return void 0;
899
+ }
900
+ function detectFromCookie(supportedLocales, cookieName) {
901
+ if (typeof document === "undefined") return void 0;
902
+ const cookies = document.cookie.split(";");
903
+ for (const cookie of cookies) {
904
+ const [key, value] = cookie.split("=").map((s) => s.trim());
905
+ if (key === cookieName && value) {
906
+ return matchLocale(value, supportedLocales);
907
+ }
908
+ }
909
+ return void 0;
910
+ }
911
+ function detectFromUrl(supportedLocales) {
912
+ if (typeof location === "undefined") return void 0;
913
+ const pathname = location.pathname;
914
+ const match = pathname.match(/^\/([a-zA-Z]{2}(?:-[a-zA-Z]{2,})?)(?:\/|$)/);
915
+ if (match?.[1]) {
916
+ return matchLocale(match[1], supportedLocales);
917
+ }
918
+ return void 0;
919
+ }
920
+ function detectFromHeader(supportedLocales, headerValue) {
921
+ if (!headerValue) return void 0;
922
+ const entries = headerValue.split(",").map((part) => {
923
+ const parts = part.trim().split(";");
924
+ const lang = parts[0]?.trim() || "";
925
+ const qStr = parts[1];
926
+ const q = qStr ? parseFloat(qStr.replace(/q=/, "")) : 1;
927
+ return { lang, q };
928
+ }).sort((a, b) => b.q - a.q);
929
+ for (const { lang } of entries) {
930
+ const match = matchLocale(lang, supportedLocales);
931
+ if (match) return match;
932
+ }
933
+ return void 0;
934
+ }
935
+ function detectLocale(options) {
936
+ const {
937
+ supportedLocales,
938
+ defaultLocale,
939
+ sources = ["navigator"],
940
+ cookieName = "NEXT_LOCALE",
941
+ headerValue
942
+ } = options;
943
+ for (const source of sources) {
944
+ let detected;
945
+ switch (source) {
946
+ case "navigator":
947
+ detected = detectFromNavigator(supportedLocales);
948
+ break;
949
+ case "cookie":
950
+ detected = detectFromCookie(supportedLocales, cookieName);
951
+ break;
952
+ case "url":
953
+ detected = detectFromUrl(supportedLocales);
954
+ break;
955
+ case "header":
956
+ detected = detectFromHeader(supportedLocales, headerValue);
957
+ break;
958
+ }
959
+ if (detected) return detected;
960
+ }
961
+ return defaultLocale;
962
+ }
963
+
964
+ // src/richtext.ts
965
+ function escapeRegExp(str) {
966
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
967
+ }
968
+ function parseRichText(template, componentNames) {
969
+ if (componentNames.length === 0) {
970
+ return [{ type: "text", content: template }];
971
+ }
972
+ const segments = [];
973
+ const namesPattern = componentNames.map(escapeRegExp).join("|");
974
+ const regex = new RegExp(`<(${namesPattern})>(.*?)</\\1>`, "gs");
975
+ let lastIndex = 0;
976
+ let match;
977
+ while ((match = regex.exec(template)) !== null) {
978
+ if (match.index > lastIndex) {
979
+ segments.push({
980
+ type: "text",
981
+ content: template.slice(lastIndex, match.index)
982
+ });
983
+ }
984
+ segments.push({
985
+ type: "component",
986
+ content: match[2] ?? "",
987
+ componentName: match[1]
988
+ });
989
+ lastIndex = match.index + match[0].length;
990
+ }
991
+ if (lastIndex < template.length) {
992
+ segments.push({
993
+ type: "text",
994
+ content: template.slice(lastIndex)
995
+ });
996
+ }
997
+ return segments;
998
+ }
999
+
673
1000
  exports.__i18n_lookup = __i18n_lookup;
674
1001
  exports.clearDictionaries = clearDictionaries;
1002
+ exports.clearFormatters = clearFormatters;
675
1003
  exports.configure = configure;
1004
+ exports.detectLocale = detectLocale;
676
1005
  exports.en_de = en_de;
677
1006
  exports.en_es = en_es;
678
1007
  exports.en_fr = en_fr;
@@ -684,6 +1013,7 @@ exports.getLoadedLocales = getLoadedLocales;
684
1013
  exports.getLoadedNamespaces = getLoadedNamespaces;
685
1014
  exports.getLocale = getLocale;
686
1015
  exports.hasTranslation = hasTranslation;
1016
+ exports.isLoaded = isLoaded;
687
1017
  exports.it = it;
688
1018
  exports.it_de = it_de;
689
1019
  exports.it_es = it_es;
@@ -692,8 +1022,11 @@ exports.it_ja = it_ja;
692
1022
  exports.it_zh = it_zh;
693
1023
  exports.ja_es = ja_es;
694
1024
  exports.ja_zh = ja_zh;
1025
+ exports.loadAsync = loadAsync;
695
1026
  exports.loadDictionaries = loadDictionaries;
696
1027
  exports.loadDictionary = loadDictionary;
1028
+ exports.parseRichText = parseRichText;
1029
+ exports.registerFormatter = registerFormatter;
697
1030
  exports.resetConfig = resetConfig;
698
1031
  exports.setLocale = setLocale;
699
1032
  exports.t = t;