Files
usbcheck.it/public/assets/js/lang.js
2025-11-28 03:06:13 +01:00

215 lines
5.9 KiB
JavaScript

// public/assets/js/lang.js
(function () {
const usbConfig = window.usbConfig || {};
const i18nConfig = usbConfig.i18n || {};
const availableLangs = i18nConfig.available || {};
const supportedLangs = Object.keys(availableLangs);
let currentLang = "en";
let translations = {};
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 applyDomainPlaceholders(text) {
if (typeof text !== "string") return text;
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);
}
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;
}
// *** Deine Priorität:
// 1) ?lang
// 2) Browser
// 3) en
function getInitialLang() {
const urlParams = new URLSearchParams(window.location.search);
const paramLang = (urlParams.get("lang") || "").toLowerCase();
// 1) URL-Parameter
if (paramLang && supportedLangs.includes(paramLang)) {
return paramLang;
}
// 2) Browsersprache
const browser = detectBrowserLang();
if (browser) {
return browser;
}
// 3) EN (immer vorhanden laut deiner Vorgabe)
if (supportedLangs.includes("en")) {
return "en";
}
// Sicherheitsfallback
if (supportedLangs.length > 0) {
return supportedLangs[0];
}
return "en";
}
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 = {};
}
}
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;
if (value.includes("{year}")) {
const year = new Date().getFullYear();
value = value.replace("{year}", year);
}
value = applyDomainPlaceholders(value);
node.innerHTML = value;
});
}
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;
}
}
// Wenn du später mal explizit via JS Sprache setzen willst
// (z.B. beim Klick auf ein eigenes UI-Element), kannst du:
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);
window.location.href = url.toString();
}
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");
}
}
});
}
// Nur falls du später data-lang in .lang-pill nutzt,
// im aktuellen header.php kommen die Links ja direkt mit ?lang=... aus PHP.
document.addEventListener("click", function (e) {
const btn = e.target.closest(".lang-pill");
if (!btn) return;
const targetLang = btn.getAttribute("data-lang");
if (!targetLang) return;
e.preventDefault();
setLang(targetLang);
});
});
})();