This commit is contained in:
2025-11-19 01:45:12 +01:00
parent 7082b192f5
commit 4efb83ae8c
2 changed files with 129 additions and 66 deletions

View File

@@ -1,23 +1,24 @@
// public/assets/js/lang-usbcheck.js
// public/assets/js/lang.js
(function () {
const translations = {
de: {
header_slogan: "USB-Sticks testen",
btn_login: "Login",
header_slogan: "USB-Sticks testen",
btn_login: "Login",
nav_how: "Ablauf",
nav_problem: "Problem",
nav_features: "Funktionen",
nav_security: "Sicherheit",
nav_faq: "FAQ",
nav_how: "Ablauf",
nav_problem: "Problem",
nav_features: "Funktionen",
nav_security: "Sicherheit",
nav_faq: "FAQ",
footer_imprint: "Impressum",
footer_privacy: "Datenschutz",
footer_imprint: "Impressum",
footer_privacy: "Datenschutz",
brand_wordmark: "usbcheck.it",
brand_subtitle: "USB-Sticks auf Fakes testen",
btn_login: "Login",
// btn_login oben bereits definiert, diese Zeile könnte theoretisch entfallen
// btn_login: "Login",
nav_how: "Wie es funktioniert",
nav_problem: "Warum es wichtig ist",
@@ -130,20 +131,21 @@
},
en: {
header_slogan: "Test USB drives",
btn_login: "Login",
header_slogan: "Test USB drives",
btn_login: "Login",
nav_how: "How it works",
nav_problem: "Why it matters",
nav_features: "Features",
nav_security: "Security",
nav_faq: "FAQ",
nav_how: "How it works",
nav_problem: "Why it matters",
nav_features: "Features",
nav_security: "Security",
nav_faq: "FAQ",
footer_imprint: "Imprint",
footer_privacy: "Privacy policy",
footer_imprint: "Imprint",
footer_privacy: "Privacy policy",
brand_wordmark: "usbcheck.it",
brand_subtitle: "Test USB drives for fakes",
btn_login: "Login",
// btn_login oben bereits gesetzt
nav_how: "How it works",
nav_problem: "Why it matters",
@@ -231,6 +233,7 @@
security_kicker: "Security & privacy",
security_title: "Privacy-first design: your test data is yours.",
security_intro: "USBCheck was designed from day one to protect your data. The browser quick test only works with test files. Your own documents, photos and backups are never read or uploaded. In Pro mode you decide if and which reports are synced to your account.",
security_card1_title: "Local-only tests",
security_card1_text: "All write and read tests run locally on your USB drive. The browser only accesses test files not your private content.",
security_card2_title: "Transparent reports",
@@ -257,20 +260,21 @@
// Italienisch (kurz, sachlich)
it: {
header_slogan: "Test delle chiavette USB",
btn_login: "Accesso",
header_slogan: "Test delle chiavette USB",
btn_login: "Accesso",
nav_how: "Come funziona",
nav_problem: "Perché è importante",
nav_features: "Funzioni",
nav_security: "Sicurezza",
nav_faq: "FAQ",
nav_how: "Come funziona",
nav_problem: "Perché è importante",
nav_features: "Funzioni",
nav_security: "Sicurezza",
nav_faq: "FAQ",
footer_imprint: "Imprint",
footer_privacy: "Privacy",
footer_imprint: "Imprint",
footer_privacy: "Privacy",
brand_wordmark: "usbcheck.it",
brand_subtitle: "Controlla le chiavette USB contraffatte",
btn_login: "Login",
// btn_login: "Login",
nav_how: "Come funziona",
nav_problem: "Perché è importante",
@@ -385,20 +389,21 @@
// Französisch (kurz, sachlich)
fr: {
header_slogan: "Tester vos clés USB",
btn_login: "Connexion",
header_slogan: "Tester vos clés USB",
btn_login: "Connexion",
nav_how: "Fonctionnement",
nav_problem: "Problème",
nav_features: "Fonctionnalités",
nav_security: "Sécurité",
nav_faq: "FAQ",
nav_how: "Fonctionnement",
nav_problem: "Problème",
nav_features: "Fonctionnalités",
nav_security: "Sécurité",
nav_faq: "FAQ",
footer_imprint: "Mentions légales",
footer_privacy: "Confidentialité",
footer_imprint: "Mentions légales",
footer_privacy: "Confidentialité",
brand_wordmark: "usbcheck.it",
brand_subtitle: "Tester les clés USB contrefaites",
btn_login: "Connexion",
// btn_login: "Connexion",
nav_how: "Fonctionnement",
nav_problem: "Pourquoi cest important",
@@ -558,6 +563,12 @@
localStorage.setItem('usbcheck_lang', lang);
applyTranslations(lang);
// Button-Label im Header aktualisieren
const currentLabel = document.getElementById('langCurrentLabel') || document.getElementById('langCurrent');
if (currentLabel) {
currentLabel.textContent = lang.toUpperCase();
}
// Optional: URL-Parameter aktualisieren (ohne Reload)
const url = new URL(window.location.href);
url.searchParams.set('lang', lang);
@@ -568,7 +579,49 @@
const initialLang = getInitialLang();
applyTranslations(initialLang);
// Sprachumschaltung
// --- Dropdown-Elemente im Header holen ---
const langCurrent = document.getElementById('langCurrent');
const langCurrentLabel = document.getElementById('langCurrentLabel');
const langMenu = document.getElementById('langMenu');
// aktuelles Label für den Header-Button setzen
if (langCurrentLabel) {
langCurrentLabel.textContent = initialLang.toUpperCase();
} else if (langCurrent) {
langCurrent.textContent = initialLang.toUpperCase();
}
// Dropdown-Logik für Sprachauswahl
if (langCurrent && langMenu) {
// Toggle Menü bei Klick auf den Button
langCurrent.addEventListener('click', function (e) {
e.stopPropagation();
langMenu.classList.toggle('hidden');
});
// Menü schließen bei Klick außerhalb
document.addEventListener('click', function (e) {
if (!langMenu.classList.contains('hidden')) {
if (
!langMenu.contains(e.target) &&
e.target !== langCurrent &&
!langCurrent.contains(e.target)
) {
langMenu.classList.add('hidden');
}
}
});
// Menü schließen, wenn eine Sprache gewählt wurde
langMenu.addEventListener('click', function (e) {
const pill = e.target.closest('.lang-pill');
if (pill) {
langMenu.classList.add('hidden');
}
});
}
// Sprachumschaltung (Click auf .lang-pill → Sprache setzen)
document.addEventListener('click', function (e) {
const btn = e.target.closest('.lang-pill');
if (btn) {

View File

@@ -30,34 +30,44 @@
<a href="#faq" class="hover:text-brand-primary transition-colors" data-i18n="nav_faq"></a>
</nav>
<!-- Language Switch: inline pills -->
<div class="flex items-center gap-1 sm:gap-1.5 text-[11px] sm:text-xs font-medium uppercase tracking-[0.18em]">
<button type="button"
class="lang-pill px-1.5 py-0.5 rounded-full border border-transparent text-brand-muted hover:text-brand-primary hover:border-brand-border transition"
data-lang="de">
DE
</button>
<span class="text-brand-border/60">·</span>
<button type="button"
class="lang-pill px-1.5 py-0.5 rounded-full border border-transparent text-brand-muted hover:text-brand-primary hover:border-brand-border transition"
data-lang="en">
EN2
</button>
<span class="text-brand-border/60">·</span>
<button type="button"
class="lang-pill px-1.5 py-0.5 rounded-full border border-transparent text-brand-muted hover:text-brand-primary hover:border-brand-border transition"
data-lang="it">
IT
</button>
<span class="text-brand-border/60">·</span>
<button type="button"
class="lang-pill px-1.5 py-0.5 rounded-full border border-transparent text-brand-muted hover:text-brand-primary hover:border-brand-border transition"
data-lang="fr">
FR
<!-- Language Switch: Button + Dropdown mit Kürzeln -->
<div class="relative">
<button id="langCurrent"
type="button"
class="flex items-center gap-1 text-xs uppercase tracking-[0.18em] text-brand-muted hover:text-brand-primary transition">
<span id="langCurrentLabel"><?= strtoupper($lang) ?></span>
<svg class="w-3 h-3 opacity-70" viewBox="0 0 20 20" aria-hidden="true">
<path d="M5 7l5 6 5-6" fill="currentColor" />
</svg>
</button>
<div id="langMenu"
class="hidden absolute right-0 mt-2 w-20 rounded-xl bg-brand-surface border border-brand-border shadow-lg py-1 text-xs">
<button type="button"
class="lang-pill block w-full text-left px-3 py-1.5 uppercase tracking-[0.18em] text-brand-muted hover:text-brand-primary hover:bg-brand-bg/60"
data-lang="de">
DE
</button>
<button type="button"
class="lang-pill block w-full text-left px-3 py-1.5 uppercase tracking-[0.18em] text-brand-muted hover:text-brand-primary hover:bg-brand-bg/60"
data-lang="en">
EN
</button>
<button type="button"
class="lang-pill block w-full text-left px-3 py-1.5 uppercase tracking-[0.18em] text-brand-muted hover:text-brand-primary hover:bg-brand-bg/60"
data-lang="it">
IT
</button>
<button type="button"
class="lang-pill block w-full text-left px-3 py-1.5 uppercase tracking-[0.18em] text-brand-muted hover:text-brand-primary hover:bg-brand-bg/60"
data-lang="fr">
FR
</button>
</div>
</div>
<!-- Login Button / Avatar -->
<button id="loginButton"
class="relative inline-flex items-center justify-center rounded-full bg-brand-primary px-4 py-1.5 text-xs font-semibold uppercase tracking-[0.18em] text-brand-bg shadow-soft hover:bg-cyan-400 transition-colors"