diff --git a/lagen/iran-konflikt/lagebild.js b/lagen/iran-konflikt/lagebild.js index 70e71bb..67621ff 100644 --- a/lagen/iran-konflikt/lagebild.js +++ b/lagen/iran-konflikt/lagebild.js @@ -14,6 +14,78 @@ var Lagebild = { timelineGroups: null, /* ===== Inline SVG Icons ===== */ + /* ===== LANGUAGE SUPPORT ===== */ + lang: { + de: { + hero: "LAGEBILD", heroResearch: "RECHERCHE", + tabBriefing: "Lagebild", tabBriefingResearch: "Recherche", + tabMap: "Karte", tabFactchecks: "Faktenchecks", tabSources: "Quellen", + statArticles: "Artikel", statSources: "Quellen", statFactchecks: "Faktenchecks", + dataSource: "Daten bereitgestellt durch AegisSight Monitor", + timelineCurrent: "Aktuell", timelineArticles: "Artikel", timelineFcs: "Faktenchecks", + srcArticlesFrom: "{count} Artikel aus {sources} Quellen", + srcArticle: "Artikel", srcClose: "Schlie\u00dfen", + mapNoData: "Keine Standortdaten verf\u00fcgbar", mapLegend: "Legende", mapArticles: "Artikel", + fcTotal: "Gesamt", fcConfirmed: "Best\u00e4tigt", fcOpen: "Offen", fcContradicted: "Widerlegt", + fcEvidence: "Evidenz:", fcProgression: "Verlauf:", fcSources: "unabh\u00e4ngige Quellen", + fcNone: "Keine Faktenchecks verf\u00fcgbar.", + fcCleaned: "{count} von {total} Faktenchecks verf\u00fcgbar (\u00e4ltere wurden bereinigt)", + ctaText: "AegisSight Monitor f\u00fcr Ihre Organisation", ctaButton: "Kontakt aufnehmen \u2192", + errorLoad: "Das Lagebild konnte nicht geladen werden. Bitte versuchen Sie es sp\u00e4ter erneut.", + snapshotHint: "", + standPrefix: "Stand: ", standSuffix: " Uhr", + stConfirmed: "Best\u00e4tigt", stUnconfirmed: "Unbest\u00e4tigt", stContradicted: "Widerlegt", + stDeveloping: "Unklar", stEstablished: "Gesichert", stDisputed: "Umstritten", + stFalse: "Falsch", stUnverified: "Nicht verifiziert", + sourceRef: "Quelle", + lastUpdate: "Letzte Aktualisierung: ", + minAgo: "vor {n} Min", hrsAgo: "vor {n} Std", + }, + en: { + hero: "SITUATION REPORT", heroResearch: "RESEARCH BRIEFING", + tabBriefing: "Briefing", tabBriefingResearch: "Research", + tabMap: "Map", tabFactchecks: "Fact Checks", tabSources: "Sources", + statArticles: "Articles", statSources: "Sources", statFactchecks: "Fact Checks", + dataSource: "Data provided by AegisSight Monitor", + timelineCurrent: "Current", timelineArticles: "Articles", timelineFcs: "Fact Checks", + srcArticlesFrom: "{count} articles from {sources} sources", + srcArticle: "Articles", srcClose: "Close", + mapNoData: "No location data available", mapLegend: "Legend", mapArticles: "Articles", + fcTotal: "Total", fcConfirmed: "Confirmed", fcOpen: "Open", fcContradicted: "Contradicted", + fcEvidence: "Evidence:", fcProgression: "History:", fcSources: "independent sources", + fcNone: "No fact checks available.", + fcCleaned: "{count} of {total} fact checks available (older ones were cleaned up)", + ctaText: "AegisSight Monitor for your organization", ctaButton: "Contact us \u2192", + errorLoad: "The briefing could not be loaded. Please try again later.", + snapshotHint: "Historical data available in German only", + standPrefix: "As of: ", standSuffix: "", + stConfirmed: "Confirmed", stUnconfirmed: "Unconfirmed", stContradicted: "Contradicted", + stDeveloping: "Developing", stEstablished: "Established", stDisputed: "Disputed", + stFalse: "False", stUnverified: "Unverified", + sourceRef: "Source", + lastUpdate: "Last update: ", + minAgo: "{n} min ago", hrsAgo: "{n} hrs ago", + } + }, + + curLang: function() { + return (typeof getCurrentLanguage === 'function') ? getCurrentLanguage() : 'de'; + }, + + t: function(key) { + var cl = this.curLang(); + return (this.lang[cl] && this.lang[cl][key]) || this.lang.de[key] || key; + }, + + getLocale: function() { + return this.curLang() === 'en' ? 'en-GB' : 'de-DE'; + }, + + getHeadline: function(article) { + if (this.curLang() === 'en') return article.headline || article.headline_de || ''; + return article.headline_de || article.headline || ''; + }, + icons: { clock: '', fileText: '', @@ -29,7 +101,10 @@ var Lagebild = { this.initScrollProgress(); this.initParticles(); try { - var resp = await fetch('data/current.json?t=' + Date.now()); + var savedLang = (typeof getCurrentLanguage === 'function') ? getCurrentLanguage() : 'de'; + var jsonFile = 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) throw new Error('HTTP ' + resp.status); this.data = await resp.json(); this.currentView = { @@ -147,11 +222,11 @@ var Lagebild = { var d = this.data; var genDate = new Date(this.toUTC(d.generated_at)); var diffMin = Math.max(1, Math.round((Date.now() - genDate.getTime()) / 60000)); - var diffText = diffMin < 60 ? ('vor ' + diffMin + ' Min') : ('vor ' + Math.round(diffMin / 60) + ' Std'); + var diffText = diffMin < 60 ? this.t('minAgo').replace('{n}', diffMin) : this.t('hrsAgo').replace('{n}', Math.round(diffMin / 60)); container.innerHTML = '
' + '' - + 'Letzte Aktualisierung: ' + diffText + '' + + '' + this.t('lastUpdate') + diffText + '' + '
'; }, @@ -160,13 +235,13 @@ var Lagebild = { var d = this.data; document.getElementById('incident-title').innerHTML = this.esc(this.fixUmlauts(d.incident.title)) + - ' \u2013 Stand: ' + this.fmtDateOnly(d.generated_at) + ', ' + this.fmtTimeOnly(d.generated_at) + ' Uhr'; + ' \u2013 ' + this.t('standPrefix') + this.fmtDateOnly(d.generated_at) + ', ' + this.fmtTimeOnly(d.generated_at) + this.t('standSuffix') + ''; // Stat Cards (3: Artikel, Quellen, Faktenchecks) var statsHtml = ''; - statsHtml += this.statCard(this.icons.fileText, '0', 'Artikel'); - statsHtml += this.statCard(this.icons.globe, '0', 'Quellen'); - statsHtml += this.statCard(this.icons.shieldCheck, '0', 'Faktenchecks'); + statsHtml += this.statCard(this.icons.fileText, '0', this.t("statArticles")); + statsHtml += this.statCard(this.icons.globe, '0', this.t("statSources")); + statsHtml += this.statCard(this.icons.shieldCheck, '0', this.t("statFactchecks")); document.getElementById('hero-stats').innerHTML = statsHtml; // Start count-up animations @@ -196,7 +271,7 @@ var Lagebild = { var progress = Math.min(elapsed / duration, 1); var eased = 1 - Math.pow(1 - progress, 3); // easeOutCubic var current = Math.round(target * eased); - element.textContent = current.toLocaleString('de-DE'); + element.textContent = current.toLocaleString(Lagebild.getLocale()); if (progress < 1) { requestAnimationFrame(update); } @@ -271,13 +346,13 @@ var Lagebild = { h += '>'; if (isActive) h += ''; h += '' + d.getUTCDate() + ''; - h += '' + d.toLocaleDateString('de-DE', { month: 'short', timeZone: 'UTC' }) + ''; + h += '' + d.toLocaleDateString(this.getLocale(), { month: 'short', timeZone: 'UTC' }) + ''; h += '' + defaultSnap.article_count + ''; if (daySnaps.length > 1) { h += '' + daySnaps.length + 'x'; } if (isActive) { - h += 'Aktuell'; + h += '' + this.t('timelineCurrent') + ''; } h += ''; } @@ -378,8 +453,8 @@ var Lagebild = { h += ' data-snapshot-id="' + snap.id + '">'; h += '' + this.fmtTimeOnly(snap.created_at) + ''; h += ''; - h += '' + snap.article_count + ' Artikel'; - h += '' + (snap.fact_check_count || 0) + ' Faktenchecks'; + h += '' + snap.article_count + ' ' + this.t('timelineArticles') + ''; + h += '' + (snap.fact_check_count || 0) + ' ' + this.t('timelineFcs') + ''; h += ''; } h += ''; @@ -453,11 +528,11 @@ var Lagebild = { var fcCount = this.currentView.fact_check_count || (this.currentView.fact_checks || []).length; var heroArt = document.getElementById('hero-art-count'); - if (heroArt) heroArt.textContent = artCount.toLocaleString('de-DE'); + if (heroArt) heroArt.textContent = artCount.toLocaleString(this.getLocale()); var heroSrc = document.getElementById('hero-src-count'); - if (heroSrc) heroSrc.textContent = srcCount.toLocaleString('de-DE'); + if (heroSrc) heroSrc.textContent = srcCount.toLocaleString(this.getLocale()); var heroFc = document.getElementById('hero-fc-count'); - if (heroFc) heroFc.textContent = fcCount.toLocaleString('de-DE'); + if (heroFc) heroFc.textContent = fcCount.toLocaleString(this.getLocale()); var fcBadge = document.getElementById('tab-badge-faktenchecks'); if (fcBadge) fcBadge.textContent = fcCount; @@ -484,7 +559,7 @@ var Lagebild = { if (src && src.url) { return '[' + nr + ']'; } - return '[' + nr + ']'; + return '[' + nr + ']'; }); document.getElementById('summary-content').innerHTML = html; @@ -504,7 +579,7 @@ var Lagebild = { var sourceMap = {}; for (var i = 0; i < articles.length; i++) { var a = articles[i]; - var name = a.source || 'Unbekannt'; + var name = a.source || 'Unknown'; if (!sourceMap[name]) sourceMap[name] = { count: 0, articles: [], languages: {}, domain: null }; sourceMap[name].count++; sourceMap[name].articles.push(a); @@ -531,7 +606,7 @@ var Lagebild = { // Header h += '
'; - h += '' + articles.length + ' Artikel aus ' + sources.length + ' Quellen'; + h += '' + this.t('srcArticlesFrom').replace('{count}', articles.length).replace('{sources}', sources.length) + ''; h += '
'; var langKeys = Object.keys(langTotals).sort(function(a, b) { return langTotals[b] - langTotals[a]; }); for (var i = 0; i < langKeys.length; i++) { @@ -613,15 +688,15 @@ var Lagebild = { h += ' '; } h += this.esc(src.name) + ''; - h += '' + src.data.count + ' Artikel'; - h += ''; + h += '' + src.data.count + ' ' + Lagebild.t('srcArticle') + ''; + h += ''; h += '
'; h += '
'; for (var j = 0; j < arts.length; j++) { var a = arts[j]; var dt = a.published_at || a.collected_at || ''; var dObj = dt ? new Date(this.toUTC(dt)) : null; - var hl = this.fixUmlauts(a.headline_de || a.headline || ''); + var hl = this.fixUmlauts(Lagebild.getHeadline(a)); h += '
'; if (a.source_url) { h += '' + this.esc(hl) + ' ' + this.icons.externalLink + ''; @@ -629,7 +704,7 @@ var Lagebild = { h += '' + this.esc(hl) + ''; } if (dObj && !isNaN(dObj.getTime())) { - h += ''; + h += ''; } h += '
'; } @@ -712,7 +787,7 @@ var Lagebild = { if (locs.length === 0) { var emptyDiv = document.createElement('div'); emptyDiv.style.cssText = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:1000;background:#151D2E;padding:20px 30px;border-radius:8px;border:1px solid #1E2D45;color:#8896AB;text-align:center;'; - emptyDiv.innerHTML = 'Keine Standortdaten verfuegbar'; + emptyDiv.innerHTML = Lagebild.t('mapNoData'); document.getElementById('map-container').appendChild(emptyDiv); } @@ -736,7 +811,7 @@ var Lagebild = { // Popup mit Artikel-Links var popupText = '' + (l.name || '') + ''; if (l.country_code) popupText += ' (' + l.country_code + ')'; - popupText += '
' + (l.article_count || 0) + ' Artikel'; + popupText += '
' + (l.article_count || 0) + ' ' + Lagebild.t('mapArticles') + ''; if (l.top_articles && l.top_articles.length > 0) { popupText += '
'; for (var j = 0; j < l.top_articles.length; j++) { @@ -774,7 +849,7 @@ var Lagebild = { legend.onAdd = function() { var div = L.DomUtil.create('div', 'map-legend'); div.style.cssText = 'background:#151D2E;padding:10px 14px;border-radius:4px;border:1px solid #1E2D45;box-shadow:0 2px 8px rgba(0,0,0,0.3);font-size:0.8rem;line-height:1.8;color:#E8ECF4;'; - var html = 'Legende
'; + var html = '' + Lagebild.t('mapLegend') + '
'; ['primary', 'secondary', 'tertiary', 'mentioned'].forEach(function(cat) { if (usedCategories[cat] && categoryLabels[cat]) { html += ' ' + categoryLabels[cat] + '
'; @@ -813,21 +888,12 @@ var Lagebild = { unverified: '?' }, - fcLabels: { - confirmed: 'Bestätigt', - unconfirmed: 'Unbestätigt', - contradicted: 'Widerlegt', - developing: 'Unklar', - established: 'Gesichert', - disputed: 'Umstritten', - 'false': 'Falsch', - unverified: 'Nicht verifiziert' - }, + fcLabels: {}, renderFactChecksTab: function() { var checks = this.currentView.fact_checks || []; if (!checks.length) { - document.getElementById('factchecks-content').innerHTML = '

Keine Faktenchecks verfügbar.

'; + document.getElementById('factchecks-content').innerHTML = '

' + this.t('fcNone') + '

'; return; } @@ -844,18 +910,18 @@ var Lagebild = { // Stat cards (clickable filters) var h = '
'; - h += ''; - h += ''; - h += ''; + h += ''; + h += ''; + h += ''; if (contradictedTotal > 0) - h += ''; + h += ''; h += '
'; // Hinweis bei unvollständiger Liste var storedFcCount = this.currentView.fact_check_count || 0; if (storedFcCount > 0 && checks.length < storedFcCount) { h += '
'; - h += checks.length + ' von ' + storedFcCount + ' Faktenchecks verfügbar (ältere wurden bereinigt)'; + h += this.t('fcCleaned').replace('{count}', checks.length).replace('{total}', storedFcCount); h += '
'; } @@ -879,7 +945,7 @@ var Lagebild = { var hasProg = fc.status_history && fc.status_history.length > 1; var icon = this.fcIcons[status] || '?'; - var label = this.fcLabels[status] || status; + var label = this.stLabel(status); h += '
'; h += '
'; @@ -893,17 +959,17 @@ var Lagebild = { // Expandable detail h += '
'; h += '
'; - h += '
' + icon + ' ' + label + ' – ' + (fc.sources_count || 0) + ' unabhängige Quellen
'; + h += '
' + icon + ' ' + label + ' – ' + (fc.sources_count || 0) + ' ' + this.t('fcSources') + '
'; if (fc.evidence) { var ev = this.fixUmlauts(fc.evidence); ev = this.esc(ev).replace(/(https?:\/\/[^\s,)]+)/g, '$1'); - h += '
Evidenz: ' + ev + '
'; + h += '
' + this.t('fcEvidence') + ' ' + ev + '
'; } if (hasProg) { h += '
'; - h += 'Verlauf:'; + h += '' + this.t('fcProgression') + ''; for (var j = 0; j < fc.status_history.length; j++) { var step = fc.status_history[j]; if (j > 0) h += ''; @@ -991,21 +1057,66 @@ var Lagebild = { initLangToggle: function() { var btn = document.querySelector('.lang-toggle'); if (!btn) return; + var self = this; btn.addEventListener('click', function(e) { e.preventDefault(); - if (typeof switchLanguage === 'function') { - var cur = (typeof getCurrentLanguage === 'function') ? getCurrentLanguage() : 'de'; - switchLanguage(cur === 'de' ? 'en' : 'de'); - } + var cur = (typeof getCurrentLanguage === 'function') ? getCurrentLanguage() : 'de'; + var newLang = cur === 'de' ? 'en' : 'de'; + if (typeof switchLanguage === 'function') switchLanguage(newLang); + self.switchContent(newLang); }); }, + switchContent: async function(lang) { + var jsonFile = 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()); + } + if (!resp.ok) throw new Error('HTTP ' + resp.status); + this.data = await resp.json(); + this.allSnapshots = {}; + this.currentView = { + summary: this.data.current_lagebild.summary_markdown, + sources_json: this.data.current_lagebild.sources_json, + updated_at: this.data.current_lagebild.updated_at || this.data.generated_at, + articles: this.data.articles, + fact_checks: this.data.fact_checks, + article_count: this.data.incident.article_count, + fact_check_count: this.data.incident.factcheck_count + }; + this.render(); + // Update hero title for language + var heroH1 = document.getElementById('hero-title'); + if (heroH1) { + var isResearch = this.data.incident && this.data.incident.type === 'research'; + heroH1.textContent = isResearch ? this.t('heroResearch') : this.t('hero'); + } + // Update tab labels + var tabBtns = document.querySelectorAll('.tab-btn'); + var isResearch = this.data.incident && this.data.incident.type === 'research'; + for (var i = 0; i < tabBtns.length; i++) { + var tab = tabBtns[i].getAttribute('data-tab'); + if (tab === 'lagebild') tabBtns[i].childNodes[0].textContent = isResearch ? this.t('tabBriefingResearch') : this.t('tabBriefing'); + else if (tab === 'karte') tabBtns[i].childNodes[0].textContent = this.t('tabMap'); + else if (tab === 'faktenchecks') tabBtns[i].childNodes[0].textContent = this.t('tabFactchecks') + ' '; + else if (tab === 'quellen') tabBtns[i].childNodes[0].textContent = this.t('tabSources') + ' '; + } + // Update data source note + var dsNote = document.querySelector('.data-source-note'); + if (dsNote) dsNote.textContent = this.t('dataSource'); + } catch(e) { + console.error('Language switch failed:', e); + } + }, + /* ===== FLOATING CTA ===== */ initFloatingCta: function() { var cta = document.createElement('div'); cta.className = 'floating-cta'; - cta.innerHTML = 'AegisSight Monitor f\u00fcr Ihre Organisation' - + 'Kontakt aufnehmen \u2192' + cta.innerHTML = '' + this.t('ctaText') + '' + + '' + this.t('ctaButton') + '' + ''; document.body.appendChild(cta); @@ -1115,9 +1226,8 @@ var Lagebild = { }, stLabel: function(s) { - return { confirmed: 'Best\u00e4tigt', unconfirmed: 'Unbest\u00e4tigt', established: 'Gesichert', - unverified: 'Nicht verifiziert', contradicted: 'Widerlegt', disputed: 'Umstritten', - developing: 'In Entwicklung', 'false': 'Falsch' }[s] || s; + var key = 'st' + s.charAt(0).toUpperCase() + s.slice(1); + return this.t(key) || s; }, mdToHtml: function(md) { @@ -1163,11 +1273,12 @@ var Lagebild = { var d = new Date(this.toUTC(iso)); if (isNaN(d.getTime())) return iso; var opts = { timeZone: TIMEZONE, weekday: 'long', day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false }; - var parts = new Intl.DateTimeFormat('de-DE', opts).formatToParts(d); + var locale = Lagebild.getLocale(); + var parts = new Intl.DateTimeFormat(locale, opts).formatToParts(d); var p = {}; parts.forEach(function(x) { p[x.type] = x.value; }); - return p.weekday + ', ' + p.day + '. ' + p.month + ' ' + p.year - + ' um ' + p.hour + ':' + p.minute + ' Uhr'; + if (locale === 'en-GB') return p.weekday + ', ' + p.day + ' ' + p.month + ' ' + p.year + ', ' + p.hour + ':' + p.minute; + return p.weekday + ', ' + p.day + '. ' + p.month + ' ' + p.year + ' um ' + p.hour + ':' + p.minute + ' Uhr'; } catch(e) { return iso; } }, @@ -1176,7 +1287,7 @@ var Lagebild = { try { var d = new Date(this.toUTC(iso)); if (isNaN(d.getTime())) return iso; - return d.toLocaleDateString('de-DE', { day: 'numeric', month: 'short', year: 'numeric', timeZone: TIMEZONE }); + return d.toLocaleDateString(Lagebild.getLocale(), { day: 'numeric', month: 'short', year: 'numeric', timeZone: TIMEZONE }); } catch(e) { return iso; } }, @@ -1185,19 +1296,19 @@ var Lagebild = { try { var d = new Date(this.toUTC(iso)); if (isNaN(d.getTime())) return iso; - return d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', timeZone: TIMEZONE }); + return d.toLocaleTimeString(Lagebild.getLocale(), { hour: '2-digit', minute: '2-digit', timeZone: TIMEZONE }); } catch(e) { return iso; } }, fmtShort: function(iso) { if (!iso) return ''; - try { return new Date(this.toUTC(iso)).toLocaleDateString('de-DE', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: TIMEZONE }); } + try { return new Date(this.toUTC(iso)).toLocaleDateString(Lagebild.getLocale(), { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: TIMEZONE }); } catch(e) { return iso; } }, showError: function() { document.getElementById('summary-content').innerHTML = - '

Das Lagebild konnte nicht geladen werden. Bitte versuchen Sie es sp\u00e4ter erneut.

'; + '

' + this.t('errorLoad') + '

'; } };