284 lines
8.5 KiB
JavaScript
284 lines
8.5 KiB
JavaScript
// 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 = `<span class="mr-1.5">${flag}</span><span>${label}</span>`;
|
|
} 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();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
})();
|