inline-i18n-multi 0.5.0 → 0.7.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
@@ -2,14 +2,232 @@
2
2
 
3
3
  var icuMessageformatParser = require('@formatjs/icu-messageformat-parser');
4
4
 
5
+ // src/config.ts
6
+ function defaultWarningHandler(warning) {
7
+ const parts = [`[inline-i18n] Missing translation for locale "${warning.requestedLocale}"`];
8
+ if (warning.key) {
9
+ parts.push(`key: "${warning.key}"`);
10
+ }
11
+ parts.push(`Available: [${warning.availableLocales.join(", ")}]`);
12
+ if (warning.fallbackUsed) {
13
+ parts.push(`Using fallback: "${warning.fallbackUsed}"`);
14
+ }
15
+ console.warn(parts.join(" | "));
16
+ }
17
+ function isDevMode() {
18
+ try {
19
+ if (typeof globalThis !== "undefined" && "process" in globalThis) {
20
+ const proc = globalThis.process;
21
+ return proc?.env?.NODE_ENV !== "production";
22
+ }
23
+ return false;
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+ var defaultConfig = {
29
+ defaultLocale: "en",
30
+ fallbackLocale: "en",
31
+ autoParentLocale: true,
32
+ fallbackChain: {},
33
+ warnOnMissing: isDevMode(),
34
+ onMissingTranslation: defaultWarningHandler,
35
+ debugMode: false,
36
+ loader: void 0,
37
+ icuCacheSize: 500
38
+ };
39
+ var config = { ...defaultConfig };
40
+ function configure(options) {
41
+ config = { ...config, ...options };
42
+ }
43
+ function getConfig() {
44
+ return {
45
+ ...defaultConfig,
46
+ ...config
47
+ };
48
+ }
49
+ function resetConfig() {
50
+ config = { ...defaultConfig };
51
+ }
52
+ function getParentLocale(locale) {
53
+ const dashIndex = locale.indexOf("-");
54
+ if (dashIndex > 0) {
55
+ return locale.substring(0, dashIndex);
56
+ }
57
+ return void 0;
58
+ }
59
+ function buildFallbackChain(locale) {
60
+ const cfg = getConfig();
61
+ if (cfg.fallbackChain[locale]) {
62
+ return [locale, ...cfg.fallbackChain[locale]];
63
+ }
64
+ const chain = [locale];
65
+ if (cfg.autoParentLocale) {
66
+ let current = locale;
67
+ while (true) {
68
+ const parent = getParentLocale(current);
69
+ if (parent && !chain.includes(parent)) {
70
+ chain.push(parent);
71
+ current = parent;
72
+ } else {
73
+ break;
74
+ }
75
+ }
76
+ }
77
+ const finalFallback = cfg.fallbackLocale;
78
+ if (finalFallback && !chain.includes(finalFallback)) {
79
+ chain.push(finalFallback);
80
+ }
81
+ return chain;
82
+ }
83
+ function emitWarning(warning) {
84
+ const cfg = getConfig();
85
+ if (cfg.warnOnMissing && cfg.onMissingTranslation) {
86
+ cfg.onMissingTranslation(warning);
87
+ }
88
+ }
89
+ function applyDebugFormat(output, debugInfo) {
90
+ const cfg = getConfig();
91
+ if (!cfg.debugMode) {
92
+ return output;
93
+ }
94
+ const options = typeof cfg.debugMode === "object" ? cfg.debugMode : { showMissingPrefix: true, showFallbackPrefix: true };
95
+ if (debugInfo.isMissing && options.showMissingPrefix !== false) {
96
+ const prefix = options.missingPrefixFormat ? options.missingPrefixFormat(debugInfo.requestedLocale, debugInfo.key) : `[MISSING: ${debugInfo.requestedLocale}] `;
97
+ return prefix + output;
98
+ }
99
+ if (debugInfo.isFallback && options.showFallbackPrefix !== false && debugInfo.usedLocale) {
100
+ const prefix = options.fallbackPrefixFormat ? options.fallbackPrefixFormat(debugInfo.requestedLocale, debugInfo.usedLocale, debugInfo.key) : `[${debugInfo.requestedLocale} -> ${debugInfo.usedLocale}] `;
101
+ return prefix + output;
102
+ }
103
+ return output;
104
+ }
105
+
5
106
  // src/context.ts
6
107
  var currentLocale = "en";
108
+ function setCookie(name, value, days) {
109
+ if (typeof document === "undefined") return;
110
+ const expires = new Date(Date.now() + days * 864e5).toUTCString();
111
+ document.cookie = `${name}=${value}; expires=${expires}; path=/; SameSite=Lax`;
112
+ }
113
+ function getCookie(name) {
114
+ if (typeof document === "undefined") return void 0;
115
+ const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));
116
+ return match?.[1];
117
+ }
118
+ function persistLocaleToStorage(locale) {
119
+ const cfg = getConfig();
120
+ if (!cfg.persistLocale) return;
121
+ const { storage, key = "LOCALE", expires = 365 } = cfg.persistLocale;
122
+ if (storage === "cookie") {
123
+ setCookie(key, locale, expires);
124
+ } else if (storage === "localStorage") {
125
+ if (typeof localStorage !== "undefined") {
126
+ try {
127
+ localStorage.setItem(key, locale);
128
+ } catch {
129
+ }
130
+ }
131
+ }
132
+ }
7
133
  function setLocale(locale) {
8
134
  currentLocale = locale;
135
+ persistLocaleToStorage(locale);
9
136
  }
10
137
  function getLocale() {
11
138
  return currentLocale;
12
139
  }
140
+ function restoreLocale() {
141
+ const cfg = getConfig();
142
+ if (!cfg.persistLocale) return void 0;
143
+ const { storage, key = "LOCALE" } = cfg.persistLocale;
144
+ let stored;
145
+ if (storage === "cookie") {
146
+ stored = getCookie(key);
147
+ } else if (storage === "localStorage") {
148
+ if (typeof localStorage !== "undefined") {
149
+ try {
150
+ stored = localStorage.getItem(key) ?? void 0;
151
+ } catch {
152
+ }
153
+ }
154
+ }
155
+ if (stored) {
156
+ currentLocale = stored;
157
+ return stored;
158
+ }
159
+ return void 0;
160
+ }
161
+ var icuCache = /* @__PURE__ */ new Map();
162
+ function clearICUCache() {
163
+ icuCache.clear();
164
+ }
165
+ function cachedParse(template) {
166
+ const cached = icuCache.get(template);
167
+ if (cached) return cached;
168
+ const ast = icuMessageformatParser.parse(template);
169
+ const cfg = getConfig();
170
+ if (cfg.icuCacheSize > 0) {
171
+ if (icuCache.size >= cfg.icuCacheSize) {
172
+ const firstKey = icuCache.keys().next().value;
173
+ if (firstKey !== void 0) {
174
+ icuCache.delete(firstKey);
175
+ }
176
+ }
177
+ icuCache.set(template, ast);
178
+ }
179
+ return ast;
180
+ }
181
+ function handleMissingVar(varName, locale) {
182
+ const cfg = getConfig();
183
+ if (cfg.missingVarHandler) {
184
+ return cfg.missingVarHandler(varName, locale);
185
+ }
186
+ return `{${varName}}`;
187
+ }
188
+ var customFormatters = /* @__PURE__ */ new Map();
189
+ var RESERVED_FORMATTER_NAMES = /* @__PURE__ */ new Set([
190
+ "plural",
191
+ "select",
192
+ "selectordinal",
193
+ "number",
194
+ "date",
195
+ "time",
196
+ "relativeTime",
197
+ "list",
198
+ "currency"
199
+ ]);
200
+ function registerFormatter(name, formatter) {
201
+ if (RESERVED_FORMATTER_NAMES.has(name)) {
202
+ throw new Error(`Cannot register formatter "${name}": reserved ICU type name`);
203
+ }
204
+ customFormatters.set(name, formatter);
205
+ }
206
+ function clearFormatters() {
207
+ customFormatters.clear();
208
+ }
209
+ function buildCustomFormatterPattern() {
210
+ if (customFormatters.size === 0) return null;
211
+ const names = [...customFormatters.keys()].map((n) => n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
212
+ return new RegExp(`\\{(\\w+),\\s*(${names})(?:,\\s*(\\w+))?\\}`, "g");
213
+ }
214
+ function preprocessCustomFormatters(template) {
215
+ const replacements = /* @__PURE__ */ new Map();
216
+ const pattern = buildCustomFormatterPattern();
217
+ if (!pattern) return { processed: template, replacements };
218
+ let counter = 0;
219
+ const processed = template.replace(pattern, (_, variable, formatterName, style) => {
220
+ const placeholder = `__CUSTOM_${counter++}__`;
221
+ replacements.set(placeholder, { variable, formatterName, style });
222
+ return `{${placeholder}}`;
223
+ });
224
+ return { processed, replacements };
225
+ }
226
+ function hasCustomFormatter(template) {
227
+ const pattern = buildCustomFormatterPattern();
228
+ if (!pattern) return false;
229
+ return pattern.test(template);
230
+ }
13
231
  var DATE_STYLES = {
14
232
  short: { dateStyle: "short" },
15
233
  medium: { dateStyle: "medium" },
@@ -51,11 +269,11 @@ function toDate(value) {
51
269
  function formatNumberElement(el, vars, locale) {
52
270
  const value = vars[el.value];
53
271
  if (value === void 0) {
54
- return `{${el.value}}`;
272
+ return handleMissingVar(el.value, locale);
55
273
  }
56
274
  const num = typeof value === "number" ? value : Number(value);
57
275
  if (isNaN(num)) {
58
- return `{${el.value}}`;
276
+ return handleMissingVar(el.value, locale);
59
277
  }
60
278
  let options = {};
61
279
  if (el.style) {
@@ -78,7 +296,7 @@ function formatNumberElement(el, vars, locale) {
78
296
  function formatDateElement(el, vars, locale) {
79
297
  const value = vars[el.value];
80
298
  if (value === void 0) {
81
- return `{${el.value}}`;
299
+ return handleMissingVar(el.value, locale);
82
300
  }
83
301
  let options = {};
84
302
  if (el.style) {
@@ -92,13 +310,13 @@ function formatDateElement(el, vars, locale) {
92
310
  const date = toDate(value);
93
311
  return new Intl.DateTimeFormat(locale, options).format(date);
94
312
  } catch {
95
- return `{${el.value}}`;
313
+ return handleMissingVar(el.value, locale);
96
314
  }
97
315
  }
98
316
  function formatTimeElement(el, vars, locale) {
99
317
  const value = vars[el.value];
100
318
  if (value === void 0) {
101
- return `{${el.value}}`;
319
+ return handleMissingVar(el.value, locale);
102
320
  }
103
321
  let options = {};
104
322
  if (el.style) {
@@ -112,7 +330,7 @@ function formatTimeElement(el, vars, locale) {
112
330
  const date = toDate(value);
113
331
  return new Intl.DateTimeFormat(locale, options).format(date);
114
332
  } catch {
115
- return `{${el.value}}`;
333
+ return handleMissingVar(el.value, locale);
116
334
  }
117
335
  }
118
336
  var CURRENCY_PATTERN = /\{(\w+),\s*currency(?:,\s*(\w+))?\}/g;
@@ -129,11 +347,11 @@ function preprocessCurrency(template) {
129
347
  function formatCurrencyValue(variableName, currencyCode, vars, locale) {
130
348
  const value = vars[variableName];
131
349
  if (value === void 0) {
132
- return `{${variableName}}`;
350
+ return handleMissingVar(variableName, locale);
133
351
  }
134
352
  const num = typeof value === "number" ? value : Number(value);
135
353
  if (isNaN(num)) {
136
- return `{${variableName}}`;
354
+ return handleMissingVar(variableName, locale);
137
355
  }
138
356
  try {
139
357
  return new Intl.NumberFormat(locale, {
@@ -161,11 +379,11 @@ function preprocessCompactNumber(template) {
161
379
  function formatCompactNumber(variableName, display, vars, locale) {
162
380
  const value = vars[variableName];
163
381
  if (value === void 0) {
164
- return `{${variableName}}`;
382
+ return handleMissingVar(variableName, locale);
165
383
  }
166
384
  const num = typeof value === "number" ? value : Number(value);
167
385
  if (isNaN(num)) {
168
- return `{${variableName}}`;
386
+ return handleMissingVar(variableName, locale);
169
387
  }
170
388
  try {
171
389
  return new Intl.NumberFormat(locale, {
@@ -207,7 +425,7 @@ function preprocessRelativeTime(template) {
207
425
  function formatRelativeTimeValue(variableName, style, vars, locale) {
208
426
  const value = vars[variableName];
209
427
  if (value === void 0) {
210
- return `{${variableName}}`;
428
+ return handleMissingVar(variableName, locale);
211
429
  }
212
430
  try {
213
431
  const date = toDate(value);
@@ -215,7 +433,7 @@ function formatRelativeTimeValue(variableName, style, vars, locale) {
215
433
  const options = style && RELATIVE_TIME_STYLES[style] || RELATIVE_TIME_STYLES.long;
216
434
  return new Intl.RelativeTimeFormat(locale, options).format(relValue, unit);
217
435
  } catch {
218
- return `{${variableName}}`;
436
+ return handleMissingVar(variableName, locale);
219
437
  }
220
438
  }
221
439
  var LIST_PATTERN = /\{(\w+),\s*list(?:,\s*(\w+))?(?:,\s*(\w+))?\}/g;
@@ -242,7 +460,7 @@ function preprocessList(template) {
242
460
  function formatListValue(variableName, type, style, vars, locale) {
243
461
  const value = vars[variableName];
244
462
  if (value === void 0 || !Array.isArray(value)) {
245
- return `{${variableName}}`;
463
+ return handleMissingVar(variableName, locale);
246
464
  }
247
465
  const options = {
248
466
  type: type || "conjunction",
@@ -254,13 +472,46 @@ function formatListValue(variableName, type, style, vars, locale) {
254
472
  return value.join(", ");
255
473
  }
256
474
  }
475
+ var PLURAL_SHORTHAND_PATTERN = /\{(\w+),\s*p,\s*([^}]+)\}/g;
476
+ function hasPluralShorthand(template) {
477
+ PLURAL_SHORTHAND_PATTERN.lastIndex = 0;
478
+ return PLURAL_SHORTHAND_PATTERN.test(template);
479
+ }
480
+ function preprocessPluralShorthand(template) {
481
+ PLURAL_SHORTHAND_PATTERN.lastIndex = 0;
482
+ return template.replace(PLURAL_SHORTHAND_PATTERN, (_, variable, args) => {
483
+ const parts = args.split("|").map((s) => s.trim());
484
+ if (parts.length === 2) {
485
+ const [singular, plural] = parts;
486
+ return `{${variable}, plural, one {# ${singular}} other {# ${plural}}}`;
487
+ }
488
+ if (parts.length === 3) {
489
+ const [zero, singular, plural] = parts;
490
+ return `{${variable}, plural, =0 {${zero}} one {# ${singular}} other {# ${plural}}}`;
491
+ }
492
+ return `{${variable}, p, ${args}}`;
493
+ });
494
+ }
257
495
  function interpolateICU(template, vars, locale) {
258
- const { processed: afterCurrency, replacements: currencyReplacements } = preprocessCurrency(template);
496
+ const afterPluralShorthand = preprocessPluralShorthand(template);
497
+ const { processed: afterCustom, replacements: customReplacements } = preprocessCustomFormatters(afterPluralShorthand);
498
+ const { processed: afterCurrency, replacements: currencyReplacements } = preprocessCurrency(afterCustom);
259
499
  const { processed: afterCompact, replacements: compactReplacements } = preprocessCompactNumber(afterCurrency);
260
500
  const { processed: afterRelTime, replacements: relTimeReplacements } = preprocessRelativeTime(afterCompact);
261
501
  const { processed: afterList, replacements: listReplacements } = preprocessList(afterRelTime);
262
- const ast = icuMessageformatParser.parse(afterList);
502
+ const ast = cachedParse(afterList);
263
503
  let result = formatElements(ast, vars, locale, null);
504
+ for (const [placeholder, { variable, formatterName, style }] of customReplacements) {
505
+ const value = vars[variable];
506
+ let formatted;
507
+ if (value === void 0) {
508
+ formatted = handleMissingVar(variable, locale);
509
+ } else {
510
+ const formatter = customFormatters.get(formatterName);
511
+ formatted = formatter ? formatter(value, locale, style) : String(value);
512
+ }
513
+ result = result.replace(`{${placeholder}}`, formatted);
514
+ }
264
515
  for (const [placeholder, { variable, currencyCode }] of currencyReplacements) {
265
516
  const formatted = formatCurrencyValue(variable, currencyCode, vars, locale);
266
517
  result = result.replace(`{${placeholder}}`, formatted);
@@ -288,7 +539,11 @@ function formatElement(el, vars, locale, currentPluralValue) {
288
539
  }
289
540
  if (icuMessageformatParser.isArgumentElement(el)) {
290
541
  const value = vars[el.value];
291
- return value !== void 0 ? String(value) : `{${el.value}}`;
542
+ if (value !== void 0) return String(value);
543
+ if (el.value.startsWith("__") && el.value.endsWith("__")) {
544
+ return `{${el.value}}`;
545
+ }
546
+ return handleMissingVar(el.value, locale);
292
547
  }
293
548
  if (icuMessageformatParser.isPoundElement(el)) {
294
549
  return currentPluralValue !== null ? String(currentPluralValue) : "#";
@@ -313,7 +568,7 @@ function formatElement(el, vars, locale, currentPluralValue) {
313
568
  function formatPlural(el, vars, locale) {
314
569
  const value = vars[el.value];
315
570
  if (typeof value !== "number") {
316
- return `{${el.value}}`;
571
+ return handleMissingVar(el.value, locale);
317
572
  }
318
573
  const adjustedValue = value - el.offset;
319
574
  const pluralRules = new Intl.PluralRules(locale, { type: el.pluralType });
@@ -328,7 +583,7 @@ function formatPlural(el, vars, locale) {
328
583
  if (el.options.other) {
329
584
  return formatElements(el.options.other.value, vars, locale, adjustedValue);
330
585
  }
331
- return `{${el.value}}`;
586
+ return handleMissingVar(el.value, locale);
332
587
  }
333
588
  function formatSelect(el, vars, locale) {
334
589
  const value = vars[el.value];
@@ -339,9 +594,9 @@ function formatSelect(el, vars, locale) {
339
594
  if (el.options.other) {
340
595
  return formatElements(el.options.other.value, vars, locale, null);
341
596
  }
342
- return `{${el.value}}`;
597
+ return handleMissingVar(el.value, locale);
343
598
  }
344
- var ICU_PATTERN = /\{[^}]+,\s*(plural|select|selectordinal|number|date|time|relativeTime|list|currency)\s*[,}]/;
599
+ var ICU_PATTERN = /\{[^}]+,\s*(plural|select|selectordinal|number|date|time|relativeTime|list|currency|p)\s*[,}]/;
345
600
  function hasICUPattern(template) {
346
601
  return ICU_PATTERN.test(template);
347
602
  }
@@ -349,114 +604,35 @@ function hasICUPattern(template) {
349
604
  // src/interpolation.ts
350
605
  var VARIABLE_PATTERN = /\{(\w+)\}/g;
351
606
  function interpolate(template, vars, locale) {
352
- if (!vars) return template;
353
- if (hasICUPattern(template)) {
354
- return interpolateICU(template, vars, locale || "en");
355
- }
356
- return template.replace(VARIABLE_PATTERN, (_, key) => {
357
- const value = vars[key];
358
- return value !== void 0 ? String(value) : `{${key}}`;
359
- });
360
- }
361
-
362
- // src/config.ts
363
- function defaultWarningHandler(warning) {
364
- const parts = [`[inline-i18n] Missing translation for locale "${warning.requestedLocale}"`];
365
- if (warning.key) {
366
- parts.push(`key: "${warning.key}"`);
367
- }
368
- parts.push(`Available: [${warning.availableLocales.join(", ")}]`);
369
- if (warning.fallbackUsed) {
370
- parts.push(`Using fallback: "${warning.fallbackUsed}"`);
371
- }
372
- console.warn(parts.join(" | "));
373
- }
374
- function isDevMode() {
375
- try {
376
- if (typeof globalThis !== "undefined" && "process" in globalThis) {
377
- const proc = globalThis.process;
378
- return proc?.env?.NODE_ENV !== "production";
379
- }
380
- return false;
381
- } catch {
382
- return false;
383
- }
384
- }
385
- var defaultConfig = {
386
- defaultLocale: "en",
387
- fallbackLocale: "en",
388
- autoParentLocale: true,
389
- fallbackChain: {},
390
- warnOnMissing: isDevMode(),
391
- onMissingTranslation: defaultWarningHandler,
392
- debugMode: false,
393
- loader: void 0
394
- };
395
- var config = { ...defaultConfig };
396
- function configure(options) {
397
- config = { ...config, ...options };
398
- }
399
- function getConfig() {
400
- return {
401
- ...defaultConfig,
402
- ...config
403
- };
404
- }
405
- function resetConfig() {
406
- config = { ...defaultConfig };
407
- }
408
- function getParentLocale(locale) {
409
- const dashIndex = locale.indexOf("-");
410
- if (dashIndex > 0) {
411
- return locale.substring(0, dashIndex);
412
- }
413
- return void 0;
414
- }
415
- function buildFallbackChain(locale) {
416
- const cfg = getConfig();
417
- if (cfg.fallbackChain[locale]) {
418
- return [locale, ...cfg.fallbackChain[locale]];
419
- }
420
- const chain = [locale];
421
- if (cfg.autoParentLocale) {
422
- let current = locale;
423
- while (true) {
424
- const parent = getParentLocale(current);
425
- if (parent && !chain.includes(parent)) {
426
- chain.push(parent);
427
- current = parent;
428
- } else {
429
- break;
607
+ const resolvedLocale = locale || "en";
608
+ if (hasICUPattern(template) || hasCustomFormatter(template) || hasPluralShorthand(template)) {
609
+ if (!vars) {
610
+ const cfg = getConfig();
611
+ if (cfg.missingVarHandler) {
612
+ return interpolateICU(template, {}, resolvedLocale);
430
613
  }
614
+ return template;
431
615
  }
616
+ return interpolateICU(template, vars, resolvedLocale);
432
617
  }
433
- const finalFallback = cfg.fallbackLocale;
434
- if (finalFallback && !chain.includes(finalFallback)) {
435
- chain.push(finalFallback);
436
- }
437
- return chain;
438
- }
439
- function emitWarning(warning) {
440
- const cfg = getConfig();
441
- if (cfg.warnOnMissing && cfg.onMissingTranslation) {
442
- cfg.onMissingTranslation(warning);
443
- }
444
- }
445
- function applyDebugFormat(output, debugInfo) {
446
- const cfg = getConfig();
447
- if (!cfg.debugMode) {
448
- return output;
449
- }
450
- const options = typeof cfg.debugMode === "object" ? cfg.debugMode : { showMissingPrefix: true, showFallbackPrefix: true };
451
- if (debugInfo.isMissing && options.showMissingPrefix !== false) {
452
- const prefix = options.missingPrefixFormat ? options.missingPrefixFormat(debugInfo.requestedLocale, debugInfo.key) : `[MISSING: ${debugInfo.requestedLocale}] `;
453
- return prefix + output;
454
- }
455
- if (debugInfo.isFallback && options.showFallbackPrefix !== false && debugInfo.usedLocale) {
456
- const prefix = options.fallbackPrefixFormat ? options.fallbackPrefixFormat(debugInfo.requestedLocale, debugInfo.usedLocale, debugInfo.key) : `[${debugInfo.requestedLocale} -> ${debugInfo.usedLocale}] `;
457
- return prefix + output;
618
+ if (!vars) {
619
+ const cfg = getConfig();
620
+ if (cfg.missingVarHandler) {
621
+ return template.replace(VARIABLE_PATTERN, (_, key) => {
622
+ return cfg.missingVarHandler(key, resolvedLocale);
623
+ });
624
+ }
625
+ return template;
458
626
  }
459
- return output;
627
+ return template.replace(VARIABLE_PATTERN, (_, key) => {
628
+ const value = vars[key];
629
+ if (value !== void 0) return String(value);
630
+ const cfg = getConfig();
631
+ if (cfg.missingVarHandler) {
632
+ return cfg.missingVarHandler(key, resolvedLocale);
633
+ }
634
+ return `{${key}}`;
635
+ });
460
636
  }
461
637
 
462
638
  // src/translate.ts
@@ -787,6 +963,93 @@ function getLoadedNamespaces() {
787
963
  return Object.keys(namespacedDictionaries);
788
964
  }
789
965
 
966
+ // src/detect.ts
967
+ function matchLocale(candidate, supportedLocales) {
968
+ const normalized = candidate.trim().toLowerCase();
969
+ const exact = supportedLocales.find((l) => l.toLowerCase() === normalized);
970
+ if (exact) return exact;
971
+ const parent = getParentLocale(candidate);
972
+ if (parent) {
973
+ const parentMatch = supportedLocales.find((l) => l.toLowerCase() === parent.toLowerCase());
974
+ if (parentMatch) return parentMatch;
975
+ }
976
+ return void 0;
977
+ }
978
+ function detectFromNavigator(supportedLocales) {
979
+ if (typeof globalThis === "undefined") return void 0;
980
+ const nav = globalThis.navigator;
981
+ if (!nav) return void 0;
982
+ const languages = nav.languages || (nav.language ? [nav.language] : []);
983
+ for (const lang of languages) {
984
+ const match = matchLocale(lang, supportedLocales);
985
+ if (match) return match;
986
+ }
987
+ return void 0;
988
+ }
989
+ function detectFromCookie(supportedLocales, cookieName) {
990
+ if (typeof document === "undefined") return void 0;
991
+ const cookies = document.cookie.split(";");
992
+ for (const cookie of cookies) {
993
+ const [key, value] = cookie.split("=").map((s) => s.trim());
994
+ if (key === cookieName && value) {
995
+ return matchLocale(value, supportedLocales);
996
+ }
997
+ }
998
+ return void 0;
999
+ }
1000
+ function detectFromUrl(supportedLocales) {
1001
+ if (typeof location === "undefined") return void 0;
1002
+ const pathname = location.pathname;
1003
+ const match = pathname.match(/^\/([a-zA-Z]{2}(?:-[a-zA-Z]{2,})?)(?:\/|$)/);
1004
+ if (match?.[1]) {
1005
+ return matchLocale(match[1], supportedLocales);
1006
+ }
1007
+ return void 0;
1008
+ }
1009
+ function detectFromHeader(supportedLocales, headerValue) {
1010
+ if (!headerValue) return void 0;
1011
+ const entries = headerValue.split(",").map((part) => {
1012
+ const parts = part.trim().split(";");
1013
+ const lang = parts[0]?.trim() || "";
1014
+ const qStr = parts[1];
1015
+ const q = qStr ? parseFloat(qStr.replace(/q=/, "")) : 1;
1016
+ return { lang, q };
1017
+ }).sort((a, b) => b.q - a.q);
1018
+ for (const { lang } of entries) {
1019
+ const match = matchLocale(lang, supportedLocales);
1020
+ if (match) return match;
1021
+ }
1022
+ return void 0;
1023
+ }
1024
+ function detectLocale(options) {
1025
+ const {
1026
+ supportedLocales,
1027
+ defaultLocale,
1028
+ sources = ["navigator"],
1029
+ cookieName = "NEXT_LOCALE",
1030
+ headerValue
1031
+ } = options;
1032
+ for (const source of sources) {
1033
+ let detected;
1034
+ switch (source) {
1035
+ case "navigator":
1036
+ detected = detectFromNavigator(supportedLocales);
1037
+ break;
1038
+ case "cookie":
1039
+ detected = detectFromCookie(supportedLocales, cookieName);
1040
+ break;
1041
+ case "url":
1042
+ detected = detectFromUrl(supportedLocales);
1043
+ break;
1044
+ case "header":
1045
+ detected = detectFromHeader(supportedLocales, headerValue);
1046
+ break;
1047
+ }
1048
+ if (detected) return detected;
1049
+ }
1050
+ return defaultLocale;
1051
+ }
1052
+
790
1053
  // src/richtext.ts
791
1054
  function escapeRegExp(str) {
792
1055
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -825,7 +1088,10 @@ function parseRichText(template, componentNames) {
825
1088
 
826
1089
  exports.__i18n_lookup = __i18n_lookup;
827
1090
  exports.clearDictionaries = clearDictionaries;
1091
+ exports.clearFormatters = clearFormatters;
1092
+ exports.clearICUCache = clearICUCache;
828
1093
  exports.configure = configure;
1094
+ exports.detectLocale = detectLocale;
829
1095
  exports.en_de = en_de;
830
1096
  exports.en_es = en_es;
831
1097
  exports.en_fr = en_fr;
@@ -850,7 +1116,9 @@ exports.loadAsync = loadAsync;
850
1116
  exports.loadDictionaries = loadDictionaries;
851
1117
  exports.loadDictionary = loadDictionary;
852
1118
  exports.parseRichText = parseRichText;
1119
+ exports.registerFormatter = registerFormatter;
853
1120
  exports.resetConfig = resetConfig;
1121
+ exports.restoreLocale = restoreLocale;
854
1122
  exports.setLocale = setLocale;
855
1123
  exports.t = t;
856
1124
  exports.zh_es = zh_es;