// 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: {...}, dk: {...}, ... } const availableLangs = i18nConfig.available || {}; // Liste der unterstützten Sprachen als Codes const supportedLangs = Object.keys(availableLangs); // Sprache, die PHP in fileload.php ermittelt hat let currentLang = (i18nConfig.current || usbConfig.lang || "en").toLowerCase(); // Wenn PHP eine Sprache setzt, die nicht in available ist, nimm erste verfügbare if (!supportedLangs.includes(currentLang) && supportedLangs.length > 0) { currentLang = supportedLangs[0]; } // Fallback, wenn gar nichts da ist if (!currentLang) { currentLang = "en"; } 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) Initiale Sprache bestimmen // // Priorität: // a) URL ?lang= (falls gültig und in available) // b) PHP (usbConfig.i18n.current / usbConfig.lang) // c) localStorage (falls gültig) // 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) Server-seitig ermittelte Sprache (inkl. HTTP_ACCEPT_LANGUAGE) const serverLang = (i18nConfig.current || usbConfig.lang || "").toLowerCase(); if (serverLang && supportedLangs.includes(serverLang)) { return serverLang; } // c) localStorage, falls gültig 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"); } // 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"; } // --------------------------------------------- // 5) 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 = {}; } } // --------------------------------------------- // 6) 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; }); } // --------------------------------------------- // 7) Label + Flag im Button aktualisieren // // Nutzt ausschließlich availableLangs aus PHP: // availableLangs[code] = { code, label, flag } // --------------------------------------------- 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; } } // --------------------------------------------- // 8) Sprache setzen // // - akzeptiert JEDE Sprache, die in availableLangs liegt // - speichert in localStorage // - schreibt ?lang= in die URL // - lädt Seite neu, damit PHP + JS synchron sind // --------------------------------------------- async function setLang(lang) { const code = (lang || "").toLowerCase(); if (!supportedLangs.includes(code)) { console.warn("Unsupported language:", code, supportedLangs); return; } // URL-Parameter setzen const url = new URL(window.location.href); url.searchParams.set("lang", code); localStorage.setItem("usbcheck_lang", code); // komplette Seite neu laden, // damit PHP (fileload.php) + Meta-Tags + JS-Konfig konsistent sind window.location.href = url.toString(); } // --------------------------------------------- // 9) DOM Ready // --------------------------------------------- document.addEventListener("DOMContentLoaded", async function () { // Initiale Sprache (kombiniert aus URL, PHP, localStorage) 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) { setLang(lang); } } }); // (Login / Avatar Dummy kann bleiben – unverändert) const loginButton = document.getElementById("loginButton"); const userAvatar = document.getElementById("userAvatar"); const avatarInitials = document.getElementById("avatarInitials"); if (loginButton && userAvatar) { loginButton.addEventListener("click", function () { loginButton.classList.add("hidden"); userAvatar.classList.remove("hidden"); if (avatarInitials && !avatarInitials.textContent.trim()) { avatarInitials.textContent = "UC"; } }); if (userAvatar) { userAvatar.addEventListener("click", function () { alert( "Account-Menü Platzhalter – hier später Profil/Logout etc. einbauen." ); }); } } }); })();