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.
- package/dist/components/InterfaceLanguageSelect.vue.d.ts +1 -1
- package/dist/components/index.cjs +236 -153
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.css.map +1 -1
- package/dist/components/index.mjs +237 -154
- package/dist/components/index.mjs.map +1 -1
- package/dist/composables/useLangie-core.d.ts +2 -0
- package/dist/core.cjs +10 -3
- package/dist/core.cjs.map +1 -1
- package/dist/core.mjs +10 -3
- package/dist/core.mjs.map +1 -1
- package/dist/index.cjs +281 -198
- package/dist/index.cjs.map +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +282 -199
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.ts +27 -0
- package/dist/useLangie.d.ts +9 -1
- package/dist/utils/displayNative.d.ts +10 -0
- package/package.json +1 -1
|
@@ -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.
|
|
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(
|
|
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 (
|
|
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(
|
|
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
|
|
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(
|
|
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}`;
|