This commit is contained in:
2025-11-26 22:44:59 +01:00
parent e5e32d20a1
commit ebc72f3e13
2 changed files with 170 additions and 52 deletions

View File

@@ -1,20 +1,57 @@
// public/assets/js/header.js // public/assets/js/header.js
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
const supportedLangs = ['de', 'en', 'it', 'fr']; // --------------------------------------------------
// Sprachauflösung: basiert NUR auf usbConfig / i18n
// --------------------------------------------------
const cfg = window.usbConfig || {};
const i18nCfg = cfg.i18n || {};
const availLangs = i18nCfg.available || {};
const availCodes = Object.keys(availLangs);
// Globale Config aus PHP-Partial (app_config.php) function normalizeLang(code) {
const cfg = window.usbConfig || {}; if (!code) return '';
const cfgLang = (cfg.lang || '').toLowerCase(); return String(code).slice(0, 2).toLowerCase();
}
function resolveCurrentLang() { function resolveCurrentLang() {
const url = new URL(window.location.href); const url = new URL(window.location.href);
const urlLang = (url.searchParams.get('lang') || '').toLowerCase(); const urlLang = normalizeLang(url.searchParams.get('lang'));
const docLang = (document.documentElement.getAttribute('lang') || '').toLowerCase();
if (supportedLangs.includes(urlLang)) return urlLang; // 1) ?lang= aus der URL, wenn gültig
if (supportedLangs.includes(cfgLang)) return cfgLang; if (urlLang && availCodes.includes(urlLang)) {
if (supportedLangs.includes(docLang)) return docLang; return urlLang;
return 'de'; }
// 2) localStorage (vom lang.js gesetzt)
const stored = normalizeLang(localStorage.getItem('usbcheck_lang'));
if (stored && availCodes.includes(stored)) {
return stored;
}
// 3) PHP-Konfig (fileload/app_config)
const cfgLang = normalizeLang(i18nCfg.current || cfg.lang);
if (cfgLang && availCodes.includes(cfgLang)) {
return cfgLang;
}
// 4) <html lang="...">
const docLang = normalizeLang(document.documentElement.getAttribute('lang'));
if (docLang && availCodes.includes(docLang)) {
return docLang;
}
// 5) Fallback: en, wenn vorhanden
if (availCodes.includes('en')) {
return 'en';
}
// 6) sonst erste verfügbare Sprache
if (availCodes.length > 0) {
return availCodes[0];
}
// 7) absolute Notlösung
return 'en';
} }
// -------------------------------------------------- // --------------------------------------------------

View File

