Backend: - UserMeResponse um output_language (de | en) erweitert. - /auth/me liefert die Org-Sprache aus organization_settings. Frontend: - Neu: static/js/i18n.js mit T(key)-Helper, I18N.load(lang) und applyDom() ueber data-i18n + data-i18n-attr. - Neu: static/i18n/de.json + en.json (sichtbare Bereiche: Sidebar, Header, Modal-Titel, Faktencheck-Status, Refresh-Hinweise). - dashboard.html: i18n.js Script-Tag vor api.js, data-i18n auf den prominenten Strings (Abmelden, + Neuer Fall, Alle/Eigene, Sidebar- Sektionen, Bericht exportieren, Faktencheck-Tab, Lage anlegen). Tutorial.init() entfernt aus DOMContentLoaded. - components.js: factCheckLabels/Tooltips/ChipLabels als Getter ueber T() mit DE-Fallbacks. - app.js: vor Setup wird I18N.load(user.output_language) aufgerufen und applyDom() ausgefuehrt. Tutorial.init() laeuft nur bei lang === de. Phase 6 von 8 (eng_demo / Org-Sprache).
72 Zeilen
2.6 KiB
JavaScript
72 Zeilen
2.6 KiB
JavaScript
// Light-i18n fuer AegisSight Monitor.
|
|
// Wird vor app.js geladen. T(key) ist global verfuegbar.
|
|
//
|
|
// Aufrufer:
|
|
// await I18N.load(lang); // 'de' oder 'en'
|
|
// const txt = T('sidebar.live_monitoring');
|
|
// I18N.applyDom(); // ersetzt alle <... data-i18n="key">...</...>
|
|
|
|
(function () {
|
|
const STORAGE_KEY = 'aegis_lang';
|
|
|
|
const I18N = {
|
|
lang: 'de',
|
|
dict: {},
|
|
|
|
async load(lang) {
|
|
if (!lang) lang = 'de';
|
|
if (lang !== 'de' && lang !== 'en') lang = 'de';
|
|
this.lang = lang;
|
|
try {
|
|
const res = await fetch(`/static/i18n/${lang}.json?v=20260513`);
|
|
if (res.ok) {
|
|
this.dict = await res.json();
|
|
} else {
|
|
console.warn(`i18n: Konnte ${lang}.json nicht laden (${res.status})`);
|
|
this.dict = {};
|
|
}
|
|
} catch (e) {
|
|
console.warn('i18n-Load fehlgeschlagen:', e);
|
|
this.dict = {};
|
|
}
|
|
try { localStorage.setItem(STORAGE_KEY, lang); } catch (_) {}
|
|
document.documentElement.setAttribute('lang', lang);
|
|
return this.dict;
|
|
},
|
|
|
|
// Synchroner Initial-Lookup aus localStorage (fuer FOUC-freies Bootstrap).
|
|
bootLang() {
|
|
try { return localStorage.getItem(STORAGE_KEY) || 'de'; } catch (_) { return 'de'; }
|
|
},
|
|
|
|
// Ersetzt alle data-i18n Attribute im DOM.
|
|
applyDom(root) {
|
|
root = root || document;
|
|
root.querySelectorAll('[data-i18n]').forEach(el => {
|
|
const key = el.getAttribute('data-i18n');
|
|
if (!key) return;
|
|
const txt = this.dict[key];
|
|
if (txt != null) el.textContent = txt;
|
|
});
|
|
// Attribute (z.B. placeholder, title): data-i18n-attr="placeholder:key,title:key2"
|
|
root.querySelectorAll('[data-i18n-attr]').forEach(el => {
|
|
const spec = el.getAttribute('data-i18n-attr') || '';
|
|
spec.split(',').forEach(pair => {
|
|
const [attr, key] = pair.split(':').map(s => s && s.trim());
|
|
if (!attr || !key) return;
|
|
const txt = this.dict[key];
|
|
if (txt != null) el.setAttribute(attr, txt);
|
|
});
|
|
});
|
|
},
|
|
};
|
|
|
|
function T(key, fallback) {
|
|
if (I18N.dict && I18N.dict[key] != null) return I18N.dict[key];
|
|
return fallback != null ? fallback : key;
|
|
}
|
|
|
|
window.I18N = I18N;
|
|
window.T = T;
|
|
})();
|