locl-js 1.0.1 → 2.0.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.mjs CHANGED
@@ -32,7 +32,7 @@ var defaultFormatters = {
32
32
  * @param {string} [args[1]="..."] - The suffix to append if the string is truncated.
33
33
  * @returns The truncated string.
34
34
  */
35
- truncate: (val, args) => {
35
+ truncate: (val, args = []) => {
36
36
  const length = parseInt(args[0]) || 10;
37
37
  const suffix = args[1] || "...";
38
38
  const str = String(val);
@@ -46,7 +46,7 @@ var defaultFormatters = {
46
46
  * @param {string} [args[1]] - The style of formatting to use (e.g., "decimal", "percent").
47
47
  * @returns The formatted number.
48
48
  */
49
- number: (val, args) => {
49
+ number: (val, args = []) => {
50
50
  const locales = args[0] || void 0;
51
51
  const options = {};
52
52
  if (args[1]) options.style = args[1];
@@ -60,7 +60,7 @@ var defaultFormatters = {
60
60
  * @param {string} [args[1]] - The locale to use.
61
61
  * @returns The formatted currency string.
62
62
  */
63
- currency: (val, args) => {
63
+ currency: (val, args = []) => {
64
64
  const currency = args[0] || "USD";
65
65
  const locales = args[1] || void 0;
66
66
  return new Intl.NumberFormat(locales, {
@@ -76,7 +76,7 @@ var defaultFormatters = {
76
76
  * @param {string} [args[1]] - The date style to use (e.g., "short", "long").
77
77
  * @returns The formatted date string.
78
78
  */
79
- date: (val, args) => {
79
+ date: (val, args = []) => {
80
80
  const locales = args[0] || void 0;
81
81
  const options = {};
82
82
  if (args[1]) options.dateStyle = args[1];
@@ -89,7 +89,7 @@ var defaultFormatters = {
89
89
  * @param {string} [args[0]] - The locale to use.
90
90
  * @returns The relative time string.
91
91
  */
92
- relativeDate: (val, args) => {
92
+ relativeDate: (val, args = []) => {
93
93
  const locales = args[0] || void 0;
94
94
  const options = { numeric: "auto" };
95
95
  const rtf = new Intl.RelativeTimeFormat(locales, options);
@@ -113,7 +113,7 @@ var defaultFormatters = {
113
113
  * @param {string} [args[1]="No"] - The string to return for a falsy value.
114
114
  * @returns "Yes" or "No".
115
115
  */
116
- yesNo: (val, args) => {
116
+ yesNo: (val, args = []) => {
117
117
  const [yesVal, noVal] = args || [];
118
118
  return val ? yesVal || "Yes" : noVal || "No";
119
119
  },
@@ -125,7 +125,7 @@ var defaultFormatters = {
125
125
  * @param {string} [args[1]="false"] - The string to return for a falsy value.
126
126
  * @returns "true" or "false".
127
127
  */
128
- boolean: (val, args) => {
128
+ boolean: (val, args = []) => {
129
129
  const [trueVal, falseVal] = args || [];
130
130
  return val ? trueVal || "true" : falseVal || "false";
131
131
  },
@@ -137,7 +137,7 @@ var defaultFormatters = {
137
137
  * @param {string} [args[1]=" "] - The string to pad with.
138
138
  * @returns The padded string.
139
139
  */
140
- padStart: (val, args) => {
140
+ padStart: (val, args = []) => {
141
141
  const length = parseInt(args[0]) || 0;
142
142
  const fill = args[1] || " ";
143
143
  return String(val).padStart(length, fill);
@@ -150,7 +150,7 @@ var defaultFormatters = {
150
150
  * @param {string} [args[1]=" "] - The string to pad with.
151
151
  * @returns The padded string.
152
152
  */
153
- padEnd: (val, args) => {
153
+ padEnd: (val, args = []) => {
154
154
  const length = parseInt(args[0]) || 0;
155
155
  const fill = args[1] || " ";
156
156
  return String(val).padEnd(length, fill);
@@ -166,25 +166,26 @@ var LocL = class _LocL {
166
166
  constructor(config) {
167
167
  this.cache = /* @__PURE__ */ new Map();
168
168
  this.proxyCache = /* @__PURE__ */ new WeakMap();
169
+ this.pluralRulesCache = /* @__PURE__ */ new Map();
170
+ this.subscribers = /* @__PURE__ */ new Set();
171
+ this.rootInstance = this;
169
172
  if (!config.resources) {
170
173
  throw new Error("[LocL] `resources` is required");
171
174
  }
172
175
  if (!config.fallbackLanguage) {
173
176
  throw new Error("[LocL] `fallbackLanguage` is required");
174
177
  }
175
- config = {
178
+ this.config = {
176
179
  useDefaultFormatters: true,
177
180
  devMode: false,
178
181
  useCache: true,
179
182
  ...config
180
183
  };
181
- config.resources = this.makeReadOnly(config.resources);
182
- this.config = config;
184
+ this.resources = { ...config.resources };
183
185
  this.language = this.isLanguage(config.language) ?? config.fallbackLanguage;
184
186
  this.fallbackLanguage = config.fallbackLanguage;
185
187
  this.scope = config.scope;
186
- this.pluralRules = new Intl.PluralRules(this.language);
187
- this.formatters = config.useDefaultFormatters ? { ...defaultFormatters, ...config.formatters ?? {} } : { ...config.formatters ?? {} };
188
+ this.formatters = this.config.useDefaultFormatters ? { ...defaultFormatters, ...config.formatters ?? {} } : { ...config.formatters ?? {} };
188
189
  }
189
190
  /**
190
191
  * Creates a new proxy translator instance with a different configuration.
@@ -196,21 +197,40 @@ var LocL = class _LocL {
196
197
  * @returns A new proxy `LocL` instance.
197
198
  */
198
199
  withConfig(config) {
200
+ const root = this.rootInstance;
199
201
  const cacheKey = `proxy::${config.language ?? this.language}::${Array.isArray(config.scope) ? config.scope.join("|") : config.scope ?? "*"}`;
200
- if (this.cache.has(cacheKey)) {
201
- return this.cache.get(cacheKey);
202
+ if (root.cache.has(cacheKey)) {
203
+ return root.cache.get(cacheKey);
202
204
  }
203
- return new Proxy(this, {
204
- get(target, prop) {
205
+ const result = new Proxy(root, {
206
+ get(target, prop, receiver) {
205
207
  if (prop === "language") {
206
208
  return config.language ?? target.language;
207
209
  }
208
210
  if (prop === "scope") {
209
- return config.scope;
211
+ return config.scope !== void 0 ? config.scope : target.scope;
212
+ }
213
+ if (prop === "rootInstance") {
214
+ return target;
215
+ }
216
+ if (prop === "subscribe" && config.language !== void 0) {
217
+ return (listener) => {
218
+ try {
219
+ listener(config.language, config.language);
220
+ } catch (err) {
221
+ console.error("[LocL] Error in subscriber:", err);
222
+ }
223
+ return () => {
224
+ };
225
+ };
210
226
  }
211
- return target[prop];
227
+ return Reflect.get(target, prop, receiver);
212
228
  }
213
229
  });
230
+ if (root.config.useCache) {
231
+ root.cache.set(cacheKey, result);
232
+ }
233
+ return result;
214
234
  }
215
235
  /**
216
236
  * Creates a new `LocL` instance with a different language or scope.
@@ -221,18 +241,106 @@ var LocL = class _LocL {
221
241
  clone(language = this.language, scope) {
222
242
  return new _LocL({
223
243
  ...this.config,
244
+ resources: this.resources,
224
245
  language,
225
246
  scope
226
247
  });
227
248
  }
249
+ /**
250
+ * Subscribes a listener to language changes.
251
+ * Conforms to the standard reactive store contract (e.g. Svelte stores).
252
+ * Calls the listener immediately with the current language and returns an unsubscribe function.
253
+ * @param listener - The subscriber callback function.
254
+ * @returns An unsubscribe function.
255
+ */
256
+ subscribe(listener) {
257
+ this.subscribers.add(listener);
258
+ try {
259
+ listener(this.language, this.language);
260
+ } catch (err) {
261
+ console.error("[LocL] Error in initial subscriber call:", err);
262
+ }
263
+ return () => {
264
+ this.subscribers.delete(listener);
265
+ };
266
+ }
267
+ /**
268
+ * Gets the current active language.
269
+ */
270
+ getLanguage() {
271
+ return this.language;
272
+ }
273
+ /**
274
+ * Adds or overrides a single translation key at runtime.
275
+ * @param lang - Target language code.
276
+ * @param key - Dotted path key.
277
+ * @param value - The translation value.
278
+ */
279
+ addResource(lang, key, value) {
280
+ if (!this.resources[lang]) {
281
+ this.resources[lang] = {};
282
+ }
283
+ const keys = key.split(".");
284
+ let current = this.resources[lang];
285
+ for (let i = 0; i < keys.length - 1; i++) {
286
+ const k = keys[i];
287
+ if (k === "__proto__" || k === "constructor" || k === "prototype") {
288
+ return;
289
+ }
290
+ if (!current[k] || typeof current[k] !== "object") {
291
+ current[k] = {};
292
+ }
293
+ current = current[k];
294
+ }
295
+ const lastKey = keys[keys.length - 1];
296
+ if (lastKey !== "__proto__" && lastKey !== "constructor" && lastKey !== "prototype") {
297
+ current[lastKey] = value;
298
+ }
299
+ this.invalidateCacheForLang(lang);
300
+ }
301
+ /**
302
+ * Deeply merges a resource bundle into the specified language at runtime.
303
+ * @param lang - Target language code.
304
+ * @param bundle - Object of translations to merge.
305
+ */
306
+ addResources(lang, bundle) {
307
+ if (!this.resources[lang]) {
308
+ this.resources[lang] = {};
309
+ }
310
+ const deepMerge = (target, source) => {
311
+ for (const k of Object.keys(source)) {
312
+ if (k === "__proto__" || k === "constructor" || k === "prototype") {
313
+ continue;
314
+ }
315
+ if (source[k] && typeof source[k] === "object" && !Array.isArray(source[k])) {
316
+ if (!target[k] || typeof target[k] !== "object") {
317
+ target[k] = {};
318
+ }
319
+ deepMerge(target[k], source[k]);
320
+ } else {
321
+ target[k] = source[k];
322
+ }
323
+ }
324
+ };
325
+ deepMerge(this.resources[lang], bundle);
326
+ this.invalidateCacheForLang(lang);
327
+ }
228
328
  /**
229
329
  * Changes the current language of the translator.
230
330
  * @param lang - The new language to set.
231
331
  */
232
332
  changeLanguage(lang) {
233
- if (Object.keys(this.config.resources).includes(lang)) {
234
- this.language = lang;
235
- this.pluralRules = new Intl.PluralRules(this.language);
333
+ const validated = this.isLanguage(lang);
334
+ if (validated && validated !== this.language) {
335
+ const prev = this.language;
336
+ this.language = validated;
337
+ for (const sub of this.subscribers) {
338
+ try {
339
+ sub(this.language, prev);
340
+ } catch (err) {
341
+ console.error("[LocL] Error in subscriber:", err);
342
+ }
343
+ }
236
344
  }
237
345
  }
238
346
  t(key, values, format) {
@@ -241,6 +349,9 @@ var LocL = class _LocL {
241
349
  }
242
350
  const pluralCheckResult = this.checkPlural(key, values);
243
351
  if (pluralCheckResult !== void 0) {
352
+ if (format && format.formatter) {
353
+ return this.applyFormat(pluralCheckResult, format.formatter, format.args);
354
+ }
244
355
  return pluralCheckResult;
245
356
  }
246
357
  const result = this.lookupWithFallback(key);
@@ -271,45 +382,54 @@ var LocL = class _LocL {
271
382
  format(value, formatter, args) {
272
383
  return this.applyFormat(value, formatter, args);
273
384
  }
385
+ plural(key, values, format) {
386
+ return this.t(key, values, format);
387
+ }
274
388
  /**
275
- * Translates a key with pluralization.
276
- * It automatically selects the correct plural form based on the `count` value.
277
- * @param key - The base key for the pluralization.
278
- * @param values - An object with a `count` property and other values to interpolate.
279
- * @param format - An object with formatting options.
280
- * @returns The translated, pluralized, and formatted string.
389
+ * Translates a key and interpolates rich elements using tag functions.
390
+ * Matches `<tag>content</tag>` in translation templates.
391
+ * @param key - The key to translate.
392
+ * @param tags - An object mapping tag names to functions that transform their inner content.
393
+ * @param values - Values to interpolate into variable placeholders ({name}, {{name}}).
394
+ * @returns An array of chunks (strings and custom rendered tag values).
395
+ * @example
396
+ * ```ts
397
+ * translator.rich("terms", {
398
+ * link: (content) => `<a href="/terms">${content}</a>`
399
+ * }, { name: "Alice" });
400
+ * ```
281
401
  */
282
- plural(key, values, format) {
283
- const category = this.pluralRules.select(values.count);
284
- const baseKey = key;
285
- const baseValue = this.lookupWithFallback(baseKey);
286
- let interpolated;
287
- if (baseValue && typeof baseValue === "object") {
288
- if (category in baseValue) {
289
- interpolated = this.interpolate(baseValue[category], values) ?? baseKey;
290
- } else if ("other" in baseValue) {
291
- this.config.devMode && console.warn(`[LocL] Missing plural: "${baseKey}" in "${this.language}"`);
292
- interpolated = this.interpolate(baseValue.other, values) ?? baseKey;
293
- } else {
294
- this.config.devMode && console.warn(`[LocL] Missing plural: "${baseKey}" in "${this.language}"`);
295
- interpolated = baseKey;
402
+ rich(key, tags, values) {
403
+ const rawStr = this.t(key, values);
404
+ if (typeof rawStr !== "string") {
405
+ return [rawStr];
406
+ }
407
+ const tagRegex = /<([a-zA-Z0-9_-]+)>([\s\S]*?)<\/\1>/g;
408
+ const result = [];
409
+ let lastIndex = 0;
410
+ let match;
411
+ while ((match = tagRegex.exec(rawStr)) !== null) {
412
+ if (match.index > lastIndex) {
413
+ result.push(rawStr.slice(lastIndex, match.index));
296
414
  }
297
- } else {
298
- const pluralKey = `${baseKey}_${category}`;
299
- const result = this.lookupWithFallback(pluralKey) ?? this.lookupWithFallback(`${baseKey}_other`) ?? this.lookupWithFallback(baseKey);
300
- interpolated = this.interpolate(result, values) ?? baseKey;
415
+ const tagName = match[1];
416
+ const content = match[2];
417
+ const renderer = tags[tagName];
418
+ typeof renderer === "function" ? result.push(renderer(content)) : result.push(match[0]);
419
+ lastIndex = match.index + match[0].length;
301
420
  }
302
- if (format && format.formatter) {
303
- return this.applyFormat(interpolated, format.formatter, format.args);
421
+ if (lastIndex < rawStr.length) {
422
+ result.push(rawStr.slice(lastIndex));
304
423
  }
305
- return interpolated;
424
+ return result;
306
425
  }
307
426
  get(key) {
308
427
  const base = this.buildTranslationObject(this.language);
309
428
  if (!key) {
310
- return base;
429
+ return base ? this.makeReadOnly(base) : void 0;
311
430
  }
312
- return this.lookupWithFallback(key, base);
431
+ const val = this.lookupWithFallback(key, base);
432
+ return val && typeof val === "object" ? this.makeReadOnly(val) : val;
313
433
  }
314
434
  /**
315
435
  * Gets a nested object from the translations.
@@ -324,12 +444,27 @@ var LocL = class _LocL {
324
444
  if (!value) {
325
445
  return null;
326
446
  }
327
- const language = value.substring(0, 2);
328
- return Object.keys(this.config.resources).includes(language) ? language : null;
447
+ const available = Object.keys(this.resources);
448
+ if (available.includes(value)) {
449
+ return value;
450
+ }
451
+ const prefix = value.substring(0, 2);
452
+ if (available.includes(prefix)) {
453
+ return prefix;
454
+ }
455
+ return null;
329
456
  }
330
457
  getCacheKey(language) {
331
- const scopeKey = Array.isArray(this.scope) ? this.scope.join("|") : this.scope ?? "*";
332
- return `${language ?? this.language}::${scopeKey}`;
458
+ const scopeKey = Array.isArray(this.scope) ? this.scope.join("|") : this.scope;
459
+ return `${language}::${scopeKey}`;
460
+ }
461
+ invalidateCacheForLang(lang) {
462
+ const prefix = `${lang}::`;
463
+ for (const key of this.cache.keys()) {
464
+ if (key.startsWith(prefix)) {
465
+ this.cache.delete(key);
466
+ }
467
+ }
333
468
  }
334
469
  lookupWithFallback(key, base) {
335
470
  const keys = key.split(".");
@@ -347,7 +482,7 @@ var LocL = class _LocL {
347
482
  return result;
348
483
  }
349
484
  buildTranslationObject(language) {
350
- const langObject = this.config.resources[language];
485
+ const langObject = this.resources[language];
351
486
  if (!this.scope) {
352
487
  return langObject;
353
488
  }
@@ -378,11 +513,10 @@ var LocL = class _LocL {
378
513
  }, combined);
379
514
  }
380
515
  }
381
- const roCombined = this.makeReadOnly(combined);
382
516
  if (this.config.useCache) {
383
- this.cache.set(cacheKey, roCombined);
517
+ this.cache.set(cacheKey, combined);
384
518
  }
385
- return roCombined;
519
+ return combined;
386
520
  }
387
521
  if (this.config.useCache) {
388
522
  this.cache.set(cacheKey, void 0);
@@ -412,13 +546,54 @@ var LocL = class _LocL {
412
546
  }
413
547
  return current;
414
548
  }
549
+ getPluralRules(lang) {
550
+ let rules = this.pluralRulesCache.get(lang);
551
+ if (!rules) {
552
+ rules = new Intl.PluralRules(lang);
553
+ this.pluralRulesCache.set(lang, rules);
554
+ }
555
+ return rules;
556
+ }
415
557
  checkPlural(key, values) {
416
- if (values?.count !== void 0 && !key.match(/_(one|few|many|other)$/)) {
417
- const category = this.pluralRules.select(values.count);
418
- const pluralKey = `${key}_${category}`;
419
- const pluralResult = this.lookupWithFallback(pluralKey);
420
- return this.interpolate(pluralResult, values);
558
+ if (values?.count !== void 0 && !key.match(/_(zero|one|two|few|many|other)$/)) {
559
+ const category = this.getPluralRules(this.language).select(values.count);
560
+ const baseValue = this.lookupWithFallback(key, void 0);
561
+ if (baseValue && typeof baseValue === "object") {
562
+ if (values.count === 0 && "zero" in baseValue) {
563
+ return this.interpolate(baseValue.zero, values);
564
+ }
565
+ if (category in baseValue) {
566
+ return this.interpolate(baseValue[category], values);
567
+ } else if ("other" in baseValue) {
568
+ this.config.devMode && console.warn(`[LocL] Missing plural: "${key}" in "${this.language}"`);
569
+ return this.interpolate(baseValue.other, values);
570
+ } else {
571
+ this.config.devMode && console.warn(`[LocL] Missing plural: "${key}" in "${this.language}"`);
572
+ return key;
573
+ }
574
+ }
575
+ if (values.count === 0) {
576
+ const zeroResult = this.lookupWithFallback(`${key}_zero`, void 0);
577
+ if (zeroResult !== void 0) {
578
+ return this.interpolate(zeroResult, values);
579
+ }
580
+ }
581
+ const categoryResult = this.lookupWithFallback(`${key}_${category}`, void 0);
582
+ if (categoryResult !== void 0) {
583
+ return this.interpolate(categoryResult, values);
584
+ }
585
+ const otherResult = this.lookupWithFallback(`${key}_other`, void 0);
586
+ if (otherResult !== void 0) {
587
+ this.config.devMode && console.warn(`[LocL] Missing plural: "${key}" in "${this.language}"`);
588
+ return this.interpolate(otherResult, values);
589
+ }
590
+ const anySuffixExists = ["zero", "one", "two", "few", "many"].some((cat) => this.lookupWithFallback(`${key}_${cat}`, void 0) !== void 0);
591
+ if (anySuffixExists) {
592
+ this.config.devMode && console.warn(`[LocL] Missing plural: "${key}" in "${this.language}"`);
593
+ return key;
594
+ }
421
595
  }
596
+ return void 0;
422
597
  }
423
598
  interpolate(result, values) {
424
599
  if (!values || typeof result !== "string") {
@@ -436,34 +611,66 @@ var LocL = class _LocL {
436
611
  depth--;
437
612
  if (depth === 0 && start >= 0) {
438
613
  const raw = result.slice(start, i + 1);
439
- tokens.push({ raw, inner: raw.slice(1, -1).trim() });
614
+ let inner = raw.slice(1, -1);
615
+ if (inner.startsWith("{") && inner.endsWith("}")) {
616
+ inner = inner.slice(1, -1);
617
+ }
618
+ tokens.push({ raw, inner });
440
619
  start = -1;
441
620
  }
442
621
  }
443
622
  }
444
623
  let output = result;
624
+ const processed = /* @__PURE__ */ new Set();
445
625
  for (const token of tokens) {
626
+ if (processed.has(token.raw)) {
627
+ continue;
628
+ }
629
+ processed.add(token.raw);
446
630
  let replacement = "";
447
- if (token.inner.includes("select")) {
448
- const [field, type, ...rest] = token.inner.split(",").map((x) => x.trim());
449
- if (type === "select") {
450
- const value = String(values[field] ?? "other");
451
- const caseRegex = /(\w+)\s*\{([^}]*)\}/g;
452
- const caseMap = {};
453
- let match;
454
- while ((match = caseRegex.exec(rest.join(" "))) !== null) {
455
- const [, key, text] = match;
456
- caseMap[key] = text.trim();
631
+ const selectMatch = token.inner.match(/^\s*([^,]+),\s*select\s*,\s*([\s\S]+)$/);
632
+ if (selectMatch) {
633
+ const field = selectMatch[1].trim();
634
+ const casesBody = selectMatch[2];
635
+ const value = String(values[field] ?? "other");
636
+ const caseMap = {};
637
+ let i = 0;
638
+ while (i < casesBody.length) {
639
+ while (i < casesBody.length && /\s/.test(casesBody[i])) i++;
640
+ if (i >= casesBody.length) break;
641
+ let keyStart = i;
642
+ while (i < casesBody.length && casesBody[i] !== "{" && !/\s/.test(casesBody[i])) i++;
643
+ const caseKey = casesBody.slice(keyStart, i).trim();
644
+ while (i < casesBody.length && casesBody[i] !== "{") i++;
645
+ if (casesBody[i] === "{") {
646
+ let bodyDepth = 1;
647
+ let bodyStart = i + 1;
648
+ i++;
649
+ while (i < casesBody.length && bodyDepth > 0) {
650
+ if (casesBody[i] === "{") bodyDepth++;
651
+ else if (casesBody[i] === "}") bodyDepth--;
652
+ i++;
653
+ }
654
+ const caseText = casesBody.slice(bodyStart, i - 1).trim();
655
+ if (caseKey) {
656
+ caseMap[caseKey] = caseText;
657
+ }
457
658
  }
458
- replacement = caseMap[value] ?? caseMap.other ?? "";
459
659
  }
660
+ const rawReplacement = caseMap[value] ?? caseMap.other ?? "";
661
+ replacement = rawReplacement.includes("{") ? this.interpolate(rawReplacement, values) : rawReplacement;
460
662
  } else {
461
663
  const [fieldName, formatterAndArgs] = token.inner.split("|").map((x) => x.trim());
462
664
  const [formatterName, argsStr] = formatterAndArgs ? formatterAndArgs.split(":") : [];
463
- const args = argsStr ? argsStr.split(",") : [];
665
+ const args = argsStr ? argsStr.split(",").map((a) => a.trim()) : [];
464
666
  const val = values[fieldName];
465
- if (formatterName && this.formatters?.[formatterName]) {
466
- replacement = this.formatters[formatterName](val, args);
667
+ if (formatterName) {
668
+ if (this.formatters?.[formatterName]) {
669
+ replacement = this.formatters[formatterName](val, args);
670
+ } else {
671
+ this.config.devMode && console.warn(`[LocL] Formatter "${formatterName}" not found.`);
672
+ replacement = val !== void 0 ? String(val) : token.raw;
673
+ }
467
674
  } else if (val instanceof Date) {
468
675
  replacement = new Intl.DateTimeFormat(this.language).format(val);
469
676
  } else if (typeof val === "number") {
@@ -472,7 +679,7 @@ var LocL = class _LocL {
472
679
  replacement = val !== void 0 ? String(val) : token.raw;
473
680
  }
474
681
  }
475
- output = output.replace(token.raw, replacement);
682
+ output = output.split(token.raw).join(replacement);
476
683
  }
477
684
  return output;
478
685
  }
@@ -508,6 +715,11 @@ var LocL = class _LocL {
508
715
  function initLocL(config) {
509
716
  return new LocL(config);
510
717
  }
718
+ function defineResources(resources) {
719
+ return resources;
720
+ }
511
721
  export {
722
+ LocL,
723
+ defineResources,
512
724
  initLocL
513
725
  };