langie 1.9.25
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/LICENSE +201 -0
- package/README.md +188 -0
- package/dist/components/index.cjs +2327 -0
- package/dist/components/index.cjs.map +1 -0
- package/dist/components/index.css +280 -0
- package/dist/components/index.css.map +1 -0
- package/dist/components/index.mjs +2288 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/core.cjs +225 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +101 -0
- package/dist/core.d.ts +101 -0
- package/dist/core.mjs +196 -0
- package/dist/core.mjs.map +1 -0
- package/dist/index.cjs +2898 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +280 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +165 -0
- package/dist/index.d.ts +165 -0
- package/dist/index.mjs +2826 -0
- package/dist/index.mjs.map +1 -0
- package/dist/teleport.css +35 -0
- package/dist/teleport.css.map +1 -0
- package/dist/teleport.d.cts +2 -0
- package/dist/teleport.d.ts +2 -0
- package/package.json +93 -0
- package/src/components.d.ts +47 -0
- package/src/env.d.ts +1 -0
- package/src/index.d.ts +161 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2826 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* langie v1.9.25
|
|
3
|
+
* (c) 2026 nlit
|
|
4
|
+
* @license Apache-2.0
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// src/useLangie.ts
|
|
11
|
+
import { watch } from "vue";
|
|
12
|
+
|
|
13
|
+
// src/composables/useLangie-core.ts
|
|
14
|
+
import { ref, reactive } from "vue";
|
|
15
|
+
|
|
16
|
+
// src/constants.ts
|
|
17
|
+
var DEFAULT_API_HOST = "https://api.langie.uk/v1";
|
|
18
|
+
var DEV_API_HOST = "http://localhost:8081/v1";
|
|
19
|
+
var API_FIELD_TEXT = "t";
|
|
20
|
+
var API_FIELD_FROM = "from";
|
|
21
|
+
var API_FIELD_TO = "to";
|
|
22
|
+
var API_FIELD_CTX = "ctx";
|
|
23
|
+
var API_FIELD_TRANSLATED = "t";
|
|
24
|
+
var API_FIELD_TRANSLATIONS = "translations";
|
|
25
|
+
var API_FIELD_ERROR = "error";
|
|
26
|
+
|
|
27
|
+
// src/utils/debug.ts
|
|
28
|
+
function devDebug(...args) {
|
|
29
|
+
if (import.meta.env && import.meta.env.DEV) {
|
|
30
|
+
console.debug(...args);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/utils/getCountryCode.ts
|
|
35
|
+
import * as ct from "countries-and-timezones";
|
|
36
|
+
async function getCountryCode() {
|
|
37
|
+
const localeCountry = getCountryCodeFromBrowser();
|
|
38
|
+
if (localeCountry) return localeCountry;
|
|
39
|
+
const timezoneCountry = getCountryFromTimezone();
|
|
40
|
+
if (timezoneCountry) return timezoneCountry;
|
|
41
|
+
return await getCountryFromIP();
|
|
42
|
+
}
|
|
43
|
+
function getCountryFromTimezone() {
|
|
44
|
+
try {
|
|
45
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
46
|
+
return getCountryFromTimezoneString(timezone);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function getCountryFromTimezoneString(timezone) {
|
|
52
|
+
try {
|
|
53
|
+
const country = ct.getCountryForTimezone(timezone);
|
|
54
|
+
return country?.id || null;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function getCountryFromIP() {
|
|
60
|
+
try {
|
|
61
|
+
const response = await fetch("https://ipapi.co/json/");
|
|
62
|
+
const data = await response.json();
|
|
63
|
+
return data.country_code || null;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function getCountryCodeFromBrowser() {
|
|
69
|
+
const langs = navigator.languages || [navigator.language];
|
|
70
|
+
for (const locale of langs) {
|
|
71
|
+
const parts = locale.split("-");
|
|
72
|
+
if (parts.length > 1) {
|
|
73
|
+
return parts[1].toUpperCase();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/utils/cache.ts
|
|
80
|
+
var DEFAULT_TTL = 7 * 24 * 60 * 60 * 1e3;
|
|
81
|
+
var DEFAULT_MAX_SIZE = 2 * 1024 * 1024;
|
|
82
|
+
var DEFAULT_MAX_ITEMS = 1e3;
|
|
83
|
+
var CacheManager = class {
|
|
84
|
+
options;
|
|
85
|
+
constructor(options = {}) {
|
|
86
|
+
this.options = {
|
|
87
|
+
ttl: options.ttl || DEFAULT_TTL,
|
|
88
|
+
maxSize: options.maxSize || DEFAULT_MAX_SIZE,
|
|
89
|
+
maxItems: options.maxItems || DEFAULT_MAX_ITEMS
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
set(key, data, ttl) {
|
|
93
|
+
if (typeof window === "undefined") return false;
|
|
94
|
+
try {
|
|
95
|
+
const item = {
|
|
96
|
+
data,
|
|
97
|
+
timestamp: Date.now(),
|
|
98
|
+
ttl: ttl || this.options.ttl
|
|
99
|
+
};
|
|
100
|
+
const serialized = JSON.stringify(item);
|
|
101
|
+
const size = new Blob([serialized]).size;
|
|
102
|
+
if (size > this.options.maxSize) {
|
|
103
|
+
console.warn(`[CacheManager] Item size ${size} exceeds max size ${this.options.maxSize}`);
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
this.cleanup();
|
|
107
|
+
const currentSize = this.getCurrentSize();
|
|
108
|
+
if (currentSize + size > this.options.maxSize) {
|
|
109
|
+
console.warn(`[CacheManager] Cache would exceed max size after adding item`);
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
localStorage.setItem(key, serialized);
|
|
113
|
+
return true;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.warn(`[CacheManager] Failed to set cache item ${key}:`, error);
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
get(key) {
|
|
120
|
+
if (typeof window === "undefined") return null;
|
|
121
|
+
try {
|
|
122
|
+
const item = localStorage.getItem(key);
|
|
123
|
+
if (!item) return null;
|
|
124
|
+
const cacheItem = JSON.parse(item);
|
|
125
|
+
const now = Date.now();
|
|
126
|
+
if (now - cacheItem.timestamp > cacheItem.ttl) {
|
|
127
|
+
localStorage.removeItem(key);
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
return cacheItem.data;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.warn(`[CacheManager] Failed to get cache item ${key}:`, error);
|
|
133
|
+
localStorage.removeItem(key);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
remove(key) {
|
|
138
|
+
if (typeof window === "undefined") return false;
|
|
139
|
+
try {
|
|
140
|
+
localStorage.removeItem(key);
|
|
141
|
+
return true;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.warn(`[CacheManager] Failed to remove cache item ${key}:`, error);
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
clear(pattern) {
|
|
148
|
+
if (typeof window === "undefined") return;
|
|
149
|
+
try {
|
|
150
|
+
if (pattern) {
|
|
151
|
+
Object.keys(localStorage).forEach((key) => {
|
|
152
|
+
if (key.includes(pattern)) {
|
|
153
|
+
localStorage.removeItem(key);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
} else {
|
|
157
|
+
Object.keys(localStorage).forEach((key) => {
|
|
158
|
+
if (key.startsWith("langie_")) {
|
|
159
|
+
localStorage.removeItem(key);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.warn("[CacheManager] Failed to clear cache:", error);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
cleanup() {
|
|
168
|
+
if (typeof window === "undefined") return;
|
|
169
|
+
try {
|
|
170
|
+
const now = Date.now();
|
|
171
|
+
const items = [];
|
|
172
|
+
Object.keys(localStorage).forEach((key) => {
|
|
173
|
+
if (key.startsWith("langie_")) {
|
|
174
|
+
try {
|
|
175
|
+
const item = localStorage.getItem(key);
|
|
176
|
+
if (item) {
|
|
177
|
+
const cacheItem = JSON.parse(item);
|
|
178
|
+
const size = new Blob([item]).size;
|
|
179
|
+
if (now - cacheItem.timestamp > cacheItem.ttl) {
|
|
180
|
+
localStorage.removeItem(key);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
items.push({
|
|
184
|
+
key,
|
|
185
|
+
size,
|
|
186
|
+
timestamp: cacheItem.timestamp
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
} catch (error) {
|
|
190
|
+
localStorage.removeItem(key);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
items.sort((a, b) => a.timestamp - b.timestamp);
|
|
195
|
+
while (items.length > this.options.maxItems) {
|
|
196
|
+
const oldest = items.shift();
|
|
197
|
+
if (oldest) {
|
|
198
|
+
localStorage.removeItem(oldest.key);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
let totalSize = items.reduce((sum, item) => sum + item.size, 0);
|
|
202
|
+
while (totalSize > this.options.maxSize && items.length > 0) {
|
|
203
|
+
const oldest = items.shift();
|
|
204
|
+
if (oldest) {
|
|
205
|
+
localStorage.removeItem(oldest.key);
|
|
206
|
+
totalSize -= oldest.size;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.warn("[CacheManager] Failed to cleanup cache:", error);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
getCurrentSize() {
|
|
214
|
+
if (typeof window === "undefined") return 0;
|
|
215
|
+
try {
|
|
216
|
+
let totalSize = 0;
|
|
217
|
+
Object.keys(localStorage).forEach((key) => {
|
|
218
|
+
if (key.startsWith("langie_")) {
|
|
219
|
+
const item = localStorage.getItem(key);
|
|
220
|
+
if (item) {
|
|
221
|
+
totalSize += new Blob([item]).size;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
return totalSize;
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.warn("[CacheManager] Failed to calculate cache size:", error);
|
|
228
|
+
return 0;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
getStats() {
|
|
232
|
+
if (typeof window === "undefined") {
|
|
233
|
+
return { size: 0, items: 0, maxSize: this.options.maxSize, maxItems: this.options.maxItems };
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
let totalSize = 0;
|
|
237
|
+
let itemCount = 0;
|
|
238
|
+
Object.keys(localStorage).forEach((key) => {
|
|
239
|
+
if (key.startsWith("langie_")) {
|
|
240
|
+
const item = localStorage.getItem(key);
|
|
241
|
+
if (item) {
|
|
242
|
+
totalSize += new Blob([item]).size;
|
|
243
|
+
itemCount++;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
return {
|
|
248
|
+
size: totalSize,
|
|
249
|
+
items: itemCount,
|
|
250
|
+
maxSize: this.options.maxSize,
|
|
251
|
+
maxItems: this.options.maxItems
|
|
252
|
+
};
|
|
253
|
+
} catch (error) {
|
|
254
|
+
console.warn("[CacheManager] Failed to get cache stats:", error);
|
|
255
|
+
return { size: 0, items: 0, maxSize: this.options.maxSize, maxItems: this.options.maxItems };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
var cacheManager = new CacheManager();
|
|
260
|
+
var setCache = (key, data, ttl) => {
|
|
261
|
+
return cacheManager.set(key, data, ttl);
|
|
262
|
+
};
|
|
263
|
+
var getCache = (key) => {
|
|
264
|
+
return cacheManager.get(key);
|
|
265
|
+
};
|
|
266
|
+
var removeCache = (key) => {
|
|
267
|
+
return cacheManager.remove(key);
|
|
268
|
+
};
|
|
269
|
+
var clearCache = (pattern) => {
|
|
270
|
+
return cacheManager.clear(pattern);
|
|
271
|
+
};
|
|
272
|
+
var getCacheStats = () => {
|
|
273
|
+
return cacheManager.getStats();
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
// src/composables/useLangie-core.ts
|
|
277
|
+
var availableLanguages = ref([]);
|
|
278
|
+
var translations = reactive({});
|
|
279
|
+
var uiTranslations = reactive({});
|
|
280
|
+
var currentLanguage = ref("en");
|
|
281
|
+
var _translatorHost = DEFAULT_API_HOST;
|
|
282
|
+
var _autoSelected = false;
|
|
283
|
+
var _languagesCache = null;
|
|
284
|
+
var _languagesPromise = null;
|
|
285
|
+
function useLangieCore(options = {}) {
|
|
286
|
+
if (options.translatorHost) {
|
|
287
|
+
_translatorHost = options.translatorHost;
|
|
288
|
+
}
|
|
289
|
+
const translatorHost = _translatorHost;
|
|
290
|
+
const defaultLanguage = options.defaultLanguage || "en";
|
|
291
|
+
const isLoading = ref(false);
|
|
292
|
+
if (defaultLanguage !== "en" && currentLanguage.value === "en") {
|
|
293
|
+
currentLanguage.value = defaultLanguage;
|
|
294
|
+
}
|
|
295
|
+
if (typeof window !== "undefined" && currentLanguage.value === "en") {
|
|
296
|
+
const savedLanguage = localStorage.getItem("interface_language");
|
|
297
|
+
if (savedLanguage) {
|
|
298
|
+
currentLanguage.value = savedLanguage;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (typeof window !== "undefined") {
|
|
302
|
+
const cachedLanguages = getCache("langie_languages_cache");
|
|
303
|
+
if (cachedLanguages && availableLanguages.value.length === 0) {
|
|
304
|
+
availableLanguages.value = cachedLanguages;
|
|
305
|
+
_languagesCache = cachedLanguages;
|
|
306
|
+
devDebug("[useLangie] Loaded languages from cache:", cachedLanguages.length);
|
|
307
|
+
} else if (cachedLanguages) {
|
|
308
|
+
devDebug(
|
|
309
|
+
"[useLangie] Cache exists but languages already loaded:",
|
|
310
|
+
availableLanguages.value.length
|
|
311
|
+
);
|
|
312
|
+
} else {
|
|
313
|
+
devDebug("[useLangie] No cached languages found");
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const setLanguage = (lang) => {
|
|
317
|
+
if (lang && lang !== currentLanguage.value) {
|
|
318
|
+
}
|
|
319
|
+
currentLanguage.value = lang;
|
|
320
|
+
if (typeof window !== "undefined") {
|
|
321
|
+
localStorage.setItem("interface_language", lang);
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
const fetchLanguages = async (opts = {}) => {
|
|
325
|
+
const { force = false, country: explicitCountry } = opts;
|
|
326
|
+
devDebug("[useLangie] fetchLanguages called:", {
|
|
327
|
+
force,
|
|
328
|
+
hasCache: !!_languagesCache,
|
|
329
|
+
hasPromise: !!_languagesPromise,
|
|
330
|
+
availableLanguagesLength: availableLanguages.value.length
|
|
331
|
+
});
|
|
332
|
+
if (!force) {
|
|
333
|
+
if (_languagesCache) {
|
|
334
|
+
devDebug("[useLangie] Returning cached languages:", _languagesCache.length);
|
|
335
|
+
return _languagesCache;
|
|
336
|
+
}
|
|
337
|
+
if (_languagesPromise) {
|
|
338
|
+
devDebug("[useLangie] Returning existing promise");
|
|
339
|
+
return _languagesPromise;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
try {
|
|
343
|
+
let countryHint = explicitCountry || null;
|
|
344
|
+
if (!countryHint && !_languagesCache && availableLanguages.value && availableLanguages.value.length) {
|
|
345
|
+
const currentLang = currentLanguage.value;
|
|
346
|
+
const entry = availableLanguages.value.find(
|
|
347
|
+
(l) => l.code === currentLang
|
|
348
|
+
);
|
|
349
|
+
if (entry) {
|
|
350
|
+
const c = Array.isArray(entry.flag_country) ? entry.flag_country[0] : typeof entry.flag_country === "string" ? entry.flag_country.split(",")[0] : null;
|
|
351
|
+
if (c) countryHint = c.toUpperCase();
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (!countryHint && !_languagesCache && typeof window !== "undefined") {
|
|
355
|
+
const cc = await getCountryCode();
|
|
356
|
+
countryHint = cc;
|
|
357
|
+
}
|
|
358
|
+
let url = "/languages";
|
|
359
|
+
const language = typeof navigator !== "undefined" && navigator.languages && navigator.languages[0] || typeof navigator !== "undefined" && navigator.language || "";
|
|
360
|
+
const timezone = typeof Intl !== "undefined" && Intl.DateTimeFormat().resolvedOptions().timeZone || "";
|
|
361
|
+
const params = new URLSearchParams();
|
|
362
|
+
if (countryHint) params.append("country", countryHint);
|
|
363
|
+
if (language) params.append("language", language);
|
|
364
|
+
if (timezone) params.append("timezone", timezone);
|
|
365
|
+
if (Array.from(params).length > 0) url += `?${params.toString()}`;
|
|
366
|
+
_languagesPromise = fetch(`${translatorHost}${url}`).then((res) => res.json());
|
|
367
|
+
const response = await _languagesPromise;
|
|
368
|
+
const rawList = Array.isArray(response) ? response : response.languages || [];
|
|
369
|
+
const mapped = rawList.map(
|
|
370
|
+
(lang) => {
|
|
371
|
+
let flag = lang.flag_country || lang.code;
|
|
372
|
+
if (Array.isArray(flag)) {
|
|
373
|
+
flag = flag[0];
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
...lang,
|
|
377
|
+
value: lang.code,
|
|
378
|
+
flag_country: flag
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
const filtered = mapped.filter((l) => {
|
|
383
|
+
if (l.code.startsWith("sr")) {
|
|
384
|
+
return l.code === "sr-latn" || l.code === "sr-cyrl";
|
|
385
|
+
}
|
|
386
|
+
return true;
|
|
387
|
+
});
|
|
388
|
+
availableLanguages.value = filtered;
|
|
389
|
+
devDebug("[useLangie] Set availableLanguages:", filtered.length);
|
|
390
|
+
if (typeof window !== "undefined") {
|
|
391
|
+
const saved = setCache("langie_languages_cache", filtered, 7 * 24 * 60 * 60 * 1e3);
|
|
392
|
+
devDebug(
|
|
393
|
+
"[useLangie] Saved languages to cache:",
|
|
394
|
+
saved ? "success" : "failed",
|
|
395
|
+
filtered.length
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
if (!_autoSelected && !localStorage.getItem("interface_language")) {
|
|
399
|
+
const locale = typeof navigator !== "undefined" ? navigator.languages?.[0] || navigator.language || "" : "";
|
|
400
|
+
const browserCode = locale.split("-")[0];
|
|
401
|
+
if (browserCode) {
|
|
402
|
+
let pick = void 0;
|
|
403
|
+
if (browserCode === "sr") {
|
|
404
|
+
const isLatin = /latn/i.test(locale) || locale === "sr";
|
|
405
|
+
const target = isLatin ? "sr-latn" : "sr-cyrl";
|
|
406
|
+
pick = mapped.find((l) => l.value === target);
|
|
407
|
+
} else if (browserCode === "sh") {
|
|
408
|
+
pick = mapped.find((l) => l.value === "sr-latn") || mapped.find((l) => l.code.startsWith("sr"));
|
|
409
|
+
} else {
|
|
410
|
+
pick = mapped.find((l) => l.value === browserCode);
|
|
411
|
+
if (!pick) {
|
|
412
|
+
pick = mapped.find((l) => l.code.startsWith(browserCode));
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (pick && pick.value) {
|
|
416
|
+
setLanguage(pick.value);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
_autoSelected = true;
|
|
420
|
+
}
|
|
421
|
+
_languagesCache = filtered;
|
|
422
|
+
_languagesPromise = null;
|
|
423
|
+
return filtered;
|
|
424
|
+
} catch (error) {
|
|
425
|
+
devDebug("[useLangie] Language fetch error:", { error });
|
|
426
|
+
return [];
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
const clearTranslations = () => {
|
|
430
|
+
Object.keys(translations).forEach((key) => delete translations[key]);
|
|
431
|
+
Object.keys(uiTranslations).forEach((key) => delete uiTranslations[key]);
|
|
432
|
+
if (typeof window !== "undefined") {
|
|
433
|
+
clearCache("translations_cache");
|
|
434
|
+
clearCache("ui_translations_cache");
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
return {
|
|
438
|
+
availableLanguages,
|
|
439
|
+
translations,
|
|
440
|
+
uiTranslations,
|
|
441
|
+
currentLanguage,
|
|
442
|
+
isLoading,
|
|
443
|
+
setLanguage,
|
|
444
|
+
fetchLanguages,
|
|
445
|
+
clearTranslations,
|
|
446
|
+
translatorHost
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/composables/useLangie-batching.ts
|
|
451
|
+
var TranslationBatching = class {
|
|
452
|
+
constructor(options = {}, translatorHost, currentLanguage2, onBatchComplete) {
|
|
453
|
+
this.options = options;
|
|
454
|
+
this.translatorHost = translatorHost;
|
|
455
|
+
this.currentLanguage = currentLanguage2;
|
|
456
|
+
this.onBatchComplete = onBatchComplete;
|
|
457
|
+
}
|
|
458
|
+
pendingRequests = /* @__PURE__ */ new Set();
|
|
459
|
+
queueMap = /* @__PURE__ */ new Map();
|
|
460
|
+
flushTimeout = null;
|
|
461
|
+
queuedThisTick = /* @__PURE__ */ new Set();
|
|
462
|
+
clearQueuedThisTickScheduled = false;
|
|
463
|
+
firstItemTime = null;
|
|
464
|
+
get initialBatchDelay() {
|
|
465
|
+
return this.options.initialBatchDelay ?? 50;
|
|
466
|
+
}
|
|
467
|
+
get followupBatchDelay() {
|
|
468
|
+
return this.options.followupBatchDelay ?? 10;
|
|
469
|
+
}
|
|
470
|
+
get maxBatchSize() {
|
|
471
|
+
return this.options.maxBatchSize ?? 50;
|
|
472
|
+
}
|
|
473
|
+
get maxWaitTime() {
|
|
474
|
+
return this.options.maxWaitTime ?? 1e3;
|
|
475
|
+
}
|
|
476
|
+
scheduleClearQueuedThisTick() {
|
|
477
|
+
if (this.clearQueuedThisTickScheduled) return;
|
|
478
|
+
this.clearQueuedThisTickScheduled = true;
|
|
479
|
+
queueMicrotask(() => {
|
|
480
|
+
this.queuedThisTick.clear();
|
|
481
|
+
this.clearQueuedThisTickScheduled = false;
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
flushQueues = async () => {
|
|
485
|
+
const allRequests = [];
|
|
486
|
+
for (const [batchKey, map] of Array.from(this.queueMap.entries())) {
|
|
487
|
+
if (!map || map.size === 0) {
|
|
488
|
+
this.queueMap.delete(batchKey);
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
const [from, to] = batchKey.split("|");
|
|
492
|
+
for (const [cacheKey, item] of map.entries()) {
|
|
493
|
+
allRequests.push({
|
|
494
|
+
[API_FIELD_TEXT]: item[API_FIELD_TEXT],
|
|
495
|
+
[API_FIELD_CTX]: item[API_FIELD_CTX],
|
|
496
|
+
[API_FIELD_FROM]: from,
|
|
497
|
+
[API_FIELD_TO]: to,
|
|
498
|
+
cacheKey,
|
|
499
|
+
__explicitToLang: item.__explicitToLang
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
this.queueMap.delete(batchKey);
|
|
503
|
+
}
|
|
504
|
+
if (allRequests.length > 0) {
|
|
505
|
+
devDebug(
|
|
506
|
+
"[TranslationBatching] Sending batch:",
|
|
507
|
+
allRequests.length,
|
|
508
|
+
"translation items",
|
|
509
|
+
allRequests
|
|
510
|
+
);
|
|
511
|
+
const chunks = this.chunkArray(allRequests, this.maxBatchSize);
|
|
512
|
+
for (const chunk of chunks) {
|
|
513
|
+
try {
|
|
514
|
+
await this.fetchAndCacheBatchMixed(chunk);
|
|
515
|
+
} catch (error) {
|
|
516
|
+
devDebug("[TranslationBatching] Batch translation error:", error);
|
|
517
|
+
chunk.forEach((req) => this.pendingRequests.delete(req.cacheKey));
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
this.firstItemTime = null;
|
|
522
|
+
};
|
|
523
|
+
chunkArray(array, size) {
|
|
524
|
+
const chunks = [];
|
|
525
|
+
for (let i = 0; i < array.length; i += size) {
|
|
526
|
+
chunks.push(array.slice(i, i + size));
|
|
527
|
+
}
|
|
528
|
+
return chunks;
|
|
529
|
+
}
|
|
530
|
+
scheduleFlush = () => {
|
|
531
|
+
if (this.flushTimeout) {
|
|
532
|
+
clearTimeout(this.flushTimeout);
|
|
533
|
+
}
|
|
534
|
+
let totalItems = 0;
|
|
535
|
+
for (const map of this.queueMap.values()) {
|
|
536
|
+
totalItems += map.size;
|
|
537
|
+
}
|
|
538
|
+
if (this.firstItemTime && Date.now() - this.firstItemTime >= this.maxWaitTime) {
|
|
539
|
+
devDebug("[TranslationBatching] Flushing due to maximum wait time:", totalItems, "items");
|
|
540
|
+
this.flushQueues();
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const delay = this.queueMap.size === 1 ? this.initialBatchDelay : this.followupBatchDelay;
|
|
544
|
+
devDebug(
|
|
545
|
+
"[TranslationBatching] Scheduling flush in",
|
|
546
|
+
delay,
|
|
547
|
+
"ms",
|
|
548
|
+
Array.from(this.queueMap.entries())
|
|
549
|
+
);
|
|
550
|
+
this.flushTimeout = setTimeout(() => {
|
|
551
|
+
this.flushQueues();
|
|
552
|
+
this.flushTimeout = null;
|
|
553
|
+
}, delay);
|
|
554
|
+
};
|
|
555
|
+
queueTranslation(text, ctx, from, to, cacheKey, explicitToLang) {
|
|
556
|
+
if (this.pendingRequests.has(cacheKey) || this.queuedThisTick.has(cacheKey)) {
|
|
557
|
+
devDebug("[TranslationBatching] Skipping duplicate:", cacheKey);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (this.firstItemTime === null) {
|
|
561
|
+
this.firstItemTime = Date.now();
|
|
562
|
+
}
|
|
563
|
+
this.queuedThisTick.add(cacheKey);
|
|
564
|
+
this.scheduleClearQueuedThisTick();
|
|
565
|
+
this.pendingRequests.add(cacheKey);
|
|
566
|
+
const batchKey = `${from}|${to}`;
|
|
567
|
+
if (!this.queueMap.has(batchKey)) {
|
|
568
|
+
this.queueMap.set(batchKey, /* @__PURE__ */ new Map());
|
|
569
|
+
}
|
|
570
|
+
this.queueMap.get(batchKey).set(cacheKey, {
|
|
571
|
+
[API_FIELD_TEXT]: text,
|
|
572
|
+
[API_FIELD_CTX]: ctx,
|
|
573
|
+
__explicitToLang: explicitToLang
|
|
574
|
+
});
|
|
575
|
+
devDebug("[TranslationBatching] Queued translation:", {
|
|
576
|
+
text,
|
|
577
|
+
ctx,
|
|
578
|
+
from,
|
|
579
|
+
to,
|
|
580
|
+
cacheKey,
|
|
581
|
+
batchKey,
|
|
582
|
+
explicitToLang
|
|
583
|
+
});
|
|
584
|
+
this.scheduleFlush();
|
|
585
|
+
}
|
|
586
|
+
async fetchAndCacheBatchMixed(requests) {
|
|
587
|
+
const grouped = {};
|
|
588
|
+
requests.forEach((req) => {
|
|
589
|
+
const key = `${req.from}|${req.to}`;
|
|
590
|
+
if (!grouped[key]) grouped[key] = [];
|
|
591
|
+
grouped[key].push(req);
|
|
592
|
+
});
|
|
593
|
+
const allResults = [];
|
|
594
|
+
const allRequests = [];
|
|
595
|
+
for (const [langPair, batchRequests] of Object.entries(grouped)) {
|
|
596
|
+
const [from, to] = langPair.split("|");
|
|
597
|
+
try {
|
|
598
|
+
const contexts = [...new Set(batchRequests.map((req) => req[API_FIELD_CTX]))];
|
|
599
|
+
const useGlobalContext = contexts.length === 1 && contexts[0] === "ui";
|
|
600
|
+
devDebug(
|
|
601
|
+
"[TranslationBatching] Sending batch for",
|
|
602
|
+
langPair,
|
|
603
|
+
"with",
|
|
604
|
+
batchRequests.length,
|
|
605
|
+
"items",
|
|
606
|
+
batchRequests
|
|
607
|
+
);
|
|
608
|
+
const response = await fetch(`${this.translatorHost}/translate`, {
|
|
609
|
+
method: "POST",
|
|
610
|
+
headers: {
|
|
611
|
+
"Content-Type": "application/json"
|
|
612
|
+
},
|
|
613
|
+
body: JSON.stringify({
|
|
614
|
+
translations: batchRequests.map((req) => ({
|
|
615
|
+
[API_FIELD_TEXT]: req[API_FIELD_TEXT],
|
|
616
|
+
...useGlobalContext ? {} : { [API_FIELD_CTX]: req[API_FIELD_CTX] }
|
|
617
|
+
})),
|
|
618
|
+
[API_FIELD_FROM]: from,
|
|
619
|
+
[API_FIELD_TO]: to,
|
|
620
|
+
...useGlobalContext ? { [API_FIELD_CTX]: "ui" } : {}
|
|
621
|
+
})
|
|
622
|
+
});
|
|
623
|
+
let result;
|
|
624
|
+
try {
|
|
625
|
+
result = await response.json();
|
|
626
|
+
} catch (parseError) {
|
|
627
|
+
devDebug("[TranslationBatching] Failed to parse response as JSON:", parseError);
|
|
628
|
+
throw new Error(`Translation request failed: ${response.status}`);
|
|
629
|
+
}
|
|
630
|
+
if (!response.ok) {
|
|
631
|
+
devDebug(
|
|
632
|
+
"[TranslationBatching] Translation request failed:",
|
|
633
|
+
response.status,
|
|
634
|
+
response.statusText
|
|
635
|
+
);
|
|
636
|
+
if (result && result[API_FIELD_ERROR]) {
|
|
637
|
+
devDebug(
|
|
638
|
+
"[TranslationBatching] HTTP error with API error message:",
|
|
639
|
+
result[API_FIELD_ERROR]
|
|
640
|
+
);
|
|
641
|
+
const errorResponses = batchRequests.map((req) => ({
|
|
642
|
+
[API_FIELD_TEXT]: req[API_FIELD_TEXT],
|
|
643
|
+
[API_FIELD_ERROR]: result[API_FIELD_ERROR]
|
|
644
|
+
}));
|
|
645
|
+
allResults.push({ [API_FIELD_TRANSLATIONS]: errorResponses });
|
|
646
|
+
allRequests.push(...batchRequests);
|
|
647
|
+
batchRequests.forEach((req) => {
|
|
648
|
+
this.pendingRequests.delete(req.cacheKey);
|
|
649
|
+
});
|
|
650
|
+
this.onBatchComplete(allResults, allRequests);
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
throw new Error(`Translation request failed: ${response.status}`);
|
|
654
|
+
}
|
|
655
|
+
if (result[API_FIELD_ERROR]) {
|
|
656
|
+
devDebug("[TranslationBatching] Top-level API error:", result[API_FIELD_ERROR]);
|
|
657
|
+
const errorResponses = batchRequests.map((req) => ({
|
|
658
|
+
[API_FIELD_TEXT]: req[API_FIELD_TEXT],
|
|
659
|
+
[API_FIELD_ERROR]: result[API_FIELD_ERROR]
|
|
660
|
+
}));
|
|
661
|
+
allResults.push({ [API_FIELD_TRANSLATIONS]: errorResponses });
|
|
662
|
+
allRequests.push(...batchRequests);
|
|
663
|
+
batchRequests.forEach((req) => {
|
|
664
|
+
this.pendingRequests.delete(req.cacheKey);
|
|
665
|
+
});
|
|
666
|
+
} else {
|
|
667
|
+
if (result[API_FIELD_TRANSLATIONS]) {
|
|
668
|
+
result[API_FIELD_TRANSLATIONS].forEach((translation, index) => {
|
|
669
|
+
if (translation[API_FIELD_ERROR]) {
|
|
670
|
+
const originalText = batchRequests[index]?.[API_FIELD_TEXT];
|
|
671
|
+
devDebug(
|
|
672
|
+
"[TranslationBatching] Translation error for",
|
|
673
|
+
originalText,
|
|
674
|
+
":",
|
|
675
|
+
translation[API_FIELD_ERROR]
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
allResults.push(result);
|
|
681
|
+
allRequests.push(...batchRequests);
|
|
682
|
+
batchRequests.forEach((req) => {
|
|
683
|
+
this.pendingRequests.delete(req.cacheKey);
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
} catch (error) {
|
|
687
|
+
devDebug("[TranslationBatching] Batch request failed for", langPair, ":", error);
|
|
688
|
+
batchRequests.forEach((req) => {
|
|
689
|
+
this.pendingRequests.delete(req.cacheKey);
|
|
690
|
+
});
|
|
691
|
+
throw error;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
this.onBatchComplete(allResults, allRequests);
|
|
695
|
+
}
|
|
696
|
+
clearPending(cacheKey) {
|
|
697
|
+
this.pendingRequests.delete(cacheKey);
|
|
698
|
+
}
|
|
699
|
+
clearAllPending() {
|
|
700
|
+
this.pendingRequests.clear();
|
|
701
|
+
}
|
|
702
|
+
cleanup() {
|
|
703
|
+
this.clearAllPending();
|
|
704
|
+
this.queueMap.clear();
|
|
705
|
+
this.queuedThisTick.clear();
|
|
706
|
+
if (this.flushTimeout) {
|
|
707
|
+
clearTimeout(this.flushTimeout);
|
|
708
|
+
this.flushTimeout = null;
|
|
709
|
+
}
|
|
710
|
+
this.clearQueuedThisTickScheduled = false;
|
|
711
|
+
this.firstItemTime = null;
|
|
712
|
+
}
|
|
713
|
+
getStats() {
|
|
714
|
+
return {
|
|
715
|
+
pendingRequests: this.pendingRequests.size,
|
|
716
|
+
queuedBatches: this.queueMap.size,
|
|
717
|
+
queuedThisTick: this.queuedThisTick.size,
|
|
718
|
+
hasFlushTimeout: !!this.flushTimeout
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
// src/useLangie.ts
|
|
724
|
+
var globalLangieInstance = null;
|
|
725
|
+
if (typeof window !== "undefined" && window.__LANGIE_SINGLETON__) {
|
|
726
|
+
globalLangieInstance = window.__LANGIE_SINGLETON__;
|
|
727
|
+
}
|
|
728
|
+
function safeLocalStorageAccess(operation) {
|
|
729
|
+
if (typeof window === "undefined") return void 0;
|
|
730
|
+
try {
|
|
731
|
+
return operation();
|
|
732
|
+
} catch (e) {
|
|
733
|
+
devDebug("[useLangie] localStorage error:", e);
|
|
734
|
+
return void 0;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (!globalLangieInstance && typeof window !== "undefined") {
|
|
738
|
+
const stored = safeLocalStorageAccess(() => localStorage.getItem("__LANGIE_SINGLETON_URL__"));
|
|
739
|
+
if (stored) {
|
|
740
|
+
const storedOptions = { translatorHost: stored };
|
|
741
|
+
globalLangieInstance = createLangieInstance(storedOptions);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
function createLangieInstance(options = {}) {
|
|
745
|
+
const core = useLangieCore(options);
|
|
746
|
+
const {
|
|
747
|
+
availableLanguages: availableLanguages2,
|
|
748
|
+
translations: translations2,
|
|
749
|
+
uiTranslations: uiTranslations2,
|
|
750
|
+
currentLanguage: currentLanguage2,
|
|
751
|
+
isLoading,
|
|
752
|
+
setLanguage,
|
|
753
|
+
fetchLanguages,
|
|
754
|
+
translatorHost,
|
|
755
|
+
clearTranslations
|
|
756
|
+
} = core;
|
|
757
|
+
const ltDefaults = {
|
|
758
|
+
ctx: "ui",
|
|
759
|
+
orig: ""
|
|
760
|
+
};
|
|
761
|
+
const setLtDefaults2 = (defaults) => {
|
|
762
|
+
Object.assign(ltDefaults, defaults);
|
|
763
|
+
};
|
|
764
|
+
const getLtDefaults2 = () => ({ ...ltDefaults });
|
|
765
|
+
const CACHE_KEY = "langie_translations_cache";
|
|
766
|
+
const UI_CACHE_KEY = "langie_ui_translations_cache";
|
|
767
|
+
const LANGUAGES_CACHE_KEY = "langie_languages_cache";
|
|
768
|
+
const loadCachedTranslations = () => {
|
|
769
|
+
if (typeof window === "undefined") return;
|
|
770
|
+
const cachedTranslations = getCache(CACHE_KEY);
|
|
771
|
+
const cachedUiTranslations = getCache(UI_CACHE_KEY);
|
|
772
|
+
if (cachedTranslations) {
|
|
773
|
+
const currentLang = currentLanguage2.value;
|
|
774
|
+
const langTranslations = cachedTranslations[currentLang] || {};
|
|
775
|
+
Object.assign(translations2, langTranslations);
|
|
776
|
+
}
|
|
777
|
+
if (cachedUiTranslations) {
|
|
778
|
+
const currentLang = currentLanguage2.value;
|
|
779
|
+
const langUiTranslations = cachedUiTranslations[currentLang] || {};
|
|
780
|
+
Object.assign(uiTranslations2, langUiTranslations);
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
const saveCachedTranslations = () => {
|
|
784
|
+
if (typeof window === "undefined") return;
|
|
785
|
+
const existingTranslations = getCache(CACHE_KEY) || {};
|
|
786
|
+
const existingUiTranslations = getCache(UI_CACHE_KEY) || {};
|
|
787
|
+
const currentLang = currentLanguage2.value;
|
|
788
|
+
existingTranslations[currentLang] = { ...translations2 };
|
|
789
|
+
existingUiTranslations[currentLang] = { ...uiTranslations2 };
|
|
790
|
+
setCache(CACHE_KEY, existingTranslations, 7 * 24 * 60 * 60 * 1e3);
|
|
791
|
+
setCache(UI_CACHE_KEY, existingUiTranslations, 7 * 24 * 60 * 60 * 1e3);
|
|
792
|
+
};
|
|
793
|
+
const loadCachedLanguages = () => {
|
|
794
|
+
if (typeof window === "undefined") return;
|
|
795
|
+
const cachedLanguages = getCache(LANGUAGES_CACHE_KEY);
|
|
796
|
+
if (cachedLanguages) {
|
|
797
|
+
availableLanguages2.value = cachedLanguages;
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
loadCachedTranslations();
|
|
801
|
+
loadCachedLanguages();
|
|
802
|
+
watch(currentLanguage2, () => {
|
|
803
|
+
Object.keys(translations2).forEach((key) => delete translations2[key]);
|
|
804
|
+
Object.keys(uiTranslations2).forEach((key) => delete uiTranslations2[key]);
|
|
805
|
+
loadCachedTranslations();
|
|
806
|
+
});
|
|
807
|
+
const batching = new TranslationBatching(
|
|
808
|
+
{
|
|
809
|
+
initialBatchDelay: options.initialBatchDelay,
|
|
810
|
+
followupBatchDelay: options.followupBatchDelay,
|
|
811
|
+
maxBatchSize: options.maxBatchSize,
|
|
812
|
+
maxWaitTime: options.maxWaitTime
|
|
813
|
+
},
|
|
814
|
+
translatorHost,
|
|
815
|
+
() => currentLanguage2.value,
|
|
816
|
+
(results, requests) => {
|
|
817
|
+
requests.forEach((req) => {
|
|
818
|
+
const cacheKey = `${req[API_FIELD_TEXT]}|${req[API_FIELD_CTX]}`;
|
|
819
|
+
const languageCacheKey = `${cacheKey}|${req[API_FIELD_FROM]}|${req[API_FIELD_TO]}`;
|
|
820
|
+
recentlyQueued.delete(languageCacheKey);
|
|
821
|
+
});
|
|
822
|
+
let translationsArray = [];
|
|
823
|
+
results.forEach((result) => {
|
|
824
|
+
if (Array.isArray(result)) {
|
|
825
|
+
translationsArray = translationsArray.concat(result);
|
|
826
|
+
} else if (result.translations && Array.isArray(result.translations)) {
|
|
827
|
+
translationsArray = translationsArray.concat(result.translations);
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
translationsArray.forEach((translation, idx) => {
|
|
831
|
+
const request = requests[idx];
|
|
832
|
+
if (!request) {
|
|
833
|
+
devDebug("[useLangie] No matching request for translation:", translation);
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
const originalText = request[API_FIELD_TEXT];
|
|
837
|
+
const originalCtx = request[API_FIELD_CTX] ?? (ltDefaults.ctx || "ui");
|
|
838
|
+
if (translation[API_FIELD_ERROR]) {
|
|
839
|
+
devDebug(
|
|
840
|
+
"[useLangie] Translation error for",
|
|
841
|
+
originalText,
|
|
842
|
+
":",
|
|
843
|
+
translation[API_FIELD_ERROR]
|
|
844
|
+
);
|
|
845
|
+
const errorKey = `${originalText}|${originalCtx}|${request[API_FIELD_FROM]}|${request[API_FIELD_TO]}`;
|
|
846
|
+
translationErrors.set(errorKey, translation[API_FIELD_ERROR]);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
if (translation[API_FIELD_FROM] && !translation[API_FIELD_TEXT]) {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
const translatedText = translation[API_FIELD_TEXT];
|
|
853
|
+
if (translatedText) {
|
|
854
|
+
const requestedLanguage = request[API_FIELD_TO];
|
|
855
|
+
const explicitToLang = request.__explicitToLang;
|
|
856
|
+
if (!explicitToLang && requestedLanguage !== currentLanguage2.value) {
|
|
857
|
+
devDebug("[useLangie] Skipping outdated translation:", {
|
|
858
|
+
original: originalText,
|
|
859
|
+
translated: translatedText,
|
|
860
|
+
requestedLanguage,
|
|
861
|
+
currentLanguage: currentLanguage2.value
|
|
862
|
+
});
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
if (translatedText === originalText) {
|
|
866
|
+
devDebug("[useLangie] Skipping cache for identical translation:", {
|
|
867
|
+
original: originalText,
|
|
868
|
+
translated: translatedText,
|
|
869
|
+
context: originalCtx
|
|
870
|
+
});
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
const effectiveCtx = originalCtx;
|
|
874
|
+
const cacheKey = `${originalText}|${effectiveCtx}`;
|
|
875
|
+
const cache = effectiveCtx === "ui" ? uiTranslations2 : translations2;
|
|
876
|
+
cache[cacheKey] = translatedText;
|
|
877
|
+
devDebug("[useLangie] Cached translation:", {
|
|
878
|
+
original: originalText,
|
|
879
|
+
translated: translatedText,
|
|
880
|
+
context: effectiveCtx,
|
|
881
|
+
cacheKey,
|
|
882
|
+
language: requestedLanguage
|
|
883
|
+
});
|
|
884
|
+
saveCachedTranslations();
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
);
|
|
889
|
+
const recentlyQueued = /* @__PURE__ */ new Set();
|
|
890
|
+
const pendingTimeouts = /* @__PURE__ */ new Set();
|
|
891
|
+
const translationErrors = /* @__PURE__ */ new Map();
|
|
892
|
+
const translateInternal = (text, ctx, originalLang, toLang, reactive2 = false) => {
|
|
893
|
+
if (reactive2) {
|
|
894
|
+
void currentLanguage2.value;
|
|
895
|
+
}
|
|
896
|
+
const from = originalLang || ltDefaults.orig || "";
|
|
897
|
+
const to = toLang || currentLanguage2.value;
|
|
898
|
+
if (from === to) {
|
|
899
|
+
return text;
|
|
900
|
+
}
|
|
901
|
+
const effectiveCtx = ctx !== void 0 ? ctx : ltDefaults.ctx || "ui";
|
|
902
|
+
const cacheKey = `${text}|${effectiveCtx}`;
|
|
903
|
+
const cache = effectiveCtx === "ui" ? uiTranslations2 : translations2;
|
|
904
|
+
if (cache[cacheKey]) {
|
|
905
|
+
return cache[cacheKey];
|
|
906
|
+
}
|
|
907
|
+
const errorKey = `${text}|${effectiveCtx}|${from}|${to}`;
|
|
908
|
+
if (translationErrors.has(errorKey)) {
|
|
909
|
+
return text;
|
|
910
|
+
}
|
|
911
|
+
const languageCacheKey = `${cacheKey}|${from}|${to}`;
|
|
912
|
+
if (recentlyQueued.has(languageCacheKey)) {
|
|
913
|
+
return text;
|
|
914
|
+
}
|
|
915
|
+
batching.queueTranslation(text, effectiveCtx, from, to, cacheKey, toLang !== void 0);
|
|
916
|
+
recentlyQueued.add(languageCacheKey);
|
|
917
|
+
const clearDelay = 100;
|
|
918
|
+
const timeoutId = setTimeout(() => {
|
|
919
|
+
recentlyQueued.delete(languageCacheKey);
|
|
920
|
+
pendingTimeouts.delete(timeoutId);
|
|
921
|
+
}, clearDelay);
|
|
922
|
+
pendingTimeouts.add(timeoutId);
|
|
923
|
+
return text;
|
|
924
|
+
};
|
|
925
|
+
const l = (text, ctx, originalLang, toLang) => translateInternal(text, ctx, originalLang, toLang, false);
|
|
926
|
+
const lr = (text, ctx, originalLang, toLang) => translateInternal(text, ctx, originalLang, toLang, true);
|
|
927
|
+
const fetchAndCacheBatch = async (items, from, to = currentLanguage2.value, globalCtx) => {
|
|
928
|
+
if (items.length === 0) return;
|
|
929
|
+
const effectiveFrom = from || ltDefaults.orig || "";
|
|
930
|
+
if (effectiveFrom === to) {
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
isLoading.value = true;
|
|
934
|
+
try {
|
|
935
|
+
const effectiveCtx = globalCtx || ltDefaults.ctx || "ui";
|
|
936
|
+
const response = await fetch(`${translatorHost}/translate`, {
|
|
937
|
+
method: "POST",
|
|
938
|
+
headers: {
|
|
939
|
+
"Content-Type": "application/json"
|
|
940
|
+
},
|
|
941
|
+
body: JSON.stringify({
|
|
942
|
+
translations: items.map((item) => ({
|
|
943
|
+
[API_FIELD_TEXT]: item[API_FIELD_TEXT],
|
|
944
|
+
[API_FIELD_CTX]: item[API_FIELD_CTX] || effectiveCtx
|
|
945
|
+
})),
|
|
946
|
+
[API_FIELD_FROM]: effectiveFrom,
|
|
947
|
+
[API_FIELD_TO]: to
|
|
948
|
+
})
|
|
949
|
+
});
|
|
950
|
+
if (!response.ok) {
|
|
951
|
+
throw new Error(`Translation request failed: ${response.status}`);
|
|
952
|
+
}
|
|
953
|
+
const result = await response.json();
|
|
954
|
+
if (result[API_FIELD_ERROR]) {
|
|
955
|
+
devDebug("[useLangie] Top-level API error:", result[API_FIELD_ERROR]);
|
|
956
|
+
const errorResponses = items.map((item) => ({
|
|
957
|
+
[API_FIELD_TEXT]: item[API_FIELD_TEXT],
|
|
958
|
+
[API_FIELD_ERROR]: result[API_FIELD_ERROR]
|
|
959
|
+
}));
|
|
960
|
+
errorResponses.forEach((translation, index) => {
|
|
961
|
+
const item = items[index];
|
|
962
|
+
const originalText = item?.[API_FIELD_TEXT];
|
|
963
|
+
if (!originalText) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
if (translation[API_FIELD_ERROR]) {
|
|
967
|
+
devDebug(
|
|
968
|
+
"[useLangie] Translation error for",
|
|
969
|
+
originalText,
|
|
970
|
+
":",
|
|
971
|
+
translation[API_FIELD_ERROR]
|
|
972
|
+
);
|
|
973
|
+
const errorKey = `${originalText}|${item[API_FIELD_CTX] || effectiveCtx}|${effectiveFrom}|${to}`;
|
|
974
|
+
translationErrors.set(errorKey, translation[API_FIELD_ERROR]);
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
if (result[API_FIELD_TRANSLATIONS]) {
|
|
981
|
+
result[API_FIELD_TRANSLATIONS].forEach(
|
|
982
|
+
(translation, index) => {
|
|
983
|
+
const item = items[index];
|
|
984
|
+
const originalText = item?.[API_FIELD_TEXT];
|
|
985
|
+
if (!originalText) {
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
if (translation[API_FIELD_ERROR]) {
|
|
989
|
+
devDebug(
|
|
990
|
+
"[useLangie] Translation error for",
|
|
991
|
+
originalText,
|
|
992
|
+
":",
|
|
993
|
+
translation[API_FIELD_ERROR]
|
|
994
|
+
);
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
if (translation[API_FIELD_FROM] && !translation[API_FIELD_TEXT]) {
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
const translatedText = translation[API_FIELD_TEXT];
|
|
1001
|
+
if (translatedText) {
|
|
1002
|
+
if (to !== currentLanguage2.value) {
|
|
1003
|
+
devDebug("[useLangie] Skipping outdated translation (batch):", {
|
|
1004
|
+
original: originalText,
|
|
1005
|
+
translated: translatedText,
|
|
1006
|
+
requestedLanguage: to,
|
|
1007
|
+
currentLanguage: currentLanguage2.value
|
|
1008
|
+
});
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
if (translatedText === originalText) {
|
|
1012
|
+
devDebug("[useLangie] Skipping cache for identical translation (batch):", {
|
|
1013
|
+
original: originalText,
|
|
1014
|
+
translated: translatedText,
|
|
1015
|
+
context: item[API_FIELD_CTX] || effectiveCtx
|
|
1016
|
+
});
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
const originalCtx = item[API_FIELD_CTX];
|
|
1020
|
+
const translationCtx = originalCtx !== void 0 ? originalCtx : effectiveCtx;
|
|
1021
|
+
const cacheKey = `${originalText}|${translationCtx}`;
|
|
1022
|
+
const cache = translationCtx === "ui" ? uiTranslations2 : translations2;
|
|
1023
|
+
cache[cacheKey] = translatedText;
|
|
1024
|
+
devDebug("[useLangie] Cached translation (batch):", {
|
|
1025
|
+
original: originalText,
|
|
1026
|
+
translated: translatedText,
|
|
1027
|
+
context: translationCtx,
|
|
1028
|
+
cacheKey,
|
|
1029
|
+
language: to
|
|
1030
|
+
});
|
|
1031
|
+
saveCachedTranslations();
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
} catch (error) {
|
|
1037
|
+
console.error("[useLangie] Translation error:", error);
|
|
1038
|
+
} finally {
|
|
1039
|
+
isLoading.value = false;
|
|
1040
|
+
}
|
|
1041
|
+
};
|
|
1042
|
+
watch(currentLanguage2, () => {
|
|
1043
|
+
recentlyQueued.clear();
|
|
1044
|
+
loadCachedTranslations();
|
|
1045
|
+
batching.cleanup();
|
|
1046
|
+
});
|
|
1047
|
+
return {
|
|
1048
|
+
// Core functionality
|
|
1049
|
+
availableLanguages: availableLanguages2,
|
|
1050
|
+
translations: translations2,
|
|
1051
|
+
uiTranslations: uiTranslations2,
|
|
1052
|
+
currentLanguage: currentLanguage2,
|
|
1053
|
+
isLoading,
|
|
1054
|
+
setLanguage,
|
|
1055
|
+
fetchLanguages,
|
|
1056
|
+
translatorHost,
|
|
1057
|
+
// Translation functions
|
|
1058
|
+
l,
|
|
1059
|
+
lr,
|
|
1060
|
+
fetchAndCacheBatch,
|
|
1061
|
+
// Error handling
|
|
1062
|
+
getTranslationError: (text, ctx, from, to) => {
|
|
1063
|
+
const effectiveCtx = ctx !== void 0 ? ctx : ltDefaults.ctx || "ui";
|
|
1064
|
+
const effectiveFrom = from || ltDefaults.orig || "";
|
|
1065
|
+
const effectiveTo = to || currentLanguage2.value;
|
|
1066
|
+
const errorKey = `${text}|${effectiveCtx}|${effectiveFrom}|${effectiveTo}`;
|
|
1067
|
+
return translationErrors.get(errorKey) || null;
|
|
1068
|
+
},
|
|
1069
|
+
// Utility functions
|
|
1070
|
+
cleanup: () => {
|
|
1071
|
+
clearTranslations();
|
|
1072
|
+
batching.cleanup();
|
|
1073
|
+
pendingTimeouts.forEach((id) => clearTimeout(id));
|
|
1074
|
+
pendingTimeouts.clear();
|
|
1075
|
+
translationErrors.clear();
|
|
1076
|
+
},
|
|
1077
|
+
getBatchingStats: () => batching.getStats(),
|
|
1078
|
+
// lt component defaults management
|
|
1079
|
+
setLtDefaults: setLtDefaults2,
|
|
1080
|
+
getLtDefaults: getLtDefaults2
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
function getGlobalLangieInstance() {
|
|
1084
|
+
if (typeof window !== "undefined") {
|
|
1085
|
+
return window.__LANGIE_SINGLETON__ || null;
|
|
1086
|
+
}
|
|
1087
|
+
return globalLangieInstance;
|
|
1088
|
+
}
|
|
1089
|
+
function setGlobalLangieInstance(instance, options) {
|
|
1090
|
+
if (typeof window !== "undefined") {
|
|
1091
|
+
;
|
|
1092
|
+
window.__LANGIE_SINGLETON__ = instance;
|
|
1093
|
+
if (options && options.translatorHost) {
|
|
1094
|
+
safeLocalStorageAccess(
|
|
1095
|
+
() => localStorage.setItem("__LANGIE_SINGLETON_URL__", options.translatorHost)
|
|
1096
|
+
);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
globalLangieInstance = instance;
|
|
1100
|
+
}
|
|
1101
|
+
function useLangie(options = {}) {
|
|
1102
|
+
const globalInstance = getGlobalLangieInstance();
|
|
1103
|
+
if (globalInstance) {
|
|
1104
|
+
const currentHost = globalInstance.translatorHost;
|
|
1105
|
+
const newHost = options.translatorHost;
|
|
1106
|
+
if (!options.translatorHost || currentHost === newHost) {
|
|
1107
|
+
return globalInstance;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
const instance = createLangieInstance(options);
|
|
1111
|
+
setGlobalLangieInstance(instance, options);
|
|
1112
|
+
return instance;
|
|
1113
|
+
}
|
|
1114
|
+
function setLtDefaults(defaults) {
|
|
1115
|
+
const instance = getGlobalLangieInstance();
|
|
1116
|
+
if (instance && instance.setLtDefaults) {
|
|
1117
|
+
instance.setLtDefaults(defaults);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
function getLtDefaults() {
|
|
1121
|
+
const instance = getGlobalLangieInstance();
|
|
1122
|
+
if (instance && instance.getLtDefaults) {
|
|
1123
|
+
return instance.getLtDefaults();
|
|
1124
|
+
}
|
|
1125
|
+
return { ctx: "ui", orig: "" };
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// src/core.ts
|
|
1129
|
+
var DEFAULT_TRANSLATOR_HOST = "http://localhost:8081";
|
|
1130
|
+
var translationCache = /* @__PURE__ */ new Map();
|
|
1131
|
+
function getTranslationCacheKey(serviceTranslations, options) {
|
|
1132
|
+
return JSON.stringify({
|
|
1133
|
+
translations: serviceTranslations.map((t) => ({
|
|
1134
|
+
[API_FIELD_TEXT]: t[API_FIELD_TEXT],
|
|
1135
|
+
[API_FIELD_FROM]: t[API_FIELD_FROM],
|
|
1136
|
+
[API_FIELD_TO]: t[API_FIELD_TO],
|
|
1137
|
+
[API_FIELD_CTX]: t[API_FIELD_CTX] || options[API_FIELD_CTX] || "ui"
|
|
1138
|
+
})),
|
|
1139
|
+
host: options.translatorHost || DEFAULT_TRANSLATOR_HOST,
|
|
1140
|
+
context: options[API_FIELD_CTX] || "ui"
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
async function translateBatch(translations2 = [], options = {}) {
|
|
1144
|
+
if (!Array.isArray(translations2) || translations2.length === 0) {
|
|
1145
|
+
throw new Error("translations must be a non-empty array");
|
|
1146
|
+
}
|
|
1147
|
+
const translatorHost = options.translatorHost || DEFAULT_TRANSLATOR_HOST;
|
|
1148
|
+
const apiKey = options.apiKey || process.env.TRANSLATOR_API_KEY;
|
|
1149
|
+
const serviceTranslations = [];
|
|
1150
|
+
const indexMap = [];
|
|
1151
|
+
translations2.forEach((tr, idx) => {
|
|
1152
|
+
const from = (tr[API_FIELD_FROM] || "").toLowerCase();
|
|
1153
|
+
const to = (tr[API_FIELD_TO] || "").toLowerCase();
|
|
1154
|
+
if (from === to) {
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
serviceTranslations.push(tr);
|
|
1158
|
+
indexMap.push(idx);
|
|
1159
|
+
});
|
|
1160
|
+
let serviceResults = [];
|
|
1161
|
+
if (serviceTranslations.length > 0) {
|
|
1162
|
+
const cacheKey = getTranslationCacheKey(serviceTranslations, options);
|
|
1163
|
+
if (translationCache.has(cacheKey)) {
|
|
1164
|
+
serviceResults = await translationCache.get(cacheKey);
|
|
1165
|
+
} else {
|
|
1166
|
+
const translationPromise = (async () => {
|
|
1167
|
+
try {
|
|
1168
|
+
const controller = new AbortController();
|
|
1169
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
1170
|
+
const resp = await fetch(`${translatorHost}/translate`, {
|
|
1171
|
+
method: "POST",
|
|
1172
|
+
headers: {
|
|
1173
|
+
"Content-Type": "application/json",
|
|
1174
|
+
...apiKey ? { Authorization: `Bearer ${apiKey}`, "X-Api-Key": apiKey } : {}
|
|
1175
|
+
},
|
|
1176
|
+
body: JSON.stringify({
|
|
1177
|
+
[API_FIELD_TRANSLATIONS]: serviceTranslations.map((t) => ({
|
|
1178
|
+
[API_FIELD_TEXT]: t[API_FIELD_TEXT],
|
|
1179
|
+
[API_FIELD_FROM]: t[API_FIELD_FROM],
|
|
1180
|
+
[API_FIELD_TO]: t[API_FIELD_TO],
|
|
1181
|
+
[API_FIELD_CTX]: t[API_FIELD_CTX] || options[API_FIELD_CTX] || "ui"
|
|
1182
|
+
})),
|
|
1183
|
+
[API_FIELD_CTX]: options[API_FIELD_CTX] || "ui"
|
|
1184
|
+
}),
|
|
1185
|
+
signal: controller.signal
|
|
1186
|
+
});
|
|
1187
|
+
clearTimeout(timeoutId);
|
|
1188
|
+
if (!resp.ok) {
|
|
1189
|
+
console.error(
|
|
1190
|
+
"[translator-sdk] Translator error response:",
|
|
1191
|
+
resp.status,
|
|
1192
|
+
resp.statusText
|
|
1193
|
+
);
|
|
1194
|
+
throw new Error(`Translator service error: ${resp.status} ${resp.statusText}`);
|
|
1195
|
+
}
|
|
1196
|
+
let parsed;
|
|
1197
|
+
try {
|
|
1198
|
+
parsed = await resp.clone().json();
|
|
1199
|
+
} catch (jsonErr) {
|
|
1200
|
+
console.error("[translator-sdk] Failed to parse JSON response:", jsonErr);
|
|
1201
|
+
throw new Error(`Failed to parse JSON response: ${jsonErr}`);
|
|
1202
|
+
}
|
|
1203
|
+
const data = parsed || {};
|
|
1204
|
+
if (data[API_FIELD_ERROR]) {
|
|
1205
|
+
console.warn(`[translator-sdk] Top-level API error:`, data[API_FIELD_ERROR]);
|
|
1206
|
+
return serviceTranslations.map((translation) => ({
|
|
1207
|
+
[API_FIELD_TEXT]: translation[API_FIELD_TEXT],
|
|
1208
|
+
[API_FIELD_ERROR]: data[API_FIELD_ERROR]
|
|
1209
|
+
}));
|
|
1210
|
+
}
|
|
1211
|
+
const results = Array.isArray(data[API_FIELD_TRANSLATIONS]) ? data[API_FIELD_TRANSLATIONS].map((translation, index) => {
|
|
1212
|
+
const originalText = serviceTranslations[index]?.[API_FIELD_TEXT] || "";
|
|
1213
|
+
if (translation[API_FIELD_ERROR]) {
|
|
1214
|
+
console.warn(
|
|
1215
|
+
`[translator-sdk] Translation error for "${originalText}":`,
|
|
1216
|
+
translation[API_FIELD_ERROR]
|
|
1217
|
+
);
|
|
1218
|
+
return {
|
|
1219
|
+
[API_FIELD_TEXT]: originalText,
|
|
1220
|
+
// Return original text on error
|
|
1221
|
+
[API_FIELD_ERROR]: translation[API_FIELD_ERROR]
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
if (translation[API_FIELD_FROM] && !translation[API_FIELD_TEXT]) {
|
|
1225
|
+
return {
|
|
1226
|
+
[API_FIELD_TEXT]: originalText,
|
|
1227
|
+
// For detection, return original text
|
|
1228
|
+
[API_FIELD_FROM]: translation[API_FIELD_FROM]
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
return {
|
|
1232
|
+
[API_FIELD_TEXT]: translation[API_FIELD_TEXT] || originalText
|
|
1233
|
+
};
|
|
1234
|
+
}) : data[API_FIELD_TEXT] ? [{ [API_FIELD_TEXT]: data[API_FIELD_TEXT] }] : [];
|
|
1235
|
+
return results;
|
|
1236
|
+
} catch (error) {
|
|
1237
|
+
translationCache.delete(cacheKey);
|
|
1238
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1239
|
+
const message2 = `Translator request to ${translatorHost} timed out after 5 seconds.`;
|
|
1240
|
+
console.error(message2);
|
|
1241
|
+
throw new Error(message2);
|
|
1242
|
+
}
|
|
1243
|
+
const message = `Failed to connect to translator at ${translatorHost}. Is the service running?`;
|
|
1244
|
+
console.error(message);
|
|
1245
|
+
throw new Error(message);
|
|
1246
|
+
}
|
|
1247
|
+
})();
|
|
1248
|
+
translationCache.set(cacheKey, translationPromise);
|
|
1249
|
+
serviceResults = await translationPromise;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
const final = translations2.map((tr, idx) => {
|
|
1253
|
+
const from = (tr[API_FIELD_FROM] || "").toLowerCase();
|
|
1254
|
+
const to = (tr[API_FIELD_TO] || "").toLowerCase();
|
|
1255
|
+
if (from === to) return { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] };
|
|
1256
|
+
const svcIdx = indexMap.indexOf(idx);
|
|
1257
|
+
if (svcIdx !== -1) return serviceResults[svcIdx] || { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] };
|
|
1258
|
+
return { [API_FIELD_TEXT]: tr[API_FIELD_TEXT] };
|
|
1259
|
+
});
|
|
1260
|
+
return final;
|
|
1261
|
+
}
|
|
1262
|
+
function clearTranslationCache() {
|
|
1263
|
+
translationCache.clear();
|
|
1264
|
+
}
|
|
1265
|
+
function getTranslationCacheSize() {
|
|
1266
|
+
return translationCache.size;
|
|
1267
|
+
}
|
|
1268
|
+
async function fetchAvailableLanguages(options = {}) {
|
|
1269
|
+
const translatorHost = options.translatorHost || DEFAULT_TRANSLATOR_HOST;
|
|
1270
|
+
const apiKey = options.apiKey || process.env.TRANSLATOR_API_KEY;
|
|
1271
|
+
const minPop = options.minPopularity !== void 0 ? Number(options.minPopularity) : Number.parseFloat(process.env.MIN_LANGUAGE_POPULARITY || "0.1");
|
|
1272
|
+
const queryParams = [];
|
|
1273
|
+
if (options.country) queryParams.push(`country=${encodeURIComponent(options.country)}`);
|
|
1274
|
+
if (options.region) queryParams.push(`region=${encodeURIComponent(options.region)}`);
|
|
1275
|
+
const queryStr = queryParams.length ? `?${queryParams.join("&")}` : "";
|
|
1276
|
+
const requestUrl = `${translatorHost}/languages${queryStr}`;
|
|
1277
|
+
try {
|
|
1278
|
+
const resp = await fetch(requestUrl, {
|
|
1279
|
+
method: "GET",
|
|
1280
|
+
headers: {
|
|
1281
|
+
"Content-Type": "application/json",
|
|
1282
|
+
...apiKey ? { Authorization: `Bearer ${apiKey}`, "X-Api-Key": apiKey } : {}
|
|
1283
|
+
}
|
|
1284
|
+
});
|
|
1285
|
+
if (!resp.ok) {
|
|
1286
|
+
throw new Error(`Translator languages error: ${resp.status}`);
|
|
1287
|
+
}
|
|
1288
|
+
const data = await resp.json();
|
|
1289
|
+
const languages = Array.isArray(data) ? data : data.languages || [];
|
|
1290
|
+
const filtered = languages.filter((lang) => {
|
|
1291
|
+
if (lang.popularity === void 0 || lang.popularity === null) return true;
|
|
1292
|
+
return Number(lang.popularity) >= minPop;
|
|
1293
|
+
});
|
|
1294
|
+
return filtered;
|
|
1295
|
+
} catch (error) {
|
|
1296
|
+
console.error("[translator-sdk] Languages fetch error:", error);
|
|
1297
|
+
throw error;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// vue-script:/Users/nlit/projects/langie-sdk/src/components/LanguageSelect.vue?type=script
|
|
1302
|
+
import { useCssVars as _useCssVars, defineComponent as _defineComponent } from "vue";
|
|
1303
|
+
import { createCommentVNode as _createCommentVNode, openBlock as _openBlock, createElementBlock as _createElementBlock, toDisplayString as _toDisplayString, createElementVNode as _createElementVNode, createTextVNode as _createTextVNode, unref as _unref, withCtx as _withCtx, createBlock as _createBlock, Fragment as _Fragment, normalizeClass as _normalizeClass } from "vue";
|
|
1304
|
+
import "@vueform/multiselect/themes/default.css";
|
|
1305
|
+
import { ref as ref2, computed, nextTick } from "vue";
|
|
1306
|
+
import Multiselect from "@vueform/multiselect";
|
|
1307
|
+
import Fuse from "fuse.js";
|
|
1308
|
+
|
|
1309
|
+
// src/language-aliases/european.ts
|
|
1310
|
+
var EUROPEAN_LANGUAGES = [
|
|
1311
|
+
// Major European Languages
|
|
1312
|
+
{
|
|
1313
|
+
lang: "english",
|
|
1314
|
+
match: [
|
|
1315
|
+
"eng",
|
|
1316
|
+
"en",
|
|
1317
|
+
"united states",
|
|
1318
|
+
"united states of america",
|
|
1319
|
+
"usa",
|
|
1320
|
+
"us",
|
|
1321
|
+
"great britain",
|
|
1322
|
+
"britain",
|
|
1323
|
+
"british",
|
|
1324
|
+
"england",
|
|
1325
|
+
"united kingdom",
|
|
1326
|
+
"uk"
|
|
1327
|
+
],
|
|
1328
|
+
suggest: ["irish", "scottish", "welsh"]
|
|
1329
|
+
},
|
|
1330
|
+
{
|
|
1331
|
+
lang: "spanish",
|
|
1332
|
+
match: [
|
|
1333
|
+
"spa",
|
|
1334
|
+
"es",
|
|
1335
|
+
"esp",
|
|
1336
|
+
"spain",
|
|
1337
|
+
"espa\xF1ol",
|
|
1338
|
+
"mexico",
|
|
1339
|
+
"mexican",
|
|
1340
|
+
"argentina",
|
|
1341
|
+
"colombia",
|
|
1342
|
+
"venezuela"
|
|
1343
|
+
],
|
|
1344
|
+
suggest: ["catalan", "galician", "basque"]
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
lang: "french",
|
|
1348
|
+
match: [
|
|
1349
|
+
"fra",
|
|
1350
|
+
"fr",
|
|
1351
|
+
"fre",
|
|
1352
|
+
"france",
|
|
1353
|
+
"fran\xE7ais",
|
|
1354
|
+
"canada",
|
|
1355
|
+
"canadian",
|
|
1356
|
+
"quebec",
|
|
1357
|
+
"belgium",
|
|
1358
|
+
"swiss"
|
|
1359
|
+
],
|
|
1360
|
+
suggest: ["occitan", "breton", "corsican"]
|
|
1361
|
+
},
|
|
1362
|
+
{
|
|
1363
|
+
lang: "german",
|
|
1364
|
+
match: [
|
|
1365
|
+
"ger",
|
|
1366
|
+
"de",
|
|
1367
|
+
"deu",
|
|
1368
|
+
"germany",
|
|
1369
|
+
"deutsch",
|
|
1370
|
+
"austria",
|
|
1371
|
+
"austrian",
|
|
1372
|
+
"switzerland",
|
|
1373
|
+
"swiss"
|
|
1374
|
+
],
|
|
1375
|
+
suggest: ["bavarian", "saxon", "luxembourgish"]
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
lang: "italian",
|
|
1379
|
+
match: ["ita", "it", "italy", "italiano", "swiss italian"],
|
|
1380
|
+
suggest: ["sardinian", "neapolitan", "venetian"]
|
|
1381
|
+
},
|
|
1382
|
+
{
|
|
1383
|
+
lang: "portuguese",
|
|
1384
|
+
match: ["por", "pt", "portugal", "brazil", "brazilian", "braz", "mozambique", "angola"],
|
|
1385
|
+
suggest: ["galician", "mirandese"]
|
|
1386
|
+
},
|
|
1387
|
+
{
|
|
1388
|
+
lang: "dutch",
|
|
1389
|
+
match: ["dut", "nl", "nld", "netherlands", "holland", "flemish", "belgium"],
|
|
1390
|
+
suggest: ["frisian", "afrikaans"]
|
|
1391
|
+
},
|
|
1392
|
+
// Slavic Languages
|
|
1393
|
+
{
|
|
1394
|
+
lang: "russian",
|
|
1395
|
+
match: ["rus", "ru", "russia", "belarus"],
|
|
1396
|
+
suggest: ["kazakh", "tatar", "belarusian", "tajik", "uzbek", "moldovan"]
|
|
1397
|
+
},
|
|
1398
|
+
{
|
|
1399
|
+
lang: "ukrainian",
|
|
1400
|
+
match: ["ukr", "uk", "ukraine"],
|
|
1401
|
+
suggest: ["russian", "belarusian"]
|
|
1402
|
+
},
|
|
1403
|
+
{
|
|
1404
|
+
lang: "polish",
|
|
1405
|
+
match: ["pol", "pl", "poland"],
|
|
1406
|
+
suggest: ["silesian", "kashubian"]
|
|
1407
|
+
},
|
|
1408
|
+
{
|
|
1409
|
+
lang: "czech",
|
|
1410
|
+
match: ["ces", "cs", "cze", "czech republic", "czechia"],
|
|
1411
|
+
suggest: ["slovak", "moravian"]
|
|
1412
|
+
},
|
|
1413
|
+
{
|
|
1414
|
+
lang: "slovak",
|
|
1415
|
+
match: ["slk", "sk", "slovakia"],
|
|
1416
|
+
suggest: ["czech", "rusyn"]
|
|
1417
|
+
},
|
|
1418
|
+
{
|
|
1419
|
+
lang: "croatian",
|
|
1420
|
+
match: ["hrv", "hr", "croatia"],
|
|
1421
|
+
suggest: ["serbian", "bosnian", "montenegrin"]
|
|
1422
|
+
},
|
|
1423
|
+
{
|
|
1424
|
+
lang: "serbian",
|
|
1425
|
+
match: ["srp", "sr", "ser", "serbia", "serbian", "srpski", "\u0441\u0440\u0431\u0438\u0458\u0430", "\u0441\u0440\u043F\u0441\u043A\u0438"],
|
|
1426
|
+
suggest: ["croatian", "bosnian", "montenegrin"]
|
|
1427
|
+
},
|
|
1428
|
+
{
|
|
1429
|
+
lang: "bulgarian",
|
|
1430
|
+
match: ["bul", "bg", "bulgaria"],
|
|
1431
|
+
suggest: ["macedonian"]
|
|
1432
|
+
},
|
|
1433
|
+
{
|
|
1434
|
+
lang: "slovenian",
|
|
1435
|
+
match: ["slv", "sl", "slovenia"],
|
|
1436
|
+
suggest: ["croatian"]
|
|
1437
|
+
},
|
|
1438
|
+
// Nordic Languages
|
|
1439
|
+
{
|
|
1440
|
+
lang: "swedish",
|
|
1441
|
+
match: ["swe", "sv", "sweden"],
|
|
1442
|
+
suggest: ["norwegian", "danish", "finnish"]
|
|
1443
|
+
},
|
|
1444
|
+
{
|
|
1445
|
+
lang: "norwegian",
|
|
1446
|
+
match: ["nor", "no", "norway"],
|
|
1447
|
+
suggest: ["swedish", "danish"]
|
|
1448
|
+
},
|
|
1449
|
+
{
|
|
1450
|
+
lang: "danish",
|
|
1451
|
+
match: ["dan", "da", "denmark"],
|
|
1452
|
+
suggest: ["norwegian", "swedish"]
|
|
1453
|
+
},
|
|
1454
|
+
{
|
|
1455
|
+
lang: "finnish",
|
|
1456
|
+
match: ["fin", "fi", "finland"],
|
|
1457
|
+
suggest: ["estonian", "swedish"]
|
|
1458
|
+
},
|
|
1459
|
+
{
|
|
1460
|
+
lang: "icelandic",
|
|
1461
|
+
match: ["isl", "is", "iceland"],
|
|
1462
|
+
suggest: ["faroese", "norwegian"]
|
|
1463
|
+
},
|
|
1464
|
+
// Other European Languages
|
|
1465
|
+
{
|
|
1466
|
+
lang: "greek",
|
|
1467
|
+
match: ["ell", "el", "gre", "greece", "hellenic"],
|
|
1468
|
+
suggest: ["macedonian", "albanian"]
|
|
1469
|
+
},
|
|
1470
|
+
{
|
|
1471
|
+
lang: "romanian",
|
|
1472
|
+
match: ["ron", "ro", "rum", "romania"],
|
|
1473
|
+
suggest: ["moldovan", "hungarian"]
|
|
1474
|
+
},
|
|
1475
|
+
{
|
|
1476
|
+
lang: "hungarian",
|
|
1477
|
+
match: ["hun", "hu", "hungary", "magyar"],
|
|
1478
|
+
suggest: ["romanian", "slovak"]
|
|
1479
|
+
},
|
|
1480
|
+
{
|
|
1481
|
+
lang: "albanian",
|
|
1482
|
+
match: ["sqi", "sq", "alb", "albania", "kosovo"],
|
|
1483
|
+
suggest: ["greek", "macedonian"]
|
|
1484
|
+
},
|
|
1485
|
+
{
|
|
1486
|
+
lang: "lithuanian",
|
|
1487
|
+
match: ["lit", "lt", "lithuania"],
|
|
1488
|
+
suggest: ["latvian", "polish"]
|
|
1489
|
+
},
|
|
1490
|
+
{
|
|
1491
|
+
lang: "latvian",
|
|
1492
|
+
match: ["lav", "lv", "latvia"],
|
|
1493
|
+
suggest: ["lithuanian", "estonian"]
|
|
1494
|
+
},
|
|
1495
|
+
{
|
|
1496
|
+
lang: "estonian",
|
|
1497
|
+
match: ["est", "et", "estonia"],
|
|
1498
|
+
suggest: ["finnish", "latvian"]
|
|
1499
|
+
},
|
|
1500
|
+
// Celtic Languages
|
|
1501
|
+
{
|
|
1502
|
+
lang: "irish",
|
|
1503
|
+
match: ["gle", "ga", "ireland", "gaelic"],
|
|
1504
|
+
suggest: ["scottish", "welsh"]
|
|
1505
|
+
},
|
|
1506
|
+
{
|
|
1507
|
+
lang: "welsh",
|
|
1508
|
+
match: ["cym", "cy", "wales", "cymru"],
|
|
1509
|
+
suggest: ["irish", "cornish"]
|
|
1510
|
+
},
|
|
1511
|
+
{
|
|
1512
|
+
lang: "scottish",
|
|
1513
|
+
match: ["gla", "gd", "scotland", "scots gaelic"],
|
|
1514
|
+
suggest: ["irish", "english"]
|
|
1515
|
+
},
|
|
1516
|
+
// Additional Languages
|
|
1517
|
+
{
|
|
1518
|
+
lang: "esperanto",
|
|
1519
|
+
match: ["epo", "eo", "esperanto"],
|
|
1520
|
+
suggest: []
|
|
1521
|
+
},
|
|
1522
|
+
{
|
|
1523
|
+
lang: "latin",
|
|
1524
|
+
match: ["lat", "la", "latin"],
|
|
1525
|
+
suggest: ["italian", "spanish", "french"]
|
|
1526
|
+
}
|
|
1527
|
+
];
|
|
1528
|
+
|
|
1529
|
+
// src/language-aliases/asian.ts
|
|
1530
|
+
var ASIAN_LANGUAGES = [
|
|
1531
|
+
// Asian Languages
|
|
1532
|
+
{
|
|
1533
|
+
lang: "chinese",
|
|
1534
|
+
match: [
|
|
1535
|
+
"chi",
|
|
1536
|
+
"zh",
|
|
1537
|
+
"zho",
|
|
1538
|
+
"zh-cn",
|
|
1539
|
+
"cn",
|
|
1540
|
+
"china",
|
|
1541
|
+
"mandarin",
|
|
1542
|
+
"zhongwen",
|
|
1543
|
+
"simplified",
|
|
1544
|
+
"traditional"
|
|
1545
|
+
],
|
|
1546
|
+
suggest: ["cantonese", "taiwanese", "hakka"]
|
|
1547
|
+
},
|
|
1548
|
+
{
|
|
1549
|
+
lang: "japanese",
|
|
1550
|
+
match: ["jpn", "ja", "jp", "japan", "nihongo"],
|
|
1551
|
+
suggest: ["okinawan"]
|
|
1552
|
+
},
|
|
1553
|
+
{
|
|
1554
|
+
lang: "korean",
|
|
1555
|
+
match: ["kor", "ko", "kr", "korea", "hangul", "south korea", "north korea"],
|
|
1556
|
+
suggest: ["jeju"]
|
|
1557
|
+
},
|
|
1558
|
+
{
|
|
1559
|
+
lang: "hindi",
|
|
1560
|
+
match: ["hin", "hi", "india", "indian", "devanagari"],
|
|
1561
|
+
suggest: ["urdu", "punjabi", "gujarati", "marathi", "bengali"]
|
|
1562
|
+
},
|
|
1563
|
+
{
|
|
1564
|
+
lang: "arabic",
|
|
1565
|
+
match: ["ara", "ar", "arab", "saudi", "egypt", "egyptian", "gulf", "levantine", "maghreb"],
|
|
1566
|
+
suggest: ["persian", "hebrew", "urdu"]
|
|
1567
|
+
},
|
|
1568
|
+
{
|
|
1569
|
+
lang: "thai",
|
|
1570
|
+
match: ["tha", "th", "thailand", "siam"],
|
|
1571
|
+
suggest: ["lao", "khmer"]
|
|
1572
|
+
},
|
|
1573
|
+
{
|
|
1574
|
+
lang: "vietnamese",
|
|
1575
|
+
match: ["vie", "vi", "vietnam"],
|
|
1576
|
+
suggest: ["khmer", "lao"]
|
|
1577
|
+
},
|
|
1578
|
+
{
|
|
1579
|
+
lang: "indonesian",
|
|
1580
|
+
match: ["ind", "id", "indonesia", "bahasa"],
|
|
1581
|
+
suggest: ["malay", "javanese", "sundanese"]
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
lang: "malay",
|
|
1585
|
+
match: ["msa", "ms", "malaysia", "brunei"],
|
|
1586
|
+
suggest: ["indonesian"]
|
|
1587
|
+
},
|
|
1588
|
+
// Central Asian & Turkic Languages
|
|
1589
|
+
{
|
|
1590
|
+
lang: "kazakh",
|
|
1591
|
+
match: ["kaz", "kk", "kazakh", "kazakhstan"],
|
|
1592
|
+
suggest: ["russian", "kyrgyz", "uzbek"]
|
|
1593
|
+
},
|
|
1594
|
+
{
|
|
1595
|
+
lang: "turkish",
|
|
1596
|
+
match: ["tur", "tr", "turkey"],
|
|
1597
|
+
suggest: ["kurdish", "azerbaijani"]
|
|
1598
|
+
},
|
|
1599
|
+
{
|
|
1600
|
+
lang: "uzbek",
|
|
1601
|
+
match: ["uzb", "uz", "uzbekistan"],
|
|
1602
|
+
suggest: ["tajik", "kazakh", "kyrgyz"]
|
|
1603
|
+
},
|
|
1604
|
+
{
|
|
1605
|
+
lang: "kyrgyz",
|
|
1606
|
+
match: ["kir", "ky", "kyrgyzstan"],
|
|
1607
|
+
suggest: ["kazakh", "uzbek"]
|
|
1608
|
+
},
|
|
1609
|
+
{
|
|
1610
|
+
lang: "tajik",
|
|
1611
|
+
match: ["tgk", "tg", "tajikistan"],
|
|
1612
|
+
suggest: ["persian", "uzbek"]
|
|
1613
|
+
},
|
|
1614
|
+
{
|
|
1615
|
+
lang: "azerbaijani",
|
|
1616
|
+
match: ["aze", "az", "azerbaijan"],
|
|
1617
|
+
suggest: ["turkish", "persian"]
|
|
1618
|
+
},
|
|
1619
|
+
// Middle Eastern Languages
|
|
1620
|
+
{
|
|
1621
|
+
lang: "persian",
|
|
1622
|
+
match: ["fas", "fa", "per", "iran", "farsi", "dari", "afghanistan"],
|
|
1623
|
+
suggest: ["tajik", "kurdish", "pashto"]
|
|
1624
|
+
},
|
|
1625
|
+
{
|
|
1626
|
+
lang: "hebrew",
|
|
1627
|
+
match: ["heb", "he", "israel", "israeli"],
|
|
1628
|
+
suggest: ["arabic", "yiddish"]
|
|
1629
|
+
},
|
|
1630
|
+
{
|
|
1631
|
+
lang: "kurdish",
|
|
1632
|
+
match: ["kur", "ku", "kurdistan", "kurmanji", "sorani"],
|
|
1633
|
+
suggest: ["turkish", "persian", "arabic"]
|
|
1634
|
+
},
|
|
1635
|
+
// South Asian Languages
|
|
1636
|
+
{
|
|
1637
|
+
lang: "urdu",
|
|
1638
|
+
match: ["urd", "ur", "pakistan", "pakistani"],
|
|
1639
|
+
suggest: ["hindi", "punjabi"]
|
|
1640
|
+
},
|
|
1641
|
+
{
|
|
1642
|
+
lang: "bengali",
|
|
1643
|
+
match: ["ben", "bn", "bangladesh", "bengal"],
|
|
1644
|
+
suggest: ["hindi", "assamese"]
|
|
1645
|
+
},
|
|
1646
|
+
{
|
|
1647
|
+
lang: "punjabi",
|
|
1648
|
+
match: ["pan", "pa", "punjab"],
|
|
1649
|
+
suggest: ["hindi", "urdu"]
|
|
1650
|
+
},
|
|
1651
|
+
{
|
|
1652
|
+
lang: "gujarati",
|
|
1653
|
+
match: ["guj", "gu", "gujarat"],
|
|
1654
|
+
suggest: ["hindi", "marathi"]
|
|
1655
|
+
},
|
|
1656
|
+
{
|
|
1657
|
+
lang: "marathi",
|
|
1658
|
+
match: ["mar", "mr", "maharashtra"],
|
|
1659
|
+
suggest: ["hindi", "gujarati"]
|
|
1660
|
+
},
|
|
1661
|
+
{
|
|
1662
|
+
lang: "tamil",
|
|
1663
|
+
match: ["tam", "ta", "tamil nadu", "sri lanka"],
|
|
1664
|
+
suggest: ["malayalam", "telugu", "kannada"]
|
|
1665
|
+
},
|
|
1666
|
+
{
|
|
1667
|
+
lang: "telugu",
|
|
1668
|
+
match: ["tel", "te", "andhra pradesh"],
|
|
1669
|
+
suggest: ["tamil", "kannada"]
|
|
1670
|
+
},
|
|
1671
|
+
{
|
|
1672
|
+
lang: "kannada",
|
|
1673
|
+
match: ["kan", "kn", "karnataka"],
|
|
1674
|
+
suggest: ["tamil", "telugu", "malayalam"]
|
|
1675
|
+
},
|
|
1676
|
+
{
|
|
1677
|
+
lang: "malayalam",
|
|
1678
|
+
match: ["mal", "ml", "kerala"],
|
|
1679
|
+
suggest: ["tamil", "kannada"]
|
|
1680
|
+
},
|
|
1681
|
+
{
|
|
1682
|
+
lang: "nepali",
|
|
1683
|
+
match: ["nep", "ne", "nepal"],
|
|
1684
|
+
suggest: ["hindi"]
|
|
1685
|
+
},
|
|
1686
|
+
{
|
|
1687
|
+
lang: "sinhala",
|
|
1688
|
+
match: ["sin", "si", "sri lanka", "ceylon"],
|
|
1689
|
+
suggest: ["tamil"]
|
|
1690
|
+
},
|
|
1691
|
+
// Southeast Asian Languages
|
|
1692
|
+
{
|
|
1693
|
+
lang: "burmese",
|
|
1694
|
+
match: ["mya", "my", "myanmar", "burma"],
|
|
1695
|
+
suggest: ["thai"]
|
|
1696
|
+
},
|
|
1697
|
+
{
|
|
1698
|
+
lang: "khmer",
|
|
1699
|
+
match: ["khm", "km", "cambodia", "cambodian"],
|
|
1700
|
+
suggest: ["vietnamese", "thai"]
|
|
1701
|
+
},
|
|
1702
|
+
{
|
|
1703
|
+
lang: "lao",
|
|
1704
|
+
match: ["lao", "lo", "laos"],
|
|
1705
|
+
suggest: ["thai", "vietnamese"]
|
|
1706
|
+
},
|
|
1707
|
+
{
|
|
1708
|
+
lang: "tagalog",
|
|
1709
|
+
match: ["tgl", "tl", "philippines", "filipino"],
|
|
1710
|
+
suggest: ["cebuano", "ilocano"]
|
|
1711
|
+
}
|
|
1712
|
+
];
|
|
1713
|
+
|
|
1714
|
+
// src/language-aliases/african.ts
|
|
1715
|
+
var AFRICAN_LANGUAGES = [
|
|
1716
|
+
// African Languages
|
|
1717
|
+
{
|
|
1718
|
+
lang: "swahili",
|
|
1719
|
+
match: ["swa", "sw", "kiswahili", "tanzania", "kenya"],
|
|
1720
|
+
suggest: ["arabic"]
|
|
1721
|
+
},
|
|
1722
|
+
{
|
|
1723
|
+
lang: "amharic",
|
|
1724
|
+
match: ["amh", "am", "ethiopia", "ethiopian"],
|
|
1725
|
+
suggest: ["tigrinya", "oromo"]
|
|
1726
|
+
},
|
|
1727
|
+
{
|
|
1728
|
+
lang: "yoruba",
|
|
1729
|
+
match: ["yor", "yo", "nigeria", "benin"],
|
|
1730
|
+
suggest: ["igbo", "hausa"]
|
|
1731
|
+
},
|
|
1732
|
+
{
|
|
1733
|
+
lang: "zulu",
|
|
1734
|
+
match: ["zul", "zu", "south africa"],
|
|
1735
|
+
suggest: ["xhosa", "afrikaans"]
|
|
1736
|
+
}
|
|
1737
|
+
];
|
|
1738
|
+
|
|
1739
|
+
// src/language-aliases/index.ts
|
|
1740
|
+
var LANGUAGE_ALIAS_TABLE = [
|
|
1741
|
+
...EUROPEAN_LANGUAGES,
|
|
1742
|
+
...ASIAN_LANGUAGES,
|
|
1743
|
+
...AFRICAN_LANGUAGES
|
|
1744
|
+
];
|
|
1745
|
+
|
|
1746
|
+
// src/search-utils.ts
|
|
1747
|
+
function applyLanguageAlias(term = "") {
|
|
1748
|
+
const s = term.toLowerCase().trim();
|
|
1749
|
+
if (!s) return { primary: term, suggestions: [] };
|
|
1750
|
+
const isMatch = (alias, input) => {
|
|
1751
|
+
if (input === alias) return true;
|
|
1752
|
+
if (alias.length >= 3 && input.length >= alias.length && input.startsWith(alias)) return true;
|
|
1753
|
+
if (input.length >= 2 && alias.startsWith(input)) return true;
|
|
1754
|
+
return false;
|
|
1755
|
+
};
|
|
1756
|
+
const hits = /* @__PURE__ */ new Set();
|
|
1757
|
+
const suggestions = /* @__PURE__ */ new Set();
|
|
1758
|
+
for (const { lang, match, suggest } of LANGUAGE_ALIAS_TABLE) {
|
|
1759
|
+
for (const m of match) {
|
|
1760
|
+
if (isMatch(m, s)) {
|
|
1761
|
+
hits.add(lang);
|
|
1762
|
+
if (suggest) {
|
|
1763
|
+
suggest.forEach((sug) => suggestions.add(sug));
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
let primary;
|
|
1769
|
+
if (hits.size === 0) {
|
|
1770
|
+
primary = term;
|
|
1771
|
+
} else if (hits.size === 1) {
|
|
1772
|
+
primary = Array.from(hits)[0];
|
|
1773
|
+
} else {
|
|
1774
|
+
primary = Array.from(hits);
|
|
1775
|
+
}
|
|
1776
|
+
return { primary, suggestions: Array.from(suggestions) };
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
// src/constants/colors.ts
|
|
1780
|
+
var COLORS = {
|
|
1781
|
+
// Primary colors
|
|
1782
|
+
primary: {
|
|
1783
|
+
blue: "#3b82f6",
|
|
1784
|
+
blueLight: "#60a5fa",
|
|
1785
|
+
blueDark: "#2563eb",
|
|
1786
|
+
blueAlpha30: "#3b82f630",
|
|
1787
|
+
blueAlpha50: "#3b82f650"
|
|
1788
|
+
},
|
|
1789
|
+
// Neutral colors
|
|
1790
|
+
neutral: {
|
|
1791
|
+
white: "#fff",
|
|
1792
|
+
gray50: "#f9fafb",
|
|
1793
|
+
gray100: "#f3f4f6",
|
|
1794
|
+
gray200: "#e5e7eb",
|
|
1795
|
+
gray300: "#d1d5db",
|
|
1796
|
+
gray400: "#9ca3af",
|
|
1797
|
+
gray500: "#6b7280",
|
|
1798
|
+
gray600: "#4b5563",
|
|
1799
|
+
gray700: "#374151",
|
|
1800
|
+
gray800: "#1f2937",
|
|
1801
|
+
gray900: "#111827"
|
|
1802
|
+
},
|
|
1803
|
+
// Border colors
|
|
1804
|
+
border: {
|
|
1805
|
+
light: "#d1d5db",
|
|
1806
|
+
dark: "#4b5563",
|
|
1807
|
+
flag: "#eee"
|
|
1808
|
+
},
|
|
1809
|
+
// Text colors
|
|
1810
|
+
text: {
|
|
1811
|
+
primary: "#1f2937",
|
|
1812
|
+
secondary: "#6b7280",
|
|
1813
|
+
placeholder: "#9ca3af",
|
|
1814
|
+
inverse: "#f9fafb"
|
|
1815
|
+
}
|
|
1816
|
+
};
|
|
1817
|
+
var THEME_COLORS = {
|
|
1818
|
+
light: {
|
|
1819
|
+
background: COLORS.neutral.white,
|
|
1820
|
+
backgroundDisabled: COLORS.neutral.gray100,
|
|
1821
|
+
border: COLORS.border.light,
|
|
1822
|
+
placeholder: COLORS.text.placeholder,
|
|
1823
|
+
ring: COLORS.primary.blueAlpha30,
|
|
1824
|
+
optionPointed: COLORS.neutral.gray100,
|
|
1825
|
+
optionPointedText: COLORS.text.primary,
|
|
1826
|
+
optionSelected: COLORS.primary.blue,
|
|
1827
|
+
optionSelectedText: COLORS.neutral.white,
|
|
1828
|
+
dropdownBackground: COLORS.neutral.white,
|
|
1829
|
+
dropdownBorder: COLORS.border.light,
|
|
1830
|
+
dropdownText: COLORS.text.primary,
|
|
1831
|
+
tagBackground: COLORS.primary.blue
|
|
1832
|
+
},
|
|
1833
|
+
dark: {
|
|
1834
|
+
background: COLORS.neutral.gray800,
|
|
1835
|
+
backgroundDisabled: COLORS.neutral.gray700,
|
|
1836
|
+
border: COLORS.border.dark,
|
|
1837
|
+
placeholder: COLORS.text.placeholder,
|
|
1838
|
+
ring: COLORS.primary.blueAlpha50,
|
|
1839
|
+
optionPointed: COLORS.neutral.gray700,
|
|
1840
|
+
optionPointedText: COLORS.neutral.gray50,
|
|
1841
|
+
optionSelected: COLORS.primary.blue,
|
|
1842
|
+
optionSelectedText: COLORS.neutral.white,
|
|
1843
|
+
dropdownBackground: COLORS.neutral.gray800,
|
|
1844
|
+
dropdownBorder: COLORS.border.dark,
|
|
1845
|
+
dropdownText: COLORS.neutral.gray50,
|
|
1846
|
+
tagBackground: COLORS.primary.blue
|
|
1847
|
+
}
|
|
1848
|
+
};
|
|
1849
|
+
var CSS_VARS = {
|
|
1850
|
+
// Multiselect variables
|
|
1851
|
+
multiselect: {
|
|
1852
|
+
bg: "--ms-bg",
|
|
1853
|
+
bgDisabled: "--ms-bg-disabled",
|
|
1854
|
+
borderColor: "--ms-border-color",
|
|
1855
|
+
placeholderColor: "--ms-placeholder-color",
|
|
1856
|
+
ringColor: "--ms-ring-color",
|
|
1857
|
+
optionBgPointed: "--ms-option-bg-pointed",
|
|
1858
|
+
optionColorPointed: "--ms-option-color-pointed",
|
|
1859
|
+
optionBgSelected: "--ms-option-bg-selected",
|
|
1860
|
+
optionColorSelected: "--ms-option-color-selected",
|
|
1861
|
+
dropdownBg: "--ms-dropdown-bg",
|
|
1862
|
+
dropdownBorderColor: "--ms-dropdown-border-color",
|
|
1863
|
+
dropdownColor: "--ms-dropdown-color",
|
|
1864
|
+
tagBg: "--ms-tag-bg"
|
|
1865
|
+
}
|
|
1866
|
+
};
|
|
1867
|
+
|
|
1868
|
+
// vue-script:/Users/nlit/projects/langie-sdk/src/components/LanguageSelect.vue?type=script
|
|
1869
|
+
var _hoisted_1 = {
|
|
1870
|
+
key: 0,
|
|
1871
|
+
class: "multiselect-single-label"
|
|
1872
|
+
};
|
|
1873
|
+
var _hoisted_2 = ["src", "alt"];
|
|
1874
|
+
var _hoisted_3 = { class: "language-text" };
|
|
1875
|
+
var _hoisted_4 = { class: "native-name" };
|
|
1876
|
+
var _hoisted_5 = ["data-lang-code"];
|
|
1877
|
+
var _hoisted_6 = ["src", "alt"];
|
|
1878
|
+
var _hoisted_7 = { class: "language-text" };
|
|
1879
|
+
var _hoisted_8 = { class: "native-name" };
|
|
1880
|
+
var _hoisted_9 = { class: "multiselect-no-options" };
|
|
1881
|
+
var LanguageSelect_default = /* @__PURE__ */ _defineComponent({
|
|
1882
|
+
__name: "LanguageSelect",
|
|
1883
|
+
props: {
|
|
1884
|
+
modelValue: {
|
|
1885
|
+
type: Object,
|
|
1886
|
+
default: void 0
|
|
1887
|
+
},
|
|
1888
|
+
languages: {
|
|
1889
|
+
type: Array,
|
|
1890
|
+
default: () => []
|
|
1891
|
+
},
|
|
1892
|
+
placeholder: {
|
|
1893
|
+
type: String,
|
|
1894
|
+
default: "Select language"
|
|
1895
|
+
},
|
|
1896
|
+
disabled: {
|
|
1897
|
+
type: Boolean,
|
|
1898
|
+
default: false
|
|
1899
|
+
},
|
|
1900
|
+
isDark: {
|
|
1901
|
+
type: Boolean,
|
|
1902
|
+
default: false
|
|
1903
|
+
}
|
|
1904
|
+
},
|
|
1905
|
+
emits: ["update:modelValue"],
|
|
1906
|
+
setup(__props, { emit: __emit }) {
|
|
1907
|
+
_useCssVars((_ctx) => ({
|
|
1908
|
+
"25bc3fde-THEME_COLORS.light.background": _unref(THEME_COLORS).light.background,
|
|
1909
|
+
"25bc3fde-THEME_COLORS.light.backgroundDisabled": _unref(THEME_COLORS).light.backgroundDisabled,
|
|
1910
|
+
"25bc3fde-THEME_COLORS.light.border": _unref(THEME_COLORS).light.border,
|
|
1911
|
+
"25bc3fde-THEME_COLORS.light.ring": _unref(THEME_COLORS).light.ring,
|
|
1912
|
+
"25bc3fde-THEME_COLORS.light.placeholder": _unref(THEME_COLORS).light.placeholder,
|
|
1913
|
+
"25bc3fde-THEME_COLORS.light.optionPointed": _unref(THEME_COLORS).light.optionPointed,
|
|
1914
|
+
"25bc3fde-THEME_COLORS.light.optionPointedText": _unref(THEME_COLORS).light.optionPointedText,
|
|
1915
|
+
"25bc3fde-THEME_COLORS.light.optionSelected": _unref(THEME_COLORS).light.optionSelected,
|
|
1916
|
+
"25bc3fde-THEME_COLORS.light.optionSelectedText": _unref(THEME_COLORS).light.optionSelectedText,
|
|
1917
|
+
"25bc3fde-THEME_COLORS.light.dropdownBackground": _unref(THEME_COLORS).light.dropdownBackground,
|
|
1918
|
+
"25bc3fde-THEME_COLORS.light.dropdownBorder": _unref(THEME_COLORS).light.dropdownBorder,
|
|
1919
|
+
"25bc3fde-THEME_COLORS.light.dropdownText": _unref(THEME_COLORS).light.dropdownText,
|
|
1920
|
+
"25bc3fde-THEME_COLORS.light.tagBackground": _unref(THEME_COLORS).light.tagBackground,
|
|
1921
|
+
"25bc3fde-THEME_COLORS.dark.background": _unref(THEME_COLORS).dark.background,
|
|
1922
|
+
"25bc3fde-THEME_COLORS.dark.backgroundDisabled": _unref(THEME_COLORS).dark.backgroundDisabled,
|
|
1923
|
+
"25bc3fde-THEME_COLORS.dark.border": _unref(THEME_COLORS).dark.border,
|
|
1924
|
+
"25bc3fde-THEME_COLORS.dark.placeholder": _unref(THEME_COLORS).dark.placeholder,
|
|
1925
|
+
"25bc3fde-THEME_COLORS.dark.ring": _unref(THEME_COLORS).dark.ring,
|
|
1926
|
+
"25bc3fde-THEME_COLORS.dark.optionPointed": _unref(THEME_COLORS).dark.optionPointed,
|
|
1927
|
+
"25bc3fde-THEME_COLORS.dark.optionPointedText": _unref(THEME_COLORS).dark.optionPointedText,
|
|
1928
|
+
"25bc3fde-THEME_COLORS.dark.optionSelected": _unref(THEME_COLORS).dark.optionSelected,
|
|
1929
|
+
"25bc3fde-THEME_COLORS.dark.optionSelectedText": _unref(THEME_COLORS).dark.optionSelectedText,
|
|
1930
|
+
"25bc3fde-THEME_COLORS.dark.dropdownBackground": _unref(THEME_COLORS).dark.dropdownBackground,
|
|
1931
|
+
"25bc3fde-THEME_COLORS.dark.dropdownBorder": _unref(THEME_COLORS).dark.dropdownBorder,
|
|
1932
|
+
"25bc3fde-THEME_COLORS.dark.dropdownText": _unref(THEME_COLORS).dark.dropdownText,
|
|
1933
|
+
"25bc3fde-COLORS.border.flag": _unref(COLORS).border.flag,
|
|
1934
|
+
"25bc3fde-COLORS.border.dark": _unref(COLORS).border.dark,
|
|
1935
|
+
"25bc3fde-COLORS.text.secondary": _unref(COLORS).text.secondary,
|
|
1936
|
+
"25bc3fde-COLORS.neutral.gray400": _unref(COLORS).neutral.gray400
|
|
1937
|
+
}));
|
|
1938
|
+
const getFlagCode = (lang) => {
|
|
1939
|
+
const flagCode = lang.flag_country || lang.code;
|
|
1940
|
+
return flagCode;
|
|
1941
|
+
};
|
|
1942
|
+
const getFlagImageUrl = (lang) => {
|
|
1943
|
+
const flagCode = getFlagCode(lang);
|
|
1944
|
+
return `/flags/${flagCode}.svg`;
|
|
1945
|
+
};
|
|
1946
|
+
const props = __props;
|
|
1947
|
+
const emit = __emit;
|
|
1948
|
+
const isLoading = computed(() => {
|
|
1949
|
+
return props.languages.length <= 0;
|
|
1950
|
+
});
|
|
1951
|
+
const searchQuery = ref2("");
|
|
1952
|
+
const validLanguages = computed(() => {
|
|
1953
|
+
return props.languages.filter((lang) => lang && lang.code && lang.name && lang.native_name);
|
|
1954
|
+
});
|
|
1955
|
+
const fuse = computed(() => {
|
|
1956
|
+
if (!validLanguages.value.length) return null;
|
|
1957
|
+
return new Fuse(validLanguages.value, {
|
|
1958
|
+
keys: ["name", "native_name", "code"],
|
|
1959
|
+
includeScore: true,
|
|
1960
|
+
threshold: 0.6,
|
|
1961
|
+
// Use highest threshold to catch all cases
|
|
1962
|
+
isCaseSensitive: false,
|
|
1963
|
+
minMatchCharLength: 1
|
|
1964
|
+
// Allow single character matches
|
|
1965
|
+
});
|
|
1966
|
+
});
|
|
1967
|
+
const multiselectKey = computed(() => {
|
|
1968
|
+
const hasApiData = props.languages.length > 0 && props.languages[0].flag_country !== null;
|
|
1969
|
+
return hasApiData ? "api-data" : "fallback-data";
|
|
1970
|
+
});
|
|
1971
|
+
const selectedLanguageKey = computed(() => {
|
|
1972
|
+
if (!props.modelValue || !props.modelValue.code) return "no-selection";
|
|
1973
|
+
const key = `${props.modelValue.code}-${props.modelValue.flag_country || "fallback"}`;
|
|
1974
|
+
return key;
|
|
1975
|
+
});
|
|
1976
|
+
const selectedLanguage = computed({
|
|
1977
|
+
get: () => props.modelValue || null,
|
|
1978
|
+
set: (value) => {
|
|
1979
|
+
if (value && value.code) {
|
|
1980
|
+
emit("update:modelValue", value);
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
});
|
|
1984
|
+
const filteredLanguages = computed(() => {
|
|
1985
|
+
const query = searchQuery.value.trim();
|
|
1986
|
+
let results;
|
|
1987
|
+
if (!query) {
|
|
1988
|
+
results = validLanguages.value;
|
|
1989
|
+
} else {
|
|
1990
|
+
if (!fuse.value) return [];
|
|
1991
|
+
const aliasResult = applyLanguageAlias(query);
|
|
1992
|
+
const searchTerm = Array.isArray(aliasResult.primary) ? aliasResult.primary[0] : aliasResult.primary;
|
|
1993
|
+
const fuseResults = fuse.value.search(searchTerm);
|
|
1994
|
+
results = fuseResults.map((result) => result.item);
|
|
1995
|
+
if (searchTerm !== query) {
|
|
1996
|
+
const originalResults = fuse.value.search(query);
|
|
1997
|
+
originalResults.forEach((result) => {
|
|
1998
|
+
if (!results.some((r) => r.code === result.item.code)) {
|
|
1999
|
+
results.push(result.item);
|
|
2000
|
+
}
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
if (aliasResult.suggestions.length > 0) {
|
|
2004
|
+
aliasResult.suggestions.forEach((suggestion) => {
|
|
2005
|
+
const suggestedLang = validLanguages.value.find((lang) => {
|
|
2006
|
+
const langName = lang.name.toLowerCase();
|
|
2007
|
+
const suggestion_lower = suggestion.toLowerCase();
|
|
2008
|
+
return langName === suggestion_lower || // Exact match
|
|
2009
|
+
langName.includes(suggestion_lower) || // Contains match
|
|
2010
|
+
lang.code.toLowerCase() === suggestion_lower || // Code match
|
|
2011
|
+
langName.startsWith(suggestion_lower) || // Starts with match
|
|
2012
|
+
// Handle common variations
|
|
2013
|
+
suggestion_lower === "tatar" && langName.includes("tatar") || suggestion_lower === "belarusian" && (langName.includes("belarus") || langName.includes("belarusian")) || suggestion_lower === "moldovan" && (langName.includes("moldov") || langName.includes("moldova"));
|
|
2014
|
+
});
|
|
2015
|
+
if (suggestedLang && !results.some((r) => r.code === suggestedLang.code)) {
|
|
2016
|
+
results.push(suggestedLang);
|
|
2017
|
+
}
|
|
2018
|
+
});
|
|
2019
|
+
}
|
|
2020
|
+
if (query.length === 2) {
|
|
2021
|
+
const lowerQuery = query.toLowerCase();
|
|
2022
|
+
const manualResults = validLanguages.value.filter(
|
|
2023
|
+
(lang) => lang.name.toLowerCase().startsWith(lowerQuery) || lang.native_name.toLowerCase().startsWith(lowerQuery)
|
|
2024
|
+
);
|
|
2025
|
+
manualResults.forEach((lang) => {
|
|
2026
|
+
if (!results.some((r) => r.code === lang.code)) {
|
|
2027
|
+
results.push(lang);
|
|
2028
|
+
}
|
|
2029
|
+
});
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
const filtered = results.filter(
|
|
2033
|
+
(lang) => !props.modelValue || !props.modelValue.code || lang.code !== props.modelValue.code
|
|
2034
|
+
);
|
|
2035
|
+
return filtered;
|
|
2036
|
+
});
|
|
2037
|
+
function handleSearch(query) {
|
|
2038
|
+
searchQuery.value = query;
|
|
2039
|
+
if (query.trim()) {
|
|
2040
|
+
nextTick(() => {
|
|
2041
|
+
const dropdown = document.querySelector(".multiselect-dropdown");
|
|
2042
|
+
const firstOption = dropdown?.querySelector(".multiselect-option");
|
|
2043
|
+
if (firstOption) {
|
|
2044
|
+
firstOption.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
2045
|
+
firstOption.classList.add("multiselect-option-is-pointed");
|
|
2046
|
+
}
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
function handleKeydown(event) {
|
|
2051
|
+
const keyboardEvent = event;
|
|
2052
|
+
if (keyboardEvent.key === "Tab" || keyboardEvent.key === "Enter") {
|
|
2053
|
+
const dropdown = document.querySelector(".multiselect-dropdown");
|
|
2054
|
+
const firstOption = dropdown?.querySelector(".multiselect-option");
|
|
2055
|
+
if (firstOption && searchQuery.value.trim()) {
|
|
2056
|
+
const langCode = firstOption.getAttribute("data-lang-code");
|
|
2057
|
+
if (langCode) {
|
|
2058
|
+
const language = validLanguages.value.find((lang) => lang.code === langCode);
|
|
2059
|
+
if (language) {
|
|
2060
|
+
selectedLanguage.value = language;
|
|
2061
|
+
searchQuery.value = "";
|
|
2062
|
+
keyboardEvent.preventDefault();
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
const onFlagError = (event) => {
|
|
2069
|
+
const target = event.target;
|
|
2070
|
+
const currentSrc = target.src;
|
|
2071
|
+
if (currentSrc && currentSrc.includes("/flags/")) {
|
|
2072
|
+
const match = currentSrc.match(/\/flags\/([a-z]{2})\.svg/);
|
|
2073
|
+
if (match) {
|
|
2074
|
+
const countryCode = match[1];
|
|
2075
|
+
target.src = `https://flagcdn.com/${countryCode}.svg`;
|
|
2076
|
+
target.onerror = () => {
|
|
2077
|
+
target.style.display = "none";
|
|
2078
|
+
};
|
|
2079
|
+
return;
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
target.style.display = "none";
|
|
2083
|
+
};
|
|
2084
|
+
return (_ctx, _cache) => {
|
|
2085
|
+
return _openBlock(), _createElementBlock(
|
|
2086
|
+
"div",
|
|
2087
|
+
{
|
|
2088
|
+
class: _normalizeClass(["language-select", { "is-dark": __props.isDark }])
|
|
2089
|
+
},
|
|
2090
|
+
[
|
|
2091
|
+
_createCommentVNode(" Show multiselect when we have languages OR when not loading "),
|
|
2092
|
+
validLanguages.value.length > 0 || !isLoading.value ? (_openBlock(), _createBlock(_unref(Multiselect), {
|
|
2093
|
+
key: multiselectKey.value,
|
|
2094
|
+
modelValue: selectedLanguage.value,
|
|
2095
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => selectedLanguage.value = $event),
|
|
2096
|
+
options: filteredLanguages.value,
|
|
2097
|
+
searchable: true,
|
|
2098
|
+
onSearchChange: handleSearch,
|
|
2099
|
+
"can-clear": false,
|
|
2100
|
+
"allow-empty": false,
|
|
2101
|
+
object: true,
|
|
2102
|
+
placeholder: validLanguages.value.length === 0 ? "No languages available" : __props.placeholder,
|
|
2103
|
+
disabled: props.disabled || validLanguages.value.length === 0,
|
|
2104
|
+
loading: isLoading.value,
|
|
2105
|
+
"track-by": "name",
|
|
2106
|
+
label: "name",
|
|
2107
|
+
"value-prop": "code",
|
|
2108
|
+
"filter-results": false,
|
|
2109
|
+
onKeydown: handleKeydown
|
|
2110
|
+
}, {
|
|
2111
|
+
singlelabel: _withCtx(({ value }) => [
|
|
2112
|
+
value ? (_openBlock(), _createElementBlock("div", _hoisted_1, [
|
|
2113
|
+
value.code ? (_openBlock(), _createElementBlock("img", {
|
|
2114
|
+
key: selectedLanguageKey.value,
|
|
2115
|
+
src: getFlagImageUrl(value),
|
|
2116
|
+
class: "lang-flag",
|
|
2117
|
+
alt: `${value.native_name || value.name} flag`,
|
|
2118
|
+
onError: onFlagError
|
|
2119
|
+
}, null, 40, _hoisted_2)) : _createCommentVNode("v-if", true),
|
|
2120
|
+
_createElementVNode("span", _hoisted_3, [
|
|
2121
|
+
_createTextVNode(
|
|
2122
|
+
_toDisplayString(value.name) + " ",
|
|
2123
|
+
1
|
|
2124
|
+
/* TEXT */
|
|
2125
|
+
),
|
|
2126
|
+
_createElementVNode(
|
|
2127
|
+
"span",
|
|
2128
|
+
_hoisted_4,
|
|
2129
|
+
"(" + _toDisplayString(value.native_name || value.name) + ")",
|
|
2130
|
+
1
|
|
2131
|
+
/* TEXT */
|
|
2132
|
+
)
|
|
2133
|
+
])
|
|
2134
|
+
])) : _createCommentVNode("v-if", true)
|
|
2135
|
+
]),
|
|
2136
|
+
option: _withCtx(({ option }) => [
|
|
2137
|
+
_createElementVNode("div", {
|
|
2138
|
+
class: "multiselect-option",
|
|
2139
|
+
"data-lang-code": option.code
|
|
2140
|
+
}, [
|
|
2141
|
+
(_openBlock(), _createElementBlock("img", {
|
|
2142
|
+
key: `${option.code}-${option.flag_country || "fallback"}`,
|
|
2143
|
+
src: getFlagImageUrl(option),
|
|
2144
|
+
class: "lang-flag",
|
|
2145
|
+
alt: `${option.name} flag`,
|
|
2146
|
+
onError: onFlagError
|
|
2147
|
+
}, null, 40, _hoisted_6)),
|
|
2148
|
+
_createElementVNode("span", _hoisted_7, [
|
|
2149
|
+
_createTextVNode(
|
|
2150
|
+
_toDisplayString(option.name) + " ",
|
|
2151
|
+
1
|
|
2152
|
+
/* TEXT */
|
|
2153
|
+
),
|
|
2154
|
+
_createElementVNode(
|
|
2155
|
+
"span",
|
|
2156
|
+
_hoisted_8,
|
|
2157
|
+
"(" + _toDisplayString(option.native_name || option.name) + ")",
|
|
2158
|
+
1
|
|
2159
|
+
/* TEXT */
|
|
2160
|
+
)
|
|
2161
|
+
])
|
|
2162
|
+
], 8, _hoisted_5)
|
|
2163
|
+
]),
|
|
2164
|
+
noresults: _withCtx(() => _cache[1] || (_cache[1] = [
|
|
2165
|
+
_createElementVNode(
|
|
2166
|
+
"div",
|
|
2167
|
+
{ class: "multiselect-no-results" },
|
|
2168
|
+
"No languages found.",
|
|
2169
|
+
-1
|
|
2170
|
+
/* CACHED */
|
|
2171
|
+
)
|
|
2172
|
+
])),
|
|
2173
|
+
nooptions: _withCtx(() => [
|
|
2174
|
+
_createElementVNode(
|
|
2175
|
+
"div",
|
|
2176
|
+
_hoisted_9,
|
|
2177
|
+
_toDisplayString(validLanguages.value.length === 0 ? "Please provide languages via the :languages prop" : "No languages available."),
|
|
2178
|
+
1
|
|
2179
|
+
/* TEXT */
|
|
2180
|
+
)
|
|
2181
|
+
]),
|
|
2182
|
+
_: 1
|
|
2183
|
+
/* STABLE */
|
|
2184
|
+
}, 8, ["modelValue", "options", "placeholder", "disabled", "loading"])) : isLoading.value && validLanguages.value.length === 0 ? (_openBlock(), _createElementBlock(
|
|
2185
|
+
_Fragment,
|
|
2186
|
+
{ key: 1 },
|
|
2187
|
+
[
|
|
2188
|
+
_createCommentVNode(" Only show skeleton loader when actually loading AND we have no languages yet "),
|
|
2189
|
+
_cache[2] || (_cache[2] = _createElementVNode(
|
|
2190
|
+
"div",
|
|
2191
|
+
{ class: "skeleton-loader" },
|
|
2192
|
+
null,
|
|
2193
|
+
-1
|
|
2194
|
+
/* CACHED */
|
|
2195
|
+
))
|
|
2196
|
+
],
|
|
2197
|
+
2112
|
|
2198
|
+
/* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */
|
|
2199
|
+
)) : _createCommentVNode("v-if", true)
|
|
2200
|
+
],
|
|
2201
|
+
2
|
|
2202
|
+
/* CLASS */
|
|
2203
|
+
);
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
});
|
|
2207
|
+
|
|
2208
|
+
// src/components/LanguageSelect.vue
|
|
2209
|
+
var LanguageSelect_default2 = LanguageSelect_default;
|
|
2210
|
+
LanguageSelect_default.__scopeId = "data-v-25bc3fde";
|
|
2211
|
+
|
|
2212
|
+
// vue-script:/Users/nlit/projects/langie-sdk/src/components/InterfaceLanguageSelect.vue?type=script
|
|
2213
|
+
import { defineComponent as _defineComponent2 } from "vue";
|
|
2214
|
+
import { openBlock as _openBlock2, createBlock as _createBlock2, createCommentVNode as _createCommentVNode2, createElementVNode as _createElementVNode2, unref as _unref2, toDisplayString as _toDisplayString2, normalizeClass as _normalizeClass2, createElementBlock as _createElementBlock2 } from "vue";
|
|
2215
|
+
import { computed as computed2, watch as watch2, onMounted, ref as ref3 } from "vue";
|
|
2216
|
+
var _hoisted_12 = { class: "interface-language-select-wrapper" };
|
|
2217
|
+
var _hoisted_22 = { class: "loader-text" };
|
|
2218
|
+
var InterfaceLanguageSelect_default = /* @__PURE__ */ _defineComponent2({
|
|
2219
|
+
__name: "InterfaceLanguageSelect",
|
|
2220
|
+
props: {
|
|
2221
|
+
placeholder: {
|
|
2222
|
+
type: String,
|
|
2223
|
+
default: "Select interface language"
|
|
2224
|
+
},
|
|
2225
|
+
disabled: {
|
|
2226
|
+
type: Boolean,
|
|
2227
|
+
default: false
|
|
2228
|
+
},
|
|
2229
|
+
isDark: {
|
|
2230
|
+
type: Boolean,
|
|
2231
|
+
default: false
|
|
2232
|
+
},
|
|
2233
|
+
translatorHost: {
|
|
2234
|
+
type: String,
|
|
2235
|
+
default: ""
|
|
2236
|
+
},
|
|
2237
|
+
apiKey: {
|
|
2238
|
+
type: String,
|
|
2239
|
+
default: ""
|
|
2240
|
+
},
|
|
2241
|
+
languages: {
|
|
2242
|
+
type: Array,
|
|
2243
|
+
default: () => []
|
|
2244
|
+
}
|
|
2245
|
+
},
|
|
2246
|
+
emits: ["update:modelValue"],
|
|
2247
|
+
setup(__props, { emit: __emit }) {
|
|
2248
|
+
const isChangingLanguage = ref3(false);
|
|
2249
|
+
const props = __props;
|
|
2250
|
+
const emit = __emit;
|
|
2251
|
+
const { availableLanguages: availableLanguages2, currentLanguage: currentLanguage2, setLanguage, fetchLanguages, lr } = useLangie();
|
|
2252
|
+
const effectiveLanguages = computed2(() => {
|
|
2253
|
+
const languages = props.languages && props.languages.length > 0 ? props.languages : availableLanguages2.value;
|
|
2254
|
+
const processed = languages.map((lang) => ({
|
|
2255
|
+
...lang,
|
|
2256
|
+
native_name: lang.native_name || lang.name,
|
|
2257
|
+
flag_country: lang.flag_country || lang.code
|
|
2258
|
+
}));
|
|
2259
|
+
return processed;
|
|
2260
|
+
});
|
|
2261
|
+
const currentLanguageObject = computed2(() => {
|
|
2262
|
+
if (!currentLanguage2.value) return null;
|
|
2263
|
+
return effectiveLanguages.value.find((lang) => lang.code === currentLanguage2.value) || null;
|
|
2264
|
+
});
|
|
2265
|
+
function detectBrowserLanguage2(languages) {
|
|
2266
|
+
if (languages.length === 0) return null;
|
|
2267
|
+
const browserLanguages = navigator.languages || [navigator.language || "en"];
|
|
2268
|
+
for (const browserLang of browserLanguages) {
|
|
2269
|
+
const langCode = browserLang.toLowerCase().split("-")[0];
|
|
2270
|
+
const exactMatch = languages.find((lang) => lang.code.toLowerCase() === langCode);
|
|
2271
|
+
if (exactMatch) {
|
|
2272
|
+
return exactMatch.code;
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
for (const browserLang of browserLanguages) {
|
|
2276
|
+
const fullLangCode = browserLang.toLowerCase();
|
|
2277
|
+
const localeMatch = languages.find((lang) => lang.code.toLowerCase() === fullLangCode);
|
|
2278
|
+
if (localeMatch) {
|
|
2279
|
+
return localeMatch.code;
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
return null;
|
|
2283
|
+
}
|
|
2284
|
+
async function handleLanguageChange(selectedLanguage) {
|
|
2285
|
+
if (selectedLanguage) {
|
|
2286
|
+
isChangingLanguage.value = true;
|
|
2287
|
+
try {
|
|
2288
|
+
setLanguage(selectedLanguage.code);
|
|
2289
|
+
localStorage.setItem("interface_language", selectedLanguage.code);
|
|
2290
|
+
emit("update:modelValue", selectedLanguage);
|
|
2291
|
+
} finally {
|
|
2292
|
+
isChangingLanguage.value = false;
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
watch2(currentLanguage2, (newLangCode) => {
|
|
2297
|
+
if (newLangCode) {
|
|
2298
|
+
localStorage.setItem("interface_language", newLangCode);
|
|
2299
|
+
}
|
|
2300
|
+
});
|
|
2301
|
+
watch2(
|
|
2302
|
+
() => effectiveLanguages.value,
|
|
2303
|
+
(newLanguages) => {
|
|
2304
|
+
if (newLanguages.length > 0 && !currentLanguage2.value) {
|
|
2305
|
+
const savedLanguageCode = localStorage.getItem("interface_language");
|
|
2306
|
+
if (savedLanguageCode) {
|
|
2307
|
+
const savedLangExists = newLanguages.find((lang) => lang.code === savedLanguageCode);
|
|
2308
|
+
if (savedLangExists) {
|
|
2309
|
+
setLanguage(savedLanguageCode);
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
const browserLang = detectBrowserLanguage2(newLanguages);
|
|
2314
|
+
if (browserLang) {
|
|
2315
|
+
setLanguage(browserLang);
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
},
|
|
2319
|
+
{ immediate: true }
|
|
2320
|
+
);
|
|
2321
|
+
onMounted(async () => {
|
|
2322
|
+
if (!props.languages || props.languages.length === 0) {
|
|
2323
|
+
const countryCode = await getCountryCode();
|
|
2324
|
+
await fetchLanguages({ country: countryCode || void 0 });
|
|
2325
|
+
}
|
|
2326
|
+
const savedLanguageCode = localStorage.getItem("interface_language");
|
|
2327
|
+
if (savedLanguageCode && savedLanguageCode !== currentLanguage2.value) {
|
|
2328
|
+
const currentLanguages = effectiveLanguages.value;
|
|
2329
|
+
const savedLangExists = currentLanguages.find((lang) => lang.code === savedLanguageCode);
|
|
2330
|
+
if (savedLangExists) {
|
|
2331
|
+
setLanguage(savedLanguageCode);
|
|
2332
|
+
} else if (currentLanguages.length > 0) {
|
|
2333
|
+
const browserLang = detectBrowserLanguage2(currentLanguages);
|
|
2334
|
+
if (browserLang) {
|
|
2335
|
+
setLanguage(browserLang);
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
} else if (!currentLanguage2.value && effectiveLanguages.value.length > 0) {
|
|
2339
|
+
const browserLang = detectBrowserLanguage2(effectiveLanguages.value);
|
|
2340
|
+
if (browserLang) {
|
|
2341
|
+
setLanguage(browserLang);
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
});
|
|
2345
|
+
watch2(
|
|
2346
|
+
currentLanguageObject,
|
|
2347
|
+
(newValue) => {
|
|
2348
|
+
if (newValue) {
|
|
2349
|
+
emit("update:modelValue", newValue);
|
|
2350
|
+
}
|
|
2351
|
+
},
|
|
2352
|
+
{ immediate: true }
|
|
2353
|
+
);
|
|
2354
|
+
return (_ctx, _cache) => {
|
|
2355
|
+
return _openBlock2(), _createElementBlock2("div", _hoisted_12, [
|
|
2356
|
+
(_openBlock2(), _createBlock2(LanguageSelect_default2, {
|
|
2357
|
+
key: `interface-lang-${effectiveLanguages.value.length}-${effectiveLanguages.value[0]?.code || "empty"}`,
|
|
2358
|
+
"model-value": currentLanguageObject.value,
|
|
2359
|
+
languages: effectiveLanguages.value,
|
|
2360
|
+
placeholder: props.placeholder,
|
|
2361
|
+
disabled: props.disabled || isChangingLanguage.value,
|
|
2362
|
+
"is-dark": props.isDark,
|
|
2363
|
+
"onUpdate:modelValue": handleLanguageChange
|
|
2364
|
+
}, null, 8, ["model-value", "languages", "placeholder", "disabled", "is-dark"])),
|
|
2365
|
+
_createCommentVNode2(" Loading overlay "),
|
|
2366
|
+
isChangingLanguage.value ? (_openBlock2(), _createElementBlock2(
|
|
2367
|
+
"div",
|
|
2368
|
+
{
|
|
2369
|
+
key: 0,
|
|
2370
|
+
class: _normalizeClass2(["language-change-loader", { "is-dark": props.isDark }])
|
|
2371
|
+
},
|
|
2372
|
+
[
|
|
2373
|
+
_cache[0] || (_cache[0] = _createElementVNode2(
|
|
2374
|
+
"div",
|
|
2375
|
+
{ class: "loader-spinner" },
|
|
2376
|
+
null,
|
|
2377
|
+
-1
|
|
2378
|
+
/* CACHED */
|
|
2379
|
+
)),
|
|
2380
|
+
_createElementVNode2(
|
|
2381
|
+
"span",
|
|
2382
|
+
_hoisted_22,
|
|
2383
|
+
_toDisplayString2(_unref2(lr)("Changing language...", "ui")),
|
|
2384
|
+
1
|
|
2385
|
+
/* TEXT */
|
|
2386
|
+
)
|
|
2387
|
+
],
|
|
2388
|
+
2
|
|
2389
|
+
/* CLASS */
|
|
2390
|
+
)) : _createCommentVNode2("v-if", true)
|
|
2391
|
+
]);
|
|
2392
|
+
};
|
|
2393
|
+
}
|
|
2394
|
+
});
|
|
2395
|
+
|
|
2396
|
+
// src/components/InterfaceLanguageSelect.vue
|
|
2397
|
+
var InterfaceLanguageSelect_default2 = InterfaceLanguageSelect_default;
|
|
2398
|
+
InterfaceLanguageSelect_default.__scopeId = "data-v-7192f8c0";
|
|
2399
|
+
|
|
2400
|
+
// vue-script:/Users/nlit/projects/langie-sdk/src/components/lt.vue?type=script
|
|
2401
|
+
import { defineComponent as _defineComponent3 } from "vue";
|
|
2402
|
+
import { toDisplayString as _toDisplayString3, openBlock as _openBlock3, createElementBlock as _createElementBlock3, Transition as _Transition, withCtx as _withCtx2, createBlock as _createBlock3 } from "vue";
|
|
2403
|
+
import { computed as computed3, useSlots, watch as watch3 } from "vue";
|
|
2404
|
+
import { ref as ref4, nextTick as nextTick2 } from "vue";
|
|
2405
|
+
var lt_default = /* @__PURE__ */ _defineComponent3({
|
|
2406
|
+
__name: "lt",
|
|
2407
|
+
props: {
|
|
2408
|
+
// Message key (optional, otherwise slot content is used)
|
|
2409
|
+
msg: {
|
|
2410
|
+
type: String,
|
|
2411
|
+
default: void 0
|
|
2412
|
+
},
|
|
2413
|
+
// Translation context shorthand
|
|
2414
|
+
ctx: {
|
|
2415
|
+
type: String,
|
|
2416
|
+
required: false,
|
|
2417
|
+
default: "ui"
|
|
2418
|
+
},
|
|
2419
|
+
// Original language shorthand
|
|
2420
|
+
orig: {
|
|
2421
|
+
type: String,
|
|
2422
|
+
required: false,
|
|
2423
|
+
default: void 0
|
|
2424
|
+
}
|
|
2425
|
+
},
|
|
2426
|
+
setup(__props) {
|
|
2427
|
+
const isNuxt = computed3(() => {
|
|
2428
|
+
if (typeof window !== "undefined") {
|
|
2429
|
+
return !!window.__NUXT__;
|
|
2430
|
+
}
|
|
2431
|
+
if (typeof process !== "undefined") {
|
|
2432
|
+
return !!process.env.NUXT_SSR_BASE || !!process.env.NUXT_PUBLIC_BASE_URL;
|
|
2433
|
+
}
|
|
2434
|
+
return false;
|
|
2435
|
+
});
|
|
2436
|
+
const props = __props;
|
|
2437
|
+
const slots = useSlots();
|
|
2438
|
+
const { lr, currentLanguage: currentLanguage2, uiTranslations: uiTranslations2, translations: translations2, getLtDefaults: getLtDefaults2 } = useLangie();
|
|
2439
|
+
const keyStr = computed3(() => {
|
|
2440
|
+
if (props.msg) return props.msg;
|
|
2441
|
+
const slotContent = slots.default ? slots.default().map((n) => n.children).join("") : "";
|
|
2442
|
+
return (slotContent || "").trim();
|
|
2443
|
+
});
|
|
2444
|
+
const forceUpdate = ref4(0);
|
|
2445
|
+
watch3(
|
|
2446
|
+
[uiTranslations2, translations2],
|
|
2447
|
+
() => {
|
|
2448
|
+
nextTick2(() => {
|
|
2449
|
+
forceUpdate.value++;
|
|
2450
|
+
});
|
|
2451
|
+
},
|
|
2452
|
+
{ deep: true }
|
|
2453
|
+
);
|
|
2454
|
+
const translated = computed3(() => {
|
|
2455
|
+
if (isNuxt.value && typeof window === "undefined") {
|
|
2456
|
+
return keyStr.value;
|
|
2457
|
+
}
|
|
2458
|
+
const globalDefaults = getLtDefaults2();
|
|
2459
|
+
const effectiveCtx = props.ctx ?? globalDefaults.ctx;
|
|
2460
|
+
const effectiveOrig = props.orig ?? globalDefaults.orig;
|
|
2461
|
+
void currentLanguage2.value;
|
|
2462
|
+
void forceUpdate.value;
|
|
2463
|
+
const cacheKey = `${keyStr.value}|${effectiveCtx}`;
|
|
2464
|
+
const cache = effectiveCtx === "ui" ? uiTranslations2 : translations2;
|
|
2465
|
+
void cache[cacheKey];
|
|
2466
|
+
const result = lr(keyStr.value, effectiveCtx, effectiveOrig);
|
|
2467
|
+
return result;
|
|
2468
|
+
});
|
|
2469
|
+
return (_ctx, _cache) => {
|
|
2470
|
+
return _openBlock3(), _createBlock3(_Transition, {
|
|
2471
|
+
name: "fade",
|
|
2472
|
+
mode: "out-in"
|
|
2473
|
+
}, {
|
|
2474
|
+
default: _withCtx2(() => [
|
|
2475
|
+
(_openBlock3(), _createElementBlock3(
|
|
2476
|
+
"span",
|
|
2477
|
+
{ key: translated.value },
|
|
2478
|
+
_toDisplayString3(translated.value),
|
|
2479
|
+
1
|
|
2480
|
+
/* TEXT */
|
|
2481
|
+
))
|
|
2482
|
+
]),
|
|
2483
|
+
_: 1
|
|
2484
|
+
/* STABLE */
|
|
2485
|
+
});
|
|
2486
|
+
};
|
|
2487
|
+
}
|
|
2488
|
+
});
|
|
2489
|
+
|
|
2490
|
+
// src/components/lt.vue
|
|
2491
|
+
var lt_default2 = lt_default;
|
|
2492
|
+
lt_default.__scopeId = "data-v-14a7b6f6";
|
|
2493
|
+
|
|
2494
|
+
// src/utils/theme.ts
|
|
2495
|
+
function setThemeColors() {
|
|
2496
|
+
const root = document.documentElement;
|
|
2497
|
+
root.style.setProperty("--langie-flag-border", COLORS.border.flag);
|
|
2498
|
+
root.style.setProperty("--langie-flag-border-dark", COLORS.border.dark);
|
|
2499
|
+
root.style.setProperty("--langie-text-secondary", COLORS.text.secondary);
|
|
2500
|
+
root.style.setProperty("--langie-text-secondary-dark", COLORS.neutral.gray400);
|
|
2501
|
+
root.style.setProperty("--langie-primary-blue", COLORS.primary.blue);
|
|
2502
|
+
root.style.setProperty("--langie-primary-blue-alpha-30", COLORS.primary.blueAlpha30);
|
|
2503
|
+
root.style.setProperty("--langie-primary-blue-alpha-50", COLORS.primary.blueAlpha50);
|
|
2504
|
+
}
|
|
2505
|
+
function clearThemeColors() {
|
|
2506
|
+
const root = document.documentElement;
|
|
2507
|
+
const properties = [
|
|
2508
|
+
"--langie-flag-border",
|
|
2509
|
+
"--langie-flag-border-dark",
|
|
2510
|
+
"--langie-text-secondary",
|
|
2511
|
+
"--langie-text-secondary-dark",
|
|
2512
|
+
"--langie-primary-blue",
|
|
2513
|
+
"--langie-primary-blue-alpha-30",
|
|
2514
|
+
"--langie-primary-blue-alpha-50"
|
|
2515
|
+
];
|
|
2516
|
+
properties.forEach((prop) => root.style.removeProperty(prop));
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
// src/utils/languageMapping.ts
|
|
2520
|
+
var BROWSER_LANGUAGE_MAP = {
|
|
2521
|
+
// Chinese variants
|
|
2522
|
+
"zh": "zh-cn",
|
|
2523
|
+
// Chinese (Simplified)
|
|
2524
|
+
// Serbian variants
|
|
2525
|
+
"sr": "sr-latn",
|
|
2526
|
+
// Serbian (default to Latin)
|
|
2527
|
+
"me": "sr-latn",
|
|
2528
|
+
// Montenegrin
|
|
2529
|
+
"sh": "sr-latn",
|
|
2530
|
+
// Serbo-Croatian
|
|
2531
|
+
// Kazakh variants
|
|
2532
|
+
"kaz": "kk",
|
|
2533
|
+
// Kazakh (browser 'kaz' or 'kk' → 'kk')
|
|
2534
|
+
"kk": "kk"
|
|
2535
|
+
};
|
|
2536
|
+
function detectBrowserLanguage() {
|
|
2537
|
+
if (typeof navigator === "undefined") {
|
|
2538
|
+
return "en";
|
|
2539
|
+
}
|
|
2540
|
+
const locale = navigator.languages?.[0] || navigator.language || "";
|
|
2541
|
+
const browserCode = locale.split("-")[0];
|
|
2542
|
+
const mappedLanguage = BROWSER_LANGUAGE_MAP[browserCode];
|
|
2543
|
+
if (mappedLanguage) {
|
|
2544
|
+
return mappedLanguage;
|
|
2545
|
+
}
|
|
2546
|
+
return browserCode || "en";
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
// src/utils/regionMapping.ts
|
|
2550
|
+
var COUNTRY_TO_REGION = {
|
|
2551
|
+
// EU
|
|
2552
|
+
DE: "EU",
|
|
2553
|
+
AT: "EU",
|
|
2554
|
+
CH: "EU",
|
|
2555
|
+
NL: "EU",
|
|
2556
|
+
BE: "EU",
|
|
2557
|
+
LU: "EU",
|
|
2558
|
+
DK: "EU",
|
|
2559
|
+
SE: "EU",
|
|
2560
|
+
NO: "EU",
|
|
2561
|
+
IS: "EU",
|
|
2562
|
+
GB: "EU",
|
|
2563
|
+
IE: "EU",
|
|
2564
|
+
FR: "EU",
|
|
2565
|
+
IT: "EU",
|
|
2566
|
+
ES: "EU",
|
|
2567
|
+
PT: "EU",
|
|
2568
|
+
RO: "EU",
|
|
2569
|
+
MT: "EU",
|
|
2570
|
+
PL: "EU",
|
|
2571
|
+
CZ: "EU",
|
|
2572
|
+
SK: "EU",
|
|
2573
|
+
SI: "EU",
|
|
2574
|
+
HR: "EU",
|
|
2575
|
+
BA: "EU",
|
|
2576
|
+
RS: "EU",
|
|
2577
|
+
ME: "EU",
|
|
2578
|
+
MK: "EU",
|
|
2579
|
+
BG: "EU",
|
|
2580
|
+
RU: "EU",
|
|
2581
|
+
BY: "EU",
|
|
2582
|
+
UA: "EU",
|
|
2583
|
+
LT: "EU",
|
|
2584
|
+
LV: "EU",
|
|
2585
|
+
EE: "EU",
|
|
2586
|
+
FI: "EU",
|
|
2587
|
+
HU: "EU",
|
|
2588
|
+
GR: "EU",
|
|
2589
|
+
CY: "EU",
|
|
2590
|
+
AL: "EU",
|
|
2591
|
+
MD: "EU",
|
|
2592
|
+
LI: "EU",
|
|
2593
|
+
AD: "EU",
|
|
2594
|
+
SM: "EU",
|
|
2595
|
+
VA: "EU",
|
|
2596
|
+
MC: "EU",
|
|
2597
|
+
// NA
|
|
2598
|
+
US: "NA",
|
|
2599
|
+
CA: "NA",
|
|
2600
|
+
MX: "NA",
|
|
2601
|
+
GT: "NA",
|
|
2602
|
+
BZ: "NA",
|
|
2603
|
+
SV: "NA",
|
|
2604
|
+
HN: "NA",
|
|
2605
|
+
NI: "NA",
|
|
2606
|
+
CR: "NA",
|
|
2607
|
+
PA: "NA",
|
|
2608
|
+
HT: "NA",
|
|
2609
|
+
JM: "NA",
|
|
2610
|
+
CU: "NA",
|
|
2611
|
+
DO: "NA",
|
|
2612
|
+
TT: "NA",
|
|
2613
|
+
BB: "NA",
|
|
2614
|
+
BS: "NA",
|
|
2615
|
+
GD: "NA",
|
|
2616
|
+
LC: "NA",
|
|
2617
|
+
VC: "NA",
|
|
2618
|
+
AG: "NA",
|
|
2619
|
+
DM: "NA",
|
|
2620
|
+
KN: "NA",
|
|
2621
|
+
// SA
|
|
2622
|
+
AR: "SA",
|
|
2623
|
+
CL: "SA",
|
|
2624
|
+
PE: "SA",
|
|
2625
|
+
CO: "SA",
|
|
2626
|
+
VE: "SA",
|
|
2627
|
+
EC: "SA",
|
|
2628
|
+
BO: "SA",
|
|
2629
|
+
PY: "SA",
|
|
2630
|
+
UY: "SA",
|
|
2631
|
+
BR: "SA",
|
|
2632
|
+
GY: "SA",
|
|
2633
|
+
SR: "SA",
|
|
2634
|
+
GF: "SA",
|
|
2635
|
+
// EA
|
|
2636
|
+
CN: "EA",
|
|
2637
|
+
TW: "EA",
|
|
2638
|
+
HK: "EA",
|
|
2639
|
+
MO: "EA",
|
|
2640
|
+
SG: "EA",
|
|
2641
|
+
JP: "EA",
|
|
2642
|
+
KR: "EA",
|
|
2643
|
+
KP: "EA",
|
|
2644
|
+
MN: "EA",
|
|
2645
|
+
// SEA
|
|
2646
|
+
VN: "SEA",
|
|
2647
|
+
KH: "SEA",
|
|
2648
|
+
TH: "SEA",
|
|
2649
|
+
LA: "SEA",
|
|
2650
|
+
MM: "SEA",
|
|
2651
|
+
ID: "SEA",
|
|
2652
|
+
MY: "SEA",
|
|
2653
|
+
PH: "SEA",
|
|
2654
|
+
BN: "SEA",
|
|
2655
|
+
TL: "SEA",
|
|
2656
|
+
// SAS
|
|
2657
|
+
IN: "SAS",
|
|
2658
|
+
PK: "SAS",
|
|
2659
|
+
BD: "SAS",
|
|
2660
|
+
LK: "SAS",
|
|
2661
|
+
NP: "SAS",
|
|
2662
|
+
BT: "SAS",
|
|
2663
|
+
MV: "SAS",
|
|
2664
|
+
AF: "SAS",
|
|
2665
|
+
// CAS
|
|
2666
|
+
KZ: "CAS",
|
|
2667
|
+
KG: "CAS",
|
|
2668
|
+
UZ: "CAS",
|
|
2669
|
+
TM: "CAS",
|
|
2670
|
+
TR: "CAS",
|
|
2671
|
+
AZ: "CAS",
|
|
2672
|
+
TJ: "CAS",
|
|
2673
|
+
IR: "CAS",
|
|
2674
|
+
// ME
|
|
2675
|
+
SA: "ME",
|
|
2676
|
+
AE: "ME",
|
|
2677
|
+
QA: "ME",
|
|
2678
|
+
KW: "ME",
|
|
2679
|
+
BH: "ME",
|
|
2680
|
+
OM: "ME",
|
|
2681
|
+
YE: "ME",
|
|
2682
|
+
JO: "ME",
|
|
2683
|
+
LB: "ME",
|
|
2684
|
+
SY: "ME",
|
|
2685
|
+
IQ: "ME",
|
|
2686
|
+
PS: "ME",
|
|
2687
|
+
IL: "ME",
|
|
2688
|
+
GE: "ME",
|
|
2689
|
+
AM: "ME",
|
|
2690
|
+
// NAF
|
|
2691
|
+
EG: "NAF",
|
|
2692
|
+
LY: "NAF",
|
|
2693
|
+
TN: "NAF",
|
|
2694
|
+
DZ: "NAF",
|
|
2695
|
+
MA: "NAF",
|
|
2696
|
+
SD: "NAF",
|
|
2697
|
+
SS: "NAF",
|
|
2698
|
+
ET: "NAF",
|
|
2699
|
+
ER: "NAF",
|
|
2700
|
+
DJ: "NAF",
|
|
2701
|
+
SO: "NAF",
|
|
2702
|
+
// WAF
|
|
2703
|
+
NG: "WAF",
|
|
2704
|
+
GH: "WAF",
|
|
2705
|
+
CI: "WAF",
|
|
2706
|
+
BF: "WAF",
|
|
2707
|
+
ML: "WAF",
|
|
2708
|
+
SN: "WAF",
|
|
2709
|
+
GN: "WAF",
|
|
2710
|
+
SL: "WAF",
|
|
2711
|
+
LR: "WAF",
|
|
2712
|
+
GM: "WAF",
|
|
2713
|
+
GW: "WAF",
|
|
2714
|
+
NE: "WAF",
|
|
2715
|
+
TD: "WAF",
|
|
2716
|
+
CF: "WAF",
|
|
2717
|
+
CM: "WAF",
|
|
2718
|
+
MR: "WAF",
|
|
2719
|
+
CV: "WAF",
|
|
2720
|
+
// EAF
|
|
2721
|
+
KE: "EAF",
|
|
2722
|
+
TZ: "EAF",
|
|
2723
|
+
UG: "EAF",
|
|
2724
|
+
RW: "EAF",
|
|
2725
|
+
BI: "EAF",
|
|
2726
|
+
MZ: "EAF",
|
|
2727
|
+
MW: "EAF",
|
|
2728
|
+
ZM: "EAF",
|
|
2729
|
+
ZW: "EAF",
|
|
2730
|
+
MG: "EAF",
|
|
2731
|
+
MU: "EAF",
|
|
2732
|
+
KM: "EAF",
|
|
2733
|
+
SC: "EAF",
|
|
2734
|
+
// SAF
|
|
2735
|
+
ZA: "SAF",
|
|
2736
|
+
NA: "SAF",
|
|
2737
|
+
BW: "SAF",
|
|
2738
|
+
LS: "SAF",
|
|
2739
|
+
SZ: "SAF",
|
|
2740
|
+
AO: "SAF",
|
|
2741
|
+
// CAF
|
|
2742
|
+
CD: "CAF",
|
|
2743
|
+
CG: "CAF",
|
|
2744
|
+
GA: "CAF",
|
|
2745
|
+
GQ: "CAF",
|
|
2746
|
+
ST: "CAF",
|
|
2747
|
+
// OC
|
|
2748
|
+
AU: "OC",
|
|
2749
|
+
NZ: "OC",
|
|
2750
|
+
PG: "OC",
|
|
2751
|
+
FJ: "OC",
|
|
2752
|
+
SB: "OC",
|
|
2753
|
+
VU: "OC",
|
|
2754
|
+
NC: "OC",
|
|
2755
|
+
PF: "OC",
|
|
2756
|
+
WS: "OC",
|
|
2757
|
+
TO: "OC",
|
|
2758
|
+
TV: "OC",
|
|
2759
|
+
KI: "OC",
|
|
2760
|
+
NR: "OC",
|
|
2761
|
+
PW: "OC",
|
|
2762
|
+
MH: "OC",
|
|
2763
|
+
FM: "OC",
|
|
2764
|
+
// CAR
|
|
2765
|
+
PR: "CAR",
|
|
2766
|
+
GP: "CAR",
|
|
2767
|
+
MQ: "CAR",
|
|
2768
|
+
AW: "CAR",
|
|
2769
|
+
CW: "CAR"
|
|
2770
|
+
};
|
|
2771
|
+
function getRegionForCountry(country) {
|
|
2772
|
+
if (!country) return "";
|
|
2773
|
+
return COUNTRY_TO_REGION[country.toUpperCase()] || "";
|
|
2774
|
+
}
|
|
2775
|
+
function getGeoRequestParams(country, language, timezone) {
|
|
2776
|
+
const params = {};
|
|
2777
|
+
if (country) {
|
|
2778
|
+
params.country = country;
|
|
2779
|
+
const region = getRegionForCountry(country);
|
|
2780
|
+
if (region) {
|
|
2781
|
+
params.region = region;
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
if (language) params.language = language;
|
|
2785
|
+
if (timezone) params.timezone = timezone;
|
|
2786
|
+
return params;
|
|
2787
|
+
}
|
|
2788
|
+
export {
|
|
2789
|
+
API_FIELD_CTX,
|
|
2790
|
+
API_FIELD_FROM,
|
|
2791
|
+
API_FIELD_TEXT,
|
|
2792
|
+
API_FIELD_TO,
|
|
2793
|
+
API_FIELD_TRANSLATED,
|
|
2794
|
+
API_FIELD_TRANSLATIONS,
|
|
2795
|
+
BROWSER_LANGUAGE_MAP,
|
|
2796
|
+
COLORS,
|
|
2797
|
+
CSS_VARS,
|
|
2798
|
+
DEFAULT_API_HOST,
|
|
2799
|
+
DEV_API_HOST,
|
|
2800
|
+
InterfaceLanguageSelect_default2 as InterfaceLanguageSelect,
|
|
2801
|
+
LANGUAGE_ALIAS_TABLE,
|
|
2802
|
+
LanguageSelect_default2 as LanguageSelect,
|
|
2803
|
+
THEME_COLORS,
|
|
2804
|
+
cacheManager,
|
|
2805
|
+
clearCache,
|
|
2806
|
+
clearThemeColors,
|
|
2807
|
+
clearTranslationCache,
|
|
2808
|
+
detectBrowserLanguage,
|
|
2809
|
+
fetchAvailableLanguages,
|
|
2810
|
+
getCache,
|
|
2811
|
+
getCacheStats,
|
|
2812
|
+
getCountryCode,
|
|
2813
|
+
getGeoRequestParams,
|
|
2814
|
+
getLtDefaults,
|
|
2815
|
+
getRegionForCountry,
|
|
2816
|
+
getTranslationCacheSize,
|
|
2817
|
+
lt_default2 as lt,
|
|
2818
|
+
removeCache,
|
|
2819
|
+
setCache,
|
|
2820
|
+
setLtDefaults,
|
|
2821
|
+
setThemeColors,
|
|
2822
|
+
translateBatch,
|
|
2823
|
+
useLangie,
|
|
2824
|
+
useLangie as useTranslator
|
|
2825
|
+
};
|
|
2826
|
+
//# sourceMappingURL=index.mjs.map
|