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.
@@ -0,0 +1,2288 @@
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
+ // vue-script:/Users/nlit/projects/langie-sdk/src/components/LanguageSelect.vue?type=script
11
+ import { useCssVars as _useCssVars, defineComponent as _defineComponent } from "vue";
12
+ 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";
13
+ import "@vueform/multiselect/themes/default.css";
14
+ import { ref, computed, nextTick } from "vue";
15
+ import Multiselect from "@vueform/multiselect";
16
+ import Fuse from "fuse.js";
17
+
18
+ // src/language-aliases/european.ts
19
+ var EUROPEAN_LANGUAGES = [
20
+ // Major European Languages
21
+ {
22
+ lang: "english",
23
+ match: [
24
+ "eng",
25
+ "en",
26
+ "united states",
27
+ "united states of america",
28
+ "usa",
29
+ "us",
30
+ "great britain",
31
+ "britain",
32
+ "british",
33
+ "england",
34
+ "united kingdom",
35
+ "uk"
36
+ ],
37
+ suggest: ["irish", "scottish", "welsh"]
38
+ },
39
+ {
40
+ lang: "spanish",
41
+ match: [
42
+ "spa",
43
+ "es",
44
+ "esp",
45
+ "spain",
46
+ "espa\xF1ol",
47
+ "mexico",
48
+ "mexican",
49
+ "argentina",
50
+ "colombia",
51
+ "venezuela"
52
+ ],
53
+ suggest: ["catalan", "galician", "basque"]
54
+ },
55
+ {
56
+ lang: "french",
57
+ match: [
58
+ "fra",
59
+ "fr",
60
+ "fre",
61
+ "france",
62
+ "fran\xE7ais",
63
+ "canada",
64
+ "canadian",
65
+ "quebec",
66
+ "belgium",
67
+ "swiss"
68
+ ],
69
+ suggest: ["occitan", "breton", "corsican"]
70
+ },
71
+ {
72
+ lang: "german",
73
+ match: [
74
+ "ger",
75
+ "de",
76
+ "deu",
77
+ "germany",
78
+ "deutsch",
79
+ "austria",
80
+ "austrian",
81
+ "switzerland",
82
+ "swiss"
83
+ ],
84
+ suggest: ["bavarian", "saxon", "luxembourgish"]
85
+ },
86
+ {
87
+ lang: "italian",
88
+ match: ["ita", "it", "italy", "italiano", "swiss italian"],
89
+ suggest: ["sardinian", "neapolitan", "venetian"]
90
+ },
91
+ {
92
+ lang: "portuguese",
93
+ match: ["por", "pt", "portugal", "brazil", "brazilian", "braz", "mozambique", "angola"],
94
+ suggest: ["galician", "mirandese"]
95
+ },
96
+ {
97
+ lang: "dutch",
98
+ match: ["dut", "nl", "nld", "netherlands", "holland", "flemish", "belgium"],
99
+ suggest: ["frisian", "afrikaans"]
100
+ },
101
+ // Slavic Languages
102
+ {
103
+ lang: "russian",
104
+ match: ["rus", "ru", "russia", "belarus"],
105
+ suggest: ["kazakh", "tatar", "belarusian", "tajik", "uzbek", "moldovan"]
106
+ },
107
+ {
108
+ lang: "ukrainian",
109
+ match: ["ukr", "uk", "ukraine"],
110
+ suggest: ["russian", "belarusian"]
111
+ },
112
+ {
113
+ lang: "polish",
114
+ match: ["pol", "pl", "poland"],
115
+ suggest: ["silesian", "kashubian"]
116
+ },
117
+ {
118
+ lang: "czech",
119
+ match: ["ces", "cs", "cze", "czech republic", "czechia"],
120
+ suggest: ["slovak", "moravian"]
121
+ },
122
+ {
123
+ lang: "slovak",
124
+ match: ["slk", "sk", "slovakia"],
125
+ suggest: ["czech", "rusyn"]
126
+ },
127
+ {
128
+ lang: "croatian",
129
+ match: ["hrv", "hr", "croatia"],
130
+ suggest: ["serbian", "bosnian", "montenegrin"]
131
+ },
132
+ {
133
+ lang: "serbian",
134
+ match: ["srp", "sr", "ser", "serbia", "serbian", "srpski", "\u0441\u0440\u0431\u0438\u0458\u0430", "\u0441\u0440\u043F\u0441\u043A\u0438"],
135
+ suggest: ["croatian", "bosnian", "montenegrin"]
136
+ },
137
+ {
138
+ lang: "bulgarian",
139
+ match: ["bul", "bg", "bulgaria"],
140
+ suggest: ["macedonian"]
141
+ },
142
+ {
143
+ lang: "slovenian",
144
+ match: ["slv", "sl", "slovenia"],
145
+ suggest: ["croatian"]
146
+ },
147
+ // Nordic Languages
148
+ {
149
+ lang: "swedish",
150
+ match: ["swe", "sv", "sweden"],
151
+ suggest: ["norwegian", "danish", "finnish"]
152
+ },
153
+ {
154
+ lang: "norwegian",
155
+ match: ["nor", "no", "norway"],
156
+ suggest: ["swedish", "danish"]
157
+ },
158
+ {
159
+ lang: "danish",
160
+ match: ["dan", "da", "denmark"],
161
+ suggest: ["norwegian", "swedish"]
162
+ },
163
+ {
164
+ lang: "finnish",
165
+ match: ["fin", "fi", "finland"],
166
+ suggest: ["estonian", "swedish"]
167
+ },
168
+ {
169
+ lang: "icelandic",
170
+ match: ["isl", "is", "iceland"],
171
+ suggest: ["faroese", "norwegian"]
172
+ },
173
+ // Other European Languages
174
+ {
175
+ lang: "greek",
176
+ match: ["ell", "el", "gre", "greece", "hellenic"],
177
+ suggest: ["macedonian", "albanian"]
178
+ },
179
+ {
180
+ lang: "romanian",
181
+ match: ["ron", "ro", "rum", "romania"],
182
+ suggest: ["moldovan", "hungarian"]
183
+ },
184
+ {
185
+ lang: "hungarian",
186
+ match: ["hun", "hu", "hungary", "magyar"],
187
+ suggest: ["romanian", "slovak"]
188
+ },
189
+ {
190
+ lang: "albanian",
191
+ match: ["sqi", "sq", "alb", "albania", "kosovo"],
192
+ suggest: ["greek", "macedonian"]
193
+ },
194
+ {
195
+ lang: "lithuanian",
196
+ match: ["lit", "lt", "lithuania"],
197
+ suggest: ["latvian", "polish"]
198
+ },
199
+ {
200
+ lang: "latvian",
201
+ match: ["lav", "lv", "latvia"],
202
+ suggest: ["lithuanian", "estonian"]
203
+ },
204
+ {
205
+ lang: "estonian",
206
+ match: ["est", "et", "estonia"],
207
+ suggest: ["finnish", "latvian"]
208
+ },
209
+ // Celtic Languages
210
+ {
211
+ lang: "irish",
212
+ match: ["gle", "ga", "ireland", "gaelic"],
213
+ suggest: ["scottish", "welsh"]
214
+ },
215
+ {
216
+ lang: "welsh",
217
+ match: ["cym", "cy", "wales", "cymru"],
218
+ suggest: ["irish", "cornish"]
219
+ },
220
+ {
221
+ lang: "scottish",
222
+ match: ["gla", "gd", "scotland", "scots gaelic"],
223
+ suggest: ["irish", "english"]
224
+ },
225
+ // Additional Languages
226
+ {
227
+ lang: "esperanto",
228
+ match: ["epo", "eo", "esperanto"],
229
+ suggest: []
230
+ },
231
+ {
232
+ lang: "latin",
233
+ match: ["lat", "la", "latin"],
234
+ suggest: ["italian", "spanish", "french"]
235
+ }
236
+ ];
237
+
238
+ // src/language-aliases/asian.ts
239
+ var ASIAN_LANGUAGES = [
240
+ // Asian Languages
241
+ {
242
+ lang: "chinese",
243
+ match: [
244
+ "chi",
245
+ "zh",
246
+ "zho",
247
+ "zh-cn",
248
+ "cn",
249
+ "china",
250
+ "mandarin",
251
+ "zhongwen",
252
+ "simplified",
253
+ "traditional"
254
+ ],
255
+ suggest: ["cantonese", "taiwanese", "hakka"]
256
+ },
257
+ {
258
+ lang: "japanese",
259
+ match: ["jpn", "ja", "jp", "japan", "nihongo"],
260
+ suggest: ["okinawan"]
261
+ },
262
+ {
263
+ lang: "korean",
264
+ match: ["kor", "ko", "kr", "korea", "hangul", "south korea", "north korea"],
265
+ suggest: ["jeju"]
266
+ },
267
+ {
268
+ lang: "hindi",
269
+ match: ["hin", "hi", "india", "indian", "devanagari"],
270
+ suggest: ["urdu", "punjabi", "gujarati", "marathi", "bengali"]
271
+ },
272
+ {
273
+ lang: "arabic",
274
+ match: ["ara", "ar", "arab", "saudi", "egypt", "egyptian", "gulf", "levantine", "maghreb"],
275
+ suggest: ["persian", "hebrew", "urdu"]
276
+ },
277
+ {
278
+ lang: "thai",
279
+ match: ["tha", "th", "thailand", "siam"],
280
+ suggest: ["lao", "khmer"]
281
+ },
282
+ {
283
+ lang: "vietnamese",
284
+ match: ["vie", "vi", "vietnam"],
285
+ suggest: ["khmer", "lao"]
286
+ },
287
+ {
288
+ lang: "indonesian",
289
+ match: ["ind", "id", "indonesia", "bahasa"],
290
+ suggest: ["malay", "javanese", "sundanese"]
291
+ },
292
+ {
293
+ lang: "malay",
294
+ match: ["msa", "ms", "malaysia", "brunei"],
295
+ suggest: ["indonesian"]
296
+ },
297
+ // Central Asian & Turkic Languages
298
+ {
299
+ lang: "kazakh",
300
+ match: ["kaz", "kk", "kazakh", "kazakhstan"],
301
+ suggest: ["russian", "kyrgyz", "uzbek"]
302
+ },
303
+ {
304
+ lang: "turkish",
305
+ match: ["tur", "tr", "turkey"],
306
+ suggest: ["kurdish", "azerbaijani"]
307
+ },
308
+ {
309
+ lang: "uzbek",
310
+ match: ["uzb", "uz", "uzbekistan"],
311
+ suggest: ["tajik", "kazakh", "kyrgyz"]
312
+ },
313
+ {
314
+ lang: "kyrgyz",
315
+ match: ["kir", "ky", "kyrgyzstan"],
316
+ suggest: ["kazakh", "uzbek"]
317
+ },
318
+ {
319
+ lang: "tajik",
320
+ match: ["tgk", "tg", "tajikistan"],
321
+ suggest: ["persian", "uzbek"]
322
+ },
323
+ {
324
+ lang: "azerbaijani",
325
+ match: ["aze", "az", "azerbaijan"],
326
+ suggest: ["turkish", "persian"]
327
+ },
328
+ // Middle Eastern Languages
329
+ {
330
+ lang: "persian",
331
+ match: ["fas", "fa", "per", "iran", "farsi", "dari", "afghanistan"],
332
+ suggest: ["tajik", "kurdish", "pashto"]
333
+ },
334
+ {
335
+ lang: "hebrew",
336
+ match: ["heb", "he", "israel", "israeli"],
337
+ suggest: ["arabic", "yiddish"]
338
+ },
339
+ {
340
+ lang: "kurdish",
341
+ match: ["kur", "ku", "kurdistan", "kurmanji", "sorani"],
342
+ suggest: ["turkish", "persian", "arabic"]
343
+ },
344
+ // South Asian Languages
345
+ {
346
+ lang: "urdu",
347
+ match: ["urd", "ur", "pakistan", "pakistani"],
348
+ suggest: ["hindi", "punjabi"]
349
+ },
350
+ {
351
+ lang: "bengali",
352
+ match: ["ben", "bn", "bangladesh", "bengal"],
353
+ suggest: ["hindi", "assamese"]
354
+ },
355
+ {
356
+ lang: "punjabi",
357
+ match: ["pan", "pa", "punjab"],
358
+ suggest: ["hindi", "urdu"]
359
+ },
360
+ {
361
+ lang: "gujarati",
362
+ match: ["guj", "gu", "gujarat"],
363
+ suggest: ["hindi", "marathi"]
364
+ },
365
+ {
366
+ lang: "marathi",
367
+ match: ["mar", "mr", "maharashtra"],
368
+ suggest: ["hindi", "gujarati"]
369
+ },
370
+ {
371
+ lang: "tamil",
372
+ match: ["tam", "ta", "tamil nadu", "sri lanka"],
373
+ suggest: ["malayalam", "telugu", "kannada"]
374
+ },
375
+ {
376
+ lang: "telugu",
377
+ match: ["tel", "te", "andhra pradesh"],
378
+ suggest: ["tamil", "kannada"]
379
+ },
380
+ {
381
+ lang: "kannada",
382
+ match: ["kan", "kn", "karnataka"],
383
+ suggest: ["tamil", "telugu", "malayalam"]
384
+ },
385
+ {
386
+ lang: "malayalam",
387
+ match: ["mal", "ml", "kerala"],
388
+ suggest: ["tamil", "kannada"]
389
+ },
390
+ {
391
+ lang: "nepali",
392
+ match: ["nep", "ne", "nepal"],
393
+ suggest: ["hindi"]
394
+ },
395
+ {
396
+ lang: "sinhala",
397
+ match: ["sin", "si", "sri lanka", "ceylon"],
398
+ suggest: ["tamil"]
399
+ },
400
+ // Southeast Asian Languages
401
+ {
402
+ lang: "burmese",
403
+ match: ["mya", "my", "myanmar", "burma"],
404
+ suggest: ["thai"]
405
+ },
406
+ {
407
+ lang: "khmer",
408
+ match: ["khm", "km", "cambodia", "cambodian"],
409
+ suggest: ["vietnamese", "thai"]
410
+ },
411
+ {
412
+ lang: "lao",
413
+ match: ["lao", "lo", "laos"],
414
+ suggest: ["thai", "vietnamese"]
415
+ },
416
+ {
417
+ lang: "tagalog",
418
+ match: ["tgl", "tl", "philippines", "filipino"],
419
+ suggest: ["cebuano", "ilocano"]
420
+ }
421
+ ];
422
+
423
+ // src/language-aliases/african.ts
424
+ var AFRICAN_LANGUAGES = [
425
+ // African Languages
426
+ {
427
+ lang: "swahili",
428
+ match: ["swa", "sw", "kiswahili", "tanzania", "kenya"],
429
+ suggest: ["arabic"]
430
+ },
431
+ {
432
+ lang: "amharic",
433
+ match: ["amh", "am", "ethiopia", "ethiopian"],
434
+ suggest: ["tigrinya", "oromo"]
435
+ },
436
+ {
437
+ lang: "yoruba",
438
+ match: ["yor", "yo", "nigeria", "benin"],
439
+ suggest: ["igbo", "hausa"]
440
+ },
441
+ {
442
+ lang: "zulu",
443
+ match: ["zul", "zu", "south africa"],
444
+ suggest: ["xhosa", "afrikaans"]
445
+ }
446
+ ];
447
+
448
+ // src/language-aliases/index.ts
449
+ var LANGUAGE_ALIAS_TABLE = [
450
+ ...EUROPEAN_LANGUAGES,
451
+ ...ASIAN_LANGUAGES,
452
+ ...AFRICAN_LANGUAGES
453
+ ];
454
+
455
+ // src/search-utils.ts
456
+ function applyLanguageAlias(term = "") {
457
+ const s = term.toLowerCase().trim();
458
+ if (!s) return { primary: term, suggestions: [] };
459
+ const isMatch = (alias, input) => {
460
+ if (input === alias) return true;
461
+ if (alias.length >= 3 && input.length >= alias.length && input.startsWith(alias)) return true;
462
+ if (input.length >= 2 && alias.startsWith(input)) return true;
463
+ return false;
464
+ };
465
+ const hits = /* @__PURE__ */ new Set();
466
+ const suggestions = /* @__PURE__ */ new Set();
467
+ for (const { lang, match, suggest } of LANGUAGE_ALIAS_TABLE) {
468
+ for (const m of match) {
469
+ if (isMatch(m, s)) {
470
+ hits.add(lang);
471
+ if (suggest) {
472
+ suggest.forEach((sug) => suggestions.add(sug));
473
+ }
474
+ }
475
+ }
476
+ }
477
+ let primary;
478
+ if (hits.size === 0) {
479
+ primary = term;
480
+ } else if (hits.size === 1) {
481
+ primary = Array.from(hits)[0];
482
+ } else {
483
+ primary = Array.from(hits);
484
+ }
485
+ return { primary, suggestions: Array.from(suggestions) };
486
+ }
487
+
488
+ // src/constants/colors.ts
489
+ var COLORS = {
490
+ // Primary colors
491
+ primary: {
492
+ blue: "#3b82f6",
493
+ blueLight: "#60a5fa",
494
+ blueDark: "#2563eb",
495
+ blueAlpha30: "#3b82f630",
496
+ blueAlpha50: "#3b82f650"
497
+ },
498
+ // Neutral colors
499
+ neutral: {
500
+ white: "#fff",
501
+ gray50: "#f9fafb",
502
+ gray100: "#f3f4f6",
503
+ gray200: "#e5e7eb",
504
+ gray300: "#d1d5db",
505
+ gray400: "#9ca3af",
506
+ gray500: "#6b7280",
507
+ gray600: "#4b5563",
508
+ gray700: "#374151",
509
+ gray800: "#1f2937",
510
+ gray900: "#111827"
511
+ },
512
+ // Border colors
513
+ border: {
514
+ light: "#d1d5db",
515
+ dark: "#4b5563",
516
+ flag: "#eee"
517
+ },
518
+ // Text colors
519
+ text: {
520
+ primary: "#1f2937",
521
+ secondary: "#6b7280",
522
+ placeholder: "#9ca3af",
523
+ inverse: "#f9fafb"
524
+ }
525
+ };
526
+ var THEME_COLORS = {
527
+ light: {
528
+ background: COLORS.neutral.white,
529
+ backgroundDisabled: COLORS.neutral.gray100,
530
+ border: COLORS.border.light,
531
+ placeholder: COLORS.text.placeholder,
532
+ ring: COLORS.primary.blueAlpha30,
533
+ optionPointed: COLORS.neutral.gray100,
534
+ optionPointedText: COLORS.text.primary,
535
+ optionSelected: COLORS.primary.blue,
536
+ optionSelectedText: COLORS.neutral.white,
537
+ dropdownBackground: COLORS.neutral.white,
538
+ dropdownBorder: COLORS.border.light,
539
+ dropdownText: COLORS.text.primary,
540
+ tagBackground: COLORS.primary.blue
541
+ },
542
+ dark: {
543
+ background: COLORS.neutral.gray800,
544
+ backgroundDisabled: COLORS.neutral.gray700,
545
+ border: COLORS.border.dark,
546
+ placeholder: COLORS.text.placeholder,
547
+ ring: COLORS.primary.blueAlpha50,
548
+ optionPointed: COLORS.neutral.gray700,
549
+ optionPointedText: COLORS.neutral.gray50,
550
+ optionSelected: COLORS.primary.blue,
551
+ optionSelectedText: COLORS.neutral.white,
552
+ dropdownBackground: COLORS.neutral.gray800,
553
+ dropdownBorder: COLORS.border.dark,
554
+ dropdownText: COLORS.neutral.gray50,
555
+ tagBackground: COLORS.primary.blue
556
+ }
557
+ };
558
+
559
+ // vue-script:/Users/nlit/projects/langie-sdk/src/components/LanguageSelect.vue?type=script
560
+ var _hoisted_1 = {
561
+ key: 0,
562
+ class: "multiselect-single-label"
563
+ };
564
+ var _hoisted_2 = ["src", "alt"];
565
+ var _hoisted_3 = { class: "language-text" };
566
+ var _hoisted_4 = { class: "native-name" };
567
+ var _hoisted_5 = ["data-lang-code"];
568
+ var _hoisted_6 = ["src", "alt"];
569
+ var _hoisted_7 = { class: "language-text" };
570
+ var _hoisted_8 = { class: "native-name" };
571
+ var _hoisted_9 = { class: "multiselect-no-options" };
572
+ var LanguageSelect_default = /* @__PURE__ */ _defineComponent({
573
+ __name: "LanguageSelect",
574
+ props: {
575
+ modelValue: {
576
+ type: Object,
577
+ default: void 0
578
+ },
579
+ languages: {
580
+ type: Array,
581
+ default: () => []
582
+ },
583
+ placeholder: {
584
+ type: String,
585
+ default: "Select language"
586
+ },
587
+ disabled: {
588
+ type: Boolean,
589
+ default: false
590
+ },
591
+ isDark: {
592
+ type: Boolean,
593
+ default: false
594
+ }
595
+ },
596
+ emits: ["update:modelValue"],
597
+ setup(__props, { emit: __emit }) {
598
+ _useCssVars((_ctx) => ({
599
+ "25bc3fde-THEME_COLORS.light.background": _unref(THEME_COLORS).light.background,
600
+ "25bc3fde-THEME_COLORS.light.backgroundDisabled": _unref(THEME_COLORS).light.backgroundDisabled,
601
+ "25bc3fde-THEME_COLORS.light.border": _unref(THEME_COLORS).light.border,
602
+ "25bc3fde-THEME_COLORS.light.ring": _unref(THEME_COLORS).light.ring,
603
+ "25bc3fde-THEME_COLORS.light.placeholder": _unref(THEME_COLORS).light.placeholder,
604
+ "25bc3fde-THEME_COLORS.light.optionPointed": _unref(THEME_COLORS).light.optionPointed,
605
+ "25bc3fde-THEME_COLORS.light.optionPointedText": _unref(THEME_COLORS).light.optionPointedText,
606
+ "25bc3fde-THEME_COLORS.light.optionSelected": _unref(THEME_COLORS).light.optionSelected,
607
+ "25bc3fde-THEME_COLORS.light.optionSelectedText": _unref(THEME_COLORS).light.optionSelectedText,
608
+ "25bc3fde-THEME_COLORS.light.dropdownBackground": _unref(THEME_COLORS).light.dropdownBackground,
609
+ "25bc3fde-THEME_COLORS.light.dropdownBorder": _unref(THEME_COLORS).light.dropdownBorder,
610
+ "25bc3fde-THEME_COLORS.light.dropdownText": _unref(THEME_COLORS).light.dropdownText,
611
+ "25bc3fde-THEME_COLORS.light.tagBackground": _unref(THEME_COLORS).light.tagBackground,
612
+ "25bc3fde-THEME_COLORS.dark.background": _unref(THEME_COLORS).dark.background,
613
+ "25bc3fde-THEME_COLORS.dark.backgroundDisabled": _unref(THEME_COLORS).dark.backgroundDisabled,
614
+ "25bc3fde-THEME_COLORS.dark.border": _unref(THEME_COLORS).dark.border,
615
+ "25bc3fde-THEME_COLORS.dark.placeholder": _unref(THEME_COLORS).dark.placeholder,
616
+ "25bc3fde-THEME_COLORS.dark.ring": _unref(THEME_COLORS).dark.ring,
617
+ "25bc3fde-THEME_COLORS.dark.optionPointed": _unref(THEME_COLORS).dark.optionPointed,
618
+ "25bc3fde-THEME_COLORS.dark.optionPointedText": _unref(THEME_COLORS).dark.optionPointedText,
619
+ "25bc3fde-THEME_COLORS.dark.optionSelected": _unref(THEME_COLORS).dark.optionSelected,
620
+ "25bc3fde-THEME_COLORS.dark.optionSelectedText": _unref(THEME_COLORS).dark.optionSelectedText,
621
+ "25bc3fde-THEME_COLORS.dark.dropdownBackground": _unref(THEME_COLORS).dark.dropdownBackground,
622
+ "25bc3fde-THEME_COLORS.dark.dropdownBorder": _unref(THEME_COLORS).dark.dropdownBorder,
623
+ "25bc3fde-THEME_COLORS.dark.dropdownText": _unref(THEME_COLORS).dark.dropdownText,
624
+ "25bc3fde-COLORS.border.flag": _unref(COLORS).border.flag,
625
+ "25bc3fde-COLORS.border.dark": _unref(COLORS).border.dark,
626
+ "25bc3fde-COLORS.text.secondary": _unref(COLORS).text.secondary,
627
+ "25bc3fde-COLORS.neutral.gray400": _unref(COLORS).neutral.gray400
628
+ }));
629
+ const getFlagCode = (lang) => {
630
+ const flagCode = lang.flag_country || lang.code;
631
+ return flagCode;
632
+ };
633
+ const getFlagImageUrl = (lang) => {
634
+ const flagCode = getFlagCode(lang);
635
+ return `/flags/${flagCode}.svg`;
636
+ };
637
+ const props = __props;
638
+ const emit = __emit;
639
+ const isLoading = computed(() => {
640
+ return props.languages.length <= 0;
641
+ });
642
+ const searchQuery = ref("");
643
+ const validLanguages = computed(() => {
644
+ return props.languages.filter((lang) => lang && lang.code && lang.name && lang.native_name);
645
+ });
646
+ const fuse = computed(() => {
647
+ if (!validLanguages.value.length) return null;
648
+ return new Fuse(validLanguages.value, {
649
+ keys: ["name", "native_name", "code"],
650
+ includeScore: true,
651
+ threshold: 0.6,
652
+ // Use highest threshold to catch all cases
653
+ isCaseSensitive: false,
654
+ minMatchCharLength: 1
655
+ // Allow single character matches
656
+ });
657
+ });
658
+ const multiselectKey = computed(() => {
659
+ const hasApiData = props.languages.length > 0 && props.languages[0].flag_country !== null;
660
+ return hasApiData ? "api-data" : "fallback-data";
661
+ });
662
+ const selectedLanguageKey = computed(() => {
663
+ if (!props.modelValue || !props.modelValue.code) return "no-selection";
664
+ const key = `${props.modelValue.code}-${props.modelValue.flag_country || "fallback"}`;
665
+ return key;
666
+ });
667
+ const selectedLanguage = computed({
668
+ get: () => props.modelValue || null,
669
+ set: (value) => {
670
+ if (value && value.code) {
671
+ emit("update:modelValue", value);
672
+ }
673
+ }
674
+ });
675
+ const filteredLanguages = computed(() => {
676
+ const query = searchQuery.value.trim();
677
+ let results;
678
+ if (!query) {
679
+ results = validLanguages.value;
680
+ } else {
681
+ if (!fuse.value) return [];
682
+ const aliasResult = applyLanguageAlias(query);
683
+ const searchTerm = Array.isArray(aliasResult.primary) ? aliasResult.primary[0] : aliasResult.primary;
684
+ const fuseResults = fuse.value.search(searchTerm);
685
+ results = fuseResults.map((result) => result.item);
686
+ if (searchTerm !== query) {
687
+ const originalResults = fuse.value.search(query);
688
+ originalResults.forEach((result) => {
689
+ if (!results.some((r) => r.code === result.item.code)) {
690
+ results.push(result.item);
691
+ }
692
+ });
693
+ }
694
+ if (aliasResult.suggestions.length > 0) {
695
+ aliasResult.suggestions.forEach((suggestion) => {
696
+ const suggestedLang = validLanguages.value.find((lang) => {
697
+ const langName = lang.name.toLowerCase();
698
+ const suggestion_lower = suggestion.toLowerCase();
699
+ return langName === suggestion_lower || // Exact match
700
+ langName.includes(suggestion_lower) || // Contains match
701
+ lang.code.toLowerCase() === suggestion_lower || // Code match
702
+ langName.startsWith(suggestion_lower) || // Starts with match
703
+ // Handle common variations
704
+ suggestion_lower === "tatar" && langName.includes("tatar") || suggestion_lower === "belarusian" && (langName.includes("belarus") || langName.includes("belarusian")) || suggestion_lower === "moldovan" && (langName.includes("moldov") || langName.includes("moldova"));
705
+ });
706
+ if (suggestedLang && !results.some((r) => r.code === suggestedLang.code)) {
707
+ results.push(suggestedLang);
708
+ }
709
+ });
710
+ }
711
+ if (query.length === 2) {
712
+ const lowerQuery = query.toLowerCase();
713
+ const manualResults = validLanguages.value.filter(
714
+ (lang) => lang.name.toLowerCase().startsWith(lowerQuery) || lang.native_name.toLowerCase().startsWith(lowerQuery)
715
+ );
716
+ manualResults.forEach((lang) => {
717
+ if (!results.some((r) => r.code === lang.code)) {
718
+ results.push(lang);
719
+ }
720
+ });
721
+ }
722
+ }
723
+ const filtered = results.filter(
724
+ (lang) => !props.modelValue || !props.modelValue.code || lang.code !== props.modelValue.code
725
+ );
726
+ return filtered;
727
+ });
728
+ function handleSearch(query) {
729
+ searchQuery.value = query;
730
+ if (query.trim()) {
731
+ nextTick(() => {
732
+ const dropdown = document.querySelector(".multiselect-dropdown");
733
+ const firstOption = dropdown?.querySelector(".multiselect-option");
734
+ if (firstOption) {
735
+ firstOption.scrollIntoView({ behavior: "smooth", block: "nearest" });
736
+ firstOption.classList.add("multiselect-option-is-pointed");
737
+ }
738
+ });
739
+ }
740
+ }
741
+ function handleKeydown(event) {
742
+ const keyboardEvent = event;
743
+ if (keyboardEvent.key === "Tab" || keyboardEvent.key === "Enter") {
744
+ const dropdown = document.querySelector(".multiselect-dropdown");
745
+ const firstOption = dropdown?.querySelector(".multiselect-option");
746
+ if (firstOption && searchQuery.value.trim()) {
747
+ const langCode = firstOption.getAttribute("data-lang-code");
748
+ if (langCode) {
749
+ const language = validLanguages.value.find((lang) => lang.code === langCode);
750
+ if (language) {
751
+ selectedLanguage.value = language;
752
+ searchQuery.value = "";
753
+ keyboardEvent.preventDefault();
754
+ }
755
+ }
756
+ }
757
+ }
758
+ }
759
+ const onFlagError = (event) => {
760
+ const target = event.target;
761
+ const currentSrc = target.src;
762
+ if (currentSrc && currentSrc.includes("/flags/")) {
763
+ const match = currentSrc.match(/\/flags\/([a-z]{2})\.svg/);
764
+ if (match) {
765
+ const countryCode = match[1];
766
+ target.src = `https://flagcdn.com/${countryCode}.svg`;
767
+ target.onerror = () => {
768
+ target.style.display = "none";
769
+ };
770
+ return;
771
+ }
772
+ }
773
+ target.style.display = "none";
774
+ };
775
+ return (_ctx, _cache) => {
776
+ return _openBlock(), _createElementBlock(
777
+ "div",
778
+ {
779
+ class: _normalizeClass(["language-select", { "is-dark": __props.isDark }])
780
+ },
781
+ [
782
+ _createCommentVNode(" Show multiselect when we have languages OR when not loading "),
783
+ validLanguages.value.length > 0 || !isLoading.value ? (_openBlock(), _createBlock(_unref(Multiselect), {
784
+ key: multiselectKey.value,
785
+ modelValue: selectedLanguage.value,
786
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => selectedLanguage.value = $event),
787
+ options: filteredLanguages.value,
788
+ searchable: true,
789
+ onSearchChange: handleSearch,
790
+ "can-clear": false,
791
+ "allow-empty": false,
792
+ object: true,
793
+ placeholder: validLanguages.value.length === 0 ? "No languages available" : __props.placeholder,
794
+ disabled: props.disabled || validLanguages.value.length === 0,
795
+ loading: isLoading.value,
796
+ "track-by": "name",
797
+ label: "name",
798
+ "value-prop": "code",
799
+ "filter-results": false,
800
+ onKeydown: handleKeydown
801
+ }, {
802
+ singlelabel: _withCtx(({ value }) => [
803
+ value ? (_openBlock(), _createElementBlock("div", _hoisted_1, [
804
+ value.code ? (_openBlock(), _createElementBlock("img", {
805
+ key: selectedLanguageKey.value,
806
+ src: getFlagImageUrl(value),
807
+ class: "lang-flag",
808
+ alt: `${value.native_name || value.name} flag`,
809
+ onError: onFlagError
810
+ }, null, 40, _hoisted_2)) : _createCommentVNode("v-if", true),
811
+ _createElementVNode("span", _hoisted_3, [
812
+ _createTextVNode(
813
+ _toDisplayString(value.name) + " ",
814
+ 1
815
+ /* TEXT */
816
+ ),
817
+ _createElementVNode(
818
+ "span",
819
+ _hoisted_4,
820
+ "(" + _toDisplayString(value.native_name || value.name) + ")",
821
+ 1
822
+ /* TEXT */
823
+ )
824
+ ])
825
+ ])) : _createCommentVNode("v-if", true)
826
+ ]),
827
+ option: _withCtx(({ option }) => [
828
+ _createElementVNode("div", {
829
+ class: "multiselect-option",
830
+ "data-lang-code": option.code
831
+ }, [
832
+ (_openBlock(), _createElementBlock("img", {
833
+ key: `${option.code}-${option.flag_country || "fallback"}`,
834
+ src: getFlagImageUrl(option),
835
+ class: "lang-flag",
836
+ alt: `${option.name} flag`,
837
+ onError: onFlagError
838
+ }, null, 40, _hoisted_6)),
839
+ _createElementVNode("span", _hoisted_7, [
840
+ _createTextVNode(
841
+ _toDisplayString(option.name) + " ",
842
+ 1
843
+ /* TEXT */
844
+ ),
845
+ _createElementVNode(
846
+ "span",
847
+ _hoisted_8,
848
+ "(" + _toDisplayString(option.native_name || option.name) + ")",
849
+ 1
850
+ /* TEXT */
851
+ )
852
+ ])
853
+ ], 8, _hoisted_5)
854
+ ]),
855
+ noresults: _withCtx(() => _cache[1] || (_cache[1] = [
856
+ _createElementVNode(
857
+ "div",
858
+ { class: "multiselect-no-results" },
859
+ "No languages found.",
860
+ -1
861
+ /* CACHED */
862
+ )
863
+ ])),
864
+ nooptions: _withCtx(() => [
865
+ _createElementVNode(
866
+ "div",
867
+ _hoisted_9,
868
+ _toDisplayString(validLanguages.value.length === 0 ? "Please provide languages via the :languages prop" : "No languages available."),
869
+ 1
870
+ /* TEXT */
871
+ )
872
+ ]),
873
+ _: 1
874
+ /* STABLE */
875
+ }, 8, ["modelValue", "options", "placeholder", "disabled", "loading"])) : isLoading.value && validLanguages.value.length === 0 ? (_openBlock(), _createElementBlock(
876
+ _Fragment,
877
+ { key: 1 },
878
+ [
879
+ _createCommentVNode(" Only show skeleton loader when actually loading AND we have no languages yet "),
880
+ _cache[2] || (_cache[2] = _createElementVNode(
881
+ "div",
882
+ { class: "skeleton-loader" },
883
+ null,
884
+ -1
885
+ /* CACHED */
886
+ ))
887
+ ],
888
+ 2112
889
+ /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */
890
+ )) : _createCommentVNode("v-if", true)
891
+ ],
892
+ 2
893
+ /* CLASS */
894
+ );
895
+ };
896
+ }
897
+ });
898
+
899
+ // src/components/LanguageSelect.vue
900
+ var LanguageSelect_default2 = LanguageSelect_default;
901
+ LanguageSelect_default.__scopeId = "data-v-25bc3fde";
902
+
903
+ // vue-script:/Users/nlit/projects/langie-sdk/src/components/InterfaceLanguageSelect.vue?type=script
904
+ import { defineComponent as _defineComponent2 } from "vue";
905
+ 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";
906
+ import { computed as computed2, watch as watch2, onMounted, ref as ref3 } from "vue";
907
+
908
+ // src/useLangie.ts
909
+ import { watch } from "vue";
910
+
911
+ // src/composables/useLangie-core.ts
912
+ import { ref as ref2, reactive } from "vue";
913
+
914
+ // src/constants.ts
915
+ var DEFAULT_API_HOST = "https://api.langie.uk/v1";
916
+ var API_FIELD_TEXT = "t";
917
+ var API_FIELD_FROM = "from";
918
+ var API_FIELD_TO = "to";
919
+ var API_FIELD_CTX = "ctx";
920
+ var API_FIELD_TRANSLATIONS = "translations";
921
+ var API_FIELD_ERROR = "error";
922
+
923
+ // src/utils/debug.ts
924
+ function devDebug(...args) {
925
+ if (import.meta.env && import.meta.env.DEV) {
926
+ console.debug(...args);
927
+ }
928
+ }
929
+
930
+ // src/utils/getCountryCode.ts
931
+ import * as ct from "countries-and-timezones";
932
+ async function getCountryCode() {
933
+ const localeCountry = getCountryCodeFromBrowser();
934
+ if (localeCountry) return localeCountry;
935
+ const timezoneCountry = getCountryFromTimezone();
936
+ if (timezoneCountry) return timezoneCountry;
937
+ return await getCountryFromIP();
938
+ }
939
+ function getCountryFromTimezone() {
940
+ try {
941
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
942
+ return getCountryFromTimezoneString(timezone);
943
+ } catch (error) {
944
+ return null;
945
+ }
946
+ }
947
+ function getCountryFromTimezoneString(timezone) {
948
+ try {
949
+ const country = ct.getCountryForTimezone(timezone);
950
+ return country?.id || null;
951
+ } catch (error) {
952
+ return null;
953
+ }
954
+ }
955
+ async function getCountryFromIP() {
956
+ try {
957
+ const response = await fetch("https://ipapi.co/json/");
958
+ const data = await response.json();
959
+ return data.country_code || null;
960
+ } catch (error) {
961
+ return null;
962
+ }
963
+ }
964
+ function getCountryCodeFromBrowser() {
965
+ const langs = navigator.languages || [navigator.language];
966
+ for (const locale of langs) {
967
+ const parts = locale.split("-");
968
+ if (parts.length > 1) {
969
+ return parts[1].toUpperCase();
970
+ }
971
+ }
972
+ return null;
973
+ }
974
+
975
+ // src/utils/cache.ts
976
+ var DEFAULT_TTL = 7 * 24 * 60 * 60 * 1e3;
977
+ var DEFAULT_MAX_SIZE = 2 * 1024 * 1024;
978
+ var DEFAULT_MAX_ITEMS = 1e3;
979
+ var CacheManager = class {
980
+ options;
981
+ constructor(options = {}) {
982
+ this.options = {
983
+ ttl: options.ttl || DEFAULT_TTL,
984
+ maxSize: options.maxSize || DEFAULT_MAX_SIZE,
985
+ maxItems: options.maxItems || DEFAULT_MAX_ITEMS
986
+ };
987
+ }
988
+ set(key, data, ttl) {
989
+ if (typeof window === "undefined") return false;
990
+ try {
991
+ const item = {
992
+ data,
993
+ timestamp: Date.now(),
994
+ ttl: ttl || this.options.ttl
995
+ };
996
+ const serialized = JSON.stringify(item);
997
+ const size = new Blob([serialized]).size;
998
+ if (size > this.options.maxSize) {
999
+ console.warn(`[CacheManager] Item size ${size} exceeds max size ${this.options.maxSize}`);
1000
+ return false;
1001
+ }
1002
+ this.cleanup();
1003
+ const currentSize = this.getCurrentSize();
1004
+ if (currentSize + size > this.options.maxSize) {
1005
+ console.warn(`[CacheManager] Cache would exceed max size after adding item`);
1006
+ return false;
1007
+ }
1008
+ localStorage.setItem(key, serialized);
1009
+ return true;
1010
+ } catch (error) {
1011
+ console.warn(`[CacheManager] Failed to set cache item ${key}:`, error);
1012
+ return false;
1013
+ }
1014
+ }
1015
+ get(key) {
1016
+ if (typeof window === "undefined") return null;
1017
+ try {
1018
+ const item = localStorage.getItem(key);
1019
+ if (!item) return null;
1020
+ const cacheItem = JSON.parse(item);
1021
+ const now = Date.now();
1022
+ if (now - cacheItem.timestamp > cacheItem.ttl) {
1023
+ localStorage.removeItem(key);
1024
+ return null;
1025
+ }
1026
+ return cacheItem.data;
1027
+ } catch (error) {
1028
+ console.warn(`[CacheManager] Failed to get cache item ${key}:`, error);
1029
+ localStorage.removeItem(key);
1030
+ return null;
1031
+ }
1032
+ }
1033
+ remove(key) {
1034
+ if (typeof window === "undefined") return false;
1035
+ try {
1036
+ localStorage.removeItem(key);
1037
+ return true;
1038
+ } catch (error) {
1039
+ console.warn(`[CacheManager] Failed to remove cache item ${key}:`, error);
1040
+ return false;
1041
+ }
1042
+ }
1043
+ clear(pattern) {
1044
+ if (typeof window === "undefined") return;
1045
+ try {
1046
+ if (pattern) {
1047
+ Object.keys(localStorage).forEach((key) => {
1048
+ if (key.includes(pattern)) {
1049
+ localStorage.removeItem(key);
1050
+ }
1051
+ });
1052
+ } else {
1053
+ Object.keys(localStorage).forEach((key) => {
1054
+ if (key.startsWith("langie_")) {
1055
+ localStorage.removeItem(key);
1056
+ }
1057
+ });
1058
+ }
1059
+ } catch (error) {
1060
+ console.warn("[CacheManager] Failed to clear cache:", error);
1061
+ }
1062
+ }
1063
+ cleanup() {
1064
+ if (typeof window === "undefined") return;
1065
+ try {
1066
+ const now = Date.now();
1067
+ const items = [];
1068
+ Object.keys(localStorage).forEach((key) => {
1069
+ if (key.startsWith("langie_")) {
1070
+ try {
1071
+ const item = localStorage.getItem(key);
1072
+ if (item) {
1073
+ const cacheItem = JSON.parse(item);
1074
+ const size = new Blob([item]).size;
1075
+ if (now - cacheItem.timestamp > cacheItem.ttl) {
1076
+ localStorage.removeItem(key);
1077
+ return;
1078
+ }
1079
+ items.push({
1080
+ key,
1081
+ size,
1082
+ timestamp: cacheItem.timestamp
1083
+ });
1084
+ }
1085
+ } catch (error) {
1086
+ localStorage.removeItem(key);
1087
+ }
1088
+ }
1089
+ });
1090
+ items.sort((a, b) => a.timestamp - b.timestamp);
1091
+ while (items.length > this.options.maxItems) {
1092
+ const oldest = items.shift();
1093
+ if (oldest) {
1094
+ localStorage.removeItem(oldest.key);
1095
+ }
1096
+ }
1097
+ let totalSize = items.reduce((sum, item) => sum + item.size, 0);
1098
+ while (totalSize > this.options.maxSize && items.length > 0) {
1099
+ const oldest = items.shift();
1100
+ if (oldest) {
1101
+ localStorage.removeItem(oldest.key);
1102
+ totalSize -= oldest.size;
1103
+ }
1104
+ }
1105
+ } catch (error) {
1106
+ console.warn("[CacheManager] Failed to cleanup cache:", error);
1107
+ }
1108
+ }
1109
+ getCurrentSize() {
1110
+ if (typeof window === "undefined") return 0;
1111
+ try {
1112
+ let totalSize = 0;
1113
+ Object.keys(localStorage).forEach((key) => {
1114
+ if (key.startsWith("langie_")) {
1115
+ const item = localStorage.getItem(key);
1116
+ if (item) {
1117
+ totalSize += new Blob([item]).size;
1118
+ }
1119
+ }
1120
+ });
1121
+ return totalSize;
1122
+ } catch (error) {
1123
+ console.warn("[CacheManager] Failed to calculate cache size:", error);
1124
+ return 0;
1125
+ }
1126
+ }
1127
+ getStats() {
1128
+ if (typeof window === "undefined") {
1129
+ return { size: 0, items: 0, maxSize: this.options.maxSize, maxItems: this.options.maxItems };
1130
+ }
1131
+ try {
1132
+ let totalSize = 0;
1133
+ let itemCount = 0;
1134
+ Object.keys(localStorage).forEach((key) => {
1135
+ if (key.startsWith("langie_")) {
1136
+ const item = localStorage.getItem(key);
1137
+ if (item) {
1138
+ totalSize += new Blob([item]).size;
1139
+ itemCount++;
1140
+ }
1141
+ }
1142
+ });
1143
+ return {
1144
+ size: totalSize,
1145
+ items: itemCount,
1146
+ maxSize: this.options.maxSize,
1147
+ maxItems: this.options.maxItems
1148
+ };
1149
+ } catch (error) {
1150
+ console.warn("[CacheManager] Failed to get cache stats:", error);
1151
+ return { size: 0, items: 0, maxSize: this.options.maxSize, maxItems: this.options.maxItems };
1152
+ }
1153
+ }
1154
+ };
1155
+ var cacheManager = new CacheManager();
1156
+ var setCache = (key, data, ttl) => {
1157
+ return cacheManager.set(key, data, ttl);
1158
+ };
1159
+ var getCache = (key) => {
1160
+ return cacheManager.get(key);
1161
+ };
1162
+ var clearCache = (pattern) => {
1163
+ return cacheManager.clear(pattern);
1164
+ };
1165
+
1166
+ // src/composables/useLangie-core.ts
1167
+ var availableLanguages = ref2([]);
1168
+ var translations = reactive({});
1169
+ var uiTranslations = reactive({});
1170
+ var currentLanguage = ref2("en");
1171
+ var _translatorHost = DEFAULT_API_HOST;
1172
+ var _autoSelected = false;
1173
+ var _languagesCache = null;
1174
+ var _languagesPromise = null;
1175
+ function useLangieCore(options = {}) {
1176
+ if (options.translatorHost) {
1177
+ _translatorHost = options.translatorHost;
1178
+ }
1179
+ const translatorHost = _translatorHost;
1180
+ const defaultLanguage = options.defaultLanguage || "en";
1181
+ const isLoading = ref2(false);
1182
+ if (defaultLanguage !== "en" && currentLanguage.value === "en") {
1183
+ currentLanguage.value = defaultLanguage;
1184
+ }
1185
+ if (typeof window !== "undefined" && currentLanguage.value === "en") {
1186
+ const savedLanguage = localStorage.getItem("interface_language");
1187
+ if (savedLanguage) {
1188
+ currentLanguage.value = savedLanguage;
1189
+ }
1190
+ }
1191
+ if (typeof window !== "undefined") {
1192
+ const cachedLanguages = getCache("langie_languages_cache");
1193
+ if (cachedLanguages && availableLanguages.value.length === 0) {
1194
+ availableLanguages.value = cachedLanguages;
1195
+ _languagesCache = cachedLanguages;
1196
+ devDebug("[useLangie] Loaded languages from cache:", cachedLanguages.length);
1197
+ } else if (cachedLanguages) {
1198
+ devDebug(
1199
+ "[useLangie] Cache exists but languages already loaded:",
1200
+ availableLanguages.value.length
1201
+ );
1202
+ } else {
1203
+ devDebug("[useLangie] No cached languages found");
1204
+ }
1205
+ }
1206
+ const setLanguage = (lang) => {
1207
+ if (lang && lang !== currentLanguage.value) {
1208
+ }
1209
+ currentLanguage.value = lang;
1210
+ if (typeof window !== "undefined") {
1211
+ localStorage.setItem("interface_language", lang);
1212
+ }
1213
+ };
1214
+ const fetchLanguages = async (opts = {}) => {
1215
+ const { force = false, country: explicitCountry } = opts;
1216
+ devDebug("[useLangie] fetchLanguages called:", {
1217
+ force,
1218
+ hasCache: !!_languagesCache,
1219
+ hasPromise: !!_languagesPromise,
1220
+ availableLanguagesLength: availableLanguages.value.length
1221
+ });
1222
+ if (!force) {
1223
+ if (_languagesCache) {
1224
+ devDebug("[useLangie] Returning cached languages:", _languagesCache.length);
1225
+ return _languagesCache;
1226
+ }
1227
+ if (_languagesPromise) {
1228
+ devDebug("[useLangie] Returning existing promise");
1229
+ return _languagesPromise;
1230
+ }
1231
+ }
1232
+ try {
1233
+ let countryHint = explicitCountry || null;
1234
+ if (!countryHint && !_languagesCache && availableLanguages.value && availableLanguages.value.length) {
1235
+ const currentLang = currentLanguage.value;
1236
+ const entry = availableLanguages.value.find(
1237
+ (l) => l.code === currentLang
1238
+ );
1239
+ if (entry) {
1240
+ const c = Array.isArray(entry.flag_country) ? entry.flag_country[0] : typeof entry.flag_country === "string" ? entry.flag_country.split(",")[0] : null;
1241
+ if (c) countryHint = c.toUpperCase();
1242
+ }
1243
+ }
1244
+ if (!countryHint && !_languagesCache && typeof window !== "undefined") {
1245
+ const cc = await getCountryCode();
1246
+ countryHint = cc;
1247
+ }
1248
+ let url = "/languages";
1249
+ const language = typeof navigator !== "undefined" && navigator.languages && navigator.languages[0] || typeof navigator !== "undefined" && navigator.language || "";
1250
+ const timezone = typeof Intl !== "undefined" && Intl.DateTimeFormat().resolvedOptions().timeZone || "";
1251
+ const params = new URLSearchParams();
1252
+ if (countryHint) params.append("country", countryHint);
1253
+ if (language) params.append("language", language);
1254
+ if (timezone) params.append("timezone", timezone);
1255
+ if (Array.from(params).length > 0) url += `?${params.toString()}`;
1256
+ _languagesPromise = fetch(`${translatorHost}${url}`).then((res) => res.json());
1257
+ const response = await _languagesPromise;
1258
+ const rawList = Array.isArray(response) ? response : response.languages || [];
1259
+ const mapped = rawList.map(
1260
+ (lang) => {
1261
+ let flag = lang.flag_country || lang.code;
1262
+ if (Array.isArray(flag)) {
1263
+ flag = flag[0];
1264
+ }
1265
+ return {
1266
+ ...lang,
1267
+ value: lang.code,
1268
+ flag_country: flag
1269
+ };
1270
+ }
1271
+ );
1272
+ const filtered = mapped.filter((l) => {
1273
+ if (l.code.startsWith("sr")) {
1274
+ return l.code === "sr-latn" || l.code === "sr-cyrl";
1275
+ }
1276
+ return true;
1277
+ });
1278
+ availableLanguages.value = filtered;
1279
+ devDebug("[useLangie] Set availableLanguages:", filtered.length);
1280
+ if (typeof window !== "undefined") {
1281
+ const saved = setCache("langie_languages_cache", filtered, 7 * 24 * 60 * 60 * 1e3);
1282
+ devDebug(
1283
+ "[useLangie] Saved languages to cache:",
1284
+ saved ? "success" : "failed",
1285
+ filtered.length
1286
+ );
1287
+ }
1288
+ if (!_autoSelected && !localStorage.getItem("interface_language")) {
1289
+ const locale = typeof navigator !== "undefined" ? navigator.languages?.[0] || navigator.language || "" : "";
1290
+ const browserCode = locale.split("-")[0];
1291
+ if (browserCode) {
1292
+ let pick = void 0;
1293
+ if (browserCode === "sr") {
1294
+ const isLatin = /latn/i.test(locale) || locale === "sr";
1295
+ const target = isLatin ? "sr-latn" : "sr-cyrl";
1296
+ pick = mapped.find((l) => l.value === target);
1297
+ } else if (browserCode === "sh") {
1298
+ pick = mapped.find((l) => l.value === "sr-latn") || mapped.find((l) => l.code.startsWith("sr"));
1299
+ } else {
1300
+ pick = mapped.find((l) => l.value === browserCode);
1301
+ if (!pick) {
1302
+ pick = mapped.find((l) => l.code.startsWith(browserCode));
1303
+ }
1304
+ }
1305
+ if (pick && pick.value) {
1306
+ setLanguage(pick.value);
1307
+ }
1308
+ }
1309
+ _autoSelected = true;
1310
+ }
1311
+ _languagesCache = filtered;
1312
+ _languagesPromise = null;
1313
+ return filtered;
1314
+ } catch (error) {
1315
+ devDebug("[useLangie] Language fetch error:", { error });
1316
+ return [];
1317
+ }
1318
+ };
1319
+ const clearTranslations = () => {
1320
+ Object.keys(translations).forEach((key) => delete translations[key]);
1321
+ Object.keys(uiTranslations).forEach((key) => delete uiTranslations[key]);
1322
+ if (typeof window !== "undefined") {
1323
+ clearCache("translations_cache");
1324
+ clearCache("ui_translations_cache");
1325
+ }
1326
+ };
1327
+ return {
1328
+ availableLanguages,
1329
+ translations,
1330
+ uiTranslations,
1331
+ currentLanguage,
1332
+ isLoading,
1333
+ setLanguage,
1334
+ fetchLanguages,
1335
+ clearTranslations,
1336
+ translatorHost
1337
+ };
1338
+ }
1339
+
1340
+ // src/composables/useLangie-batching.ts
1341
+ var TranslationBatching = class {
1342
+ constructor(options = {}, translatorHost, currentLanguage2, onBatchComplete) {
1343
+ this.options = options;
1344
+ this.translatorHost = translatorHost;
1345
+ this.currentLanguage = currentLanguage2;
1346
+ this.onBatchComplete = onBatchComplete;
1347
+ }
1348
+ pendingRequests = /* @__PURE__ */ new Set();
1349
+ queueMap = /* @__PURE__ */ new Map();
1350
+ flushTimeout = null;
1351
+ queuedThisTick = /* @__PURE__ */ new Set();
1352
+ clearQueuedThisTickScheduled = false;
1353
+ firstItemTime = null;
1354
+ get initialBatchDelay() {
1355
+ return this.options.initialBatchDelay ?? 50;
1356
+ }
1357
+ get followupBatchDelay() {
1358
+ return this.options.followupBatchDelay ?? 10;
1359
+ }
1360
+ get maxBatchSize() {
1361
+ return this.options.maxBatchSize ?? 50;
1362
+ }
1363
+ get maxWaitTime() {
1364
+ return this.options.maxWaitTime ?? 1e3;
1365
+ }
1366
+ scheduleClearQueuedThisTick() {
1367
+ if (this.clearQueuedThisTickScheduled) return;
1368
+ this.clearQueuedThisTickScheduled = true;
1369
+ queueMicrotask(() => {
1370
+ this.queuedThisTick.clear();
1371
+ this.clearQueuedThisTickScheduled = false;
1372
+ });
1373
+ }
1374
+ flushQueues = async () => {
1375
+ const allRequests = [];
1376
+ for (const [batchKey, map] of Array.from(this.queueMap.entries())) {
1377
+ if (!map || map.size === 0) {
1378
+ this.queueMap.delete(batchKey);
1379
+ continue;
1380
+ }
1381
+ const [from, to] = batchKey.split("|");
1382
+ for (const [cacheKey, item] of map.entries()) {
1383
+ allRequests.push({
1384
+ [API_FIELD_TEXT]: item[API_FIELD_TEXT],
1385
+ [API_FIELD_CTX]: item[API_FIELD_CTX],
1386
+ [API_FIELD_FROM]: from,
1387
+ [API_FIELD_TO]: to,
1388
+ cacheKey,
1389
+ __explicitToLang: item.__explicitToLang
1390
+ });
1391
+ }
1392
+ this.queueMap.delete(batchKey);
1393
+ }
1394
+ if (allRequests.length > 0) {
1395
+ devDebug(
1396
+ "[TranslationBatching] Sending batch:",
1397
+ allRequests.length,
1398
+ "translation items",
1399
+ allRequests
1400
+ );
1401
+ const chunks = this.chunkArray(allRequests, this.maxBatchSize);
1402
+ for (const chunk of chunks) {
1403
+ try {
1404
+ await this.fetchAndCacheBatchMixed(chunk);
1405
+ } catch (error) {
1406
+ devDebug("[TranslationBatching] Batch translation error:", error);
1407
+ chunk.forEach((req) => this.pendingRequests.delete(req.cacheKey));
1408
+ }
1409
+ }
1410
+ }
1411
+ this.firstItemTime = null;
1412
+ };
1413
+ chunkArray(array, size) {
1414
+ const chunks = [];
1415
+ for (let i = 0; i < array.length; i += size) {
1416
+ chunks.push(array.slice(i, i + size));
1417
+ }
1418
+ return chunks;
1419
+ }
1420
+ scheduleFlush = () => {
1421
+ if (this.flushTimeout) {
1422
+ clearTimeout(this.flushTimeout);
1423
+ }
1424
+ let totalItems = 0;
1425
+ for (const map of this.queueMap.values()) {
1426
+ totalItems += map.size;
1427
+ }
1428
+ if (this.firstItemTime && Date.now() - this.firstItemTime >= this.maxWaitTime) {
1429
+ devDebug("[TranslationBatching] Flushing due to maximum wait time:", totalItems, "items");
1430
+ this.flushQueues();
1431
+ return;
1432
+ }
1433
+ const delay = this.queueMap.size === 1 ? this.initialBatchDelay : this.followupBatchDelay;
1434
+ devDebug(
1435
+ "[TranslationBatching] Scheduling flush in",
1436
+ delay,
1437
+ "ms",
1438
+ Array.from(this.queueMap.entries())
1439
+ );
1440
+ this.flushTimeout = setTimeout(() => {
1441
+ this.flushQueues();
1442
+ this.flushTimeout = null;
1443
+ }, delay);
1444
+ };
1445
+ queueTranslation(text, ctx, from, to, cacheKey, explicitToLang) {
1446
+ if (this.pendingRequests.has(cacheKey) || this.queuedThisTick.has(cacheKey)) {
1447
+ devDebug("[TranslationBatching] Skipping duplicate:", cacheKey);
1448
+ return;
1449
+ }
1450
+ if (this.firstItemTime === null) {
1451
+ this.firstItemTime = Date.now();
1452
+ }
1453
+ this.queuedThisTick.add(cacheKey);
1454
+ this.scheduleClearQueuedThisTick();
1455
+ this.pendingRequests.add(cacheKey);
1456
+ const batchKey = `${from}|${to}`;
1457
+ if (!this.queueMap.has(batchKey)) {
1458
+ this.queueMap.set(batchKey, /* @__PURE__ */ new Map());
1459
+ }
1460
+ this.queueMap.get(batchKey).set(cacheKey, {
1461
+ [API_FIELD_TEXT]: text,
1462
+ [API_FIELD_CTX]: ctx,
1463
+ __explicitToLang: explicitToLang
1464
+ });
1465
+ devDebug("[TranslationBatching] Queued translation:", {
1466
+ text,
1467
+ ctx,
1468
+ from,
1469
+ to,
1470
+ cacheKey,
1471
+ batchKey,
1472
+ explicitToLang
1473
+ });
1474
+ this.scheduleFlush();
1475
+ }
1476
+ async fetchAndCacheBatchMixed(requests) {
1477
+ const grouped = {};
1478
+ requests.forEach((req) => {
1479
+ const key = `${req.from}|${req.to}`;
1480
+ if (!grouped[key]) grouped[key] = [];
1481
+ grouped[key].push(req);
1482
+ });
1483
+ const allResults = [];
1484
+ const allRequests = [];
1485
+ for (const [langPair, batchRequests] of Object.entries(grouped)) {
1486
+ const [from, to] = langPair.split("|");
1487
+ try {
1488
+ const contexts = [...new Set(batchRequests.map((req) => req[API_FIELD_CTX]))];
1489
+ const useGlobalContext = contexts.length === 1 && contexts[0] === "ui";
1490
+ devDebug(
1491
+ "[TranslationBatching] Sending batch for",
1492
+ langPair,
1493
+ "with",
1494
+ batchRequests.length,
1495
+ "items",
1496
+ batchRequests
1497
+ );
1498
+ const response = await fetch(`${this.translatorHost}/translate`, {
1499
+ method: "POST",
1500
+ headers: {
1501
+ "Content-Type": "application/json"
1502
+ },
1503
+ body: JSON.stringify({
1504
+ translations: batchRequests.map((req) => ({
1505
+ [API_FIELD_TEXT]: req[API_FIELD_TEXT],
1506
+ ...useGlobalContext ? {} : { [API_FIELD_CTX]: req[API_FIELD_CTX] }
1507
+ })),
1508
+ [API_FIELD_FROM]: from,
1509
+ [API_FIELD_TO]: to,
1510
+ ...useGlobalContext ? { [API_FIELD_CTX]: "ui" } : {}
1511
+ })
1512
+ });
1513
+ let result;
1514
+ try {
1515
+ result = await response.json();
1516
+ } catch (parseError) {
1517
+ devDebug("[TranslationBatching] Failed to parse response as JSON:", parseError);
1518
+ throw new Error(`Translation request failed: ${response.status}`);
1519
+ }
1520
+ if (!response.ok) {
1521
+ devDebug(
1522
+ "[TranslationBatching] Translation request failed:",
1523
+ response.status,
1524
+ response.statusText
1525
+ );
1526
+ if (result && result[API_FIELD_ERROR]) {
1527
+ devDebug(
1528
+ "[TranslationBatching] HTTP error with API error message:",
1529
+ result[API_FIELD_ERROR]
1530
+ );
1531
+ const errorResponses = batchRequests.map((req) => ({
1532
+ [API_FIELD_TEXT]: req[API_FIELD_TEXT],
1533
+ [API_FIELD_ERROR]: result[API_FIELD_ERROR]
1534
+ }));
1535
+ allResults.push({ [API_FIELD_TRANSLATIONS]: errorResponses });
1536
+ allRequests.push(...batchRequests);
1537
+ batchRequests.forEach((req) => {
1538
+ this.pendingRequests.delete(req.cacheKey);
1539
+ });
1540
+ this.onBatchComplete(allResults, allRequests);
1541
+ return;
1542
+ }
1543
+ throw new Error(`Translation request failed: ${response.status}`);
1544
+ }
1545
+ if (result[API_FIELD_ERROR]) {
1546
+ devDebug("[TranslationBatching] Top-level API error:", result[API_FIELD_ERROR]);
1547
+ const errorResponses = batchRequests.map((req) => ({
1548
+ [API_FIELD_TEXT]: req[API_FIELD_TEXT],
1549
+ [API_FIELD_ERROR]: result[API_FIELD_ERROR]
1550
+ }));
1551
+ allResults.push({ [API_FIELD_TRANSLATIONS]: errorResponses });
1552
+ allRequests.push(...batchRequests);
1553
+ batchRequests.forEach((req) => {
1554
+ this.pendingRequests.delete(req.cacheKey);
1555
+ });
1556
+ } else {
1557
+ if (result[API_FIELD_TRANSLATIONS]) {
1558
+ result[API_FIELD_TRANSLATIONS].forEach((translation, index) => {
1559
+ if (translation[API_FIELD_ERROR]) {
1560
+ const originalText = batchRequests[index]?.[API_FIELD_TEXT];
1561
+ devDebug(
1562
+ "[TranslationBatching] Translation error for",
1563
+ originalText,
1564
+ ":",
1565
+ translation[API_FIELD_ERROR]
1566
+ );
1567
+ }
1568
+ });
1569
+ }
1570
+ allResults.push(result);
1571
+ allRequests.push(...batchRequests);
1572
+ batchRequests.forEach((req) => {
1573
+ this.pendingRequests.delete(req.cacheKey);
1574
+ });
1575
+ }
1576
+ } catch (error) {
1577
+ devDebug("[TranslationBatching] Batch request failed for", langPair, ":", error);
1578
+ batchRequests.forEach((req) => {
1579
+ this.pendingRequests.delete(req.cacheKey);
1580
+ });
1581
+ throw error;
1582
+ }
1583
+ }
1584
+ this.onBatchComplete(allResults, allRequests);
1585
+ }
1586
+ clearPending(cacheKey) {
1587
+ this.pendingRequests.delete(cacheKey);
1588
+ }
1589
+ clearAllPending() {
1590
+ this.pendingRequests.clear();
1591
+ }
1592
+ cleanup() {
1593
+ this.clearAllPending();
1594
+ this.queueMap.clear();
1595
+ this.queuedThisTick.clear();
1596
+ if (this.flushTimeout) {
1597
+ clearTimeout(this.flushTimeout);
1598
+ this.flushTimeout = null;
1599
+ }
1600
+ this.clearQueuedThisTickScheduled = false;
1601
+ this.firstItemTime = null;
1602
+ }
1603
+ getStats() {
1604
+ return {
1605
+ pendingRequests: this.pendingRequests.size,
1606
+ queuedBatches: this.queueMap.size,
1607
+ queuedThisTick: this.queuedThisTick.size,
1608
+ hasFlushTimeout: !!this.flushTimeout
1609
+ };
1610
+ }
1611
+ };
1612
+
1613
+ // src/useLangie.ts
1614
+ var globalLangieInstance = null;
1615
+ if (typeof window !== "undefined" && window.__LANGIE_SINGLETON__) {
1616
+ globalLangieInstance = window.__LANGIE_SINGLETON__;
1617
+ }
1618
+ function safeLocalStorageAccess(operation) {
1619
+ if (typeof window === "undefined") return void 0;
1620
+ try {
1621
+ return operation();
1622
+ } catch (e) {
1623
+ devDebug("[useLangie] localStorage error:", e);
1624
+ return void 0;
1625
+ }
1626
+ }
1627
+ if (!globalLangieInstance && typeof window !== "undefined") {
1628
+ const stored = safeLocalStorageAccess(() => localStorage.getItem("__LANGIE_SINGLETON_URL__"));
1629
+ if (stored) {
1630
+ const storedOptions = { translatorHost: stored };
1631
+ globalLangieInstance = createLangieInstance(storedOptions);
1632
+ }
1633
+ }
1634
+ function createLangieInstance(options = {}) {
1635
+ const core = useLangieCore(options);
1636
+ const {
1637
+ availableLanguages: availableLanguages2,
1638
+ translations: translations2,
1639
+ uiTranslations: uiTranslations2,
1640
+ currentLanguage: currentLanguage2,
1641
+ isLoading,
1642
+ setLanguage,
1643
+ fetchLanguages,
1644
+ translatorHost,
1645
+ clearTranslations
1646
+ } = core;
1647
+ const ltDefaults = {
1648
+ ctx: "ui",
1649
+ orig: ""
1650
+ };
1651
+ const setLtDefaults = (defaults) => {
1652
+ Object.assign(ltDefaults, defaults);
1653
+ };
1654
+ const getLtDefaults = () => ({ ...ltDefaults });
1655
+ const CACHE_KEY = "langie_translations_cache";
1656
+ const UI_CACHE_KEY = "langie_ui_translations_cache";
1657
+ const LANGUAGES_CACHE_KEY = "langie_languages_cache";
1658
+ const loadCachedTranslations = () => {
1659
+ if (typeof window === "undefined") return;
1660
+ const cachedTranslations = getCache(CACHE_KEY);
1661
+ const cachedUiTranslations = getCache(UI_CACHE_KEY);
1662
+ if (cachedTranslations) {
1663
+ const currentLang = currentLanguage2.value;
1664
+ const langTranslations = cachedTranslations[currentLang] || {};
1665
+ Object.assign(translations2, langTranslations);
1666
+ }
1667
+ if (cachedUiTranslations) {
1668
+ const currentLang = currentLanguage2.value;
1669
+ const langUiTranslations = cachedUiTranslations[currentLang] || {};
1670
+ Object.assign(uiTranslations2, langUiTranslations);
1671
+ }
1672
+ };
1673
+ const saveCachedTranslations = () => {
1674
+ if (typeof window === "undefined") return;
1675
+ const existingTranslations = getCache(CACHE_KEY) || {};
1676
+ const existingUiTranslations = getCache(UI_CACHE_KEY) || {};
1677
+ const currentLang = currentLanguage2.value;
1678
+ existingTranslations[currentLang] = { ...translations2 };
1679
+ existingUiTranslations[currentLang] = { ...uiTranslations2 };
1680
+ setCache(CACHE_KEY, existingTranslations, 7 * 24 * 60 * 60 * 1e3);
1681
+ setCache(UI_CACHE_KEY, existingUiTranslations, 7 * 24 * 60 * 60 * 1e3);
1682
+ };
1683
+ const loadCachedLanguages = () => {
1684
+ if (typeof window === "undefined") return;
1685
+ const cachedLanguages = getCache(LANGUAGES_CACHE_KEY);
1686
+ if (cachedLanguages) {
1687
+ availableLanguages2.value = cachedLanguages;
1688
+ }
1689
+ };
1690
+ loadCachedTranslations();
1691
+ loadCachedLanguages();
1692
+ watch(currentLanguage2, () => {
1693
+ Object.keys(translations2).forEach((key) => delete translations2[key]);
1694
+ Object.keys(uiTranslations2).forEach((key) => delete uiTranslations2[key]);
1695
+ loadCachedTranslations();
1696
+ });
1697
+ const batching = new TranslationBatching(
1698
+ {
1699
+ initialBatchDelay: options.initialBatchDelay,
1700
+ followupBatchDelay: options.followupBatchDelay,
1701
+ maxBatchSize: options.maxBatchSize,
1702
+ maxWaitTime: options.maxWaitTime
1703
+ },
1704
+ translatorHost,
1705
+ () => currentLanguage2.value,
1706
+ (results, requests) => {
1707
+ requests.forEach((req) => {
1708
+ const cacheKey = `${req[API_FIELD_TEXT]}|${req[API_FIELD_CTX]}`;
1709
+ const languageCacheKey = `${cacheKey}|${req[API_FIELD_FROM]}|${req[API_FIELD_TO]}`;
1710
+ recentlyQueued.delete(languageCacheKey);
1711
+ });
1712
+ let translationsArray = [];
1713
+ results.forEach((result) => {
1714
+ if (Array.isArray(result)) {
1715
+ translationsArray = translationsArray.concat(result);
1716
+ } else if (result.translations && Array.isArray(result.translations)) {
1717
+ translationsArray = translationsArray.concat(result.translations);
1718
+ }
1719
+ });
1720
+ translationsArray.forEach((translation, idx) => {
1721
+ const request = requests[idx];
1722
+ if (!request) {
1723
+ devDebug("[useLangie] No matching request for translation:", translation);
1724
+ return;
1725
+ }
1726
+ const originalText = request[API_FIELD_TEXT];
1727
+ const originalCtx = request[API_FIELD_CTX] ?? (ltDefaults.ctx || "ui");
1728
+ if (translation[API_FIELD_ERROR]) {
1729
+ devDebug(
1730
+ "[useLangie] Translation error for",
1731
+ originalText,
1732
+ ":",
1733
+ translation[API_FIELD_ERROR]
1734
+ );
1735
+ const errorKey = `${originalText}|${originalCtx}|${request[API_FIELD_FROM]}|${request[API_FIELD_TO]}`;
1736
+ translationErrors.set(errorKey, translation[API_FIELD_ERROR]);
1737
+ return;
1738
+ }
1739
+ if (translation[API_FIELD_FROM] && !translation[API_FIELD_TEXT]) {
1740
+ return;
1741
+ }
1742
+ const translatedText = translation[API_FIELD_TEXT];
1743
+ if (translatedText) {
1744
+ const requestedLanguage = request[API_FIELD_TO];
1745
+ const explicitToLang = request.__explicitToLang;
1746
+ if (!explicitToLang && requestedLanguage !== currentLanguage2.value) {
1747
+ devDebug("[useLangie] Skipping outdated translation:", {
1748
+ original: originalText,
1749
+ translated: translatedText,
1750
+ requestedLanguage,
1751
+ currentLanguage: currentLanguage2.value
1752
+ });
1753
+ return;
1754
+ }
1755
+ if (translatedText === originalText) {
1756
+ devDebug("[useLangie] Skipping cache for identical translation:", {
1757
+ original: originalText,
1758
+ translated: translatedText,
1759
+ context: originalCtx
1760
+ });
1761
+ return;
1762
+ }
1763
+ const effectiveCtx = originalCtx;
1764
+ const cacheKey = `${originalText}|${effectiveCtx}`;
1765
+ const cache = effectiveCtx === "ui" ? uiTranslations2 : translations2;
1766
+ cache[cacheKey] = translatedText;
1767
+ devDebug("[useLangie] Cached translation:", {
1768
+ original: originalText,
1769
+ translated: translatedText,
1770
+ context: effectiveCtx,
1771
+ cacheKey,
1772
+ language: requestedLanguage
1773
+ });
1774
+ saveCachedTranslations();
1775
+ }
1776
+ });
1777
+ }
1778
+ );
1779
+ const recentlyQueued = /* @__PURE__ */ new Set();
1780
+ const pendingTimeouts = /* @__PURE__ */ new Set();
1781
+ const translationErrors = /* @__PURE__ */ new Map();
1782
+ const translateInternal = (text, ctx, originalLang, toLang, reactive2 = false) => {
1783
+ if (reactive2) {
1784
+ void currentLanguage2.value;
1785
+ }
1786
+ const from = originalLang || ltDefaults.orig || "";
1787
+ const to = toLang || currentLanguage2.value;
1788
+ if (from === to) {
1789
+ return text;
1790
+ }
1791
+ const effectiveCtx = ctx !== void 0 ? ctx : ltDefaults.ctx || "ui";
1792
+ const cacheKey = `${text}|${effectiveCtx}`;
1793
+ const cache = effectiveCtx === "ui" ? uiTranslations2 : translations2;
1794
+ if (cache[cacheKey]) {
1795
+ return cache[cacheKey];
1796
+ }
1797
+ const errorKey = `${text}|${effectiveCtx}|${from}|${to}`;
1798
+ if (translationErrors.has(errorKey)) {
1799
+ return text;
1800
+ }
1801
+ const languageCacheKey = `${cacheKey}|${from}|${to}`;
1802
+ if (recentlyQueued.has(languageCacheKey)) {
1803
+ return text;
1804
+ }
1805
+ batching.queueTranslation(text, effectiveCtx, from, to, cacheKey, toLang !== void 0);
1806
+ recentlyQueued.add(languageCacheKey);
1807
+ const clearDelay = 100;
1808
+ const timeoutId = setTimeout(() => {
1809
+ recentlyQueued.delete(languageCacheKey);
1810
+ pendingTimeouts.delete(timeoutId);
1811
+ }, clearDelay);
1812
+ pendingTimeouts.add(timeoutId);
1813
+ return text;
1814
+ };
1815
+ const l = (text, ctx, originalLang, toLang) => translateInternal(text, ctx, originalLang, toLang, false);
1816
+ const lr = (text, ctx, originalLang, toLang) => translateInternal(text, ctx, originalLang, toLang, true);
1817
+ const fetchAndCacheBatch = async (items, from, to = currentLanguage2.value, globalCtx) => {
1818
+ if (items.length === 0) return;
1819
+ const effectiveFrom = from || ltDefaults.orig || "";
1820
+ if (effectiveFrom === to) {
1821
+ return;
1822
+ }
1823
+ isLoading.value = true;
1824
+ try {
1825
+ const effectiveCtx = globalCtx || ltDefaults.ctx || "ui";
1826
+ const response = await fetch(`${translatorHost}/translate`, {
1827
+ method: "POST",
1828
+ headers: {
1829
+ "Content-Type": "application/json"
1830
+ },
1831
+ body: JSON.stringify({
1832
+ translations: items.map((item) => ({
1833
+ [API_FIELD_TEXT]: item[API_FIELD_TEXT],
1834
+ [API_FIELD_CTX]: item[API_FIELD_CTX] || effectiveCtx
1835
+ })),
1836
+ [API_FIELD_FROM]: effectiveFrom,
1837
+ [API_FIELD_TO]: to
1838
+ })
1839
+ });
1840
+ if (!response.ok) {
1841
+ throw new Error(`Translation request failed: ${response.status}`);
1842
+ }
1843
+ const result = await response.json();
1844
+ if (result[API_FIELD_ERROR]) {
1845
+ devDebug("[useLangie] Top-level API error:", result[API_FIELD_ERROR]);
1846
+ const errorResponses = items.map((item) => ({
1847
+ [API_FIELD_TEXT]: item[API_FIELD_TEXT],
1848
+ [API_FIELD_ERROR]: result[API_FIELD_ERROR]
1849
+ }));
1850
+ errorResponses.forEach((translation, index) => {
1851
+ const item = items[index];
1852
+ const originalText = item?.[API_FIELD_TEXT];
1853
+ if (!originalText) {
1854
+ return;
1855
+ }
1856
+ if (translation[API_FIELD_ERROR]) {
1857
+ devDebug(
1858
+ "[useLangie] Translation error for",
1859
+ originalText,
1860
+ ":",
1861
+ translation[API_FIELD_ERROR]
1862
+ );
1863
+ const errorKey = `${originalText}|${item[API_FIELD_CTX] || effectiveCtx}|${effectiveFrom}|${to}`;
1864
+ translationErrors.set(errorKey, translation[API_FIELD_ERROR]);
1865
+ return;
1866
+ }
1867
+ });
1868
+ return;
1869
+ }
1870
+ if (result[API_FIELD_TRANSLATIONS]) {
1871
+ result[API_FIELD_TRANSLATIONS].forEach(
1872
+ (translation, index) => {
1873
+ const item = items[index];
1874
+ const originalText = item?.[API_FIELD_TEXT];
1875
+ if (!originalText) {
1876
+ return;
1877
+ }
1878
+ if (translation[API_FIELD_ERROR]) {
1879
+ devDebug(
1880
+ "[useLangie] Translation error for",
1881
+ originalText,
1882
+ ":",
1883
+ translation[API_FIELD_ERROR]
1884
+ );
1885
+ return;
1886
+ }
1887
+ if (translation[API_FIELD_FROM] && !translation[API_FIELD_TEXT]) {
1888
+ return;
1889
+ }
1890
+ const translatedText = translation[API_FIELD_TEXT];
1891
+ if (translatedText) {
1892
+ if (to !== currentLanguage2.value) {
1893
+ devDebug("[useLangie] Skipping outdated translation (batch):", {
1894
+ original: originalText,
1895
+ translated: translatedText,
1896
+ requestedLanguage: to,
1897
+ currentLanguage: currentLanguage2.value
1898
+ });
1899
+ return;
1900
+ }
1901
+ if (translatedText === originalText) {
1902
+ devDebug("[useLangie] Skipping cache for identical translation (batch):", {
1903
+ original: originalText,
1904
+ translated: translatedText,
1905
+ context: item[API_FIELD_CTX] || effectiveCtx
1906
+ });
1907
+ return;
1908
+ }
1909
+ const originalCtx = item[API_FIELD_CTX];
1910
+ const translationCtx = originalCtx !== void 0 ? originalCtx : effectiveCtx;
1911
+ const cacheKey = `${originalText}|${translationCtx}`;
1912
+ const cache = translationCtx === "ui" ? uiTranslations2 : translations2;
1913
+ cache[cacheKey] = translatedText;
1914
+ devDebug("[useLangie] Cached translation (batch):", {
1915
+ original: originalText,
1916
+ translated: translatedText,
1917
+ context: translationCtx,
1918
+ cacheKey,
1919
+ language: to
1920
+ });
1921
+ saveCachedTranslations();
1922
+ }
1923
+ }
1924
+ );
1925
+ }
1926
+ } catch (error) {
1927
+ console.error("[useLangie] Translation error:", error);
1928
+ } finally {
1929
+ isLoading.value = false;
1930
+ }
1931
+ };
1932
+ watch(currentLanguage2, () => {
1933
+ recentlyQueued.clear();
1934
+ loadCachedTranslations();
1935
+ batching.cleanup();
1936
+ });
1937
+ return {
1938
+ // Core functionality
1939
+ availableLanguages: availableLanguages2,
1940
+ translations: translations2,
1941
+ uiTranslations: uiTranslations2,
1942
+ currentLanguage: currentLanguage2,
1943
+ isLoading,
1944
+ setLanguage,
1945
+ fetchLanguages,
1946
+ translatorHost,
1947
+ // Translation functions
1948
+ l,
1949
+ lr,
1950
+ fetchAndCacheBatch,
1951
+ // Error handling
1952
+ getTranslationError: (text, ctx, from, to) => {
1953
+ const effectiveCtx = ctx !== void 0 ? ctx : ltDefaults.ctx || "ui";
1954
+ const effectiveFrom = from || ltDefaults.orig || "";
1955
+ const effectiveTo = to || currentLanguage2.value;
1956
+ const errorKey = `${text}|${effectiveCtx}|${effectiveFrom}|${effectiveTo}`;
1957
+ return translationErrors.get(errorKey) || null;
1958
+ },
1959
+ // Utility functions
1960
+ cleanup: () => {
1961
+ clearTranslations();
1962
+ batching.cleanup();
1963
+ pendingTimeouts.forEach((id) => clearTimeout(id));
1964
+ pendingTimeouts.clear();
1965
+ translationErrors.clear();
1966
+ },
1967
+ getBatchingStats: () => batching.getStats(),
1968
+ // lt component defaults management
1969
+ setLtDefaults,
1970
+ getLtDefaults
1971
+ };
1972
+ }
1973
+ function getGlobalLangieInstance() {
1974
+ if (typeof window !== "undefined") {
1975
+ return window.__LANGIE_SINGLETON__ || null;
1976
+ }
1977
+ return globalLangieInstance;
1978
+ }
1979
+ function setGlobalLangieInstance(instance, options) {
1980
+ if (typeof window !== "undefined") {
1981
+ ;
1982
+ window.__LANGIE_SINGLETON__ = instance;
1983
+ if (options && options.translatorHost) {
1984
+ safeLocalStorageAccess(
1985
+ () => localStorage.setItem("__LANGIE_SINGLETON_URL__", options.translatorHost)
1986
+ );
1987
+ }
1988
+ }
1989
+ globalLangieInstance = instance;
1990
+ }
1991
+ function useLangie(options = {}) {
1992
+ const globalInstance = getGlobalLangieInstance();
1993
+ if (globalInstance) {
1994
+ const currentHost = globalInstance.translatorHost;
1995
+ const newHost = options.translatorHost;
1996
+ if (!options.translatorHost || currentHost === newHost) {
1997
+ return globalInstance;
1998
+ }
1999
+ }
2000
+ const instance = createLangieInstance(options);
2001
+ setGlobalLangieInstance(instance, options);
2002
+ return instance;
2003
+ }
2004
+
2005
+ // vue-script:/Users/nlit/projects/langie-sdk/src/components/InterfaceLanguageSelect.vue?type=script
2006
+ var _hoisted_12 = { class: "interface-language-select-wrapper" };
2007
+ var _hoisted_22 = { class: "loader-text" };
2008
+ var InterfaceLanguageSelect_default = /* @__PURE__ */ _defineComponent2({
2009
+ __name: "InterfaceLanguageSelect",
2010
+ props: {
2011
+ placeholder: {
2012
+ type: String,
2013
+ default: "Select interface language"
2014
+ },
2015
+ disabled: {
2016
+ type: Boolean,
2017
+ default: false
2018
+ },
2019
+ isDark: {
2020
+ type: Boolean,
2021
+ default: false
2022
+ },
2023
+ translatorHost: {
2024
+ type: String,
2025
+ default: ""
2026
+ },
2027
+ apiKey: {
2028
+ type: String,
2029
+ default: ""
2030
+ },
2031
+ languages: {
2032
+ type: Array,
2033
+ default: () => []
2034
+ }
2035
+ },
2036
+ emits: ["update:modelValue"],
2037
+ setup(__props, { emit: __emit }) {
2038
+ const isChangingLanguage = ref3(false);
2039
+ const props = __props;
2040
+ const emit = __emit;
2041
+ const { availableLanguages: availableLanguages2, currentLanguage: currentLanguage2, setLanguage, fetchLanguages, lr } = useLangie();
2042
+ const effectiveLanguages = computed2(() => {
2043
+ const languages = props.languages && props.languages.length > 0 ? props.languages : availableLanguages2.value;
2044
+ const processed = languages.map((lang) => ({
2045
+ ...lang,
2046
+ native_name: lang.native_name || lang.name,
2047
+ flag_country: lang.flag_country || lang.code
2048
+ }));
2049
+ return processed;
2050
+ });
2051
+ const currentLanguageObject = computed2(() => {
2052
+ if (!currentLanguage2.value) return null;
2053
+ return effectiveLanguages.value.find((lang) => lang.code === currentLanguage2.value) || null;
2054
+ });
2055
+ function detectBrowserLanguage(languages) {
2056
+ if (languages.length === 0) return null;
2057
+ const browserLanguages = navigator.languages || [navigator.language || "en"];
2058
+ for (const browserLang of browserLanguages) {
2059
+ const langCode = browserLang.toLowerCase().split("-")[0];
2060
+ const exactMatch = languages.find((lang) => lang.code.toLowerCase() === langCode);
2061
+ if (exactMatch) {
2062
+ return exactMatch.code;
2063
+ }
2064
+ }
2065
+ for (const browserLang of browserLanguages) {
2066
+ const fullLangCode = browserLang.toLowerCase();
2067
+ const localeMatch = languages.find((lang) => lang.code.toLowerCase() === fullLangCode);
2068
+ if (localeMatch) {
2069
+ return localeMatch.code;
2070
+ }
2071
+ }
2072
+ return null;
2073
+ }
2074
+ async function handleLanguageChange(selectedLanguage) {
2075
+ if (selectedLanguage) {
2076
+ isChangingLanguage.value = true;
2077
+ try {
2078
+ setLanguage(selectedLanguage.code);
2079
+ localStorage.setItem("interface_language", selectedLanguage.code);
2080
+ emit("update:modelValue", selectedLanguage);
2081
+ } finally {
2082
+ isChangingLanguage.value = false;
2083
+ }
2084
+ }
2085
+ }
2086
+ watch2(currentLanguage2, (newLangCode) => {
2087
+ if (newLangCode) {
2088
+ localStorage.setItem("interface_language", newLangCode);
2089
+ }
2090
+ });
2091
+ watch2(
2092
+ () => effectiveLanguages.value,
2093
+ (newLanguages) => {
2094
+ if (newLanguages.length > 0 && !currentLanguage2.value) {
2095
+ const savedLanguageCode = localStorage.getItem("interface_language");
2096
+ if (savedLanguageCode) {
2097
+ const savedLangExists = newLanguages.find((lang) => lang.code === savedLanguageCode);
2098
+ if (savedLangExists) {
2099
+ setLanguage(savedLanguageCode);
2100
+ return;
2101
+ }
2102
+ }
2103
+ const browserLang = detectBrowserLanguage(newLanguages);
2104
+ if (browserLang) {
2105
+ setLanguage(browserLang);
2106
+ }
2107
+ }
2108
+ },
2109
+ { immediate: true }
2110
+ );
2111
+ onMounted(async () => {
2112
+ if (!props.languages || props.languages.length === 0) {
2113
+ const countryCode = await getCountryCode();
2114
+ await fetchLanguages({ country: countryCode || void 0 });
2115
+ }
2116
+ const savedLanguageCode = localStorage.getItem("interface_language");
2117
+ if (savedLanguageCode && savedLanguageCode !== currentLanguage2.value) {
2118
+ const currentLanguages = effectiveLanguages.value;
2119
+ const savedLangExists = currentLanguages.find((lang) => lang.code === savedLanguageCode);
2120
+ if (savedLangExists) {
2121
+ setLanguage(savedLanguageCode);
2122
+ } else if (currentLanguages.length > 0) {
2123
+ const browserLang = detectBrowserLanguage(currentLanguages);
2124
+ if (browserLang) {
2125
+ setLanguage(browserLang);
2126
+ }
2127
+ }
2128
+ } else if (!currentLanguage2.value && effectiveLanguages.value.length > 0) {
2129
+ const browserLang = detectBrowserLanguage(effectiveLanguages.value);
2130
+ if (browserLang) {
2131
+ setLanguage(browserLang);
2132
+ }
2133
+ }
2134
+ });
2135
+ watch2(
2136
+ currentLanguageObject,
2137
+ (newValue) => {
2138
+ if (newValue) {
2139
+ emit("update:modelValue", newValue);
2140
+ }
2141
+ },
2142
+ { immediate: true }
2143
+ );
2144
+ return (_ctx, _cache) => {
2145
+ return _openBlock2(), _createElementBlock2("div", _hoisted_12, [
2146
+ (_openBlock2(), _createBlock2(LanguageSelect_default2, {
2147
+ key: `interface-lang-${effectiveLanguages.value.length}-${effectiveLanguages.value[0]?.code || "empty"}`,
2148
+ "model-value": currentLanguageObject.value,
2149
+ languages: effectiveLanguages.value,
2150
+ placeholder: props.placeholder,
2151
+ disabled: props.disabled || isChangingLanguage.value,
2152
+ "is-dark": props.isDark,
2153
+ "onUpdate:modelValue": handleLanguageChange
2154
+ }, null, 8, ["model-value", "languages", "placeholder", "disabled", "is-dark"])),
2155
+ _createCommentVNode2(" Loading overlay "),
2156
+ isChangingLanguage.value ? (_openBlock2(), _createElementBlock2(
2157
+ "div",
2158
+ {
2159
+ key: 0,
2160
+ class: _normalizeClass2(["language-change-loader", { "is-dark": props.isDark }])
2161
+ },
2162
+ [
2163
+ _cache[0] || (_cache[0] = _createElementVNode2(
2164
+ "div",
2165
+ { class: "loader-spinner" },
2166
+ null,
2167
+ -1
2168
+ /* CACHED */
2169
+ )),
2170
+ _createElementVNode2(
2171
+ "span",
2172
+ _hoisted_22,
2173
+ _toDisplayString2(_unref2(lr)("Changing language...", "ui")),
2174
+ 1
2175
+ /* TEXT */
2176
+ )
2177
+ ],
2178
+ 2
2179
+ /* CLASS */
2180
+ )) : _createCommentVNode2("v-if", true)
2181
+ ]);
2182
+ };
2183
+ }
2184
+ });
2185
+
2186
+ // src/components/InterfaceLanguageSelect.vue
2187
+ var InterfaceLanguageSelect_default2 = InterfaceLanguageSelect_default;
2188
+ InterfaceLanguageSelect_default.__scopeId = "data-v-7192f8c0";
2189
+
2190
+ // vue-script:/Users/nlit/projects/langie-sdk/src/components/lt.vue?type=script
2191
+ import { defineComponent as _defineComponent3 } from "vue";
2192
+ import { toDisplayString as _toDisplayString3, openBlock as _openBlock3, createElementBlock as _createElementBlock3, Transition as _Transition, withCtx as _withCtx2, createBlock as _createBlock3 } from "vue";
2193
+ import { computed as computed3, useSlots, watch as watch3 } from "vue";
2194
+ import { ref as ref4, nextTick as nextTick2 } from "vue";
2195
+ var lt_default = /* @__PURE__ */ _defineComponent3({
2196
+ __name: "lt",
2197
+ props: {
2198
+ // Message key (optional, otherwise slot content is used)
2199
+ msg: {
2200
+ type: String,
2201
+ default: void 0
2202
+ },
2203
+ // Translation context shorthand
2204
+ ctx: {
2205
+ type: String,
2206
+ required: false,
2207
+ default: "ui"
2208
+ },
2209
+ // Original language shorthand
2210
+ orig: {
2211
+ type: String,
2212
+ required: false,
2213
+ default: void 0
2214
+ }
2215
+ },
2216
+ setup(__props) {
2217
+ const isNuxt = computed3(() => {
2218
+ if (typeof window !== "undefined") {
2219
+ return !!window.__NUXT__;
2220
+ }
2221
+ if (typeof process !== "undefined") {
2222
+ return !!process.env.NUXT_SSR_BASE || !!process.env.NUXT_PUBLIC_BASE_URL;
2223
+ }
2224
+ return false;
2225
+ });
2226
+ const props = __props;
2227
+ const slots = useSlots();
2228
+ const { lr, currentLanguage: currentLanguage2, uiTranslations: uiTranslations2, translations: translations2, getLtDefaults } = useLangie();
2229
+ const keyStr = computed3(() => {
2230
+ if (props.msg) return props.msg;
2231
+ const slotContent = slots.default ? slots.default().map((n) => n.children).join("") : "";
2232
+ return (slotContent || "").trim();
2233
+ });
2234
+ const forceUpdate = ref4(0);
2235
+ watch3(
2236
+ [uiTranslations2, translations2],
2237
+ () => {
2238
+ nextTick2(() => {
2239
+ forceUpdate.value++;
2240
+ });
2241
+ },
2242
+ { deep: true }
2243
+ );
2244
+ const translated = computed3(() => {
2245
+ if (isNuxt.value && typeof window === "undefined") {
2246
+ return keyStr.value;
2247
+ }
2248
+ const globalDefaults = getLtDefaults();
2249
+ const effectiveCtx = props.ctx ?? globalDefaults.ctx;
2250
+ const effectiveOrig = props.orig ?? globalDefaults.orig;
2251
+ void currentLanguage2.value;
2252
+ void forceUpdate.value;
2253
+ const cacheKey = `${keyStr.value}|${effectiveCtx}`;
2254
+ const cache = effectiveCtx === "ui" ? uiTranslations2 : translations2;
2255
+ void cache[cacheKey];
2256
+ const result = lr(keyStr.value, effectiveCtx, effectiveOrig);
2257
+ return result;
2258
+ });
2259
+ return (_ctx, _cache) => {
2260
+ return _openBlock3(), _createBlock3(_Transition, {
2261
+ name: "fade",
2262
+ mode: "out-in"
2263
+ }, {
2264
+ default: _withCtx2(() => [
2265
+ (_openBlock3(), _createElementBlock3(
2266
+ "span",
2267
+ { key: translated.value },
2268
+ _toDisplayString3(translated.value),
2269
+ 1
2270
+ /* TEXT */
2271
+ ))
2272
+ ]),
2273
+ _: 1
2274
+ /* STABLE */
2275
+ });
2276
+ };
2277
+ }
2278
+ });
2279
+
2280
+ // src/components/lt.vue
2281
+ var lt_default2 = lt_default;
2282
+ lt_default.__scopeId = "data-v-14a7b6f6";
2283
+ export {
2284
+ InterfaceLanguageSelect_default2 as InterfaceLanguageSelect,
2285
+ LanguageSelect_default2 as LanguageSelect,
2286
+ lt_default2 as lt
2287
+ };
2288
+ //# sourceMappingURL=index.mjs.map