feat: add oikos-locale-picker component and language settings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ulas
2026-03-31 23:29:13 +02:00
parent 26a8434369
commit 431d6af356
4 changed files with 95 additions and 2 deletions
+80
View File
@@ -0,0 +1,80 @@
/**
* oikos-locale-picker — Sprachauswahl-Web-Component
* Zeigt Radio-Buttons für System/Deutsch/English.
* Bei Auswahl: setLocale() oder localStorage-Eintrag löschen (System).
* Dependencies: i18n.js
*/
import { t, setLocale, getLocale, getSupportedLocales } from '/i18n.js';
const LOCALE_LABELS = {
de: 'Deutsch',
en: 'English',
};
class OikosLocalePicker extends HTMLElement {
connectedCallback() {
this._render();
this._onLocaleChanged = () => this._render();
window.addEventListener('locale-changed', this._onLocaleChanged);
}
disconnectedCallback() {
window.removeEventListener('locale-changed', this._onLocaleChanged);
}
_render() {
this.textContent = '';
const stored = localStorage.getItem('oikos-locale');
const wrapper = document.createElement('div');
wrapper.className = 'locale-picker';
// System-Option
const systemOption = this._createOption(
'system',
t('settings.localeSystem'),
!stored,
() => {
localStorage.removeItem('oikos-locale');
location.reload();
}
);
wrapper.appendChild(systemOption);
// Sprach-Optionen
for (const locale of getSupportedLocales()) {
const option = this._createOption(
locale,
LOCALE_LABELS[locale] || locale,
stored === locale,
() => setLocale(locale)
);
wrapper.appendChild(option);
}
this.appendChild(wrapper);
}
_createOption(value, label, checked, onChange) {
const item = document.createElement('label');
item.className = 'locale-picker__option';
const radio = document.createElement('input');
radio.type = 'radio';
radio.name = 'locale';
radio.value = value;
radio.checked = checked;
radio.addEventListener('change', onChange);
const span = document.createElement('span');
span.textContent = label;
item.appendChild(radio);
item.appendChild(span);
return item;
}
}
customElements.define('oikos-locale-picker', OikosLocalePicker);
+3 -1
View File
@@ -508,7 +508,9 @@
"logout": "Abmelden",
"synchronizing": "Synchronisiere…",
"googleDisconnectConfirm": "Google Calendar-Verbindung trennen?",
"appleDisconnectConfirm": "Apple Calendar-Verbindung trennen?"
"appleDisconnectConfirm": "Apple Calendar-Verbindung trennen?",
"localeSystem": "System",
"languageTitle": "Sprache"
},
"login": {
+3 -1
View File
@@ -508,7 +508,9 @@
"logout": "Log out",
"synchronizing": "Syncing…",
"googleDisconnectConfirm": "Disconnect Google Calendar?",
"appleDisconnectConfirm": "Disconnect Apple Calendar?"
"appleDisconnectConfirm": "Disconnect Apple Calendar?",
"localeSystem": "System",
"languageTitle": "Language"
},
"login": {
+9
View File
@@ -6,6 +6,7 @@
import { api, auth } from '/api.js';
import { t, formatDate, formatTime } from '/i18n.js';
import '/components/oikos-locale-picker.js';
/**
* @param {HTMLElement} container
@@ -74,6 +75,14 @@ export async function render(container, { user }) {
</div>
</section>
<!-- Sprache -->
<section class="settings-section">
<h2 class="settings-section__title">${t('settings.languageTitle')}</h2>
<div class="settings-card">
<oikos-locale-picker></oikos-locale-picker>
</div>
</section>
<!-- Mein Konto -->
<section class="settings-section">
<h2 class="settings-section__title">${t('settings.sectionAccount')}</h2>