langie 1.11.1 → 1.12.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.
@@ -55,11 +55,11 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
55
55
  "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
56
56
  }>, {
57
57
  translatorHost: string;
58
+ apiKey: string;
58
59
  languages: TranslatorLanguage[];
59
60
  placeholder: string;
60
61
  disabled: boolean;
61
62
  isDark: boolean;
62
- apiKey: string;
63
63
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
64
64
  declare const _default: typeof __VLS_export;
65
65
  export default _default;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * langie v1.11.1
2
+ * langie v1.12.0
3
3
  * (c) 2026 nlit
4
4
  * @license Apache-2.0
5
5
  *
@@ -589,6 +589,12 @@ function applyLanguageAlias(term = "") {
589
589
  return { primary, suggestions: Array.from(suggestions) };
590
590
  }
591
591
 
592
+ // src/utils/displayNative.ts
593
+ var TRAILING_PAREN = /\s*[((][^()()]*[))]\s*$/;
594
+ function displayNative(name = "", native = "") {
595
+ return name !== native && TRAILING_PAREN.test(name) && TRAILING_PAREN.test(native) ? native.replace(TRAILING_PAREN, "") : native;
596
+ }
597
+
592
598
  // src/constants/colors.ts
593
599
  var COLORS = {
594
600
  // Primary colors
@@ -928,7 +934,7 @@ var LanguageSelect_default = /* @__PURE__ */ (0, import_vue.defineComponent)({
928
934
  value.native_name && value.native_name !== value.name ? ((0, import_vue2.openBlock)(), (0, import_vue2.createElementBlock)(
929
935
  "span",
930
936
  _hoisted_4,
931
- "(" + (0, import_vue2.toDisplayString)(value.native_name) + ")",
937
+ "(" + (0, import_vue2.toDisplayString)((0, import_vue2.unref)(displayNative)(value.name, value.native_name)) + ")",
932
938
  1
933
939
  /* TEXT */
934
940
  )) : (0, import_vue2.createCommentVNode)("v-if", true)
@@ -956,7 +962,7 @@ var LanguageSelect_default = /* @__PURE__ */ (0, import_vue.defineComponent)({
956
962
  option.native_name && option.native_name !== option.name ? ((0, import_vue2.openBlock)(), (0, import_vue2.createElementBlock)(
957
963
  "span",
958
964
  _hoisted_8,
959
- "(" + (0, import_vue2.toDisplayString)(option.native_name) + ")",
965
+ "(" + (0, import_vue2.toDisplayString)((0, import_vue2.unref)(displayNative)(option.name, option.native_name)) + ")",
960
966
  1
961
967
  /* TEXT */
962
968
  )) : (0, import_vue2.createCommentVNode)("v-if", true)
@@ -1019,9 +1025,6 @@ var import_vue8 = require("vue");
1019
1025
  // src/useLangie.ts
1020
1026
  var import_vue5 = require("vue");
1021
1027
 
1022
- // src/composables/useLangie-core.ts
1023
- var import_vue4 = require("vue");
1024
-
1025
1028
  // src/constants.ts
1026
1029
  var DEFAULT_API_HOST = "https://api.langie.uk/v1";
1027
1030
  var API_FIELD_TEXT = "t";
@@ -1032,6 +1035,153 @@ var API_FIELD_TRANSLATIONS = "translations";
1032
1035
  var API_FIELD_ERROR = "error";
1033
1036
  var API_FIELD_TURNSTILE = "turnstileToken";
1034
1037
 
1038
+ // src/core.ts
1039
+ var DEFAULT_TRANSLATOR_HOST = "http://localhost:8081";
1040
+ var DEFAULT_API_KEY_ENV = "TRANSLATOR_API_KEY";
1041
+ function resolveApiKey(options) {
1042
+ if (options.apiKey) return options.apiKey;
1043
+ const env = typeof process !== "undefined" ? process.env : void 0;
1044
+ if (!env) return void 0;
1045
+ return env[options.apiKeyEnv || DEFAULT_API_KEY_ENV];
1046
+ }
1047
+ var translationCache = /* @__PURE__ */ new Map();
1048
+ function getTranslationCacheKey(serviceTranslations, options) {
1049
+ return JSON.stringify({
1050
+ translations: serviceTranslations.map((t) => ({
1051
+ [API_FIELD_TEXT]: t[API_FIELD_TEXT],
1052
+ [API_FIELD_FROM]: t[API_FIELD_FROM],
1053
+ [API_FIELD_TO]: t[API_FIELD_TO],
1054
+ [API_FIELD_CTX]: t[API_FIELD_CTX] || options[API_FIELD_CTX] || "ui"
1055
+ })),
1056
+ host: options.translatorHost || DEFAULT_TRANSLATOR_HOST,
1057
+ context: options[API_FIELD_CTX] || "ui"
1058
+ });
1059
+ }
1060
+ async function translateBatch(translations2 = [], options = {}) {
1061
+ if (!Array.isArray(translations2) || translations2.length === 0) {
1062
+ throw new Error("translations must be a non-empty array");
1063
+ }
1064
+ const translatorHost = options.translatorHost || DEFAULT_TRANSLATOR_HOST;
1065
+ const apiKey = resolveApiKey(options);
1066
+ const serviceTranslations = [];
1067
+ const indexMap = [];
1068
+ translations2.forEach((tr, idx) => {
1069
+ const from = (tr[API_FIELD_FROM] || "").toLowerCase();
1070
+ const to = (tr[API_FIELD_TO] || "").toLowerCase();
1071
+ if (from === to) {
1072
+ return;
1073
+ }
1074
+ serviceTranslations.push(tr);
1075
+ indexMap.push(idx);
1076
+ });
1077
+ let serviceResults = [];
1078
+ if (serviceTranslations.length > 0) {
1079
+ const cacheKey = getTranslationCacheKey(serviceTranslations, options);
1080
+ if (translationCache.has(cacheKey)) {
1081
+ serviceResults = await translationCache.get(cacheKey);
1082
+ } else {
1083
+ const translationPromise = (async () => {
1084
+ try {
1085
+ const turnstileToken = options.getTurnstileToken ? await options.getTurnstileToken() : void 0;
1086
+ const controller = new AbortController();
1087
+ const timeoutId = setTimeout(() => controller.abort(), 5e3);
1088
+ const resp = await fetch(`${translatorHost}/translate`, {
1089
+ method: "POST",
1090
+ headers: {
1091
+ "Content-Type": "application/json",
1092
+ ...apiKey ? { Authorization: `Bearer ${apiKey}`, "X-Api-Key": apiKey } : {}
1093
+ },
1094
+ body: JSON.stringify({
1095
+ [API_FIELD_TRANSLATIONS]: serviceTranslations.map((t) => ({
1096
+ [API_FIELD_TEXT]: t[API_FIELD_TEXT],
1097
+ [API_FIELD_FROM]: t[API_FIELD_FROM],
1098
+ [API_FIELD_TO]: t[API_FIELD_TO],
1099
+ [API_FIELD_CTX]: t[API_FIELD_CTX] || options[API_FIELD_CTX] || "ui"
1100
+ })),
1101
+ [API_FIELD_CTX]: options[API_FIELD_CTX] || "ui",
1102
+ ...turnstileToken ? { [API_FIELD_TURNSTILE]: turnstileToken } : {}
1103
+ }),
1104
+ signal: controller.signal
1105
+ });
1106
+ clearTimeout(timeoutId);
1107
+ if (!resp.ok) {
1108
+ console.error(
1109
+ "[translator-sdk] Translator error response:",
1110
+ resp.status,
1111
+ resp.statusText
1112
+ );
1113
+ throw new Error(`Translator service error: ${resp.status} ${resp.statusText}`);
1114
+ }
1115
+ let parsed;
1116
+ try {
1117
+ parsed = await resp.clone().json();
1118
+ } catch (jsonErr) {
1119
+ console.error("[translator-sdk] Failed to parse JSON response:", jsonErr);
1120
+ throw new Error(`Failed to parse JSON response: ${jsonErr}`);
1121
+ }
1122
+ const data = parsed || {};
1123
+ if (data[API_FIELD_ERROR]) {
1124
+ console.warn(`[translator-sdk] Top-level API error:`, data[API_FIELD_ERROR]);
1125
+ return serviceTranslations.map((translation) => ({
1126
+ [API_FIELD_TEXT]: translation[API_FIELD_TEXT],
1127
+ [API_FIELD_ERROR]: data[API_FIELD_ERROR]
1128
+ }));
1129
+ }
1130
+ const results = Array.isArray(data[API_FIELD_TRANSLATIONS]) ? data[API_FIELD_TRANSLATIONS].map((translation, index) => {
1131
+ const originalText = serviceTranslations[index]?.[API_FIELD_TEXT] || "";
1132
+ if (translation[API_FIELD_ERROR]) {
1133
+ console.warn(
1134
+ `[translator-sdk] Translation error for "${originalText}":`,
1135
+ translation[API_FIELD_ERROR]
1136
+ );
1137
+ return {
1138
+ [API_FIELD_TEXT]: originalText,
1139
+ // Return original text on error
1140
+ [API_FIELD_ERROR]: translation[API_FIELD_ERROR]
1141
+ };
1142
+ }
1143
+ if (translation[API_FIELD_FROM] && !translation[API_FIELD_TEXT]) {
1144
+ return {
1145
+ [API_FIELD_TEXT]: originalText,
1146
+ // For detection, return original text
1147
+ [API_FIELD_FROM]: translation[API_FIELD_FROM]
1148
+ };
1149
+ }
1150
+ return {
1151
+ [API_FIELD_TEXT]: translation[API_FIELD_TEXT] || originalText
1152
+ };
1153
+ }) : data[API_FIELD_TEXT] ? [{ [API_FIELD_TEXT]: data[API_FIELD_TEXT] }] : [];
1154
+ return results;
1155
+ } catch (error) {
1156
+ translationCache.delete(cacheKey);
1157
+ if (error instanceof Error && error.name === "AbortError") {
1158
+ const message2 = `Translator request to ${translatorHost} timed out after 5 seconds.`;
1159
+ console.error(message2);
1160
+ throw new Error(message2);
1161
+ }
1162
+ const message = `Failed to connect to translator at ${translatorHost}. Is the service running?`;
1163
+ console.error(message);
1164
+ throw new Error(message);
1165
+ }
1166
+ })();
1167
+ translationCache.set(cacheKey, translationPromise);
1168
+ serviceResults = await translationPromise;
1169
+ }
1170
+ }
1171
+ const final = translations2.map((tr, idx) => {
1172
+ const from = (tr[API_FIELD_FROM] || "").toLowerCase();
1173
+ const to = (tr[API_FIELD_TO] || "").toLowerCase();
1174
+ if (from === to) return { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] };
1175
+ const svcIdx = indexMap.indexOf(idx);
1176
+ if (svcIdx !== -1) return serviceResults[svcIdx] || { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] };
1177
+ return { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] };
1178
+ });
1179
+ return final;
1180
+ }
1181
+
1182
+ // src/composables/useLangie-core.ts
1183
+ var import_vue4 = require("vue");
1184
+
1035
1185
  // src/utils/debug.ts
1036
1186
  var import_meta = {};
1037
1187
  function devDebug(...args) {
@@ -1276,143 +1426,6 @@ var clearCache = (pattern) => {
1276
1426
  return cacheManager.clear(pattern);
1277
1427
  };
1278
1428
 
1279
- // src/core.ts
1280
- var DEFAULT_TRANSLATOR_HOST = "http://localhost:8081";
1281
- var translationCache = /* @__PURE__ */ new Map();
1282
- function getTranslationCacheKey(serviceTranslations, options) {
1283
- return JSON.stringify({
1284
- translations: serviceTranslations.map((t) => ({
1285
- [API_FIELD_TEXT]: t[API_FIELD_TEXT],
1286
- [API_FIELD_FROM]: t[API_FIELD_FROM],
1287
- [API_FIELD_TO]: t[API_FIELD_TO],
1288
- [API_FIELD_CTX]: t[API_FIELD_CTX] || options[API_FIELD_CTX] || "ui"
1289
- })),
1290
- host: options.translatorHost || DEFAULT_TRANSLATOR_HOST,
1291
- context: options[API_FIELD_CTX] || "ui"
1292
- });
1293
- }
1294
- async function translateBatch(translations2 = [], options = {}) {
1295
- if (!Array.isArray(translations2) || translations2.length === 0) {
1296
- throw new Error("translations must be a non-empty array");
1297
- }
1298
- const translatorHost = options.translatorHost || DEFAULT_TRANSLATOR_HOST;
1299
- const apiKey = options.apiKey || process.env.TRANSLATOR_API_KEY;
1300
- const serviceTranslations = [];
1301
- const indexMap = [];
1302
- translations2.forEach((tr, idx) => {
1303
- const from = (tr[API_FIELD_FROM] || "").toLowerCase();
1304
- const to = (tr[API_FIELD_TO] || "").toLowerCase();
1305
- if (from === to) {
1306
- return;
1307
- }
1308
- serviceTranslations.push(tr);
1309
- indexMap.push(idx);
1310
- });
1311
- let serviceResults = [];
1312
- if (serviceTranslations.length > 0) {
1313
- const cacheKey = getTranslationCacheKey(serviceTranslations, options);
1314
- if (translationCache.has(cacheKey)) {
1315
- serviceResults = await translationCache.get(cacheKey);
1316
- } else {
1317
- const translationPromise = (async () => {
1318
- try {
1319
- const turnstileToken = options.getTurnstileToken ? await options.getTurnstileToken() : void 0;
1320
- const controller = new AbortController();
1321
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
1322
- const resp = await fetch(`${translatorHost}/translate`, {
1323
- method: "POST",
1324
- headers: {
1325
- "Content-Type": "application/json",
1326
- ...apiKey ? { Authorization: `Bearer ${apiKey}`, "X-Api-Key": apiKey } : {}
1327
- },
1328
- body: JSON.stringify({
1329
- [API_FIELD_TRANSLATIONS]: serviceTranslations.map((t) => ({
1330
- [API_FIELD_TEXT]: t[API_FIELD_TEXT],
1331
- [API_FIELD_FROM]: t[API_FIELD_FROM],
1332
- [API_FIELD_TO]: t[API_FIELD_TO],
1333
- [API_FIELD_CTX]: t[API_FIELD_CTX] || options[API_FIELD_CTX] || "ui"
1334
- })),
1335
- [API_FIELD_CTX]: options[API_FIELD_CTX] || "ui",
1336
- ...turnstileToken ? { [API_FIELD_TURNSTILE]: turnstileToken } : {}
1337
- }),
1338
- signal: controller.signal
1339
- });
1340
- clearTimeout(timeoutId);
1341
- if (!resp.ok) {
1342
- console.error(
1343
- "[translator-sdk] Translator error response:",
1344
- resp.status,
1345
- resp.statusText
1346
- );
1347
- throw new Error(`Translator service error: ${resp.status} ${resp.statusText}`);
1348
- }
1349
- let parsed;
1350
- try {
1351
- parsed = await resp.clone().json();
1352
- } catch (jsonErr) {
1353
- console.error("[translator-sdk] Failed to parse JSON response:", jsonErr);
1354
- throw new Error(`Failed to parse JSON response: ${jsonErr}`);
1355
- }
1356
- const data = parsed || {};
1357
- if (data[API_FIELD_ERROR]) {
1358
- console.warn(`[translator-sdk] Top-level API error:`, data[API_FIELD_ERROR]);
1359
- return serviceTranslations.map((translation) => ({
1360
- [API_FIELD_TEXT]: translation[API_FIELD_TEXT],
1361
- [API_FIELD_ERROR]: data[API_FIELD_ERROR]
1362
- }));
1363
- }
1364
- const results = Array.isArray(data[API_FIELD_TRANSLATIONS]) ? data[API_FIELD_TRANSLATIONS].map((translation, index) => {
1365
- const originalText = serviceTranslations[index]?.[API_FIELD_TEXT] || "";
1366
- if (translation[API_FIELD_ERROR]) {
1367
- console.warn(
1368
- `[translator-sdk] Translation error for "${originalText}":`,
1369
- translation[API_FIELD_ERROR]
1370
- );
1371
- return {
1372
- [API_FIELD_TEXT]: originalText,
1373
- // Return original text on error
1374
- [API_FIELD_ERROR]: translation[API_FIELD_ERROR]
1375
- };
1376
- }
1377
- if (translation[API_FIELD_FROM] && !translation[API_FIELD_TEXT]) {
1378
- return {
1379
- [API_FIELD_TEXT]: originalText,
1380
- // For detection, return original text
1381
- [API_FIELD_FROM]: translation[API_FIELD_FROM]
1382
- };
1383
- }
1384
- return {
1385
- [API_FIELD_TEXT]: translation[API_FIELD_TEXT] || originalText
1386
- };
1387
- }) : data[API_FIELD_TEXT] ? [{ [API_FIELD_TEXT]: data[API_FIELD_TEXT] }] : [];
1388
- return results;
1389
- } catch (error) {
1390
- translationCache.delete(cacheKey);
1391
- if (error instanceof Error && error.name === "AbortError") {
1392
- const message2 = `Translator request to ${translatorHost} timed out after 5 seconds.`;
1393
- console.error(message2);
1394
- throw new Error(message2);
1395
- }
1396
- const message = `Failed to connect to translator at ${translatorHost}. Is the service running?`;
1397
- console.error(message);
1398
- throw new Error(message);
1399
- }
1400
- })();
1401
- translationCache.set(cacheKey, translationPromise);
1402
- serviceResults = await translationPromise;
1403
- }
1404
- }
1405
- const final = translations2.map((tr, idx) => {
1406
- const from = (tr[API_FIELD_FROM] || "").toLowerCase();
1407
- const to = (tr[API_FIELD_TO] || "").toLowerCase();
1408
- if (from === to) return { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] };
1409
- const svcIdx = indexMap.indexOf(idx);
1410
- if (svcIdx !== -1) return serviceResults[svcIdx] || { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] };
1411
- return { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] };
1412
- });
1413
- return final;
1414
- }
1415
-
1416
1429
  // src/utils/localizeLanguageNames.ts
1417
1430
  var CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
1418
1431
  var NAME_CTX = "language name";
@@ -1459,7 +1472,9 @@ var currentLanguage = (0, import_vue4.ref)("en");
1459
1472
  var _translatorHost = DEFAULT_API_HOST;
1460
1473
  var _autoSelected = false;
1461
1474
  var _languagesCache = null;
1475
+ var _languagesCacheAt = 0;
1462
1476
  var _languagesPromise = null;
1477
+ var LANGUAGES_CACHE_KEY = "langie_languages_cache";
1463
1478
  function useLangieCore(options = {}) {
1464
1479
  if (options.translatorHost) {
1465
1480
  _translatorHost = options.translatorHost;
@@ -1477,7 +1492,7 @@ function useLangieCore(options = {}) {
1477
1492
  }
1478
1493
  }
1479
1494
  if (typeof window !== "undefined") {
1480
- const cachedLanguages = getCache("langie_languages_cache");
1495
+ const cachedLanguages = getCache(LANGUAGES_CACHE_KEY);
1481
1496
  if (cachedLanguages && availableLanguages.value.length === 0) {
1482
1497
  availableLanguages.value = cachedLanguages;
1483
1498
  devDebug("[useLangie] Seeded languages from cache (will revalidate):", cachedLanguages.length);
@@ -1510,15 +1525,18 @@ function useLangieCore(options = {}) {
1510
1525
  applyLocalizedNames(lang);
1511
1526
  };
1512
1527
  const fetchLanguages = async (opts = {}) => {
1513
- const { force = false, country: explicitCountry } = opts;
1528
+ const { force = false, country: explicitCountry, maxAgeMs } = opts;
1529
+ const cacheFresh = !!_languagesCache && (maxAgeMs === void 0 || Date.now() - _languagesCacheAt < maxAgeMs);
1514
1530
  devDebug("[useLangie] fetchLanguages called:", {
1515
1531
  force,
1532
+ maxAgeMs,
1533
+ cacheFresh,
1516
1534
  hasCache: !!_languagesCache,
1517
1535
  hasPromise: !!_languagesPromise,
1518
1536
  availableLanguagesLength: availableLanguages.value.length
1519
1537
  });
1520
1538
  if (!force) {
1521
- if (_languagesCache) {
1539
+ if (cacheFresh) {
1522
1540
  devDebug("[useLangie] Returning cached languages:", _languagesCache.length);
1523
1541
  return _languagesCache;
1524
1542
  }
@@ -1576,7 +1594,7 @@ function useLangieCore(options = {}) {
1576
1594
  availableLanguages.value = filtered;
1577
1595
  devDebug("[useLangie] Set availableLanguages:", filtered.length);
1578
1596
  if (typeof window !== "undefined") {
1579
- const saved = setCache("langie_languages_cache", filtered, 7 * 24 * 60 * 60 * 1e3);
1597
+ const saved = setCache(LANGUAGES_CACHE_KEY, filtered, 7 * 24 * 60 * 60 * 1e3);
1580
1598
  devDebug(
1581
1599
  "[useLangie] Saved languages to cache:",
1582
1600
  saved ? "success" : "failed",
@@ -1607,6 +1625,7 @@ function useLangieCore(options = {}) {
1607
1625
  _autoSelected = true;
1608
1626
  }
1609
1627
  _languagesCache = filtered;
1628
+ _languagesCacheAt = Date.now();
1610
1629
  _languagesPromise = null;
1611
1630
  applyLocalizedNames(currentLanguage.value);
1612
1631
  return filtered;
@@ -1615,6 +1634,14 @@ function useLangieCore(options = {}) {
1615
1634
  return [];
1616
1635
  }
1617
1636
  };
1637
+ const invalidateLanguages = () => {
1638
+ _languagesCache = null;
1639
+ _languagesCacheAt = 0;
1640
+ _languagesPromise = null;
1641
+ if (typeof window !== "undefined") {
1642
+ clearCache(LANGUAGES_CACHE_KEY);
1643
+ }
1644
+ };
1618
1645
  const clearTranslations = () => {
1619
1646
  Object.keys(translations).forEach((key) => delete translations[key]);
1620
1647
  Object.keys(uiTranslations).forEach((key) => delete uiTranslations[key]);
@@ -1631,6 +1658,7 @@ function useLangieCore(options = {}) {
1631
1658
  isLoading,
1632
1659
  setLanguage,
1633
1660
  fetchLanguages,
1661
+ invalidateLanguages,
1634
1662
  clearTranslations,
1635
1663
  translatorHost
1636
1664
  };
@@ -1948,6 +1976,7 @@ function createLangieInstance(options = {}) {
1948
1976
  isLoading,
1949
1977
  setLanguage,
1950
1978
  fetchLanguages,
1979
+ invalidateLanguages,
1951
1980
  translatorHost,
1952
1981
  clearTranslations
1953
1982
  } = core;
@@ -1959,9 +1988,17 @@ function createLangieInstance(options = {}) {
1959
1988
  Object.assign(ltDefaults, defaults);
1960
1989
  };
1961
1990
  const getLtDefaults = () => ({ ...ltDefaults });
1991
+ const seed = {};
1992
+ if (options.ssrTranslations) seedTranslations(options.ssrTranslations);
1993
+ function seedTranslations(entries) {
1994
+ for (const lang of Object.keys(entries)) {
1995
+ seed[lang] = { ...seed[lang] || {}, ...entries[lang] };
1996
+ }
1997
+ }
1998
+ const getSeed = (lang, text, ctx) => seed[lang]?.[`${text}|${ctx}`];
1962
1999
  const CACHE_KEY = "langie_translations_cache";
1963
2000
  const UI_CACHE_KEY = "langie_ui_translations_cache";
1964
- const LANGUAGES_CACHE_KEY = "langie_languages_cache";
2001
+ const LANGUAGES_CACHE_KEY2 = "langie_languages_cache";
1965
2002
  const loadCachedTranslations = () => {
1966
2003
  if (typeof window === "undefined") return;
1967
2004
  const cachedTranslations = getCache(CACHE_KEY);
@@ -1989,7 +2026,7 @@ function createLangieInstance(options = {}) {
1989
2026
  };
1990
2027
  const loadCachedLanguages = () => {
1991
2028
  if (typeof window === "undefined") return;
1992
- const cachedLanguages = getCache(LANGUAGES_CACHE_KEY);
2029
+ const cachedLanguages = getCache(LANGUAGES_CACHE_KEY2);
1993
2030
  if (cachedLanguages) {
1994
2031
  availableLanguages2.value = cachedLanguages;
1995
2032
  }
@@ -2103,6 +2140,10 @@ function createLangieInstance(options = {}) {
2103
2140
  if (cache[cacheKey]) {
2104
2141
  return cache[cacheKey];
2105
2142
  }
2143
+ const seeded = getSeed(to, text, effectiveCtx);
2144
+ if (seeded) {
2145
+ return seeded;
2146
+ }
2106
2147
  const errorKey = `${text}|${effectiveCtx}|${from}|${to}`;
2107
2148
  if (translationErrors.has(errorKey)) {
2108
2149
  return text;
@@ -2111,6 +2152,7 @@ function createLangieInstance(options = {}) {
2111
2152
  if (recentlyQueued.has(languageCacheKey)) {
2112
2153
  return text;
2113
2154
  }
2155
+ options.onTranslationMiss?.({ text, ctx: effectiveCtx, from, to });
2114
2156
  batching.queueTranslation(text, effectiveCtx, from, to, cacheKey, toLang !== void 0);
2115
2157
  recentlyQueued.add(languageCacheKey);
2116
2158
  const clearDelay = 100;
@@ -2123,6 +2165,42 @@ function createLangieInstance(options = {}) {
2123
2165
  };
2124
2166
  const l = (text, ctx, originalLang, toLang) => translateInternal(text, ctx, originalLang, toLang, false);
2125
2167
  const lr = (text, ctx, originalLang, toLang) => translateInternal(text, ctx, originalLang, toLang, true);
2168
+ const peekTranslation = (text, ctx, toLang) => {
2169
+ const effectiveCtx = ctx !== void 0 ? ctx : ltDefaults.ctx || "ui";
2170
+ const from = ltDefaults.orig || "";
2171
+ const to = toLang || currentLanguage2.value;
2172
+ if (from === to) return text;
2173
+ const baseKey = `${text}|${effectiveCtx}`;
2174
+ const cacheKey = to !== currentLanguage2.value ? `${baseKey}|${from}|${to}` : baseKey;
2175
+ const cache = effectiveCtx === "ui" ? uiTranslations2 : translations2;
2176
+ return cache[cacheKey] || getSeed(to, text, effectiveCtx) || text;
2177
+ };
2178
+ const warmTranslations = async (keys, lang, from = ltDefaults.orig || "") => {
2179
+ const valid = keys.filter((k) => k && k.text);
2180
+ if (valid.length === 0) return {};
2181
+ const items = valid.map((k) => ({
2182
+ [API_FIELD_TEXT]: k.text,
2183
+ [API_FIELD_FROM]: from,
2184
+ [API_FIELD_TO]: lang,
2185
+ [API_FIELD_CTX]: k.ctx ?? ltDefaults.ctx ?? "ui"
2186
+ }));
2187
+ const results = await translateBatch(items, {
2188
+ translatorHost,
2189
+ apiKey: options.apiKey,
2190
+ apiKeyEnv: options.apiKeyEnv
2191
+ });
2192
+ const resolved = {};
2193
+ results.forEach((res, i) => {
2194
+ const src = valid[i];
2195
+ const translated = res?.[API_FIELD_TEXT];
2196
+ if (src && translated && translated !== src.text) {
2197
+ const ctx = src.ctx ?? ltDefaults.ctx ?? "ui";
2198
+ resolved[`${src.text}|${ctx}`] = translated;
2199
+ }
2200
+ });
2201
+ if (Object.keys(resolved).length > 0) seedTranslations({ [lang]: resolved });
2202
+ return resolved;
2203
+ };
2126
2204
  const fetchAndCacheBatch = async (items, from, to = currentLanguage2.value, globalCtx) => {
2127
2205
  if (items.length === 0) return;
2128
2206
  const effectiveFrom = from || ltDefaults.orig || "";
@@ -2252,11 +2330,16 @@ function createLangieInstance(options = {}) {
2252
2330
  isLoading,
2253
2331
  setLanguage,
2254
2332
  fetchLanguages,
2333
+ invalidateLanguages,
2255
2334
  translatorHost,
2256
2335
  // Translation functions
2257
2336
  l,
2258
2337
  lr,
2338
+ peekTranslation,
2259
2339
  fetchAndCacheBatch,
2340
+ // SSR translation seam
2341
+ seedTranslations,
2342
+ warmTranslations,
2260
2343
  // Error handling
2261
2344
  getTranslationError: (text, ctx, from, to) => {
2262
2345
  const effectiveCtx = ctx !== void 0 ? ctx : ltDefaults.ctx || "ui";
@@ -2534,7 +2617,7 @@ var lt_default = /* @__PURE__ */ (0, import_vue9.defineComponent)({
2534
2617
  });
2535
2618
  const props = __props;
2536
2619
  const slots = (0, import_vue11.useSlots)();
2537
- const { lr, currentLanguage: currentLanguage2, uiTranslations: uiTranslations2, translations: translations2, getLtDefaults } = useLangie();
2620
+ const { lr, peekTranslation, currentLanguage: currentLanguage2, uiTranslations: uiTranslations2, translations: translations2, getLtDefaults } = useLangie();
2538
2621
  const keyStr = (0, import_vue11.computed)(() => {
2539
2622
  if (props.msg) return props.msg;
2540
2623
  const slotContent = slots.default ? slots.default().map((n) => n.children).join("") : "";
@@ -2551,12 +2634,12 @@ var lt_default = /* @__PURE__ */ (0, import_vue9.defineComponent)({
2551
2634
  { deep: true }
2552
2635
  );
2553
2636
  const translated = (0, import_vue11.computed)(() => {
2554
- if (isNuxt.value && typeof window === "undefined") {
2555
- return keyStr.value;
2556
- }
2557
2637
  const globalDefaults = getLtDefaults();
2558
2638
  const effectiveCtx = props.ctx ?? globalDefaults.ctx;
2559
2639
  const effectiveOrig = props.orig ?? globalDefaults.orig;
2640
+ if (isNuxt.value && typeof window === "undefined") {
2641
+ return peekTranslation(keyStr.value, effectiveCtx);
2642
+ }
2560
2643
  void currentLanguage2.value;
2561
2644
  void forceUpdate.value;
2562
2645
  const cacheKey = `${keyStr.value}|${effectiveCtx}`;