// public/assets/js/lang.js (function () { // --------------------------------------------- // 1) Basis-Config aus PHP (app_config.php) // --------------------------------------------- const usbConfig = window.usbConfig || {}; const i18nConfig = usbConfig.i18n || {}; // Map: { de: {code, label, flag}, en: {...}, ... } const availableLangs = i18nConfig.available || {}; // Liste der unterstützten Sprachen als Codes const supportedLangs = Object.keys(availableLangs); // Aktuelle Sprache (wird unten per getInitialLang() gesetzt) let currentLang = "en"; // Translations-Map (geflacht) let translations = {}; // --------------------------------------------- // 2) Helper: Translations flatten // --------------------------------------------- function flattenTranslations(obj, target = {}) { Object.keys(obj || {}).forEach((key) => { const val = obj[key]; if (val && typeof val === "object" && !Array.isArray(val)) { flattenTranslations(val, target); } else { target[key] = val; } }); return target; } // --------------------------------------------- // 3) Domain-Platzhalter ersetzen // --------------------------------------------- function applyDomainPlaceholders(text) { if (typeof text !== "string") return text; // Primär aus usbConfig.domains, Fallback auf window.appDomains const domains = usbConfig.domains || window.appDomains || {}; const replacements = { "{{primary_domain}}": domains.primaryDomain || "usbcheck.it", "{{primary_url}}": domains.primaryUrl || "https://" + (domains.primaryDomain || "usbcheck.it"), "{{fakecheck_domain}}": domains.fakecheckDomain || "ismyusbfake.com", "{{fakecheck_url}}": domains.fakecheckUrl || "https://" + (domains.fakecheckDomain || "ismyusbfake.com"), }; return Object.keys(replacements).reduce((acc, token) => { const value = replacements[token]; return acc.split(token).join(value); }, text); } // ------------------------------------------------ // 4) Browsersprache erkennen (2-Buchstaben-Code) // ------------------------------------------------ function detectBrowserLang() { const nav = window.navigator || {}; const candidates = []; if (Array.isArray(nav.languages)) { candidates.push(...nav.languages); } if (typeof nav.language === "string") { candidates.push(nav.language); } if (typeof nav.userLanguage === "string") { candidates.push(nav.userLanguage); } for (const raw of candidates) { const code = (raw || "").slice(0, 2).toLowerCase(); if (supportedLangs.includes(code)) { return code; } } return null; } // --------------------------------------------- // 5) Initiale Sprache bestimmen // // Priorität: // a) URL ?lang= (falls gültig) // b) localStorage (explizit gewählte Sprache) // c) Browsersprache // d) 'en', wenn vorhanden // e) erste verfügbare Sprache // --------------------------------------------- function getInitialLang() { const urlParams = new URLSearchParams(window.location.search); const paramLang = (urlParams.get("lang") || "").toLowerCase(); // a) URL-Parameter, wenn gültig if (paramLang && supportedLangs.includes(paramLang)) { return paramLang; } // b) localStorage (vom User über Switcher gewählt) const stored = (localStorage.getItem("usbcheck_lang") || "").toLowerCase(); if (stored) { if (supportedLangs.includes(stored)) { return stored; } // Ungültigen alten Wert aufräumen localStorage.removeItem("usbcheck_lang"); } // c) Browsersprache const browserLang = detectBrowserLang(); if (browserLang) { return browserLang; } // d) Wenn 'en' existiert → nimm 'en' if (supportedLangs.includes("en")) { return "en"; } // e) Sonst erste verfügbare Sprache if (supportedLangs.length > 0) { return supportedLangs[0]; } // Worst-Case (sollte nie eintreten) return "en"; } // --------------------------------------------- // 6) i18n JSON per Fetch laden // --------------------------------------------- async function loadLangFile(lang) { const code = (lang || "").toLowerCase(); if (!supportedLangs.includes(code)) { console.warn("i18n: unsupported language in loadLangFile:", code); translations = {}; return; } try { const res = await fetch(`/assets/i18n/${code}.json`, { cache: "no-store", }); if (!res.ok) throw new Error(`Failed to load /assets/i18n/${code}.json`); const raw = await res.json(); translations = flattenTranslations(raw); } catch (err) { console.error("i18n load error:", err); translations = {}; } } // --------------------------------------------- // 7) Data-i18n-Texte einsetzen // --------------------------------------------- function applyTranslations() { document.documentElement.setAttribute("lang", currentLang); document.querySelectorAll("[data-i18n]").forEach((node) => { const key = node.getAttribute("data-i18n"); if (!key) return; let value = translations[key]; if (typeof value !== "string") return; // {year}-Platzhalter ersetzen (z. B. footer_copy) if (value.includes("{year}")) { const year = new Date().getFullYear(); value = value.replace("{year}", year); } value = applyDomainPlaceholders(value); node.innerHTML = value; }); } // --------------------------------------------- // 8) Label + Flag im Button aktualisieren // --------------------------------------------- function updateLangCurrentLabel(lang) { const code = (lang || "").toLowerCase(); const info = availableLangs[code] || { code: code || "en", label: (code || "en").toUpperCase(), flag: "", }; const node = document.getElementById("langCurrentLabel") || document.getElementById("langCurrent"); if (!node) return; const flag = info.flag || ""; const label = info.label || info.code.toUpperCase(); if (flag) { node.innerHTML = `${flag}${label}`; } else { node.textContent = label; } } // --------------------------------------------- // 9) Sprache setzen (via Switcher) // // - akzeptiert nur Sprachen aus availableLangs // - speichert in localStorage // - schreibt ?lang= in die URL // - lädt Seite neu, damit PHP + JS synchron sind // --------------------------------------------- function setLang(lang) { const code = (lang || "").toLowerCase(); if (!supportedLangs.includes(code)) { console.warn("Unsupported language:", code, supportedLangs); return; } const url = new URL(window.location.href); url.searchParams.set("lang", code); localStorage.setItem("usbcheck_lang", code); window.location.href = url.toString(); } // --------------------------------------------- // 10) DOM Ready // --------------------------------------------- document.addEventListener("DOMContentLoaded", async function () { // Initiale Sprache (kombiniert aus URL, localStorage, Browser) currentLang = getInitialLang(); updateLangCurrentLabel(currentLang); // i18n JSON laden & Texte einsetzen await loadLangFile(currentLang); applyTranslations(); const langCurrent = document.getElementById("langCurrent"); const langMenu = document.getElementById("langMenu"); if (langCurrent && langMenu) { langCurrent.addEventListener("click", function (e) { e.stopPropagation(); langMenu.classList.toggle("hidden"); }); document.addEventListener("click", function (e) { if (!langMenu.classList.contains("hidden")) { if (!langMenu.contains(e.target) && !langCurrent.contains(e.target)) { langMenu.classList.add("hidden"); } } }); langMenu.addEventListener("click", function (e) { const pill = e.target.closest(".lang-pill"); if (pill) { langMenu.classList.add("hidden"); } }); } // Sprachumschaltung über .lang-pill (Buttons im Dropdown) document.addEventListener("click", function (e) { const btn = e.target.closest(".lang-pill"); if (btn) { const lang = btn.getAttribute("data-lang"); if (lang) { // Nur wenn data-lang gesetzt ist → JS-gesteuerter Wechsel setLang(lang); e.preventDefault(); } } }); }); })();