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:
@@ -5,6 +5,9 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Lagebild: Cyberangriffe auf deutsche Infrastruktur - AegisSight</title>
|
||||
<meta name="robots" content="noindex, nofollow, noarchive, nosnippet, noimageindex">
|
||||
<link rel="alternate" hreflang="de" href="https://aegis-sight.de/lagen/cyberangriffe/">
|
||||
<link rel="alternate" hreflang="en" href="https://aegis-sight.de/en/situations/cyber-attacks/">
|
||||
<link rel="alternate" hreflang="x-default" href="https://aegis-sight.de/lagen/cyberangriffe/">
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<link rel="stylesheet" href="/css/fonts.css">
|
||||
@@ -33,7 +36,7 @@
|
||||
<div class="lang-switcher" role="group" aria-label="Sprache">
|
||||
<span class="lang-active" lang="de" aria-current="true">DE</span>
|
||||
<span class="lang-sep" aria-hidden="true">|</span>
|
||||
<a class="lang-link" href="/en/" lang="en" hreflang="en" rel="alternate">EN</a>
|
||||
<a class="lang-link" href="/en/situations/cyber-attacks/" lang="en" hreflang="en" rel="alternate">EN</a>
|
||||
</div>
|
||||
<button class="mobile-menu-toggle" aria-label="Menü öffnen" aria-expanded="false">
|
||||
<span class="hamburger"><span></span><span></span><span></span></span>
|
||||
@@ -54,7 +57,7 @@
|
||||
<div class="lang-switcher" role="group" aria-label="Sprache">
|
||||
<span class="lang-active" lang="de" aria-current="true">DE</span>
|
||||
<span class="lang-sep" aria-hidden="true">|</span>
|
||||
<a class="lang-link" href="/en/" lang="en" hreflang="en" rel="alternate">EN</a>
|
||||
<a class="lang-link" href="/en/situations/cyber-attacks/" lang="en" hreflang="en" rel="alternate">EN</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mobile-menu-overlay"></div>
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Recherche: Rechtliche Lage von Deepfakes in Deutschland - AegisSight</title>
|
||||
<meta name="robots" content="noindex, nofollow, noarchive, nosnippet, noimageindex">
|
||||
<link rel="alternate" hreflang="de" href="https://aegis-sight.de/lagen/deepfakes/">
|
||||
<link rel="alternate" hreflang="en" href="https://aegis-sight.de/en/situations/deepfakes/">
|
||||
<link rel="alternate" hreflang="x-default" href="https://aegis-sight.de/lagen/deepfakes/">
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<link rel="stylesheet" href="/css/fonts.css">
|
||||
@@ -36,7 +39,7 @@
|
||||
<div class="lang-switcher" role="group" aria-label="Sprache">
|
||||
<span class="lang-active" lang="de" aria-current="true">DE</span>
|
||||
<span class="lang-sep" aria-hidden="true">|</span>
|
||||
<a class="lang-link" href="/en/" lang="en" hreflang="en" rel="alternate">EN</a>
|
||||
<a class="lang-link" href="/en/situations/deepfakes/" lang="en" hreflang="en" rel="alternate">EN</a>
|
||||
</div>
|
||||
<button class="mobile-menu-toggle" aria-label="Menü öffnen" aria-expanded="false">
|
||||
<span class="hamburger"><span></span><span></span><span></span></span>
|
||||
@@ -59,7 +62,7 @@
|
||||
<div class="lang-switcher" role="group" aria-label="Sprache">
|
||||
<span class="lang-active" lang="de" aria-current="true">DE</span>
|
||||
<span class="lang-sep" aria-hidden="true">|</span>
|
||||
<a class="lang-link" href="/en/" lang="en" hreflang="en" rel="alternate">EN</a>
|
||||
<a class="lang-link" href="/en/situations/deepfakes/" lang="en" hreflang="en" rel="alternate">EN</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mobile-menu-overlay"></div>
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Lagebild Irankonflikt - AegisSight</title>
|
||||
<meta name="robots" content="noindex, nofollow, noarchive, nosnippet, noimageindex">
|
||||
<link rel="alternate" hreflang="de" href="https://aegis-sight.de/lagen/iran-konflikt/">
|
||||
<link rel="alternate" hreflang="en" href="https://aegis-sight.de/en/situations/iran-conflict/">
|
||||
<link rel="alternate" hreflang="x-default" href="https://aegis-sight.de/lagen/iran-konflikt/">
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<link rel="stylesheet" href="/css/fonts.css">
|
||||
@@ -36,7 +39,7 @@
|
||||
<div class="lang-switcher" role="group" aria-label="Sprache">
|
||||
<span class="lang-active" lang="de" aria-current="true">DE</span>
|
||||
<span class="lang-sep" aria-hidden="true">|</span>
|
||||
<a class="lang-link" href="/en/" lang="en" hreflang="en" rel="alternate">EN</a>
|
||||
<a class="lang-link" href="/en/situations/iran-conflict/" lang="en" hreflang="en" rel="alternate">EN</a>
|
||||
</div>
|
||||
<button class="mobile-menu-toggle" aria-label="Menü öffnen" aria-expanded="false">
|
||||
<span class="hamburger"><span></span><span></span><span></span></span>
|
||||
@@ -59,7 +62,7 @@
|
||||
<div class="lang-switcher" role="group" aria-label="Sprache">
|
||||
<span class="lang-active" lang="de" aria-current="true">DE</span>
|
||||
<span class="lang-sep" aria-hidden="true">|</span>
|
||||
<a class="lang-link" href="/en/" lang="en" hreflang="en" rel="alternate">EN</a>
|
||||
<a class="lang-link" href="/en/situations/iran-conflict/" lang="en" hreflang="en" rel="alternate">EN</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mobile-menu-overlay"></div>
|
||||
|
||||
@@ -41,6 +41,8 @@ var Lagebild = {
|
||||
sourceRef: "Quelle",
|
||||
lastUpdate: "Letzte Aktualisierung: ",
|
||||
minAgo: "vor {n} Min", hrsAgo: "vor {n} Std",
|
||||
emptyDevelopments: "Noch keine Entwicklungen erfasst.",
|
||||
emptySummary: "Keine Zusammenfassung verfügbar.",
|
||||
},
|
||||
en: {
|
||||
hero: "SITUATION REPORT", heroResearch: "RESEARCH BRIEFING",
|
||||
@@ -67,11 +69,22 @@ var Lagebild = {
|
||||
sourceRef: "Source",
|
||||
lastUpdate: "Last update: ",
|
||||
minAgo: "{n} min ago", hrsAgo: "{n} hrs ago",
|
||||
emptyDevelopments: "No developments recorded yet.",
|
||||
emptySummary: "No summary available.",
|
||||
}
|
||||
},
|
||||
|
||||
curLang: function() {
|
||||
return (typeof getCurrentLanguage === 'function') ? getCurrentLanguage() : 'de';
|
||||
var l = (document.documentElement.lang || 'de').toLowerCase();
|
||||
return l.indexOf('en') === 0 ? 'en' : 'de';
|
||||
},
|
||||
|
||||
/* Where to fetch data/current.json from. Defaults to relative ("data/")
|
||||
which works on the German pages. English mirror pages set
|
||||
window.LAGEBILD_DATA_BASE to the absolute path of the German page so
|
||||
both languages share the same data files. */
|
||||
dataBase: function() {
|
||||
return (typeof window !== 'undefined' && window.LAGEBILD_DATA_BASE) || '';
|
||||
},
|
||||
|
||||
t: function(key) {
|
||||
@@ -103,10 +116,11 @@ var Lagebild = {
|
||||
this.initScrollProgress();
|
||||
this.initParticles();
|
||||
try {
|
||||
var savedLang = (typeof getCurrentLanguage === 'function') ? getCurrentLanguage() : 'de';
|
||||
var jsonFile = savedLang === 'en' ? 'data/current_en.json' : 'data/current.json';
|
||||
var savedLang = this.curLang();
|
||||
var base = this.dataBase();
|
||||
var jsonFile = base + (savedLang === 'en' ? 'data/current_en.json' : 'data/current.json');
|
||||
var resp = await fetch(jsonFile + '?t=' + Date.now());
|
||||
if (!resp.ok && savedLang === 'en') { resp = await fetch('data/current.json?t=' + Date.now()); }
|
||||
if (!resp.ok && savedLang === 'en') { resp = await fetch(base + 'data/current.json?t=' + Date.now()); }
|
||||
if (!resp.ok) throw new Error('HTTP ' + resp.status);
|
||||
this.data = await resp.json();
|
||||
this.currentView = {
|
||||
@@ -150,12 +164,12 @@ var Lagebild = {
|
||||
var dev = inc.latest_developments || '';
|
||||
var sources = (this.currentView && this.currentView.sources_json) || [];
|
||||
var html = this.renderLatestDevelopmentsHtml(dev, sources);
|
||||
el.innerHTML = html || '<p class="empty-hint">Noch keine Entwicklungen erfasst.</p>';
|
||||
el.innerHTML = html || '<p class="empty-hint">' + this.t('emptyDevelopments') + '</p>';
|
||||
} else {
|
||||
var md = (this.currentView && this.currentView.summary) || '';
|
||||
var zf = this.extractZusammenfassung(md);
|
||||
if (!zf) {
|
||||
el.innerHTML = '<p class="empty-hint">Keine Zusammenfassung verfügbar.</p>';
|
||||
el.innerHTML = '<p class="empty-hint">' + this.t('emptySummary') + '</p>';
|
||||
return;
|
||||
}
|
||||
var body = this.mdToHtml(this.fixUmlauts(zf));
|
||||
@@ -168,7 +182,7 @@ var Lagebild = {
|
||||
if (src && src.url) {
|
||||
return '<a class="citation-ref" href="' + self.esc(src.url) + '" target="_blank" rel="noopener" title="' + self.esc(src.name || '') + '">[' + nr + ']</a>';
|
||||
}
|
||||
return '<a class="citation-ref" title="Quelle ' + nr + '">[' + nr + ']</a>';
|
||||
return '<a class="citation-ref" title="' + self.t('sourceRef') + ' ' + nr + '">[' + nr + ']</a>';
|
||||
});
|
||||
el.innerHTML = body;
|
||||
}
|
||||
@@ -638,7 +652,7 @@ var Lagebild = {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var resp = await fetch('data/snapshot-' + id + '.json');
|
||||
var resp = await fetch(this.dataBase() + 'data/snapshot-' + id + '.json');
|
||||
if (!resp.ok) throw new Error('HTTP ' + resp.status);
|
||||
var sd = await resp.json();
|
||||
var sj = sd.sources_json;
|
||||
@@ -1214,11 +1228,12 @@ var Lagebild = {
|
||||
},
|
||||
|
||||
switchContent: async function(lang) {
|
||||
var jsonFile = lang === 'en' ? 'data/current_en.json' : 'data/current.json';
|
||||
var base = this.dataBase();
|
||||
var jsonFile = base + (lang === 'en' ? 'data/current_en.json' : 'data/current.json');
|
||||
try {
|
||||
var resp = await fetch(jsonFile + '?t=' + Date.now());
|
||||
if (!resp.ok && lang === 'en') {
|
||||
resp = await fetch('data/current.json?t=' + Date.now());
|
||||
resp = await fetch(base + 'data/current.json?t=' + Date.now());
|
||||
}
|
||||
if (!resp.ok) throw new Error('HTTP ' + resp.status);
|
||||
this.data = await resp.json();
|
||||
@@ -1267,7 +1282,7 @@ var Lagebild = {
|
||||
cta.className = 'floating-cta';
|
||||
cta.innerHTML = '<span class="floating-cta-text">' + this.t('ctaText') + '</span>'
|
||||
+ '<a href="mailto:info@aegis-sight.de" class="floating-cta-btn">' + this.t('ctaButton') + '</a>'
|
||||
+ '<button class="floating-cta-close" aria-label="Schlie\u00dfen">×</button>';
|
||||
+ '<button class="floating-cta-close" aria-label="' + this.t('srcClose') + '">×</button>';
|
||||
document.body.appendChild(cta);
|
||||
|
||||
// Show after scrolling past hero
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren