gt-react 10.19.16 → 10.19.18

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/macros.mjs CHANGED
@@ -28,7 +28,46 @@ var __copyProps = (to, from, except, desc) => {
28
28
  };
29
29
  var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
30
  //#endregion
31
- //#region ../core/dist/base64-CWITCfhU.mjs
31
+ //#region ../core/dist/base64-r7YWJYWt.mjs
32
+ function ensureSentence(text) {
33
+ const trimmed = text.trim();
34
+ if (!trimmed) return "";
35
+ return /[.!?)]$/.test(trimmed) ? trimmed : `${trimmed}.`;
36
+ }
37
+ function stripSentence(text) {
38
+ const trimmed = text.trim();
39
+ let end = trimmed.length;
40
+ while (end > 0) {
41
+ const char = trimmed[end - 1];
42
+ if (char !== "." && char !== "!" && char !== "?") break;
43
+ end -= 1;
44
+ }
45
+ return trimmed.slice(0, end);
46
+ }
47
+ function lowercaseFirstWord(text) {
48
+ return text.replace(/^[A-Z][a-z]/, (match) => match.toLowerCase());
49
+ }
50
+ function formatDetails(details) {
51
+ if (!details) return "";
52
+ const detailText = Array.isArray(details) ? details.join(", ") : details;
53
+ if (!detailText.trim()) return "";
54
+ return ensureSentence(`Details: ${detailText}`);
55
+ }
56
+ function createDiagnosticMessage({ source, severity, whatHappened, reassurance, why, fix, wayOut, details, docsUrl }) {
57
+ const prefix = source ? severity ? `${source} ${severity}:` : `${source}:` : severity ? `${severity}:` : "";
58
+ const whatAndWhy = why ? `${stripSentence(whatHappened)} because ${lowercaseFirstWord(stripSentence(why))}` : whatHappened;
59
+ const shouldCombineWayOut = !!fix && !!wayOut && /^[a-z]/.test(stripSentence(wayOut));
60
+ const messageParts = [
61
+ whatAndWhy,
62
+ reassurance,
63
+ shouldCombineWayOut ? `${stripSentence(fix)}, or ${lowercaseFirstWord(stripSentence(wayOut))}` : fix,
64
+ shouldCombineWayOut ? void 0 : wayOut,
65
+ formatDetails(details)
66
+ ].filter((part) => !!part).map(ensureSentence);
67
+ if (docsUrl) messageParts.push(`Learn more: ${docsUrl}`);
68
+ const message = messageParts.join(" ");
69
+ return prefix ? `${prefix} ${message}` : message;
70
+ }
32
71
  const defaultCacheUrl = "https://cdn.gtx.dev";
33
72
  //#endregion
34
73
  //#region ../core/dist/isVariable-fAKEB7gF.mjs
@@ -4303,7 +4342,7 @@ function sanitizeJsxChildren(childrenAsObjects) {
4303
4342
  return Array.isArray(childrenAsObjects) ? childrenAsObjects.map(sanitizeChild) : sanitizeChild(childrenAsObjects);
4304
4343
  }
4305
4344
  //#endregion
4306
- //#region ../i18n/dist/versionId-B2xfz6jP.mjs
4345
+ //#region ../i18n/dist/versionId-BTjLA0FZ.mjs
4307
4346
  /**
4308
4347
  * Throw errors if there are any errors and log warnings if there are any warnings
4309
4348
  * @param {ValidationResult[]} results - The results to print
@@ -4349,9 +4388,9 @@ function getLoadTranslationsType(config) {
4349
4388
  * Requirements:
4350
4389
  * - REMOTE:
4351
4390
  * - GT_REMOTE:
4352
- * - projectId is required
4391
+ * - projectId is needed
4353
4392
  * - CUSTOM:
4354
- * - loadTranslations is required
4393
+ * - loadTranslations is needed
4355
4394
  * - DISABLED:
4356
4395
  * - no requirements
4357
4396
  */
@@ -4363,13 +4402,19 @@ function validateLoadTranslations(params) {
4363
4402
  case "gt-remote":
4364
4403
  if (!projectId) results.push({
4365
4404
  type: "warning",
4366
- message: "projectId is required when loading translations from a remote store"
4405
+ message: createDiagnosticMessage({
4406
+ whatHappened: "Loading translations from a remote store needs a projectId",
4407
+ fix: "Add projectId to the I18nManager config or disable remote translation loading"
4408
+ })
4367
4409
  });
4368
4410
  break;
4369
4411
  case "custom":
4370
4412
  if (!loadTranslations) results.push({
4371
4413
  type: "error",
4372
- message: "loadTranslations is required when loading translations from a custom loader"
4414
+ message: createDiagnosticMessage({
4415
+ whatHappened: "Custom translation loading needs loadTranslations",
4416
+ fix: "Provide a loadTranslations function or disable custom translation loading"
4417
+ })
4373
4418
  });
4374
4419
  break;
4375
4420
  case "disabled": break;
@@ -4382,8 +4427,9 @@ function validateLoadTranslations(params) {
4382
4427
  * @returns The runtime translation type
4383
4428
  */
4384
4429
  function getTranslationApiType(params) {
4385
- if ((params.runtimeUrl === void 0 || params.runtimeUrl === "https://runtime2.gtx.dev") && params.projectId && (params.devApiKey || params.apiKey)) return "gt";
4386
- else if (params.runtimeUrl) return "custom";
4430
+ const usesDefaultRuntimeUrl = params.runtimeUrl === void 0 || params.runtimeUrl === "https://runtime2.gtx.dev";
4431
+ if (usesDefaultRuntimeUrl && params.projectId && (params.devApiKey || params.apiKey)) return "gt";
4432
+ else if (params.runtimeUrl && !usesDefaultRuntimeUrl) return "custom";
4387
4433
  else return "disabled";
4388
4434
  }
4389
4435
  /**
@@ -4399,8 +4445,8 @@ function getTranslationApiType(params) {
4399
4445
  * Requirements:
4400
4446
  * - CUSTOM:
4401
4447
  * - GT:
4402
- * - projectId is required
4403
- * - devApiKey or apiKey is required
4448
+ * - projectId is needed
4449
+ * - devApiKey or apiKey is needed
4404
4450
  * - DISABLED:
4405
4451
  * - no requirements
4406
4452
  *
@@ -4413,11 +4459,17 @@ function validateTranslationApi(params) {
4413
4459
  case "gt":
4414
4460
  if (!params.projectId) results.push({
4415
4461
  type: "warning",
4416
- message: "projectId is required"
4462
+ message: createDiagnosticMessage({
4463
+ whatHappened: "Runtime translation needs a projectId",
4464
+ fix: "Add projectId to the I18nManager config or disable runtime translation"
4465
+ })
4417
4466
  });
4418
4467
  if (!params.devApiKey && !params.apiKey) results.push({
4419
4468
  type: "warning",
4420
- message: "devApiKey or apiKey is required"
4469
+ message: createDiagnosticMessage({
4470
+ whatHappened: "Runtime translation needs devApiKey or apiKey",
4471
+ fix: "Add credentials to the I18nManager config or disable runtime translation"
4472
+ })
4421
4473
  });
4422
4474
  break;
4423
4475
  case "disabled": break;
@@ -4446,7 +4498,10 @@ function validateLocales(params) {
4446
4498
  new Set([...defaultLocale ? [defaultLocale] : [], ...locales || []]).forEach((locale) => {
4447
4499
  if (!isValidLocale(locale, customMapping)) results.push({
4448
4500
  type: "error",
4449
- message: `Invalid locale: ${locale}`
4501
+ message: createDiagnosticMessage({
4502
+ whatHappened: `Locale "${locale}" is not valid`,
4503
+ fix: "Use a valid BCP 47 locale code or add a custom mapping"
4504
+ })
4450
4505
  });
4451
4506
  });
4452
4507
  return results;
@@ -4461,7 +4516,10 @@ function validateDictionary(params) {
4461
4516
  const results = [];
4462
4517
  if (params.loadDictionary && !params.dictionary) results.push({
4463
4518
  type: "error",
4464
- message: "dictionary is required when loadDictionary is provided"
4519
+ message: createDiagnosticMessage({
4520
+ whatHappened: "loadDictionary needs a source dictionary",
4521
+ fix: "Provide dictionary so the default locale has source content"
4522
+ })
4465
4523
  });
4466
4524
  return results;
4467
4525
  }
@@ -4550,83 +4608,269 @@ function routeCreateTranslationLoader({ type, remoteTranslationLoaderParams, loa
4550
4608
  case "disabled": return createFallbackTranslationLoader();
4551
4609
  }
4552
4610
  }
4553
- function isPlainObject(value) {
4554
- if (value == null || typeof value !== "object") return false;
4555
- const prototype = Object.getPrototypeOf(value);
4556
- return prototype === Object.prototype || prototype === null;
4611
+ function getDictionaryPath(id) {
4612
+ const path = id ? id.split(".") : [];
4613
+ for (const segment of path) assertSafeDictionaryPathSegment(segment, id);
4614
+ return path;
4615
+ }
4616
+ function assertSafeDictionaryPathSegment(segment, path) {
4617
+ if (segment === "__proto__" || segment === "constructor" || segment === "prototype") throw new Error(`Dictionary path "${path}" contains an unsafe segment`);
4557
4618
  }
4558
- function copyCacheValue(value) {
4559
- if (Array.isArray(value)) return [...value];
4560
- if (isPlainObject(value)) return { ...value };
4561
- return value;
4619
+ function isDictionaryObject(value) {
4620
+ return typeof value === "object" && value != null && !Array.isArray(value);
4621
+ }
4622
+ function cloneDictionaryValue(value) {
4623
+ if (value === void 0 || typeof value === "string") return value;
4624
+ return structuredClone(value);
4625
+ }
4626
+ function getDictionaryValueAtPath(dictionary, path) {
4627
+ let current = dictionary;
4628
+ for (const segment of getDictionaryPath(path)) {
4629
+ if (!isDictionaryObject(current)) return;
4630
+ current = current[segment];
4631
+ }
4632
+ return current;
4633
+ }
4634
+ function setDictionaryValueAtPath(dictionary, path, value) {
4635
+ const segments = getDictionaryPath(path);
4636
+ if (isDictionaryObject(value)) assertSafeDictionaryObject(value, path);
4637
+ if (segments.length === 0) {
4638
+ if (isDictionaryObject(value)) replaceDictionary(dictionary, value);
4639
+ return;
4640
+ }
4641
+ let current = dictionary;
4642
+ for (const segment of segments.slice(0, -1)) {
4643
+ const next = current[segment];
4644
+ if (!isDictionaryObject(next)) current[segment] = {};
4645
+ current = current[segment];
4646
+ }
4647
+ const leafSegment = segments[segments.length - 1];
4648
+ current[leafSegment] = value;
4649
+ }
4650
+ function getDictionaryEntry(value) {
4651
+ if (!isDictionaryLeafNode(value)) return;
4652
+ return {
4653
+ entry: Array.isArray(value) ? value[0] : value,
4654
+ options: Array.isArray(value) ? value[1] ?? {} : {}
4655
+ };
4656
+ }
4657
+ function getDictionaryValue(value) {
4658
+ if (Object.keys(value.options).length === 0) return value.entry;
4659
+ return [value.entry, value.options];
4660
+ }
4661
+ function resolveDictionaryLookupOptions(options) {
4662
+ const { $format, context, ...rest } = options;
4663
+ return {
4664
+ ...rest,
4665
+ $format: isStringFormat($format) ? $format : "ICU",
4666
+ ...rest.$context === void 0 && typeof context === "string" && { $context: context }
4667
+ };
4668
+ }
4669
+ function isDictionaryLeafNode(value) {
4670
+ if (typeof value === "string") return true;
4671
+ if (!Array.isArray(value) || typeof value[0] !== "string") return false;
4672
+ if (value.length === 1) return true;
4673
+ return value.length === 2 && isDictionaryOptions(value[1]);
4674
+ }
4675
+ function isDictionaryOptions(value) {
4676
+ if (typeof value !== "object" || value == null || Array.isArray(value)) return false;
4677
+ const options = value;
4678
+ return (options.$context === void 0 || typeof options.$context === "string") && (options.$format === void 0 || isStringFormat(options.$format)) && (options.$maxChars === void 0 || typeof options.$maxChars === "number") && (options.context === void 0 || typeof options.context === "string");
4679
+ }
4680
+ function isStringFormat(value) {
4681
+ return value === "ICU" || value === "I18NEXT" || value === "STRING";
4682
+ }
4683
+ function replaceDictionary(target, source) {
4684
+ for (const key of Object.keys(target)) delete target[key];
4685
+ for (const key of Object.keys(source)) target[key] = source[key];
4562
4686
  }
4687
+ function assertSafeDictionaryObject(dictionary, parentPath = "") {
4688
+ for (const [key, value] of Object.entries(dictionary)) {
4689
+ const path = parentPath ? `${parentPath}.${key}` : key;
4690
+ assertSafeDictionaryPathSegment(key, path);
4691
+ if (isDictionaryObject(value)) assertSafeDictionaryObject(value, path);
4692
+ }
4693
+ }
4694
+ var DictionarySourceNotFoundError = class extends Error {
4695
+ constructor(id) {
4696
+ super(`I18nManager: source dictionary entry ${id} is not defined`);
4697
+ this.name = "DictionarySourceNotFoundError";
4698
+ }
4699
+ };
4563
4700
  /**
4564
- * Cache class
4565
- * This is designed in such a way that it is the responsibility of the client
4566
- * to invoke the cache miss method when a cache miss occurs.
4567
- *
4568
- * TODO: maybe add "OutputValue" as a reflection of "InputKey"
4701
+ * Builds the dictionary value for a requested path by combining existing target
4702
+ * translations with runtime translations of any source leaves that are missing.
4569
4703
  */
4570
- var Cache = class {
4571
- /**
4572
- * Constructor
4573
- * @param {Object} params - The parameters for the cache
4574
- * @param {Record<CacheKey, CacheValue>} params.init - The initial cache
4575
- * @param {CacheLifecycle} [lifecycle] - Optional lifecycle callbacks
4576
- */
4577
- constructor(init, lifecycle) {
4578
- this.cache = {};
4579
- this.fallbackPromises = {};
4704
+ async function materializeDictionaryValue({ key, sourceValue, targetValue, translateEntry }) {
4705
+ if (getDictionaryEntry(targetValue) !== void 0) return cloneDictionaryValue(targetValue);
4706
+ if (isDictionaryObject(targetValue) && !isDictionaryObject(sourceValue)) return cloneDictionaryValue(targetValue);
4707
+ const sourceEntry = getDictionaryEntry(sourceValue);
4708
+ if (sourceEntry !== void 0) return await translateEntry(key, sourceEntry);
4709
+ if (!isDictionaryObject(sourceValue)) throw new DictionarySourceNotFoundError(key);
4710
+ const targetDictionary = isDictionaryObject(targetValue) ? targetValue : {};
4711
+ const keys = new Set([...Object.keys(sourceValue), ...Object.keys(targetDictionary)]);
4712
+ const entries = await Promise.all(Array.from(keys).map(async (childKey) => {
4713
+ const childPath = key ? `${key}.${childKey}` : childKey;
4714
+ assertSafeDictionaryPathSegment(childKey, childPath);
4715
+ const childSource = sourceValue[childKey];
4716
+ if (childSource === void 0) return [childKey, cloneDictionaryValue(targetDictionary[childKey])];
4717
+ return [childKey, await materializeDictionaryValue({
4718
+ key: childPath,
4719
+ sourceValue: childSource,
4720
+ targetValue: targetDictionary[childKey],
4721
+ translateEntry
4722
+ })];
4723
+ }));
4724
+ return Object.fromEntries(entries);
4725
+ }
4726
+ function cloneDictionaryEntry(entry) {
4727
+ return {
4728
+ entry: entry.entry,
4729
+ options: structuredClone(entry.options)
4730
+ };
4731
+ }
4732
+ var DictionaryCache = class {
4733
+ constructor({ init, lifecycle = {}, runtimeTranslate }) {
4734
+ this.pendingTranslations = /* @__PURE__ */ new Map();
4735
+ this.pendingMaterializations = /* @__PURE__ */ new Map();
4580
4736
  this.cache = structuredClone(init);
4581
- this.onHit = lifecycle?.onHit;
4582
- this.onMiss = lifecycle?.onMiss;
4737
+ this.runtimeTranslate = runtimeTranslate;
4738
+ this.lifecycle = lifecycle;
4583
4739
  }
4584
- /**
4585
- * Set the value for a key
4586
- */
4587
- setCache(cacheKey, value) {
4588
- this.cache[cacheKey] = value;
4740
+ getEntry(key) {
4741
+ const value = getDictionaryValueAtPath(this.cache, key);
4742
+ const entry = getDictionaryEntry(value);
4743
+ if (entry === void 0) return;
4744
+ const outputEntry = cloneDictionaryEntry(entry);
4745
+ this.lifecycle.onHit?.({
4746
+ inputKey: key,
4747
+ cacheKey: key,
4748
+ cacheValue: value,
4749
+ outputValue: outputEntry
4750
+ });
4751
+ return outputEntry;
4589
4752
  }
4590
- /**
4591
- * Look up the key
4592
- */
4593
- getCache(key) {
4594
- const cacheKey = this.genKey(key);
4595
- return this.cache[cacheKey];
4753
+ getValue(key) {
4754
+ const value = getDictionaryValueAtPath(this.cache, key);
4755
+ if (value === void 0) return;
4756
+ const outputValue = cloneDictionaryValue(value);
4757
+ this.lifecycle.onDictionaryObjectCacheHit?.({
4758
+ inputKey: key,
4759
+ cacheKey: key,
4760
+ cacheValue: value,
4761
+ outputValue
4762
+ });
4763
+ return outputValue;
4764
+ }
4765
+ setValue(key, value) {
4766
+ setDictionaryValueAtPath(this.cache, key, cloneDictionaryValue(value));
4596
4767
  }
4597
- /**
4598
- * Get the internal cache
4599
- * @returns The internal cache
4600
- *
4601
- * @internal - used by gt-tanstack-start
4602
- */
4603
4768
  getInternalCache() {
4604
- return Object.fromEntries(Object.entries(this.cache).map(([key, value]) => [key, copyCacheValue(value)]));
4769
+ return cloneDictionaryValue(this.cache);
4770
+ }
4771
+ async materializeValue(key, sourceValue, targetValue = getDictionaryValueAtPath(this.cache, key)) {
4772
+ let materializationPromise = this.pendingMaterializations.get(key);
4773
+ if (!materializationPromise) {
4774
+ materializationPromise = materializeDictionaryValue({
4775
+ key,
4776
+ sourceValue,
4777
+ targetValue,
4778
+ translateEntry: async (entryKey, sourceEntry) => getDictionaryValue(await this.materializeEntry(entryKey, sourceEntry))
4779
+ }).then((value) => {
4780
+ this.setValue(key, value);
4781
+ return value;
4782
+ });
4783
+ this.pendingMaterializations.set(key, materializationPromise);
4784
+ }
4785
+ try {
4786
+ return await materializationPromise;
4787
+ } finally {
4788
+ this.pendingMaterializations.delete(key);
4789
+ }
4605
4790
  }
4606
- /**
4607
- * Get the mutable cache for subclasses that need custom read/write behavior.
4608
- */
4609
- getMutableCache() {
4610
- return this.cache;
4791
+ async materializeEntry(key, sourceEntry) {
4792
+ let translationPromise = this.pendingTranslations.get(key);
4793
+ if (!translationPromise) {
4794
+ translationPromise = this.runtimeTranslate(key, sourceEntry).then((value) => {
4795
+ setDictionaryValueAtPath(this.cache, key, value);
4796
+ const entry = getDictionaryEntry(value);
4797
+ if (entry === void 0) throw new Error("DictionaryCache materializeEntry did not return a DictionaryEntry");
4798
+ this.lifecycle.onMiss?.({
4799
+ inputKey: key,
4800
+ cacheKey: key,
4801
+ cacheValue: value,
4802
+ outputValue: cloneDictionaryEntry(entry)
4803
+ });
4804
+ return cloneDictionaryEntry(entry);
4805
+ });
4806
+ this.pendingTranslations.set(key, translationPromise);
4807
+ }
4808
+ try {
4809
+ return cloneDictionaryEntry(await translationPromise);
4810
+ } finally {
4811
+ this.pendingTranslations.delete(key);
4812
+ }
4611
4813
  }
4612
- /**
4613
- * Fallback to the value from the fallback function on a cache miss
4614
- * @important assumes that the fallback error handling done upstream
4615
- */
4616
- async missCache(...args) {
4617
- const key = args[0];
4618
- const cacheKey = this.genKey(key);
4619
- if (this.fallbackPromises[cacheKey] !== void 0) return await this.fallbackPromises[cacheKey];
4620
- const fallbackPromise = this.fallback(...args);
4621
- this.fallbackPromises[cacheKey] = fallbackPromise;
4814
+ };
4815
+ var ResourceCache = class {
4816
+ constructor({ load, lifecycle = {}, ttl }) {
4817
+ this.cache = /* @__PURE__ */ new Map();
4818
+ this.pendingLoads = /* @__PURE__ */ new Map();
4819
+ this.loadResource = load;
4820
+ this.lifecycle = lifecycle;
4821
+ this.ttl = ttl === null ? -1 : ttl ?? 6e4;
4822
+ }
4823
+ get(key) {
4824
+ const entry = this.cache.get(key);
4825
+ if (!entry || this.isExpired(entry)) return;
4826
+ this.lifecycle.onHit?.({
4827
+ inputKey: key,
4828
+ cacheKey: key,
4829
+ cacheValue: entry,
4830
+ outputValue: entry.value
4831
+ });
4832
+ return entry.value;
4833
+ }
4834
+ set(key, value, { expiresAt = this.getExpiresAt() } = {}) {
4835
+ this.cache.set(key, {
4836
+ expiresAt,
4837
+ value
4838
+ });
4839
+ }
4840
+ async getOrLoad(key) {
4841
+ return this.get(key) ?? await this.load(key);
4842
+ }
4843
+ async load(key) {
4844
+ let loadPromise = this.pendingLoads.get(key);
4845
+ if (!loadPromise) {
4846
+ loadPromise = this.loadResource(key).then((value) => {
4847
+ const entry = {
4848
+ expiresAt: this.getExpiresAt(),
4849
+ value
4850
+ };
4851
+ this.cache.set(key, entry);
4852
+ this.lifecycle.onMiss?.({
4853
+ inputKey: key,
4854
+ cacheKey: key,
4855
+ cacheValue: entry,
4856
+ outputValue: entry.value
4857
+ });
4858
+ return entry;
4859
+ });
4860
+ this.pendingLoads.set(key, loadPromise);
4861
+ }
4622
4862
  try {
4623
- const value = await fallbackPromise;
4624
- this.setCache(cacheKey, value);
4625
- return value;
4863
+ return (await loadPromise).value;
4626
4864
  } finally {
4627
- delete this.fallbackPromises[cacheKey];
4865
+ this.pendingLoads.delete(key);
4628
4866
  }
4629
4867
  }
4868
+ getExpiresAt() {
4869
+ return this.ttl < 0 ? this.ttl : Date.now() + this.ttl;
4870
+ }
4871
+ isExpired(entry) {
4872
+ return entry.expiresAt > 0 && entry.expiresAt < Date.now();
4873
+ }
4630
4874
  };
4631
4875
  /**
4632
4876
  * Hash a message string
@@ -4671,20 +4915,22 @@ function normalizeBatchConfig(batchConfig) {
4671
4915
  * Locale logic is handled at the LocalesCache level. Use a callback function that has the
4672
4916
  * locale parameter embedded if you wish to use the locale code.
4673
4917
  */
4674
- var TranslationsCache = class extends Cache {
4918
+ var TranslationsCache = class {
4675
4919
  /**
4676
4920
  * Constructor
4677
4921
  * @param {Object} params - The parameters for the cache
4678
4922
  * @param {Record<Hash, TranslationValue>} params.init - The initial cache
4679
4923
  * @param {Function} params.fallback - Get the fallback value for a cache miss
4680
4924
  */
4681
- constructor({ init, translateMany, lifecycle, batchConfig }) {
4682
- super(init, lifecycle);
4683
- this._queue = [];
4684
- this._batchTimer = null;
4685
- this._activeRequests = 0;
4686
- this._translateMany = translateMany;
4687
- this._batchConfig = normalizeBatchConfig(batchConfig);
4925
+ constructor({ init, translateMany, lifecycle = {}, batchConfig }) {
4926
+ this.pendingTranslations = /* @__PURE__ */ new Map();
4927
+ this.queue = [];
4928
+ this.batchTimer = null;
4929
+ this.activeRequests = 0;
4930
+ this.cache = structuredClone(init);
4931
+ this.translateMany = translateMany;
4932
+ this.batchConfig = normalizeBatchConfig(batchConfig);
4933
+ this.lifecycle = lifecycle;
4688
4934
  }
4689
4935
  /**
4690
4936
  * Get the translation value for a given key
@@ -4692,10 +4938,11 @@ var TranslationsCache = class extends Cache {
4692
4938
  * @returns The translation value
4693
4939
  */
4694
4940
  get(key) {
4695
- const value = this.getCache(key);
4696
- if (value != null && this.onHit) this.onHit({
4941
+ const cacheKey = this.getCacheKey(key);
4942
+ const value = this.cache[cacheKey];
4943
+ if (value != null) this.lifecycle.onHit?.({
4697
4944
  inputKey: key,
4698
- cacheKey: this.genKey(key),
4945
+ cacheKey,
4699
4946
  cacheValue: value,
4700
4947
  outputValue: value
4701
4948
  });
@@ -4707,75 +4954,64 @@ var TranslationsCache = class extends Cache {
4707
4954
  * @returns The translation value
4708
4955
  */
4709
4956
  async miss(key) {
4710
- const value = await this.missCache(key);
4711
- if (value != null && this.onMiss) this.onMiss({
4712
- inputKey: key,
4713
- cacheKey: this.genKey(key),
4714
- cacheValue: value,
4715
- outputValue: value
4716
- });
4717
- return value;
4957
+ const cacheKey = this.getCacheKey(key);
4958
+ let translationPromise = this.pendingTranslations.get(cacheKey);
4959
+ if (!translationPromise) {
4960
+ translationPromise = this.translate(key);
4961
+ this.pendingTranslations.set(cacheKey, translationPromise);
4962
+ }
4963
+ try {
4964
+ const value = await translationPromise;
4965
+ if (value != null) this.lifecycle.onMiss?.({
4966
+ inputKey: key,
4967
+ cacheKey,
4968
+ cacheValue: value,
4969
+ outputValue: value
4970
+ });
4971
+ return value;
4972
+ } finally {
4973
+ this.pendingTranslations.delete(cacheKey);
4974
+ }
4718
4975
  }
4719
- /**
4720
- * Generate a key for the cache
4721
- * @param key - The translation key
4722
- * @returns The key
4723
- */
4724
- genKey(key) {
4976
+ getInternalCache() {
4977
+ return structuredClone(this.cache);
4978
+ }
4979
+ getCacheKey(key) {
4725
4980
  return hashMessage(key.message, key.options);
4726
4981
  }
4727
- /**
4728
- * Get the fallback value for a cache miss
4729
- * @param key - The translation key
4730
- * @returns The fallback value
4731
- */
4732
- fallback(key) {
4733
- const translationPromise = this._enqueueTranslation(key);
4734
- if (this._queue.length >= this._batchConfig.maxBatchSize) this._flushNow();
4735
- else this._scheduleBatch();
4982
+ translate(key) {
4983
+ const translationPromise = this.enqueueTranslation(key);
4984
+ if (this.queue.length >= this.batchConfig.maxBatchSize) this.flushNow();
4985
+ else this.scheduleBatch();
4736
4986
  return translationPromise;
4737
4987
  }
4738
- /**
4739
- * Flush the queue now
4740
- */
4741
- _flushNow() {
4742
- if (this._batchTimer) {
4743
- clearTimeout(this._batchTimer);
4744
- this._batchTimer = null;
4988
+ flushNow() {
4989
+ if (this.batchTimer) {
4990
+ clearTimeout(this.batchTimer);
4991
+ this.batchTimer = null;
4745
4992
  }
4746
- this._drainQueue();
4747
- }
4748
- /**
4749
- * Schedule a batch of translations
4750
- */
4751
- _scheduleBatch() {
4752
- if (this._batchTimer) return;
4753
- this._batchTimer = setTimeout(() => {
4754
- this._batchTimer = null;
4755
- this._drainQueue();
4756
- }, this._batchConfig.batchInterval);
4757
- }
4758
- /**
4759
- * Drain the queue
4760
- */
4761
- _drainQueue() {
4762
- while (this._queue.length > 0 && this._activeRequests < this._batchConfig.maxConcurrentRequests) {
4763
- const batch = this._queue.splice(0, this._batchConfig.maxBatchSize);
4764
- this._sendBatchRequest(batch);
4993
+ this.drainQueue();
4994
+ }
4995
+ scheduleBatch() {
4996
+ if (this.batchTimer) return;
4997
+ this.batchTimer = setTimeout(() => {
4998
+ this.batchTimer = null;
4999
+ this.drainQueue();
5000
+ }, this.batchConfig.batchInterval);
5001
+ }
5002
+ drainQueue() {
5003
+ while (this.queue.length > 0 && this.activeRequests < this.batchConfig.maxConcurrentRequests) {
5004
+ const batch = this.queue.splice(0, this.batchConfig.maxBatchSize);
5005
+ this.sendBatchRequest(batch);
4765
5006
  }
4766
- if (this._queue.length > 0) this._scheduleBatch();
5007
+ if (this.queue.length > 0) this.scheduleBatch();
4767
5008
  }
4768
- /**
4769
- * Enqueue translation request and return a promise that resolves when the translation is ready
4770
- * @param {TranslationKey<TranslationValue>} key - The translation key
4771
- * @returns {Promise<TranslationValue>} The translation promise
4772
- */
4773
- _enqueueTranslation(key) {
4774
- const hash = this.genKey(key);
5009
+ enqueueTranslation(key) {
5010
+ const hash = this.getCacheKey(key);
4775
5011
  const options = key.options;
4776
5012
  const metadataOptions = options;
4777
5013
  return new Promise((resolve, reject) => {
4778
- this._queue.push({
5014
+ this.queue.push({
4779
5015
  key: hash,
4780
5016
  source: key.message,
4781
5017
  metadata: {
@@ -4790,38 +5026,28 @@ var TranslationsCache = class extends Cache {
4790
5026
  });
4791
5027
  });
4792
5028
  }
4793
- /**
4794
- * Send a batch request for translations
4795
- * @param {QueueEntry<TranslationValue>[]} batch - The batch of requests to send
4796
- */
4797
- async _sendBatchRequest(batch) {
4798
- this._activeRequests++;
5029
+ async sendBatchRequest(batch) {
5030
+ this.activeRequests++;
4799
5031
  const requests = convertBatchToTranslateManyParams(batch);
4800
- const response = await this._sendBatchRequestWithErrorHandling(batch, requests);
4801
- if (response) this._handleTranslationResponse(batch, response);
4802
- this._activeRequests--;
5032
+ const response = await this.sendBatchRequestWithErrorHandling(batch, requests);
5033
+ if (response) this.handleTranslationResponse(batch, response);
5034
+ this.activeRequests--;
4803
5035
  }
4804
- /**
4805
- * Send a translation request with error handling
4806
- */
4807
- async _sendBatchRequestWithErrorHandling(batch, requests) {
5036
+ async sendBatchRequestWithErrorHandling(batch, requests) {
4808
5037
  try {
4809
- return await this._translateMany(requests);
5038
+ return await this.translateMany(requests);
4810
5039
  } catch (error) {
4811
5040
  for (const entry of batch) entry.reject(error);
4812
5041
  return;
4813
5042
  }
4814
5043
  }
4815
- /**
4816
- * Handle a translation response
4817
- */
4818
- _handleTranslationResponse(batch, response) {
5044
+ handleTranslationResponse(batch, response) {
4819
5045
  for (const entry of batch) {
4820
5046
  const { key } = entry;
4821
5047
  const result = response[key];
4822
5048
  if (result && result.success) {
4823
5049
  const translation = result.translation;
4824
- this.setCache(key, translation);
5050
+ this.cache[key] = translation;
4825
5051
  entry.resolve(translation);
4826
5052
  } else entry.reject(result?.error);
4827
5053
  }
@@ -4839,415 +5065,87 @@ function convertBatchToTranslateManyParams(batch) {
4839
5065
  return acc;
4840
5066
  }, {});
4841
5067
  }
4842
- /**
4843
- * Default cache expiry time in milliseconds
4844
- */
4845
- const DEFAULT_CACHE_EXPIRY_TIME = 6e4;
4846
- /**
4847
- * Cache for looking up translations by locale
4848
- */
4849
- var LocalesCache = class extends Cache {
4850
- /**
4851
- * Constructor
4852
- * @param {Object} params - The parameters for the cache
4853
- * @param {Record<string, CacheEntry<TranslationValue>>} params.init - The initial cache
4854
- * @param {number | null} params.ttl - The time to live for cache entries
4855
- * @param {SafeTranslationsLoader<TranslationValue>} params.loadTranslations - The translation loader function
4856
- * @param {CreateTranslateMany} params.createTranslateMany - Factory function for creating a translate many function
4857
- */
4858
- constructor({ init = {}, ttl, batchConfig, loadTranslations, createTranslateMany, lifecycle: { onLocalesCacheHit: onHit, onLocalesCacheMiss: onMiss, onTranslationsCacheHit, onTranslationsCacheMiss } }) {
4859
- super(init, {
4860
- onHit,
4861
- onMiss
5068
+ var LocalesCache = class {
5069
+ constructor({ ttl, batchConfig, defaultLocale, dictionary = {}, loadTranslations, loadDictionary, createTranslateMany, translateDictionaryEntry, lifecycle }) {
5070
+ this.translations = new ResourceCache({
5071
+ ttl,
5072
+ load: async (locale) => new TranslationsCache({
5073
+ init: await loadTranslations(locale),
5074
+ lifecycle: createTranslationsCacheLifecycle(locale, lifecycle),
5075
+ translateMany: createTranslateMany(locale),
5076
+ batchConfig
5077
+ }),
5078
+ lifecycle: {
5079
+ onHit: lifecycle.onLocalesCacheHit,
5080
+ onMiss: lifecycle.onLocalesCacheMiss
5081
+ }
4862
5082
  });
4863
- this.ttl = DEFAULT_CACHE_EXPIRY_TIME;
4864
- this.ttl = ttl === null ? -1 : ttl ?? 6e4;
4865
- this._translationLoader = loadTranslations;
4866
- this._createTranslateMany = createTranslateMany;
4867
- this._batchConfig = batchConfig;
4868
- this._onTranslationsCacheHit = onTranslationsCacheHit;
4869
- this._onTranslationsCacheMiss = onTranslationsCacheMiss;
4870
- }
4871
- /**
4872
- * Get the translations for a given locale
4873
- * @param key - The locale
4874
- * @returns The translations
4875
- */
4876
- get(key) {
4877
- const entry = this.getCache(key);
4878
- if (!entry || entry.expiresAt > 0 && entry.expiresAt < Date.now()) return;
4879
- const value = entry.translationsCache;
4880
- if (value != null && this.onHit) this.onHit({
4881
- inputKey: key,
4882
- cacheKey: this.genKey(key),
4883
- cacheValue: entry,
4884
- outputValue: value
5083
+ this.dictionaries = new ResourceCache({
5084
+ ttl,
5085
+ load: async (locale) => createDictionaryCache({
5086
+ locale,
5087
+ dictionary: await loadDictionary(locale),
5088
+ translate: translateDictionaryEntry,
5089
+ lifecycle
5090
+ }),
5091
+ lifecycle: {
5092
+ onHit: lifecycle.onLocalesDictionaryCacheHit,
5093
+ onMiss: lifecycle.onLocalesDictionaryCacheMiss
5094
+ }
4885
5095
  });
4886
- return value;
5096
+ this.dictionaries.set(defaultLocale, createDictionaryCache({
5097
+ locale: defaultLocale,
5098
+ dictionary,
5099
+ translate: translateDictionaryEntry,
5100
+ lifecycle
5101
+ }), { expiresAt: -1 });
4887
5102
  }
4888
- /**
4889
- * Miss the cache
4890
- * @param key - The locale
4891
- * @returns The translations cache
4892
- */
4893
- async miss(key) {
4894
- const cacheValue = await this.missCache(key);
4895
- const value = cacheValue.translationsCache;
4896
- if (value != null && this.onMiss) this.onMiss({
4897
- inputKey: key,
4898
- cacheKey: this.genKey(key),
4899
- cacheValue,
4900
- outputValue: value
4901
- });
4902
- return value;
5103
+ getTranslations(locale) {
5104
+ return this.translations.get(locale);
4903
5105
  }
4904
- /**
4905
- * Generate the cache key for a given locale
4906
- * @param key - The locale
4907
- * @returns The cache key
4908
- *
4909
- * This is just an identity function, no transformation needed
4910
- */
4911
- genKey(key) {
4912
- return key;
5106
+ getOrLoadTranslations(locale) {
5107
+ return this.translations.getOrLoad(locale);
4913
5108
  }
4914
- /**
4915
- * Fallback for a cache miss
4916
- * @param locale - The locale
4917
- * @returns The cache entry
4918
- */
4919
- async fallback(locale) {
4920
- return {
4921
- translationsCache: new TranslationsCache({
4922
- init: await this._translationLoader(locale),
4923
- lifecycle: this._createTranslationsCacheLifecycle(locale),
4924
- translateMany: this._createTranslateMany(locale),
4925
- batchConfig: this._batchConfig
4926
- }),
4927
- expiresAt: this.ttl < 0 ? this.ttl : Date.now() + this.ttl
4928
- };
5109
+ getDictionary(locale) {
5110
+ return this.dictionaries.get(locale);
4929
5111
  }
4930
- /**
4931
- * Create the translations cache lifecycle
4932
- * @param locale - The locale
4933
- * @returns The translations cache lifecycle
4934
- */
4935
- _createTranslationsCacheLifecycle(locale) {
4936
- return {
4937
- onHit: this._onTranslationsCacheHit ? (params) => this._onTranslationsCacheHit({
4938
- locale,
4939
- ...params
4940
- }) : void 0,
4941
- onMiss: this._onTranslationsCacheMiss ? (params) => this._onTranslationsCacheMiss({
4942
- locale,
4943
- ...params
4944
- }) : void 0
4945
- };
5112
+ getOrLoadDictionary(locale) {
5113
+ return this.dictionaries.getOrLoad(locale);
4946
5114
  }
4947
5115
  };
4948
- function getDictionaryPath(id) {
4949
- if (!id) return [];
4950
- return id.split(".");
4951
- }
4952
- function isDictionaryValue(value) {
4953
- return typeof value === "object" && value != null && !Array.isArray(value);
4954
- }
4955
- function getDictionaryEntry(value) {
4956
- if (!isDictionaryLeafNode(value)) return;
4957
- return {
4958
- entry: Array.isArray(value) ? value[0] : value,
4959
- options: Array.isArray(value) ? value[1] ?? {} : {}
4960
- };
4961
- }
4962
- function getDictionaryValue(value) {
4963
- if (Object.keys(value.options).length === 0) return value.entry;
4964
- return [value.entry, value.options];
4965
- }
4966
- function resolveDictionaryLookupOptions(options) {
4967
- const { $format, ...rest } = options;
5116
+ function createTranslationsCacheLifecycle(locale, lifecycle) {
4968
5117
  return {
4969
- ...rest,
4970
- $format: isStringFormat($format) ? $format : "ICU",
4971
- ...rest.$context === void 0 && typeof rest.context === "string" && { $context: rest.context }
5118
+ onHit: withLocale(locale, lifecycle.onTranslationsCacheHit),
5119
+ onMiss: withLocale(locale, lifecycle.onTranslationsCacheMiss)
4972
5120
  };
4973
5121
  }
4974
- function isDictionaryLeafNode(value) {
4975
- if (typeof value === "string") return true;
4976
- if (!Array.isArray(value) || typeof value[0] !== "string") return false;
4977
- if (value.length === 1) return true;
4978
- return value.length === 2 && isDictionaryOptions(value[1]);
4979
- }
4980
- function isDictionaryOptions(value) {
4981
- if (typeof value !== "object" || value == null || Array.isArray(value)) return false;
4982
- const options = value;
4983
- return (options.$context === void 0 || typeof options.$context === "string") && (options.$format === void 0 || isStringFormat(options.$format)) && (options.$maxChars === void 0 || typeof options.$maxChars === "number") && (options.context === void 0 || typeof options.context === "string");
4984
- }
4985
- function isStringFormat(value) {
4986
- return value === "ICU" || value === "I18NEXT" || value === "STRING";
5122
+ function createDictionaryCache({ locale, dictionary, translate, lifecycle }) {
5123
+ const { onDictionaryCacheHit, onDictionaryCacheMiss, onDictionaryObjectCacheHit } = lifecycle;
5124
+ return new DictionaryCache({
5125
+ init: dictionary,
5126
+ runtimeTranslate: (key, sourceEntry) => translate(locale, key, sourceEntry),
5127
+ lifecycle: {
5128
+ onHit: withLocale(locale, onDictionaryCacheHit),
5129
+ onMiss: withLocale(locale, onDictionaryCacheMiss),
5130
+ onDictionaryObjectCacheHit: withLocale(locale, onDictionaryObjectCacheHit)
5131
+ }
5132
+ });
4987
5133
  }
4988
- function replaceDictionary(target, source) {
4989
- for (const key of Object.keys(target)) delete target[key];
4990
- Object.assign(target, source);
5134
+ function withLocale(locale, callback) {
5135
+ return callback ? (params) => callback({
5136
+ locale,
5137
+ ...params
5138
+ }) : void 0;
4991
5139
  }
4992
- var DictionarySourceNotFoundError = class extends Error {
4993
- constructor(id) {
4994
- super(`I18nManager: source dictionary entry ${id} is not defined`);
4995
- this.name = "DictionarySourceNotFoundError";
4996
- }
4997
- };
4998
- /**
4999
- * A cache for a single locale's dictionary
5000
- *
5001
- * Principles:
5002
- * - This class is language agnostic, and should never store the locale code as a parameter.
5003
- * Locale logic is handled at the LocalesDictionaryCache level. Use a callback function
5004
- * that has the locale parameter embedded if you wish to use the locale code.
5005
- */
5006
- var DictionaryCache = class extends Cache {
5007
- /**
5008
- * Constructor
5009
- * @param {Object} params - The parameters for the cache
5010
- * @param {Dictionary} params.init - The initial cache
5011
- */
5012
- constructor({ init, lifecycle, runtimeTranslate }) {
5013
- super(init, lifecycle);
5014
- this._runtimeTranslate = runtimeTranslate;
5015
- this.onHitObj = lifecycle?.onHitObj;
5016
- this.onMissObj = lifecycle?.onMissObj;
5017
- }
5018
- /**
5019
- * Get the dictionary value for a given key
5020
- * @param key - The dictionary key
5021
- * @returns The dictionary value
5022
- */
5023
- get(key) {
5024
- const value = this.getCache(key);
5025
- const entry = getDictionaryEntry(value);
5026
- if (entry === void 0) return;
5027
- if (this.onHit) this.onHit({
5028
- inputKey: key,
5029
- cacheKey: this.genKey(key),
5030
- cacheValue: value,
5031
- outputValue: entry
5032
- });
5033
- return entry;
5034
- }
5035
- set(key, value) {
5036
- const dictionaryValue = getDictionaryValue(value);
5037
- this.setCache(this.genKey(key), dictionaryValue);
5038
- }
5039
- getObj(key) {
5040
- const value = this.getCache(key);
5041
- if (value === void 0) return;
5042
- const outputValue = structuredClone(value);
5043
- if (this.onHitObj) this.onHitObj({
5044
- inputKey: key,
5045
- cacheKey: this.genKey(key),
5046
- cacheValue: value,
5047
- outputValue
5048
- });
5049
- return outputValue;
5050
- }
5051
- setObj(key, value) {
5052
- this.setCache(this.genKey(key), structuredClone(value));
5053
- }
5054
- async missObj(key, sourceObject) {
5055
- const sourceEntry = getDictionaryEntry(sourceObject);
5056
- if (sourceEntry !== void 0) return getDictionaryValue(await this.miss(key, sourceEntry));
5057
- if (!isDictionaryValue(sourceObject)) throw new DictionarySourceNotFoundError(key);
5058
- const translatedEntries = await Promise.all(Object.entries(sourceObject).map(async ([childKey, childSource]) => {
5059
- const childPath = key ? `${key}.${childKey}` : childKey;
5060
- return [childKey, await this.missObj(childPath, childSource)];
5061
- }));
5062
- const translatedObject = Object.fromEntries(translatedEntries);
5063
- this.setObj(key, translatedObject);
5064
- return translatedObject;
5065
- }
5066
- /**
5067
- * Miss the cache
5068
- * @param key - The dictionary key
5069
- * @returns The dictionary value
5070
- */
5071
- async miss(key, sourceEntry) {
5072
- const value = await this.missCache(key, sourceEntry);
5073
- const entry = getDictionaryEntry(value);
5074
- if (entry === void 0) throw new Error("DictionaryCache missCache did not return a DictionaryEntry");
5075
- if (this.onMiss) this.onMiss({
5076
- inputKey: key,
5077
- cacheKey: this.genKey(key),
5078
- cacheValue: value,
5079
- outputValue: entry
5080
- });
5081
- return entry;
5082
- }
5083
- /**
5084
- * Set the value for a key
5085
- */
5086
- setCache(cacheKey, value) {
5087
- const cache = this.getMutableCache();
5088
- const dictionaryPath = getDictionaryPath(cacheKey);
5089
- if (dictionaryPath.length === 0) {
5090
- if (isDictionaryValue(value)) replaceDictionary(cache, value);
5091
- return;
5092
- }
5093
- let current = cache;
5094
- for (const key of dictionaryPath.slice(0, -1)) {
5095
- const next = current[key];
5096
- if (!isDictionaryValue(next)) current[key] = {};
5097
- current = current[key];
5098
- }
5099
- current[dictionaryPath[dictionaryPath.length - 1]] = value;
5100
- }
5101
- /**
5102
- * Look up the key
5103
- */
5104
- getCache(key) {
5105
- const dictionaryPath = getDictionaryPath(this.genKey(key));
5106
- let current = this.getMutableCache();
5107
- if (dictionaryPath.length === 0) return current;
5108
- for (const pathSegment of dictionaryPath) {
5109
- if (!isDictionaryValue(current)) return;
5110
- current = current[pathSegment];
5111
- }
5112
- return current;
5113
- }
5114
- /**
5115
- * Generate a key for the cache
5116
- * @param key - The dictionary key
5117
- * @returns The key
5118
- */
5119
- genKey(key) {
5120
- return key;
5121
- }
5122
- /**
5123
- * Get the fallback value for a cache miss
5124
- * @param key - The dictionary key
5125
- * @returns The fallback value
5126
- *
5127
- * @throws {Error} - If the fallback is not implemented
5128
- */
5129
- fallback(key, sourceEntry) {
5130
- return this._runtimeTranslate(key, sourceEntry);
5131
- }
5132
- };
5133
- /**
5134
- * Cache for looking up dictionaries by locale
5135
- */
5136
- var LocalesDictionaryCache = class extends Cache {
5137
- /**
5138
- * Constructor
5139
- * @param {Object} params - The parameters for the cache
5140
- * @param {number | null} params.ttl - The time to live for cache entries
5141
- * @param {DictionaryLoader} params.loadDictionary - The dictionary loader function
5142
- */
5143
- constructor({ ttl, defaultLocale, dictionary = {}, loadDictionary, runtimeTranslate, lifecycle: { onLocalesDictionaryCacheHit: onHit, onLocalesDictionaryCacheMiss: onMiss, onDictionaryCacheHit, onDictionaryCacheMiss, onDictionaryObjectCacheHit } }) {
5144
- super({}, {
5145
- onHit,
5146
- onMiss
5147
- });
5148
- this.ttl = DEFAULT_CACHE_EXPIRY_TIME;
5149
- this.ttl = ttl === null ? -1 : ttl ?? 6e4;
5150
- this._dictionaryLoader = loadDictionary;
5151
- this._runtimeTranslate = runtimeTranslate;
5152
- this._onDictionaryCacheHit = onDictionaryCacheHit;
5153
- this._onDictionaryCacheMiss = onDictionaryCacheMiss;
5154
- this._onDictionaryObjectCacheHit = onDictionaryObjectCacheHit;
5155
- this.setCache(defaultLocale, {
5156
- dictionaryCache: new DictionaryCache({
5157
- init: dictionary,
5158
- runtimeTranslate: this._createDictionaryRuntimeTranslate(defaultLocale),
5159
- lifecycle: this._createDictionaryCacheLifecycle(defaultLocale)
5160
- }),
5161
- expiresAt: -1
5162
- });
5163
- }
5164
- /**
5165
- * Get the dictionary for a given locale
5166
- * @param key - The locale
5167
- * @returns The dictionary
5168
- */
5169
- get(key) {
5170
- const entry = this.getCache(key);
5171
- if (!entry || entry.expiresAt > 0 && entry.expiresAt < Date.now()) return;
5172
- const value = entry.dictionaryCache;
5173
- if (value != null && this.onHit) this.onHit({
5174
- inputKey: key,
5175
- cacheKey: this.genKey(key),
5176
- cacheValue: entry,
5177
- outputValue: value
5178
- });
5179
- return value;
5180
- }
5181
- /**
5182
- * Miss the cache
5183
- * @param key - The locale
5184
- * @returns The dictionary cache
5185
- */
5186
- async miss(key) {
5187
- const cacheValue = await this.missCache(key);
5188
- const value = cacheValue.dictionaryCache;
5189
- if (value != null && this.onMiss) this.onMiss({
5190
- inputKey: key,
5191
- cacheKey: this.genKey(key),
5192
- cacheValue,
5193
- outputValue: value
5194
- });
5195
- return value;
5196
- }
5197
- /**
5198
- * Generate the cache key for a given locale
5199
- * @param key - The locale
5200
- * @returns The cache key
5201
- *
5202
- * This is just an identity function, no transformation needed
5203
- */
5204
- genKey(key) {
5205
- return key;
5206
- }
5207
- /**
5208
- * Fallback for a cache miss
5209
- * @param locale - The locale
5210
- * @returns The cache entry
5211
- */
5212
- async fallback(locale) {
5213
- return {
5214
- dictionaryCache: new DictionaryCache({
5215
- init: await this._dictionaryLoader(locale),
5216
- runtimeTranslate: this._createDictionaryRuntimeTranslate(locale),
5217
- lifecycle: this._createDictionaryCacheLifecycle(locale)
5218
- }),
5219
- expiresAt: this.ttl < 0 ? this.ttl : Date.now() + this.ttl
5220
- };
5221
- }
5222
- /**
5223
- * Create the dictionary cache lifecycle
5224
- * @param locale - The locale
5225
- * @returns The dictionary cache lifecycle
5226
- */
5227
- _createDictionaryCacheLifecycle(locale) {
5228
- return {
5229
- onHit: this._onDictionaryCacheHit ? (params) => this._onDictionaryCacheHit({
5230
- locale,
5231
- ...params
5232
- }) : void 0,
5233
- onMiss: this._onDictionaryCacheMiss ? (params) => this._onDictionaryCacheMiss({
5234
- locale,
5235
- ...params
5236
- }) : void 0,
5237
- onHitObj: this._onDictionaryObjectCacheHit ? (params) => this._onDictionaryObjectCacheHit({
5238
- locale,
5239
- ...params
5240
- }) : void 0
5241
- };
5242
- }
5243
- _createDictionaryRuntimeTranslate(locale) {
5244
- return (key, sourceEntry) => this._runtimeTranslate(locale, key, sourceEntry);
5245
- }
5246
- };
5140
+ const LOCALES_CACHE_HIT_EVENT_NAME = "locales-cache-hit";
5247
5141
  const LOCALES_CACHE_MISS_EVENT_NAME = "locales-cache-miss";
5142
+ const TRANSLATIONS_CACHE_HIT_EVENT_NAME = "translations-cache-hit";
5248
5143
  const TRANSLATIONS_CACHE_MISS_EVENT_NAME = "translations-cache-miss";
5144
+ const LOCALES_DICTIONARY_CACHE_HIT_EVENT_NAME = "locales-dictionary-cache-hit";
5249
5145
  const LOCALES_DICTIONARY_CACHE_MISS_EVENT_NAME = "locales-dictionary-cache-miss";
5146
+ const DICTIONARY_CACHE_HIT_EVENT_NAME = "dictionary-cache-hit";
5250
5147
  const DICTIONARY_CACHE_MISS_EVENT_NAME = "dictionary-cache-miss";
5148
+ const DICTIONARY_OBJECT_CACHE_HIT_EVENT_NAME = "dictionary-object-cache-hit";
5251
5149
  /**
5252
5150
  * Maps consumer-facing lifecycle callbacks to internal locales cache lifecycle callbacks.
5253
5151
  * The consumer API exposes simplified params (locale, hash, value) while the internal
@@ -5255,22 +5153,24 @@ const DICTIONARY_CACHE_MISS_EVENT_NAME = "dictionary-cache-miss";
5255
5153
  *
5256
5154
  * @deprecated - move to subscription api instead
5257
5155
  */
5258
- function createLifecycleCallbacks(emit) {
5156
+ function createLifecycleCallbacks(emit, hasListeners = () => true) {
5259
5157
  return {
5260
5158
  onLocalesCacheHit: (params) => {
5261
- emit("locales-cache-hit", {
5159
+ if (!hasListeners("locales-cache-hit")) return;
5160
+ emit(LOCALES_CACHE_HIT_EVENT_NAME, {
5262
5161
  locale: params.inputKey,
5263
5162
  translations: params.outputValue.getInternalCache()
5264
5163
  });
5265
5164
  },
5266
5165
  onLocalesCacheMiss: (params) => {
5166
+ if (!hasListeners("locales-cache-miss")) return;
5267
5167
  emit(LOCALES_CACHE_MISS_EVENT_NAME, {
5268
5168
  locale: params.inputKey,
5269
5169
  translations: params.outputValue.getInternalCache()
5270
5170
  });
5271
5171
  },
5272
5172
  onTranslationsCacheHit: (params) => {
5273
- emit("translations-cache-hit", {
5173
+ emit(TRANSLATIONS_CACHE_HIT_EVENT_NAME, {
5274
5174
  locale: params.locale,
5275
5175
  hash: params.cacheKey,
5276
5176
  translation: params.outputValue
@@ -5284,19 +5184,21 @@ function createLifecycleCallbacks(emit) {
5284
5184
  });
5285
5185
  },
5286
5186
  onLocalesDictionaryCacheHit: (params) => {
5287
- emit("locales-dictionary-cache-hit", {
5187
+ if (!hasListeners("locales-dictionary-cache-hit")) return;
5188
+ emit(LOCALES_DICTIONARY_CACHE_HIT_EVENT_NAME, {
5288
5189
  locale: params.inputKey,
5289
5190
  dictionary: params.outputValue.getInternalCache()
5290
5191
  });
5291
5192
  },
5292
5193
  onLocalesDictionaryCacheMiss: (params) => {
5194
+ if (!hasListeners("locales-dictionary-cache-miss")) return;
5293
5195
  emit(LOCALES_DICTIONARY_CACHE_MISS_EVENT_NAME, {
5294
5196
  locale: params.inputKey,
5295
5197
  dictionary: params.outputValue.getInternalCache()
5296
5198
  });
5297
5199
  },
5298
5200
  onDictionaryCacheHit: (params) => {
5299
- emit("dictionary-cache-hit", {
5201
+ emit(DICTIONARY_CACHE_HIT_EVENT_NAME, {
5300
5202
  locale: params.locale,
5301
5203
  id: params.cacheKey,
5302
5204
  dictionaryEntry: params.outputValue
@@ -5310,7 +5212,7 @@ function createLifecycleCallbacks(emit) {
5310
5212
  });
5311
5213
  },
5312
5214
  onDictionaryObjectCacheHit: (params) => {
5313
- emit("dictionary-object-cache-hit", {
5215
+ emit(DICTIONARY_OBJECT_CACHE_HIT_EVENT_NAME, {
5314
5216
  locale: params.locale,
5315
5217
  id: params.cacheKey,
5316
5218
  dictionaryValue: params.outputValue
@@ -5345,6 +5247,9 @@ var EventEmitter = class {
5345
5247
  emit(eventName, event) {
5346
5248
  this.listeners[eventName]?.forEach((subscriber) => subscriber(event));
5347
5249
  }
5250
+ hasListeners(eventName) {
5251
+ return (this.listeners[eventName]?.size ?? 0) > 0;
5252
+ }
5348
5253
  };
5349
5254
  /**
5350
5255
  * Subscribes to the lifecycle callbacks and emits the events to the event emitter
@@ -5354,7 +5259,7 @@ var EventEmitter = class {
5354
5259
  * and is only used internally
5355
5260
  */
5356
5261
  function subscribeLifecycleCallbacks({ onLocalesCacheHit, onLocalesCacheMiss, onTranslationsCacheHit, onTranslationsCacheMiss, onLocalesDictionaryCacheHit, onLocalesDictionaryCacheMiss, onDictionaryCacheHit, onDictionaryCacheMiss, onDictionaryObjectCacheHit }, subscribe) {
5357
- if (onLocalesCacheHit) subscribe("locales-cache-hit", (event) => {
5262
+ if (onLocalesCacheHit) subscribe(LOCALES_CACHE_HIT_EVENT_NAME, (event) => {
5358
5263
  onLocalesCacheHit({
5359
5264
  ...event,
5360
5265
  value: event.translations
@@ -5366,7 +5271,7 @@ function subscribeLifecycleCallbacks({ onLocalesCacheHit, onLocalesCacheMiss, on
5366
5271
  value: event.translations
5367
5272
  });
5368
5273
  });
5369
- if (onTranslationsCacheHit) subscribe("translations-cache-hit", (event) => {
5274
+ if (onTranslationsCacheHit) subscribe(TRANSLATIONS_CACHE_HIT_EVENT_NAME, (event) => {
5370
5275
  onTranslationsCacheHit({
5371
5276
  ...event,
5372
5277
  value: event.translation
@@ -5378,19 +5283,19 @@ function subscribeLifecycleCallbacks({ onLocalesCacheHit, onLocalesCacheMiss, on
5378
5283
  value: event.translation
5379
5284
  });
5380
5285
  });
5381
- if (onLocalesDictionaryCacheHit) subscribe("locales-dictionary-cache-hit", (event) => {
5286
+ if (onLocalesDictionaryCacheHit) subscribe(LOCALES_DICTIONARY_CACHE_HIT_EVENT_NAME, (event) => {
5382
5287
  onLocalesDictionaryCacheHit(event);
5383
5288
  });
5384
5289
  if (onLocalesDictionaryCacheMiss) subscribe(LOCALES_DICTIONARY_CACHE_MISS_EVENT_NAME, (event) => {
5385
5290
  onLocalesDictionaryCacheMiss(event);
5386
5291
  });
5387
- if (onDictionaryCacheHit) subscribe("dictionary-cache-hit", (event) => {
5292
+ if (onDictionaryCacheHit) subscribe(DICTIONARY_CACHE_HIT_EVENT_NAME, (event) => {
5388
5293
  onDictionaryCacheHit(event);
5389
5294
  });
5390
5295
  if (onDictionaryCacheMiss) subscribe(DICTIONARY_CACHE_MISS_EVENT_NAME, (event) => {
5391
5296
  onDictionaryCacheMiss(event);
5392
5297
  });
5393
- if (onDictionaryObjectCacheHit) subscribe("dictionary-object-cache-hit", (event) => {
5298
+ if (onDictionaryObjectCacheHit) subscribe(DICTIONARY_OBJECT_CACHE_HIT_EVENT_NAME, (event) => {
5394
5299
  onDictionaryObjectCacheHit(event);
5395
5300
  });
5396
5301
  }
@@ -5421,26 +5326,32 @@ var I18nManager = class extends EventEmitter {
5421
5326
  locales: this.config.locales,
5422
5327
  customMapping: this.config.customMapping
5423
5328
  });
5424
- const loadTranslations = createTranslationLoader(params);
5425
- const loadDictionary = createDictionaryLoader(params);
5329
+ const loadTranslations = routeCreateTranslationLoader({
5330
+ loadTranslations: params.loadTranslations,
5331
+ type: getLoadTranslationsType(params),
5332
+ remoteTranslationLoaderParams: {
5333
+ cacheUrl: params.cacheUrl,
5334
+ projectId: params.projectId,
5335
+ _versionId: params._versionId,
5336
+ _branchId: params._branchId,
5337
+ customMapping: params.customMapping
5338
+ }
5339
+ });
5340
+ const loadDictionary = params.loadDictionary ?? (() => Promise.resolve({}));
5426
5341
  const runtimeTranslationTimeout = this.config.runtimeTranslation?.timeout ?? DEFAULT_TRANSLATION_TIMEOUT;
5427
5342
  const runtimeTranslationMetadata = this.config.runtimeTranslation?.metadata ?? {};
5428
5343
  const createTranslateMany = createTranslateManyFactory(this.getGTClassClean(), runtimeTranslationTimeout, runtimeTranslationMetadata);
5429
5344
  subscribeLifecycleCallbacks(params.lifecycle ?? {}, (...args) => this.subscribe(...args));
5430
- const lifecycle = createLifecycleCallbacks((...args) => this.emit(...args));
5345
+ const lifecycle = createLifecycleCallbacks((...args) => this.emit(...args), (eventName) => this.hasListeners(eventName));
5431
5346
  this.localesCache = new LocalesCache({
5432
- loadTranslations,
5433
- createTranslateMany,
5434
- lifecycle,
5435
- ttl: this.config.cacheExpiryTime,
5436
- batchConfig: this.config.batchConfig
5437
- });
5438
- this.localesDictionaryCache = new LocalesDictionaryCache({
5439
5347
  defaultLocale: this.config.defaultLocale,
5440
5348
  dictionary: params.dictionary,
5349
+ loadTranslations,
5441
5350
  loadDictionary,
5442
- runtimeTranslate: (locale, id, sourceEntry) => this.dictionaryRuntimeTranslate(locale, id, sourceEntry),
5351
+ createTranslateMany,
5352
+ translateDictionaryEntry: (locale, id, sourceEntry) => this.translateDictionaryEntry(locale, id, sourceEntry),
5443
5353
  ttl: this.config.cacheExpiryTime,
5354
+ batchConfig: this.config.batchConfig,
5444
5355
  lifecycle
5445
5356
  });
5446
5357
  }
@@ -5512,9 +5423,7 @@ var I18nManager = class extends EventEmitter {
5512
5423
  try {
5513
5424
  const translationLocale = this.resolveCacheLocale(locale);
5514
5425
  if (!translationLocale) return {};
5515
- let txCache = this.localesCache.get(translationLocale);
5516
- if (!txCache) txCache = await this.localesCache.miss(translationLocale);
5517
- return txCache.getInternalCache();
5426
+ return (await this.localesCache.getOrLoadTranslations(translationLocale)).getInternalCache();
5518
5427
  } catch (error) {
5519
5428
  this.handleError(error);
5520
5429
  return {};
@@ -5527,10 +5436,8 @@ var I18nManager = class extends EventEmitter {
5527
5436
  async loadDictionary(locale) {
5528
5437
  try {
5529
5438
  const dictionaryLocale = this.resolveCacheLocale(locale);
5530
- if (!dictionaryLocale) return this.localesDictionaryCache.get(this.config.defaultLocale)?.getInternalCache() ?? {};
5531
- let dictionaryCache = this.localesDictionaryCache.get(dictionaryLocale);
5532
- if (!dictionaryCache) dictionaryCache = await this.localesDictionaryCache.miss(dictionaryLocale);
5533
- return dictionaryCache.getInternalCache();
5439
+ if (!dictionaryLocale) return this.getDefaultDictionaryCache()?.getInternalCache() ?? {};
5440
+ return (await this.localesCache.getOrLoadDictionary(dictionaryLocale)).getInternalCache();
5534
5441
  } catch (error) {
5535
5442
  this.handleError(error);
5536
5443
  return {};
@@ -5541,8 +5448,8 @@ var I18nManager = class extends EventEmitter {
5541
5448
  */
5542
5449
  lookupDictionary(locale, id) {
5543
5450
  try {
5544
- const dictionaryLocale = this.resolveCacheLocale(locale) ?? this.config.defaultLocale;
5545
- return this.localesDictionaryCache.get(dictionaryLocale)?.get(id);
5451
+ const dictionaryLocale = this.resolveDictionaryCacheLocale(locale);
5452
+ return this.localesCache.getDictionary(dictionaryLocale)?.getEntry(id);
5546
5453
  } catch (error) {
5547
5454
  this.handleError(error);
5548
5455
  return;
@@ -5553,8 +5460,8 @@ var I18nManager = class extends EventEmitter {
5553
5460
  */
5554
5461
  lookupDictionaryObj(locale, id) {
5555
5462
  try {
5556
- const dictionaryLocale = this.resolveCacheLocale(locale) ?? this.config.defaultLocale;
5557
- return this.localesDictionaryCache.get(dictionaryLocale)?.getObj(id);
5463
+ const dictionaryLocale = this.resolveDictionaryCacheLocale(locale);
5464
+ return this.localesCache.getDictionary(dictionaryLocale)?.getValue(id);
5558
5465
  } catch (error) {
5559
5466
  this.handleError(error);
5560
5467
  return;
@@ -5567,19 +5474,10 @@ var I18nManager = class extends EventEmitter {
5567
5474
  async lookupDictionaryWithFallback(locale, id) {
5568
5475
  try {
5569
5476
  const dictionaryLocale = this.resolveCacheLocale(locale);
5570
- if (!dictionaryLocale) {
5571
- const sourceEntry = this.localesDictionaryCache.get(this.config.defaultLocale)?.get(id);
5572
- if (sourceEntry === void 0) throw new DictionarySourceNotFoundError(id);
5573
- return sourceEntry;
5574
- }
5575
- let dictionaryCache = this.localesDictionaryCache.get(dictionaryLocale);
5576
- if (!dictionaryCache) dictionaryCache = await this.localesDictionaryCache.miss(dictionaryLocale);
5577
- let dictionaryEntry = dictionaryCache.get(id);
5578
- if (dictionaryEntry === void 0) {
5579
- const sourceEntry = this.localesDictionaryCache.get(this.config.defaultLocale)?.get(id);
5580
- if (sourceEntry === void 0) throw new DictionarySourceNotFoundError(id);
5581
- dictionaryEntry = await dictionaryCache.miss(id, sourceEntry);
5582
- }
5477
+ if (!dictionaryLocale) return this.getSourceDictionaryEntry(id);
5478
+ const dictionaryCache = await this.localesCache.getOrLoadDictionary(dictionaryLocale);
5479
+ let dictionaryEntry = dictionaryCache.getEntry(id);
5480
+ if (dictionaryEntry === void 0) dictionaryEntry = await dictionaryCache.materializeEntry(id, this.getSourceDictionaryEntry(id));
5583
5481
  return dictionaryEntry;
5584
5482
  } catch (error) {
5585
5483
  this.handleError(error);
@@ -5593,25 +5491,41 @@ var I18nManager = class extends EventEmitter {
5593
5491
  async lookupDictionaryObjWithFallback(locale, id) {
5594
5492
  try {
5595
5493
  const dictionaryLocale = this.resolveCacheLocale(locale);
5596
- if (!dictionaryLocale) {
5597
- const sourceObject = this.localesDictionaryCache.get(this.config.defaultLocale)?.getObj(id);
5598
- if (sourceObject === void 0) throw new DictionarySourceNotFoundError(id);
5599
- return sourceObject;
5600
- }
5601
- let dictionaryCache = this.localesDictionaryCache.get(dictionaryLocale);
5602
- if (!dictionaryCache) dictionaryCache = await this.localesDictionaryCache.miss(dictionaryLocale);
5603
- let dictionaryObject = dictionaryCache.getObj(id);
5604
- if (dictionaryObject === void 0) {
5605
- const sourceObject = this.localesDictionaryCache.get(this.config.defaultLocale)?.getObj(id);
5606
- if (sourceObject === void 0) throw new DictionarySourceNotFoundError(id);
5607
- dictionaryObject = await dictionaryCache.missObj(id, sourceObject);
5494
+ if (!dictionaryLocale) return this.getSourceDictionaryObject(id);
5495
+ const dictionaryCache = await this.localesCache.getOrLoadDictionary(dictionaryLocale);
5496
+ const targetObject = dictionaryCache.getValue(id);
5497
+ const sourceObject = this.getSourceDictionaryObject(id, { throwOnMissing: false });
5498
+ if (sourceObject === void 0) {
5499
+ if (targetObject !== void 0) return targetObject;
5500
+ throw new DictionarySourceNotFoundError(id);
5608
5501
  }
5609
- return dictionaryObject;
5502
+ return await dictionaryCache.materializeValue(id, sourceObject, targetObject);
5610
5503
  } catch (error) {
5611
5504
  this.handleError(error);
5612
5505
  return;
5613
5506
  }
5614
5507
  }
5508
+ async translateDictionaryEntry(locale, id, sourceEntry) {
5509
+ const translation = await this.lookupTranslationWithFallbackResolved(locale, sourceEntry.entry, resolveDictionaryLookupOptions(sourceEntry.options));
5510
+ if (typeof translation !== "string") throw new Error(`Dictionary entry "${id}" could not be translated into a string. Check the source entry and translation loader output.`);
5511
+ return translation;
5512
+ }
5513
+ getSourceDictionaryEntry(id) {
5514
+ const sourceEntry = this.getDefaultDictionaryCache()?.getEntry(id);
5515
+ if (sourceEntry === void 0) throw new DictionarySourceNotFoundError(id);
5516
+ return sourceEntry;
5517
+ }
5518
+ getSourceDictionaryObject(id, { throwOnMissing = true } = {}) {
5519
+ const sourceObject = this.getDefaultDictionaryCache()?.getValue(id);
5520
+ if (sourceObject === void 0 && throwOnMissing) throw new DictionarySourceNotFoundError(id);
5521
+ return sourceObject;
5522
+ }
5523
+ getDefaultDictionaryCache() {
5524
+ return this.localesCache.getDictionary(this.config.defaultLocale);
5525
+ }
5526
+ resolveDictionaryCacheLocale(locale) {
5527
+ return this.resolveCacheLocale(locale) ?? this.config.defaultLocale;
5528
+ }
5615
5529
  /**
5616
5530
  * Just lookup a translation
5617
5531
  */
@@ -5619,7 +5533,7 @@ var I18nManager = class extends EventEmitter {
5619
5533
  try {
5620
5534
  const { translationLocale, options: lookupOptions } = this.resolveLookupParams(locale, options);
5621
5535
  if (!translationLocale) return message;
5622
- const txCache = this.localesCache.get(translationLocale);
5536
+ const txCache = this.localesCache.getTranslations(translationLocale);
5623
5537
  if (!txCache) return void 0;
5624
5538
  return txCache.get({
5625
5539
  message,
@@ -5657,9 +5571,7 @@ var I18nManager = class extends EventEmitter {
5657
5571
  if (!translationLocale) return (message) => message;
5658
5572
  const resolvedPrefetchEntries = resolvePrefetchEntriesByLocale(prefetchEntries, translationLocale, (entryLocale) => this.resolveCacheLocale(entryLocale) ?? this.resolveLocale(entryLocale));
5659
5573
  if (resolvedPrefetchEntries.length !== prefetchEntries.length) logger_default.warn(`I18nManager: getLookupTranslation(): prefetchEntries must all be the same locale, ignoring all entries that are not for ${translationLocale}`);
5660
- let txCache = this.localesCache.get(translationLocale);
5661
- if (!txCache) txCache = await this.localesCache.miss(translationLocale);
5662
- if (!txCache) return () => void 0;
5574
+ const txCache = await this.localesCache.getOrLoadTranslations(translationLocale);
5663
5575
  await Promise.all(resolvedPrefetchEntries.filter((entry) => txCache.get(entry) == null).map((entry) => txCache.miss(entry)));
5664
5576
  return (message, options = {}) => {
5665
5577
  return txCache.get({
@@ -5731,7 +5643,7 @@ var I18nManager = class extends EventEmitter {
5731
5643
  }
5732
5644
  resolveLocale(locale) {
5733
5645
  const resolvedLocale = this.localeConfig.determineLocale(locale);
5734
- if (!this.localeConfig.isValidLocale(locale) || !resolvedLocale) throw new Error(`I18nManager: validateLocale(): locale ${locale} is not valid`);
5646
+ if (!this.localeConfig.isValidLocale(locale) || !resolvedLocale) throw new Error(`Locale "${locale}" is not valid. Use a valid BCP 47 locale code or add a custom mapping.`);
5735
5647
  return resolvedLocale;
5736
5648
  }
5737
5649
  /**
@@ -5761,8 +5673,7 @@ var I18nManager = class extends EventEmitter {
5761
5673
  async lookupTranslationWithFallbackResolved(locale, message, options) {
5762
5674
  const { translationLocale, options: lookupOptions } = this.resolveLookupParams(locale, options);
5763
5675
  if (!translationLocale) return message;
5764
- let txCache = this.localesCache.get(translationLocale);
5765
- if (!txCache) txCache = await this.localesCache.miss(translationLocale);
5676
+ const txCache = await this.localesCache.getOrLoadTranslations(translationLocale);
5766
5677
  let translation = txCache.get({
5767
5678
  message,
5768
5679
  options: lookupOptions
@@ -5774,14 +5685,6 @@ var I18nManager = class extends EventEmitter {
5774
5685
  return translation;
5775
5686
  }
5776
5687
  /**
5777
- * Runtime lookup function for dictionaries
5778
- */
5779
- async dictionaryRuntimeTranslate(locale, id, sourceEntry) {
5780
- const translation = await this.lookupTranslationWithFallbackResolved(locale, sourceEntry.entry, resolveDictionaryLookupOptions(sourceEntry.options));
5781
- if (typeof translation !== "string") throw new Error(`I18nManager: dictionaryRuntimeTranslate(): unable to translate dictionary entry ${id}`);
5782
- return translation;
5783
- }
5784
- /**
5785
5688
  * A helper function to create a gt class that is locale agnostic
5786
5689
  * This is helpful for when our getLocale function is bound to a
5787
5690
  * specific context
@@ -5878,28 +5781,6 @@ function resolvePrefetchEntriesByLocale(prefetchEntries, locale, resolveLocale)
5878
5781
  }
5879
5782
  });
5880
5783
  }
5881
- /**
5882
- * Helper function for creating a translation loader
5883
- */
5884
- function createTranslationLoader(params) {
5885
- return routeCreateTranslationLoader({
5886
- loadTranslations: params.loadTranslations,
5887
- type: getLoadTranslationsType(params),
5888
- remoteTranslationLoaderParams: {
5889
- cacheUrl: params.cacheUrl,
5890
- projectId: params.projectId,
5891
- _versionId: params._versionId,
5892
- _branchId: params._branchId,
5893
- customMapping: params.customMapping
5894
- }
5895
- });
5896
- }
5897
- /**
5898
- * Helper function for creating a dictionary loader
5899
- */
5900
- function createDictionaryLoader(params) {
5901
- return params.loadDictionary ?? (() => Promise.resolve({}));
5902
- }
5903
5784
  let i18nManager = void 0;
5904
5785
  let fallbackDefaultLocale = "en";
5905
5786
  let conditionStore = { getLocale: () => fallbackDefaultLocale };
@@ -5910,7 +5791,7 @@ let conditionStore = { getLocale: () => fallbackDefaultLocale };
5910
5791
  */
5911
5792
  function getI18nManager() {
5912
5793
  if (!i18nManager) {
5913
- logger_default.warn("getI18nManager(): Translation failed because I18nManager not initialized.");
5794
+ logger_default.warn("getI18nManager(): I18nManager was not initialized. Falling back to the default locale until initializeGT() configures translations.");
5914
5795
  i18nManager = new I18nManager({
5915
5796
  defaultLocale: "en",
5916
5797
  locales: ["en"]
@@ -6042,10 +5923,30 @@ function resolveTranslationSyncWithFallback(locale, message, options = {}) {
6042
5923
  //#endregion
6043
5924
  //#region src/shared/messages.ts
6044
5925
  const PACKAGE_NAME = "gt-react";
6045
- `${PACKAGE_NAME}${PACKAGE_NAME}`;
6046
- `${PACKAGE_NAME}`;
6047
- `${PACKAGE_NAME}`;
6048
- const createTranslationFailedDueToBrowserEnvironmentWarning = (message) => `${PACKAGE_NAME} Warning: Translation failed for t("${typeof message === "string" ? message : "`" + message?.join("${}") + "`"}") because it was used outside of a browser environment. Falling back to original message.`;
5926
+ createDiagnosticMessage({
5927
+ source: `${PACKAGE_NAME}/browser`,
5928
+ severity: "Error",
5929
+ whatHappened: "This module requires a browser environment",
5930
+ fix: "Import it only from client-side code"
5931
+ });
5932
+ createDiagnosticMessage({
5933
+ source: PACKAGE_NAME,
5934
+ severity: "Error",
5935
+ whatHappened: "A browser-only module was imported outside the browser",
5936
+ fix: "Move this import to client-side code"
5937
+ });
5938
+ createDiagnosticMessage({
5939
+ source: PACKAGE_NAME,
5940
+ severity: "Error",
5941
+ whatHappened: "BrowserI18nManager is not initialized",
5942
+ fix: "Call initializeGT() before using browser translation APIs"
5943
+ });
5944
+ const createTranslationFailedDueToBrowserEnvironmentWarning = (message) => createDiagnosticMessage({
5945
+ source: PACKAGE_NAME,
5946
+ severity: "Warning",
5947
+ whatHappened: `t("${typeof message === "string" ? message : "`" + message?.join("${}") + "`"}") could not be translated because it ran outside the browser`,
5948
+ wayOut: "The original message will render as a fallback"
5949
+ });
6049
5950
  //#endregion
6050
5951
  //#region src/i18n-context/functions/translation/t.ts
6051
5952
  /**
@@ -6091,7 +5992,7 @@ const t = (messageOrStrings, ...values) => {
6091
5992
  */
6092
5993
  function handleTaggedTemplateLiteralTranslation(messageOrStrings, values) {
6093
5994
  const locale = getLocale();
6094
- const translatedInterpolatedTemplate = resolveTranslationSync(locale, interpolateTemplateLiteral(messageOrStrings, values));
5995
+ const translatedInterpolatedTemplate = resolveTranslationSync(locale, messageOrStrings.map((string, index) => string + (values[index] ?? "")).join(""));
6095
5996
  if (translatedInterpolatedTemplate) return translatedInterpolatedTemplate;
6096
5997
  const { message, variables } = extractInterpolatableValues(messageOrStrings, values);
6097
5998
  return resolveTranslationSyncWithFallback(locale, message, variables);
@@ -6120,17 +6021,6 @@ function extractInterpolatableValues(strings, values) {
6120
6021
  variables
6121
6022
  };
6122
6023
  }
6123
- /**
6124
- * Interpolate a template literal
6125
- * @param message - The message to interpolate.
6126
- * @param variables - The variables to interpolate.
6127
- * @returns The interpolated message.
6128
- */
6129
- function interpolateTemplateLiteral(strings, values) {
6130
- return strings.map((string, index) => {
6131
- return string + (values[index] ?? "");
6132
- }).join("");
6133
- }
6134
6024
  //#endregion
6135
6025
  //#region src/macros.ts
6136
6026
  globalThis.t = t;