locl-js 1.0.2 → 2.0.1
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 +540 -0
- package/dist/LocL-v4yNIiKt.d.mts +438 -0
- package/dist/LocL-v4yNIiKt.d.ts +438 -0
- package/dist/compat.d.mts +54 -0
- package/dist/compat.d.ts +54 -0
- package/dist/compat.js +138 -0
- package/dist/compat.mjs +112 -0
- package/dist/index.d.mts +20 -341
- package/dist/index.d.ts +20 -341
- package/dist/index.js +296 -77
- package/dist/index.mjs +291 -74
- package/dist/react.d.mts +118 -0
- package/dist/react.d.ts +118 -0
- package/dist/react.js +229 -0
- package/dist/react.mjs +189 -0
- package/package.json +24 -1
- package/readme.md +0 -321
package/dist/index.js
CHANGED
|
@@ -18,11 +18,13 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
|
|
20
20
|
// src/index.ts
|
|
21
|
-
var
|
|
22
|
-
__export(
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
LocL: () => LocL,
|
|
24
|
+
defineResources: () => defineResources,
|
|
23
25
|
initLocL: () => initLocL
|
|
24
26
|
});
|
|
25
|
-
module.exports = __toCommonJS(
|
|
27
|
+
module.exports = __toCommonJS(src_exports);
|
|
26
28
|
|
|
27
29
|
// src/formatters.ts
|
|
28
30
|
var defaultFormatters = {
|
|
@@ -192,25 +194,26 @@ var LocL = class _LocL {
|
|
|
192
194
|
constructor(config) {
|
|
193
195
|
this.cache = /* @__PURE__ */ new Map();
|
|
194
196
|
this.proxyCache = /* @__PURE__ */ new WeakMap();
|
|
197
|
+
this.pluralRulesCache = /* @__PURE__ */ new Map();
|
|
198
|
+
this.subscribers = /* @__PURE__ */ new Set();
|
|
199
|
+
this.rootInstance = this;
|
|
195
200
|
if (!config.resources) {
|
|
196
201
|
throw new Error("[LocL] `resources` is required");
|
|
197
202
|
}
|
|
198
203
|
if (!config.fallbackLanguage) {
|
|
199
204
|
throw new Error("[LocL] `fallbackLanguage` is required");
|
|
200
205
|
}
|
|
201
|
-
config = {
|
|
206
|
+
this.config = {
|
|
202
207
|
useDefaultFormatters: true,
|
|
203
208
|
devMode: false,
|
|
204
209
|
useCache: true,
|
|
205
210
|
...config
|
|
206
211
|
};
|
|
207
|
-
|
|
208
|
-
this.config = config;
|
|
212
|
+
this.resources = { ...config.resources };
|
|
209
213
|
this.language = this.isLanguage(config.language) ?? config.fallbackLanguage;
|
|
210
214
|
this.fallbackLanguage = config.fallbackLanguage;
|
|
211
215
|
this.scope = config.scope;
|
|
212
|
-
this.
|
|
213
|
-
this.formatters = config.useDefaultFormatters ? { ...defaultFormatters, ...config.formatters ?? {} } : { ...config.formatters ?? {} };
|
|
216
|
+
this.formatters = this.config.useDefaultFormatters ? { ...defaultFormatters, ...config.formatters ?? {} } : { ...config.formatters ?? {} };
|
|
214
217
|
}
|
|
215
218
|
/**
|
|
216
219
|
* Creates a new proxy translator instance with a different configuration.
|
|
@@ -222,22 +225,39 @@ var LocL = class _LocL {
|
|
|
222
225
|
* @returns A new proxy `LocL` instance.
|
|
223
226
|
*/
|
|
224
227
|
withConfig(config) {
|
|
228
|
+
const root = this.rootInstance;
|
|
225
229
|
const cacheKey = `proxy::${config.language ?? this.language}::${Array.isArray(config.scope) ? config.scope.join("|") : config.scope ?? "*"}`;
|
|
226
|
-
if (
|
|
227
|
-
return
|
|
230
|
+
if (root.cache.has(cacheKey)) {
|
|
231
|
+
return root.cache.get(cacheKey);
|
|
228
232
|
}
|
|
229
|
-
const result = new Proxy(
|
|
230
|
-
get(target, prop) {
|
|
233
|
+
const result = new Proxy(root, {
|
|
234
|
+
get(target, prop, receiver) {
|
|
231
235
|
if (prop === "language") {
|
|
232
236
|
return config.language ?? target.language;
|
|
233
237
|
}
|
|
234
238
|
if (prop === "scope") {
|
|
235
|
-
return config.scope;
|
|
239
|
+
return config.scope !== void 0 ? config.scope : target.scope;
|
|
240
|
+
}
|
|
241
|
+
if (prop === "rootInstance") {
|
|
242
|
+
return target;
|
|
243
|
+
}
|
|
244
|
+
if (prop === "subscribe" && config.language !== void 0) {
|
|
245
|
+
return (listener) => {
|
|
246
|
+
try {
|
|
247
|
+
listener(config.language, config.language);
|
|
248
|
+
} catch (err) {
|
|
249
|
+
console.error("[LocL] Error in subscriber:", err);
|
|
250
|
+
}
|
|
251
|
+
return () => {
|
|
252
|
+
};
|
|
253
|
+
};
|
|
236
254
|
}
|
|
237
|
-
return target
|
|
255
|
+
return Reflect.get(target, prop, receiver);
|
|
238
256
|
}
|
|
239
257
|
});
|
|
240
|
-
|
|
258
|
+
if (root.config.useCache) {
|
|
259
|
+
root.cache.set(cacheKey, result);
|
|
260
|
+
}
|
|
241
261
|
return result;
|
|
242
262
|
}
|
|
243
263
|
/**
|
|
@@ -249,18 +269,110 @@ var LocL = class _LocL {
|
|
|
249
269
|
clone(language = this.language, scope) {
|
|
250
270
|
return new _LocL({
|
|
251
271
|
...this.config,
|
|
272
|
+
resources: this.resources,
|
|
252
273
|
language,
|
|
253
274
|
scope
|
|
254
275
|
});
|
|
255
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Subscribes a listener to language changes.
|
|
279
|
+
* Conforms to the standard reactive store contract (e.g. Svelte stores).
|
|
280
|
+
* Calls the listener immediately with the current language and returns an unsubscribe function.
|
|
281
|
+
* @param listener - The subscriber callback function.
|
|
282
|
+
* @returns An unsubscribe function.
|
|
283
|
+
*/
|
|
284
|
+
subscribe(listener) {
|
|
285
|
+
this.subscribers.add(listener);
|
|
286
|
+
try {
|
|
287
|
+
listener(this.language, this.language);
|
|
288
|
+
} catch (err) {
|
|
289
|
+
console.error("[LocL] Error in initial subscriber call:", err);
|
|
290
|
+
}
|
|
291
|
+
return () => {
|
|
292
|
+
this.subscribers.delete(listener);
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Gets the current active language.
|
|
297
|
+
*/
|
|
298
|
+
getLanguage() {
|
|
299
|
+
return this.language;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Adds or overrides a single translation key at runtime.
|
|
303
|
+
* @param lang - Target language code.
|
|
304
|
+
* @param key - Dotted path key.
|
|
305
|
+
* @param value - The translation value.
|
|
306
|
+
*/
|
|
307
|
+
addResource(lang, key, value) {
|
|
308
|
+
if (this.isUnsafeObjectKey(lang)) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const keys = key.split(".");
|
|
312
|
+
if (keys.some((k) => this.isUnsafeObjectKey(k))) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (!this.resources[lang]) {
|
|
316
|
+
this.resources[lang] = /* @__PURE__ */ Object.create(null);
|
|
317
|
+
}
|
|
318
|
+
let current = this.resources[lang];
|
|
319
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
320
|
+
const k = keys[i];
|
|
321
|
+
if (!current[k] || typeof current[k] !== "object") {
|
|
322
|
+
current[k] = /* @__PURE__ */ Object.create(null);
|
|
323
|
+
}
|
|
324
|
+
current = current[k];
|
|
325
|
+
}
|
|
326
|
+
const lastKey = keys[keys.length - 1];
|
|
327
|
+
current[lastKey] = value;
|
|
328
|
+
this.invalidateCacheForLang(lang);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Deeply merges a resource bundle into the specified language at runtime.
|
|
332
|
+
* @param lang - Target language code.
|
|
333
|
+
* @param bundle - Object of translations to merge.
|
|
334
|
+
*/
|
|
335
|
+
addResources(lang, bundle) {
|
|
336
|
+
if (this.isUnsafeObjectKey(lang)) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (!this.resources[lang]) {
|
|
340
|
+
this.resources[lang] = /* @__PURE__ */ Object.create(null);
|
|
341
|
+
}
|
|
342
|
+
const deepMerge = (target, source) => {
|
|
343
|
+
for (const k of Object.keys(source)) {
|
|
344
|
+
if (this.isUnsafeObjectKey(k)) {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (source[k] && typeof source[k] === "object" && !Array.isArray(source[k])) {
|
|
348
|
+
if (!target[k] || typeof target[k] !== "object") {
|
|
349
|
+
target[k] = /* @__PURE__ */ Object.create(null);
|
|
350
|
+
}
|
|
351
|
+
deepMerge(target[k], source[k]);
|
|
352
|
+
} else {
|
|
353
|
+
target[k] = source[k];
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
deepMerge(this.resources[lang], bundle);
|
|
358
|
+
this.invalidateCacheForLang(lang);
|
|
359
|
+
}
|
|
256
360
|
/**
|
|
257
361
|
* Changes the current language of the translator.
|
|
258
362
|
* @param lang - The new language to set.
|
|
259
363
|
*/
|
|
260
364
|
changeLanguage(lang) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
365
|
+
const validated = this.isLanguage(lang);
|
|
366
|
+
if (validated && validated !== this.language) {
|
|
367
|
+
const prev = this.language;
|
|
368
|
+
this.language = validated;
|
|
369
|
+
for (const sub of this.subscribers) {
|
|
370
|
+
try {
|
|
371
|
+
sub(this.language, prev);
|
|
372
|
+
} catch (err) {
|
|
373
|
+
console.error("[LocL] Error in subscriber:", err);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
264
376
|
}
|
|
265
377
|
}
|
|
266
378
|
t(key, values, format) {
|
|
@@ -269,6 +381,9 @@ var LocL = class _LocL {
|
|
|
269
381
|
}
|
|
270
382
|
const pluralCheckResult = this.checkPlural(key, values);
|
|
271
383
|
if (pluralCheckResult !== void 0) {
|
|
384
|
+
if (format && format.formatter) {
|
|
385
|
+
return this.applyFormat(pluralCheckResult, format.formatter, format.args);
|
|
386
|
+
}
|
|
272
387
|
return pluralCheckResult;
|
|
273
388
|
}
|
|
274
389
|
const result = this.lookupWithFallback(key);
|
|
@@ -299,45 +414,54 @@ var LocL = class _LocL {
|
|
|
299
414
|
format(value, formatter, args) {
|
|
300
415
|
return this.applyFormat(value, formatter, args);
|
|
301
416
|
}
|
|
417
|
+
plural(key, values, format) {
|
|
418
|
+
return this.t(key, values, format);
|
|
419
|
+
}
|
|
302
420
|
/**
|
|
303
|
-
* Translates a key
|
|
304
|
-
*
|
|
305
|
-
* @param key - The
|
|
306
|
-
* @param
|
|
307
|
-
* @param
|
|
308
|
-
* @returns
|
|
421
|
+
* Translates a key and interpolates rich elements using tag functions.
|
|
422
|
+
* Matches `<tag>content</tag>` in translation templates.
|
|
423
|
+
* @param key - The key to translate.
|
|
424
|
+
* @param tags - An object mapping tag names to functions that transform their inner content.
|
|
425
|
+
* @param values - Values to interpolate into variable placeholders ({name}, {{name}}).
|
|
426
|
+
* @returns An array of chunks (strings and custom rendered tag values).
|
|
427
|
+
* @example
|
|
428
|
+
* ```ts
|
|
429
|
+
* translator.rich("terms", {
|
|
430
|
+
* link: (content) => `<a href="/terms">${content}</a>`
|
|
431
|
+
* }, { name: "Alice" });
|
|
432
|
+
* ```
|
|
309
433
|
*/
|
|
310
|
-
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
this.config.devMode && console.warn(`[LocL] Missing plural: "${baseKey}" in "${this.language}"`);
|
|
323
|
-
interpolated = baseKey;
|
|
434
|
+
rich(key, tags, values) {
|
|
435
|
+
const rawStr = this.t(key, values);
|
|
436
|
+
if (typeof rawStr !== "string") {
|
|
437
|
+
return [rawStr];
|
|
438
|
+
}
|
|
439
|
+
const tagRegex = /<([a-zA-Z0-9_-]+)>([\s\S]*?)<\/\1>/g;
|
|
440
|
+
const result = [];
|
|
441
|
+
let lastIndex = 0;
|
|
442
|
+
let match;
|
|
443
|
+
while ((match = tagRegex.exec(rawStr)) !== null) {
|
|
444
|
+
if (match.index > lastIndex) {
|
|
445
|
+
result.push(rawStr.slice(lastIndex, match.index));
|
|
324
446
|
}
|
|
325
|
-
|
|
326
|
-
const
|
|
327
|
-
const
|
|
328
|
-
|
|
447
|
+
const tagName = match[1];
|
|
448
|
+
const content = match[2];
|
|
449
|
+
const renderer = tags[tagName];
|
|
450
|
+
typeof renderer === "function" ? result.push(renderer(content)) : result.push(match[0]);
|
|
451
|
+
lastIndex = match.index + match[0].length;
|
|
329
452
|
}
|
|
330
|
-
if (
|
|
331
|
-
|
|
453
|
+
if (lastIndex < rawStr.length) {
|
|
454
|
+
result.push(rawStr.slice(lastIndex));
|
|
332
455
|
}
|
|
333
|
-
return
|
|
456
|
+
return result;
|
|
334
457
|
}
|
|
335
458
|
get(key) {
|
|
336
459
|
const base = this.buildTranslationObject(this.language);
|
|
337
460
|
if (!key) {
|
|
338
|
-
return base;
|
|
461
|
+
return base ? this.makeReadOnly(base) : void 0;
|
|
339
462
|
}
|
|
340
|
-
|
|
463
|
+
const val = this.lookupWithFallback(key, base);
|
|
464
|
+
return val && typeof val === "object" ? this.makeReadOnly(val) : val;
|
|
341
465
|
}
|
|
342
466
|
/**
|
|
343
467
|
* Gets a nested object from the translations.
|
|
@@ -352,12 +476,27 @@ var LocL = class _LocL {
|
|
|
352
476
|
if (!value) {
|
|
353
477
|
return null;
|
|
354
478
|
}
|
|
355
|
-
const
|
|
356
|
-
|
|
479
|
+
const available = Object.keys(this.resources);
|
|
480
|
+
if (available.includes(value)) {
|
|
481
|
+
return value;
|
|
482
|
+
}
|
|
483
|
+
const prefix = value.substring(0, 2);
|
|
484
|
+
if (available.includes(prefix)) {
|
|
485
|
+
return prefix;
|
|
486
|
+
}
|
|
487
|
+
return null;
|
|
357
488
|
}
|
|
358
489
|
getCacheKey(language) {
|
|
359
|
-
const scopeKey = Array.isArray(this.scope) ? this.scope.join("|") : this.scope
|
|
360
|
-
return `${language
|
|
490
|
+
const scopeKey = Array.isArray(this.scope) ? this.scope.join("|") : this.scope;
|
|
491
|
+
return `${language}::${scopeKey}`;
|
|
492
|
+
}
|
|
493
|
+
invalidateCacheForLang(lang) {
|
|
494
|
+
const prefix = `${lang}::`;
|
|
495
|
+
for (const key of this.cache.keys()) {
|
|
496
|
+
if (key.startsWith(prefix)) {
|
|
497
|
+
this.cache.delete(key);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
361
500
|
}
|
|
362
501
|
lookupWithFallback(key, base) {
|
|
363
502
|
const keys = key.split(".");
|
|
@@ -375,7 +514,7 @@ var LocL = class _LocL {
|
|
|
375
514
|
return result;
|
|
376
515
|
}
|
|
377
516
|
buildTranslationObject(language) {
|
|
378
|
-
const langObject = this.
|
|
517
|
+
const langObject = this.resources[language];
|
|
379
518
|
if (!this.scope) {
|
|
380
519
|
return langObject;
|
|
381
520
|
}
|
|
@@ -406,11 +545,10 @@ var LocL = class _LocL {
|
|
|
406
545
|
}, combined);
|
|
407
546
|
}
|
|
408
547
|
}
|
|
409
|
-
const roCombined = this.makeReadOnly(combined);
|
|
410
548
|
if (this.config.useCache) {
|
|
411
|
-
this.cache.set(cacheKey,
|
|
549
|
+
this.cache.set(cacheKey, combined);
|
|
412
550
|
}
|
|
413
|
-
return
|
|
551
|
+
return combined;
|
|
414
552
|
}
|
|
415
553
|
if (this.config.useCache) {
|
|
416
554
|
this.cache.set(cacheKey, void 0);
|
|
@@ -429,7 +567,7 @@ var LocL = class _LocL {
|
|
|
429
567
|
return void 0;
|
|
430
568
|
}
|
|
431
569
|
const keyTest = keysCopy.join(".");
|
|
432
|
-
if (
|
|
570
|
+
if (Object.prototype.hasOwnProperty.call(current, keyTest)) {
|
|
433
571
|
return current[keyTest];
|
|
434
572
|
}
|
|
435
573
|
if (!(key in current)) {
|
|
@@ -440,13 +578,54 @@ var LocL = class _LocL {
|
|
|
440
578
|
}
|
|
441
579
|
return current;
|
|
442
580
|
}
|
|
581
|
+
getPluralRules(lang) {
|
|
582
|
+
let rules = this.pluralRulesCache.get(lang);
|
|
583
|
+
if (!rules) {
|
|
584
|
+
rules = new Intl.PluralRules(lang);
|
|
585
|
+
this.pluralRulesCache.set(lang, rules);
|
|
586
|
+
}
|
|
587
|
+
return rules;
|
|
588
|
+
}
|
|
443
589
|
checkPlural(key, values) {
|
|
444
|
-
if (values?.count !== void 0 && !key.match(/_(one|few|many|other)$/)) {
|
|
445
|
-
const category = this.
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
590
|
+
if (values?.count !== void 0 && !key.match(/_(zero|one|two|few|many|other)$/)) {
|
|
591
|
+
const category = this.getPluralRules(this.language).select(values.count);
|
|
592
|
+
const baseValue = this.lookupWithFallback(key, void 0);
|
|
593
|
+
if (baseValue && typeof baseValue === "object") {
|
|
594
|
+
if (values.count === 0 && "zero" in baseValue) {
|
|
595
|
+
return this.interpolate(baseValue.zero, values);
|
|
596
|
+
}
|
|
597
|
+
if (category in baseValue) {
|
|
598
|
+
return this.interpolate(baseValue[category], values);
|
|
599
|
+
} else if ("other" in baseValue) {
|
|
600
|
+
this.config.devMode && console.warn(`[LocL] Missing plural: "${key}" in "${this.language}"`);
|
|
601
|
+
return this.interpolate(baseValue.other, values);
|
|
602
|
+
} else {
|
|
603
|
+
this.config.devMode && console.warn(`[LocL] Missing plural: "${key}" in "${this.language}"`);
|
|
604
|
+
return key;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (values.count === 0) {
|
|
608
|
+
const zeroResult = this.lookupWithFallback(`${key}_zero`, void 0);
|
|
609
|
+
if (zeroResult !== void 0) {
|
|
610
|
+
return this.interpolate(zeroResult, values);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
const categoryResult = this.lookupWithFallback(`${key}_${category}`, void 0);
|
|
614
|
+
if (categoryResult !== void 0) {
|
|
615
|
+
return this.interpolate(categoryResult, values);
|
|
616
|
+
}
|
|
617
|
+
const otherResult = this.lookupWithFallback(`${key}_other`, void 0);
|
|
618
|
+
if (otherResult !== void 0) {
|
|
619
|
+
this.config.devMode && console.warn(`[LocL] Missing plural: "${key}" in "${this.language}"`);
|
|
620
|
+
return this.interpolate(otherResult, values);
|
|
621
|
+
}
|
|
622
|
+
const anySuffixExists = ["zero", "one", "two", "few", "many"].some((cat) => this.lookupWithFallback(`${key}_${cat}`, void 0) !== void 0);
|
|
623
|
+
if (anySuffixExists) {
|
|
624
|
+
this.config.devMode && console.warn(`[LocL] Missing plural: "${key}" in "${this.language}"`);
|
|
625
|
+
return key;
|
|
626
|
+
}
|
|
449
627
|
}
|
|
628
|
+
return void 0;
|
|
450
629
|
}
|
|
451
630
|
interpolate(result, values) {
|
|
452
631
|
if (!values || typeof result !== "string") {
|
|
@@ -464,34 +643,66 @@ var LocL = class _LocL {
|
|
|
464
643
|
depth--;
|
|
465
644
|
if (depth === 0 && start >= 0) {
|
|
466
645
|
const raw = result.slice(start, i + 1);
|
|
467
|
-
|
|
646
|
+
let inner = raw.slice(1, -1);
|
|
647
|
+
if (inner.startsWith("{") && inner.endsWith("}")) {
|
|
648
|
+
inner = inner.slice(1, -1);
|
|
649
|
+
}
|
|
650
|
+
tokens.push({ raw, inner });
|
|
468
651
|
start = -1;
|
|
469
652
|
}
|
|
470
653
|
}
|
|
471
654
|
}
|
|
472
655
|
let output = result;
|
|
656
|
+
const processed = /* @__PURE__ */ new Set();
|
|
473
657
|
for (const token of tokens) {
|
|
658
|
+
if (processed.has(token.raw)) {
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
processed.add(token.raw);
|
|
474
662
|
let replacement = "";
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
663
|
+
const selectMatch = token.inner.match(/^\s*([^,]+),\s*select\s*,\s*([\s\S]+)$/);
|
|
664
|
+
if (selectMatch) {
|
|
665
|
+
const field = selectMatch[1].trim();
|
|
666
|
+
const casesBody = selectMatch[2];
|
|
667
|
+
const value = String(values[field] ?? "other");
|
|
668
|
+
const caseMap = {};
|
|
669
|
+
let i = 0;
|
|
670
|
+
while (i < casesBody.length) {
|
|
671
|
+
while (i < casesBody.length && /\s/.test(casesBody[i])) i++;
|
|
672
|
+
if (i >= casesBody.length) break;
|
|
673
|
+
let keyStart = i;
|
|
674
|
+
while (i < casesBody.length && casesBody[i] !== "{" && !/\s/.test(casesBody[i])) i++;
|
|
675
|
+
const caseKey = casesBody.slice(keyStart, i).trim();
|
|
676
|
+
while (i < casesBody.length && casesBody[i] !== "{") i++;
|
|
677
|
+
if (casesBody[i] === "{") {
|
|
678
|
+
let bodyDepth = 1;
|
|
679
|
+
let bodyStart = i + 1;
|
|
680
|
+
i++;
|
|
681
|
+
while (i < casesBody.length && bodyDepth > 0) {
|
|
682
|
+
if (casesBody[i] === "{") bodyDepth++;
|
|
683
|
+
else if (casesBody[i] === "}") bodyDepth--;
|
|
684
|
+
i++;
|
|
685
|
+
}
|
|
686
|
+
const caseText = casesBody.slice(bodyStart, i - 1).trim();
|
|
687
|
+
if (caseKey) {
|
|
688
|
+
caseMap[caseKey] = caseText;
|
|
689
|
+
}
|
|
485
690
|
}
|
|
486
|
-
replacement = caseMap[value] ?? caseMap.other ?? "";
|
|
487
691
|
}
|
|
692
|
+
const rawReplacement = caseMap[value] ?? caseMap.other ?? "";
|
|
693
|
+
replacement = rawReplacement.includes("{") ? this.interpolate(rawReplacement, values) : rawReplacement;
|
|
488
694
|
} else {
|
|
489
695
|
const [fieldName, formatterAndArgs] = token.inner.split("|").map((x) => x.trim());
|
|
490
696
|
const [formatterName, argsStr] = formatterAndArgs ? formatterAndArgs.split(":") : [];
|
|
491
|
-
const args = argsStr ? argsStr.split(",") : [];
|
|
697
|
+
const args = argsStr ? argsStr.split(",").map((a) => a.trim()) : [];
|
|
492
698
|
const val = values[fieldName];
|
|
493
|
-
if (formatterName
|
|
494
|
-
|
|
699
|
+
if (formatterName) {
|
|
700
|
+
if (this.formatters?.[formatterName]) {
|
|
701
|
+
replacement = this.formatters[formatterName](val, args);
|
|
702
|
+
} else {
|
|
703
|
+
this.config.devMode && console.warn(`[LocL] Formatter "${formatterName}" not found.`);
|
|
704
|
+
replacement = val !== void 0 ? String(val) : token.raw;
|
|
705
|
+
}
|
|
495
706
|
} else if (val instanceof Date) {
|
|
496
707
|
replacement = new Intl.DateTimeFormat(this.language).format(val);
|
|
497
708
|
} else if (typeof val === "number") {
|
|
@@ -500,7 +711,7 @@ var LocL = class _LocL {
|
|
|
500
711
|
replacement = val !== void 0 ? String(val) : token.raw;
|
|
501
712
|
}
|
|
502
713
|
}
|
|
503
|
-
output = output.
|
|
714
|
+
output = output.split(token.raw).join(replacement);
|
|
504
715
|
}
|
|
505
716
|
return output;
|
|
506
717
|
}
|
|
@@ -530,13 +741,21 @@ var LocL = class _LocL {
|
|
|
530
741
|
this.proxyCache.set(obj, proxy);
|
|
531
742
|
return proxy;
|
|
532
743
|
}
|
|
744
|
+
isUnsafeObjectKey(key) {
|
|
745
|
+
return key === "__proto__" || key === "constructor" || key === "prototype";
|
|
746
|
+
}
|
|
533
747
|
};
|
|
534
748
|
|
|
535
749
|
// src/index.ts
|
|
536
750
|
function initLocL(config) {
|
|
537
751
|
return new LocL(config);
|
|
538
752
|
}
|
|
753
|
+
function defineResources(resources) {
|
|
754
|
+
return resources;
|
|
755
|
+
}
|
|
539
756
|
// Annotate the CommonJS export names for ESM import in node:
|
|
540
757
|
0 && (module.exports = {
|
|
758
|
+
LocL,
|
|
759
|
+
defineResources,
|
|
541
760
|
initLocL
|
|
542
761
|
});
|