169 lines
5.1 KiB
JavaScript
169 lines
5.1 KiB
JavaScript
// public/assets/js/lang.js
|
||
|
||
(function () {
|
||
const supportedLangs = ["de", "en", "it", "fr"];
|
||
let currentLang = "en";
|
||
let translations = {};
|
||
|
||
const langMeta = {
|
||
de: { flag: "🇩🇪", label: "DE" },
|
||
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 = {}) {
|
||
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;
|
||
}
|
||
|
||
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;
|
||
|
||
const navLang = (navigator.language || navigator.userLanguage || "en")
|
||
.slice(0, 2)
|
||
.toLowerCase();
|
||
if (supportedLangs.includes(navLang)) return navLang;
|
||
|
||
return "en";
|
||
}
|
||
|
||
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); // 👈 hier wird strukturiert -> flach gemacht
|
||
} catch (err) {
|
||
console.error("i18n load error:", err);
|
||
translations = {};
|
||
}
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
node.innerHTML = value;
|
||
});
|
||
}
|
||
|
||
function updateLangCurrentLabel(lang) {
|
||
const meta = langMeta[lang] || { flag: "", label: lang.toUpperCase() };
|
||
const node =
|
||
document.getElementById("langCurrentLabel") ||
|
||
document.getElementById("langCurrent");
|
||
if (!node) return;
|
||
|
||
if (meta.flag) {
|
||
node.innerHTML = `<span class="mr-1.5">${meta.flag}</span><span>${meta.label}</span>`;
|
||
} else {
|
||
node.textContent = meta.label;
|
||
}
|
||
}
|
||
|
||
async function setLang(lang) {
|
||
if (!supportedLangs.includes(lang)) return;
|
||
currentLang = lang;
|
||
localStorage.setItem("usbcheck_lang", lang);
|
||
|
||
const url = new URL(window.location.href);
|
||
url.searchParams.set("lang", lang);
|
||
window.history.replaceState({}, "", url.toString());
|
||
|
||
await loadLangFile(lang);
|
||
applyTranslations();
|
||
updateLangCurrentLabel(lang);
|
||
}
|
||
|
||
document.addEventListener("DOMContentLoaded", async function () {
|
||
currentLang = getInitialLang();
|
||
updateLangCurrentLabel(currentLang);
|
||
|
||
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
|
||
document.addEventListener("click", function (e) {
|
||
const btn = e.target.closest(".lang-pill");
|
||
if (btn) {
|
||
const lang = btn.getAttribute("data-lang");
|
||
setLang(lang);
|
||
}
|
||
});
|
||
|
||
// Login / Avatar – Dummy
|
||
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";
|
||
}
|
||
});
|
||
|
||
userAvatar.addEventListener("click", function () {
|
||
alert("Account-Menü Platzhalter – hier später Profil/Logout etc. einbauen.");
|
||
});
|
||
}
|
||
});
|
||
})();
|