Files
usbcheck.it/public/assets/js/lang.js
2025-11-26 22:44:59 +01:00

269 lines
8.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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";
// 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;
const 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) 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() {
const urlParams = new URLSearchParams(window.location.search);
const paramLang = urlParams.get("lang");
if (paramLang && supportedLangs.includes(paramLang)) {
return paramLang;
}
const stored = localStorage.getItem("usbcheck_lang");
if (stored && supportedLangs.includes(stored)) {
return stored;
}
if (supportedLangs.includes(currentLang)) {
return currentLang;
}
if (supportedLangs.length > 0) {
return supportedLangs[0];
}
return "en";
}
// ---------------------------------------------
// 5) i18n JSON per Fetch laden
// ---------------------------------------------
async function loadLangFile(lang) {
try {
const res = await fetch(`/assets/i18n/${lang}.json`, {
cache: "no-store",
});
if (!res.ok) throw new Error(`Failed to load /assets/i18n/${lang}.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 info = availableLangs[lang] || {
code: lang,
label: lang.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;
}
}
// ---------------------------------------------
// 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) {
if (!supportedLangs.includes(lang)) {
console.warn("Unsupported language:", lang, supportedLangs);
return;
}
// URL-Parameter setzen
const url = new URL(window.location.href);
url.searchParams.set("lang", lang);
localStorage.setItem("usbcheck_lang", lang);
// 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 PHP, URL, 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."
);
});
}
}
});
})();