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:
claude-dev
2026-03-05 16:13:11 +01:00
Ursprung 1644f8786c
Commit 44997d511b
7 geänderte Dateien mit 422 neuen und 362 gelöschten Zeilen

Datei anzeigen

@@ -335,6 +335,16 @@ const TRANSLATIONS = {
'sources.feeds_count': { de: '{n} Feeds', en: '{n} Feeds' },
'sources.feed_count': { de: '{n} Feed', en: '{n} Feed' },
'sources.articles_from_sources': { de: '{articles} Artikel aus {sources} Quellen', en: '{articles} articles from {sources} sources' },
'sources.cat_short_nachrichtenagentur': { de: 'Agentur', en: 'Agency' },
'sources.cat_short_oeffentlich_rechtlich': { de: 'ÖR', en: 'Public' },
'sources.cat_short_qualitaetszeitung': { de: 'Qualität', en: 'Quality' },
'sources.cat_short_behoerde': { de: 'Behörde', en: 'Gov' },
'sources.cat_short_fachmedien': { de: 'Fach', en: 'Trade' },
'sources.cat_short_think_tank': { de: 'Think Tank', en: 'Think Tank' },
'sources.cat_short_international': { de: 'Intl.', en: 'Intl.' },
'sources.cat_short_regional': { de: 'Regional', en: 'Regional' },
'sources.cat_short_boulevard': { de: 'Boulevard', en: 'Tabloid' },
'sources.cat_short_sonstige': { de: 'Sonstige', en: 'Other' },
// ── Sidebar labels ─────────────────────────────────────
'sidebar.no_adhoc': { de: 'Keine Ad-hoc-Lagen', en: 'No ad-hoc incidents' },
@@ -494,6 +504,11 @@ const LangManager = (() => {
const key = el.getAttribute('data-i18n-title');
if (key) el.setAttribute('title', t(key));
});
// lang-switcher active state
document.querySelectorAll('.lang-btn').forEach(btn => {
btn.classList.toggle('active', btn.getAttribute('data-lang') === _lang);
});
}
/**