language
This commit is contained in:
@@ -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';
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|||||||
@@ -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."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user