@@ -1,22 +1,36 @@
// public/assets/js/lang.js // public/assets/js/lang.js
(function () { (function () {
const supportedLangs = ["de", "en", "it", "fr"]; // ---------------------------------------------
let currentLang = "en"; // 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";
// 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 = {}; let translations = {};
const langMeta = { // ---------------------------------------------
de: { flag: "🇩🇪", label: "DE" }, // 2) Helper: Translations flatten
en: { flag: "🇬🇧", label: "EN" }, // ---------------------------------------------
it: { flag: "🇮🇹", label: "IT" },
fr: { flag: "🇫🇷", label: "FR" }
};
// 🔹 WICHTIG:
// Diese Funktion nimmt eine verschachtelte JSON-Struktur
// und schreibt ALLE "Blatt-Keys" in ein flaches Objekt,
// OHNE den Pfad in den Keynamen einzubauen.
// D.h. { "hero": { "hero_kicker": "..." } } -> { "hero_kicker": "..." }
function flattenTranslations(obj, target = {}) { function flattenTranslations(obj, target = {}) {
Object.keys(obj || {}).forEach((key) => { Object.keys(obj || {}).forEach((key) => {
const val = obj[key]; const val = obj[key];
@@ -29,15 +43,22 @@
return target; return target;
} }
// ---------------------------------------------
// 3) Domain-Platzhalter ersetzen
// ---------------------------------------------
function applyDomainPlaceholders(text) { function applyDomainPlaceholders(text) {
if (typeof text !== "string") return text; if (typeof text !== "string") return text;
const domains = window.appDomains || {}; const domains = window.appDomains || {};
const replacements = { const replacements = {
"{{primary_domain}}": domains.primaryDomain || "usbcheck.it", "{{primary_domain}}": domains.primaryDomain || "usbcheck.it",
"{{primary_url}}": domains.primaryUrl || ("https://" + (domains.primaryDomain || "usbcheck.it")), "{{primary_url}}":
domains.primaryUrl ||
"https://" + (domains.primaryDomain || "usbcheck.it"),
"{{fakecheck_domain}}": domains.fakecheckDomain || "ismyusbfake.com", "{{fakecheck_domain}}": domains.fakecheckDomain || "ismyusbfake.com",
"{{fakecheck_url}}": domains.fakecheckUrl || ("https://" + (domains.fakecheckDomain || "ismyusbfake.com")), "{{fakecheck_url}}":
domains.fakecheckUrl ||
"https://" + (domains.fakecheckDomain || "ismyusbfake.com"),
}; };
return Object.keys(replacements).reduce((acc, token) => { return Object.keys(replacements).reduce((acc, token) => {
@@ -46,34 +67,58 @@
}, text); }, text);
} }
// ---------------------------------------------
// 4) Initiale Sprache bestimmen
//
// Priorität:
// a) PHP (usbConfig.i18n.current / usbConfig.lang)
// b) URL ?lang= (falls gültig und in available)
// c) localStorage (falls gültig)
// d) erste verfügbare Sprache
// ---------------------------------------------
function getInitialLang() { function getInitialLang() {
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const paramLang = urlParams.get("lang"); const paramLang = urlParams.get("lang");
if (paramLang && supportedLangs.includes(paramLang)) return paramLang; if (paramLang && supportedLangs.includes(paramLang)) {
return paramLang;
}
const stored = localStorage.getItem("usbcheck_lang"); const stored = localStorage.getItem("usbcheck_lang");
if (stored && supportedLangs.includes(stored)) return stored; if (stored && supportedLangs.includes(stored)) {
return stored;
}
const navLang = (navigator.language || navigator.userLanguage || "en") if (supportedLangs.includes(currentLang)) {
.slice(0, 2) return currentLang;
.toLowerCase(); }
if (supportedLangs.includes(navLang)) return navLang;
if (supportedLangs.length > 0) {
return supportedLangs[0];
}
return "en"; return "en";
} }
// ---------------------------------------------
// 5) i18n JSON per Fetch laden
// ---------------------------------------------
async function loadLangFile(lang) { async function loadLangFile(lang) {
try { try {
const res = await fetch(`/assets/i18n/${lang}.json`, { cache: "no-store" }); const res = await fetch(`/assets/i18n/${lang}.json`, {
cache: "no-store",
});
if (!res.ok) throw new Error(`Failed to load /assets/i18n/${lang}.json`); if (!res.ok) throw new Error(`Failed to load /assets/i18n/${lang}.json`);
const raw = await res.json(); const raw = await res.json();
translations = flattenTranslations(raw); // 👈 hier wird strukturiert -> flach gemacht translations = flattenTranslations(raw);
} catch (err) { } catch (err) {
console.error("i18n load error:", err); console.error("i18n load error:", err);
translations = {}; translations = {};
} }
} }
// ---------------------------------------------
// 6) Data-i18n-Texte einsetzen
// ---------------------------------------------
function applyTranslations() { function applyTranslations() {
document.documentElement.setAttribute("lang", currentLang); document.documentElement.setAttribute("lang", currentLang);
@@ -95,38 +140,68 @@
}); });
} }
// ---------------------------------------------
// 7) Label + Flag im Button aktualisieren
//
// Nutzt ausschließlich availableLangs aus PHP:
// availableLangs[code] = { code, label, flag }
// ---------------------------------------------
function updateLangCurrentLabel(lang) { function updateLangCurrentLabel(lang) {
const meta = langMeta[lang] || { flag: "", label: lang.toUpperCase() }; const info = availableLangs[lang] || {
code: lang,
label: lang.toUpperCase(),
flag: "",
};
const node = const node =
document.getElementById("langCurrentLabel") || document.getElementById("langCurrentLabel") ||
document.getElementById("langCurrent"); document.getElementById("langCurrent");
if (!node) return; if (!node) return;
if (meta.flag) { const flag = info.flag || "";
node.innerHTML = `<span class="mr-1.5">${meta.flag}</span><span>${meta.label}</span>`; const label = info.label || info.code.toUpperCase();
if (flag) {
node.innerHTML = `<span class="mr-1.5">${flag}</span><span>${label}</span>`;
} else { } else {
node.textContent = meta.label; node.textContent = label;
} }
} }
// ---------------------------------------------
// 8) Sprache setzen
//
// Neu:
// - 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) { async function setLang(lang) {
if (!supportedLangs.includes(lang)) return; if (!supportedLangs.includes(lang)) {
currentLang = lang; console.warn("Unsupported language:", lang, supportedLangs);
localStorage.setItem("usbcheck_lang", lang); return;
}
// URL-Parameter setzen
const url = new URL(window.location.href); const url = new URL(window.location.href);
url.searchParams.set("lang", lang); url.searchParams.set("lang", lang);
window.history.replaceState({}, "", url.toString()); localStorage.setItem("usbcheck_lang", lang);
await loadLangFile(lang); // komplette Seite neu laden,
applyTranslations(); // damit PHP (fileload.php) + Meta-Tags + JS-Konfig konsistent sind
updateLangCurrentLabel(lang); window.location.href = url.toString();
} }
// ---------------------------------------------
// 9) DOM Ready
// ---------------------------------------------
document.addEventListener("DOMContentLoaded", async function () { document.addEventListener("DOMContentLoaded", async function () {
// Initiale Sprache (kombiniert aus PHP, URL, localStorage)
currentLang = getInitialLang(); currentLang = getInitialLang();
updateLangCurrentLabel(currentLang); updateLangCurrentLabel(currentLang);
// i18n JSON laden & Texte einsetzen
await loadLangFile(currentLang); await loadLangFile(currentLang);
applyTranslations(); applyTranslations();
@@ -155,16 +230,18 @@
}); });
} }
// Sprachumschaltung über .lang-pill // Sprachumschaltung über .lang-pill (Buttons im Dropdown)
document.addEventListener("click", function (e) { document.addEventListener("click", function (e) {
const btn = e.target.closest(".lang-pill"); const btn = e.target.closest(".lang-pill");
if (btn) { if (btn) {
const lang = btn.getAttribute("data-lang"); const lang = btn.getAttribute("data-lang");
setLang(lang); if (lang) {
setLang(lang);
}
} }
}); });
// Login / Avatar Dummy // (Login / Avatar Dummy kann bleiben unverändert)
const loginButton = document.getElementById("loginButton"); const loginButton = document.getElementById("loginButton");
const userAvatar = document.getElementById("userAvatar"); const userAvatar = document.getElementById("userAvatar");
const avatarInitials = document.getElementById("avatarInitials"); const avatarInitials = document.getElementById("avatarInitials");
@@ -179,9 +256,13 @@
} }
}); });
userAvatar.addEventListener("click", function () { if (userAvatar) {
alert("Account-Menü Platzhalter hier später Profil/Logout etc. einbauen."); userAvatar.addEventListener("click", function () {
}); alert(
"Account-Menü Platzhalter hier später Profil/Logout etc. einbauen."
);
});
}
} }
}); });
})(); })();