i18n: Complete DE/EN language switcher integration
- Add LangManager with 270+ translation keys, anti-flicker lang detection - Replace all hardcoded German strings in app.js, components.js, dashboard.html, index.html - Dynamic getter properties for fact-check labels, category badges - Language-aware map tiles (DE/EN OSM servers), CSP updated for tile.openstreetmap.org - Lang switcher in header bar and login page - Locale-aware date formatting, translateApiError for backend messages Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script>(function(){var t=localStorage.getItem('osint_theme');if(t)document.documentElement.setAttribute('data-theme',t);try{var a=JSON.parse(localStorage.getItem('osint_a11y')||'{}');Object.keys(a).forEach(function(k){if(a[k])document.documentElement.setAttribute('data-a11y-'+k,'true');});}catch(e){}})()</script>
|
||||
<script>(function(){var t=localStorage.getItem('osint_theme');if(t)document.documentElement.setAttribute('data-theme',t);try{var a=JSON.parse(localStorage.getItem('osint_a11y')||'{}');Object.keys(a).forEach(function(k){if(a[k])document.documentElement.setAttribute('data-a11y-'+k,'true');});}catch(e){}var l=localStorage.getItem('osint_lang')||'de';document.documentElement.lang=l;})()</script>
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon-16x16.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
|
||||
@@ -12,15 +12,16 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=20260304a">
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=20260305a">
|
||||
<script src="/static/js/lang.js?v=20260305a"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a href="#login-form" class="skip-link">Zum Anmeldeformular springen</a>
|
||||
<a href="#login-form" class="skip-link" data-i18n="login.skip_link">Zum Anmeldeformular springen</a>
|
||||
<main class="login-container">
|
||||
<div class="login-box">
|
||||
<div class="login-logo">
|
||||
<h1>Aegis<span style="color: var(--accent)">Sight</span></h1>
|
||||
<div class="subtitle">Lagemonitor</div>
|
||||
<div class="subtitle" data-i18n="login.subtitle">Lagemonitor</div>
|
||||
</div>
|
||||
|
||||
<div id="login-error" class="login-error" role="alert" aria-live="assertive"></div>
|
||||
@@ -29,29 +30,34 @@
|
||||
<!-- Schritt 1: E-Mail eingeben -->
|
||||
<form id="email-form">
|
||||
<div class="form-group">
|
||||
<label for="email">E-Mail-Adresse</label>
|
||||
<input type="email" id="email" name="email" autocomplete="email" required aria-required="true" placeholder="name@organisation.de">
|
||||
<label for="email" data-i18n="login.email_label">E-Mail-Adresse</label>
|
||||
<input type="email" id="email" name="email" autocomplete="email" required aria-required="true" placeholder="name@organisation.de" data-i18n-placeholder="login.email_placeholder">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-full" id="email-btn">Anmelden</button>
|
||||
<button type="submit" class="btn btn-primary btn-full" id="email-btn" data-i18n="login.submit">Anmelden</button>
|
||||
</form>
|
||||
|
||||
<!-- Schritt 2: Code eingeben -->
|
||||
<form id="code-form" style="display:none;">
|
||||
<p style="color: var(--text-secondary); margin: 0 0 16px 0; font-size: 14px;">
|
||||
<p id="code-sent-text" style="color: var(--text-secondary); margin: 0 0 16px 0; font-size: 14px;">
|
||||
Ein 6-stelliger Code wurde an <strong id="sent-email"></strong> gesendet.
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label for="code">Code eingeben</label>
|
||||
<label for="code" data-i18n="login.code_label">Code eingeben</label>
|
||||
<input type="text" id="code" name="code" autocomplete="one-time-code" required aria-required="true"
|
||||
placeholder="000000" maxlength="6" pattern="[0-9]{6}"
|
||||
style="text-align:center; font-size:24px; letter-spacing:8px; font-family:monospace;">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-full" id="code-btn">Verifizieren</button>
|
||||
<button type="button" class="btn btn-secondary btn-full" id="back-btn" style="margin-top:8px;">Zurück</button>
|
||||
<button type="submit" class="btn btn-primary btn-full" id="code-btn" data-i18n="login.verify">Verifizieren</button>
|
||||
<button type="button" class="btn btn-secondary btn-full" id="back-btn" style="margin-top:8px;" data-i18n="login.back">Zurück</button>
|
||||
</form>
|
||||
|
||||
<div style="text-align:center;margin-top:16px;">
|
||||
<button class="btn btn-secondary btn-small theme-toggle-btn" id="theme-toggle" onclick="ThemeManager.toggle()" title="Theme wechseln" aria-label="Theme wechseln">☼</button>
|
||||
<div class="lang-switcher" id="lang-switcher" style="display:inline-flex;margin-bottom:8px;">
|
||||
<button class="lang-btn" data-lang="de" onclick="LangManager.setLang('de')" title="Deutsch">DE</button>
|
||||
<button class="lang-btn" data-lang="en" onclick="LangManager.setLang('en')" title="English">EN</button>
|
||||
</div>
|
||||
<br>
|
||||
<button class="btn btn-secondary btn-small theme-toggle-btn" id="theme-toggle" onclick="ThemeManager.toggle()" data-i18n-title="header.theme_toggle" title="Theme wechseln" aria-label="Theme wechseln">☼</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@@ -132,7 +138,7 @@
|
||||
errorEl.style.display = 'none';
|
||||
successEl.style.display = 'none';
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Wird gesendet...';
|
||||
btn.textContent = LangManager.t('login.sending');
|
||||
|
||||
currentEmail = document.getElementById('email').value.trim();
|
||||
|
||||
@@ -159,7 +165,7 @@
|
||||
errorEl.style.display = 'block';
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Anmelden';
|
||||
btn.textContent = LangManager.t('login.submit');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -170,7 +176,7 @@
|
||||
const btn = document.getElementById('code-btn');
|
||||
errorEl.style.display = 'none';
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Wird geprüft...';
|
||||
btn.textContent = LangManager.t('login.verifying');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/auth/verify-code', {
|
||||
@@ -196,7 +202,7 @@
|
||||
errorEl.style.display = 'block';
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Verifizieren';
|
||||
btn.textContent = LangManager.t('login.verify');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren