fix: weather icons in PWA + locale picker dropdown

- SW: skip cross-origin asset requests (opaque responses caused weather
  icons to break in PWA standalone mode on Android)
- Replace oikos-locale-picker radio buttons with <select> dropdown
- Add settings.localeLabel i18n key (de + en)
- Bump SW cache to v22

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ulas
2026-04-01 09:42:06 +02:00
parent cd9f26911b
commit af69431eac
4 changed files with 37 additions and 46 deletions
+28 -41
View File
@@ -1,6 +1,6 @@
/**
* oikos-locale-picker — Sprachauswahl-Web-Component
* Zeigt Radio-Buttons für System/Deutsch/English.
* Zeigt ein <select>-Dropdown für System/Deutsch/English.
* Bei Auswahl: setLocale() oder localStorage-Eintrag löschen (System).
* Dependencies: i18n.js
*/
@@ -24,56 +24,43 @@ class OikosLocalePicker extends HTMLElement {
}
_render() {
this.textContent = '';
const stored = localStorage.getItem('oikos-locale');
const wrapper = document.createElement('div');
wrapper.className = 'locale-picker';
const label = document.createElement('label');
label.className = 'locale-picker__label';
label.htmlFor = 'locale-select';
label.textContent = t('settings.localeLabel');
const select = document.createElement('select');
select.className = 'form-input locale-picker__select';
select.id = 'locale-select';
// System-Option
const systemOption = this._createOption(
'system',
t('settings.localeSystem'),
!stored,
() => {
localStorage.removeItem('oikos-locale');
location.reload();
}
);
wrapper.appendChild(systemOption);
const systemOpt = document.createElement('option');
systemOpt.value = 'system';
systemOpt.textContent = t('settings.localeSystem');
systemOpt.selected = !stored;
select.appendChild(systemOpt);
// Sprach-Optionen
for (const locale of getSupportedLocales()) {
const option = this._createOption(
locale,
LOCALE_LABELS[locale] || locale,
stored === locale,
() => setLocale(locale)
);
wrapper.appendChild(option);
const opt = document.createElement('option');
opt.value = locale;
opt.textContent = LOCALE_LABELS[locale] || locale;
opt.selected = stored === locale;
select.appendChild(opt);
}
this.appendChild(wrapper);
}
select.addEventListener('change', () => {
if (select.value === 'system') {
localStorage.removeItem('oikos-locale');
location.reload();
} else {
setLocale(select.value);
}
});
_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;
this.replaceChildren(label, select);
}
}
+1
View File
@@ -510,6 +510,7 @@
"googleDisconnectConfirm": "Google Calendar-Verbindung trennen?",
"appleDisconnectConfirm": "Apple Calendar-Verbindung trennen?",
"localeSystem": "System",
"localeLabel": "Sprache",
"languageTitle": "Sprache"
},
+1
View File
@@ -510,6 +510,7 @@
"googleDisconnectConfirm": "Disconnect Google Calendar?",
"appleDisconnectConfirm": "Disconnect Apple Calendar?",
"localeSystem": "System",
"localeLabel": "Language",
"languageTitle": "Language"
},
+7 -5
View File
@@ -12,9 +12,9 @@
* API: Immer Netzwerk (kein Caching von Nutzerdaten)
*/
const SHELL_CACHE = 'oikos-shell-v21';
const PAGES_CACHE = 'oikos-pages-v21';
const ASSETS_CACHE = 'oikos-assets-v21';
const SHELL_CACHE = 'oikos-shell-v22';
const PAGES_CACHE = 'oikos-pages-v22';
const ASSETS_CACHE = 'oikos-assets-v22';
const ALL_CACHES = [SHELL_CACHE, PAGES_CACHE, ASSETS_CACHE];
// App-Shell: sofort benötigt für ersten Render
@@ -123,8 +123,10 @@ self.addEventListener('fetch', (event) => {
return;
}
// Bilder + Fonts: Cache-First, langer TTL
if (isAsset(url.pathname)) {
// Bilder + Fonts: Cache-First, langer TTL — nur Same-Origin
// Cross-Origin-Assets (z.B. Wetter-Icons von openweathermap.org) nicht
// abfangen: opaque Responses führen im PWA-Modus zu Darstellungsfehlern.
if (isAsset(url.pathname) && url.origin === self.location.origin) {
event.respondWith(cacheFirst(request, ASSETS_CACHE));
return;
}