From c522b22cd98b7fb0e0af51c7381f177e2aa036e8 Mon Sep 17 00:00:00 2001 From: mrwnslz Date: Sat, 3 Oct 2026 13:53:01 +0200 Subject: [PATCH] =?UTF-8?q?Oberfl=C3=A4che=20eingedeutscht:=20Radio,=20B?= =?UTF-8?q?=C3=BCcher/Reader,=20Fokus,=20Konto,=20Anmeldung;=20sw=20v71?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - app.js, Templates, Stream-Fenster, Manifest: alle sichtbaren Texte deutsch (Eingabefenster "Abbrechen", Markierungs-Export, Lesezeichen, Reader-Einstellungen …) - LANGUAGE_CODE 'de': Formular-, Passwort- und Validierungsmeldungen von Django deutsch - Empfehlungen: "Was du unter der Woche abends oft hörst:" - Fehlercodes der API bleiben englisch (Vertrag für Clients und Tests); was die Seite davon zeigt, übersetzt _germanError() Co-Authored-By: Claude Opus 5.5 --- accounts/views.py | 2 +- diora/settings.py | 2 +- radio/tests.py | 2 +- radio/views.py | 11 +- static/js/app.js | 299 ++++++++++++++++------------- static/js/sw.js | 2 +- static/manifest.json | 2 +- templates/accounts/login.html | 8 +- templates/accounts/register.html | 8 +- templates/accounts/settings.html | 55 +++--- templates/base.html | 14 +- templates/radio/player.html | 120 ++++++------ templates/radio/stream_player.html | 18 +- 13 files changed, 284 insertions(+), 259 deletions(-) diff --git a/accounts/views.py b/accounts/views.py index 519c073..30878ff 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -97,7 +97,7 @@ def lastfm_connect(request): return render(request, 'accounts/settings.html', { 'profile': request.user.profile, 'has_lastfm': request.user.profile.has_lastfm(), - 'lastfm_error': f"Could not connect to Last.fm: {exc}", + 'lastfm_error': f"Verbindung zu Last.fm fehlgeschlagen: {exc}", }) diff --git a/diora/settings.py b/diora/settings.py index ffbe165..c4d3f6a 100644 --- a/diora/settings.py +++ b/diora/settings.py @@ -89,7 +89,7 @@ AUTH_PASSWORD_VALIDATORS = [ {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}, ] -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = 'de' TIME_ZONE = 'UTC' USE_I18N = True USE_TZ = True diff --git a/radio/tests.py b/radio/tests.py index e8c52fd..204d77c 100644 --- a/radio/tests.py +++ b/radio/tests.py @@ -23,7 +23,7 @@ class PlayerPagePodcastsTests(TestCase): def test_logged_out_page_asks_to_log_in(self): html = self.client.get('/').content.decode() - self.assertIn('to subscribe to podcasts.', html) + self.assertIn('um Podcasts zu abonnieren.', html) self.assertNotIn('dpw-loading', html) def test_podcast_ui_files_are_there(self): diff --git a/radio/views.py b/radio/views.py index 408297e..c3a7d90 100644 --- a/radio/views.py +++ b/radio/views.py @@ -365,15 +365,16 @@ def stop_play(request): # --------------------------------------------------------------------------- def _time_context_label(hour, is_weekend): - prefix = 'weekend' if is_weekend else 'weekday' + # Shown as "Was du {label} oft hörst:". + prefix = 'am Wochenende' if is_weekend else 'unter der Woche' if 5 <= hour <= 11: - period = 'morning' + period = 'morgens' elif 12 <= hour <= 16: - period = 'afternoon' + period = 'nachmittags' elif 17 <= hour <= 21: - period = 'evening' + period = 'abends' else: - period = 'night' + period = 'nachts' return f'{prefix} {period}' diff --git a/static/js/app.js b/static/js/app.js index 822d78b..6016b1b 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -130,6 +130,24 @@ function customConfirm(message, { danger = false } = {}) { return _showModal({ message, showCancel: true, danger }); } +// The server answers with short English error codes (an API contract, tests and +// other clients rely on them); what the page shows of them is German. +const _GERMAN_ERRORS = { + 'no stations found in file': 'In der Datei wurden keine Sender gefunden.', + 'file must be .m3u or .m3u8': 'Die Datei muss eine .m3u- oder .m3u8-Datei sein.', + 'no file uploaded': 'Keine Datei hochgeladen.', + 'only jpeg, png, or webp allowed': 'Nur JPEG, PNG oder WebP erlaubt.', + 'file too large (max 50 MB)': 'Datei zu groß (höchstens 50 MB).', + 'file too large (max 5 MB)': 'Datei zu groß (höchstens 5 MB).', + 'authentication required': 'Bitte anmelden.', + 'not found': 'Nicht gefunden.', + 'invalid JSON': 'Ungültige Anfrage.', + 'upstream unreachable': 'Der Server dahinter ist nicht erreichbar.', +}; +function _germanError(code) { + return _GERMAN_ERRORS[code] || code; +} + function customPrompt(message, defaultValue = '') { return _showModal({ message, showInput: true, inputValue: defaultValue, showCancel: true }); } @@ -162,7 +180,7 @@ function playStation(url, name, stationId) { $('now-playing-station').textContent = name; $('now-playing-track').textContent = ''; $('play-stop-btn').style.display = ''; - $('play-stop-btn').textContent = '⏹ Stop'; + $('play-stop-btn').textContent = '⏹ Stopp'; $('play-stop-btn').classList.add('playing'); $('save-station-btn').style.display = ''; @@ -200,7 +218,7 @@ function stopPlayback(clearStation = true) { sseSource = null; } - $('play-stop-btn').textContent = '▶ Play'; + $('play-stop-btn').textContent = '▶ Abspielen'; $('play-stop-btn').classList.remove('playing'); $('save-station-btn').style.display = 'none'; $('creamfresh-vote-up-btn').style.display = 'none'; @@ -215,7 +233,7 @@ function stopPlayback(clearStation = true) { if (clearStation) { currentStation = null; currentTrack = ''; - stationEl.textContent = '— no station —'; + stationEl.textContent = '— kein Sender —'; trackEl.textContent = ''; $('play-stop-btn').style.display = 'none'; } @@ -297,11 +315,11 @@ async function loadRecommendations() { const res = await fetch('/radio/recommendations/'); const data = await res.json(); if (!data.recommendations.length) { - container.innerHTML = '

Play more stations to get recommendations.

'; + container.innerHTML = '

Hör mehr Sender, dann gibt es hier Empfehlungen.

'; return; } const label = data.context; - let html = `

Based on your ${label} listening: