i18n: EN-Lagen-Seiten + zweisprachiges Kontaktformular (Phase 3+4)

Phase 3 - Englische Lagebild-Seiten:
- /en/situations/iran-conflict/, /en/situations/cyber-attacks/,
  /en/situations/deepfakes/ erstellt (Mirror der DE-Lagen mit
  englischer UI)
- lagebild.js: curLang() liest jetzt direkt <html lang>; neuer
  dataBase()-Helper, damit EN-Seiten die JSON-Daten aus dem
  DE-Pfad nachladen koennen (window.LAGEBILD_DATA_BASE pro Seite)
- 4 zuvor hardcodierte DE-Strings (emptyDevelopments, emptySummary,
  Quelle-Tooltip, Schliessen-Aria) ueber t() und das vorhandene
  lang.de/lang.en-Dictionary uebersetzt
- DE-Lagen-Seiten: hreflang-Tags wieder aktiv, Toggle zeigt nun
  korrekt auf das EN-Pendant statt /en/
- en/index.html Karussell-Buttons zeigen auf EN-Lagen
- Sitemap mit hreflang-Alternativen fuer alle Lagen ergaenzt

Phase 4 - Kontaktformular zweisprachig (Frontend):
- js/app.js submitContact() liest <html lang>, sendet lang im POST
  und zeigt Sende-/Fehler-Texte in der jeweiligen Sprache
- Backend (contact-form.py) wird separat ausgerollt, ist aber
  abwaertskompatibel: bei fehlendem lang-Param defaultet es auf de

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
2026-05-06 23:28:05 +02:00
Ursprung 8c8130509a
Commit 645fb33898
10 geänderte Dateien mit 691 neuen und 26 gelöschten Zeilen

Datei anzeigen

@@ -631,8 +631,12 @@ function mdToHtml(md) {
// Form submit -> server-side SMTP
window.submitContact = function (e) {
e.preventDefault();
var lang = (document.documentElement.lang || 'de').toLowerCase().indexOf('en') === 0 ? 'en' : 'de';
var T = lang === 'en'
? { sending: 'Sending...', send: 'Send message', sendError: 'Error sending message', netError: 'Connection error. Please try again.' }
: { sending: 'Wird gesendet...', send: 'Nachricht senden', sendError: 'Fehler beim Senden', netError: 'Verbindungsfehler. Bitte versuchen Sie es erneut.' };
var btn = e.target.querySelector('button[type="submit"]');
if (btn) { btn.disabled = true; btn.textContent = 'Wird gesendet...'; }
if (btn) { btn.disabled = true; btn.textContent = T.sending; }
fetch('/api/contact', {
method: 'POST',
@@ -641,7 +645,8 @@ function mdToHtml(md) {
name: document.getElementById('cf-name').value,
organisation: document.getElementById('cf-org').value,
email: document.getElementById('cf-email').value,
message: document.getElementById('cf-message').value
message: document.getElementById('cf-message').value,
lang: lang
})
})
.then(function (r) { return r.json().then(function (d) { return { ok: r.ok, data: d }; }); })
@@ -651,13 +656,13 @@ function mdToHtml(md) {
document.getElementById('form-success').style.display = 'block';
if (window.umami) umami.track('contact_form_success');
} else {
alert(res.data.error || 'Fehler beim Senden');
if (btn) { btn.disabled = false; btn.textContent = 'Nachricht senden'; }
alert(res.data.error || T.sendError);
if (btn) { btn.disabled = false; btn.textContent = T.send; }
}
})
.catch(function () {
alert('Verbindungsfehler. Bitte versuchen Sie es erneut.');
if (btn) { btn.disabled = false; btn.textContent = 'Nachricht senden'; }
alert(T.netError);
if (btn) { btn.disabled = false; btn.textContent = T.send; }
});
return false;
};