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/README.md +325 -28
- package/dist/index.d.mts +97 -5
- package/dist/index.d.ts +97 -5
- package/dist/index.js +451 -118
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +446 -119
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
282
|
-
if (hasICUPattern(template)) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
|
|
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
|
|
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;
|