180 lines
5.5 KiB
JavaScript
180 lines
5.5 KiB
JavaScript
// public/assets/js/lang.js
|
||
|
||
(function () {
|
||
const supportedLangs = ["de", "en", "it", "fr"];
|
||
let translations = {};
|
||
|
||
// Mapping für Flaggen + Label im Header
|
||
const langMeta = {
|
||
de: { flag: "🇩🇪", label: "DE" },
|
||
en: { flag: "🇬🇧", label: "EN" }, // oder 🇺🇸 wenn dir lieber
|
||
it: { flag: "🇮🇹", label: "IT" },
|
||
fr: { flag: "🇫🇷", label: "FR" }
|
||
};
|
||
|
||
function flattenTranslations(obj, prefix = "") {
|
||
const result = {};
|
||
Object.keys(obj).forEach((key) => {
|
||
const value = obj[key];
|
||
const newKey = prefix ? `${prefix}_${key}` : key;
|
||
|
||
if (value && typeof value === "object" && !Array.isArray(value)) {
|
||
Object.assign(result, flattenTranslations(value, newKey));
|
||
} else {
|
||
result[newKey] = value;
|
||
}
|
||
});
|
||
return result;
|
||
}
|
||
|
||
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 data = await res.json();
|
||
translations = flattenTranslations(data);
|
||
} catch (err) {
|
||
console.error("i18n load error:", err);
|
||
translations = {};
|
||
}
|
||
}
|
||
|
||
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 || "en").slice(0, 2).toLowerCase();
|
||
if (supportedLangs.includes(navLang)) return navLang;
|
||
|
||
return "en";
|
||
}
|
||
|
||
function applyTranslations() {
|
||
document.documentElement.setAttribute("lang", getCurrentLang());
|
||
|
||
document.querySelectorAll("[data-i18n]").forEach((node) => {
|
||
const key = node.getAttribute("data-i18n");
|
||
if (!key || !translations[key]) return;
|
||
|
||
let value = translations[key];
|
||
|
||
// {year}-Placeholder ersetzen (für footer_copy etc.)
|
||
if (typeof value === "string" && value.includes("{year}")) {
|
||
const year = new Date().getFullYear();
|
||
value = value.replace("{year}", year);
|
||
}
|
||
|
||
node.innerHTML = value;
|
||
});
|
||
}
|
||
|
||
function getCurrentLang() {
|
||
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;
|
||
|
||
return "en";
|
||
}
|
||
|
||
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;
|
||
|
||
localStorage.setItem("usbcheck_lang", lang);
|
||
|
||
// URL-Parameter ohne Reload aktualisieren
|
||
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 () {
|
||
const initialLang = getInitialLang();
|
||
|
||
// Header-Label initial
|
||
updateLangCurrentLabel(initialLang);
|
||
|
||
// Übersetzungen laden und anwenden
|
||
await loadLangFile(initialLang);
|
||
applyTranslations();
|
||
|
||
// Dropdown-Elemente
|
||
const langCurrent = document.getElementById("langCurrent");
|
||
const langMenu = document.getElementById("langMenu");
|
||
|
||
// Dropdown öffnen/schließen
|
||
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 / Klick auf .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 – UI-Dummy wie vorher
|
||
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.");
|
||
});
|
||
}
|
||
});
|
||
})();
|