From e4dcfb3845476c3fc2e60cd8e9fa1c04fbdbc0da Mon Sep 17 00:00:00 2001 From: npeter83 Date: Mon, 13 Jul 2026 05:13:08 +0200 Subject: [PATCH] feat(i18n): retire the German interface language Only English and Hungarian remain. Drops the de locale files, the LANGUAGES entry, the Google-locale mapping in auth, and the German welcome-message template. A legacy "de" preference falls back to English gracefully. Content- language detection and subtitle languages are untouched (media, not UI). --- README.md | 4 +- backend/app/auth.py | 2 +- backend/app/routes/messages.py | 8 - frontend/src/components/LanguageSwitcher.tsx | 2 +- frontend/src/i18n/index.ts | 1 - frontend/src/i18n/locales/de/about.json | 12 - frontend/src/i18n/locales/de/audit.json | 18 -- .../src/i18n/locales/de/auditActions.json | 21 -- frontend/src/i18n/locales/de/card.json | 20 -- frontend/src/i18n/locales/de/channel.json | 27 --- frontend/src/i18n/locales/de/channels.json | 124 ---------- frontend/src/i18n/locales/de/common.json | 21 -- frontend/src/i18n/locales/de/config.json | 204 ---------------- frontend/src/i18n/locales/de/datatable.json | 15 -- frontend/src/i18n/locales/de/downloads.json | 201 --------------- frontend/src/i18n/locales/de/editor.json | 45 ---- frontend/src/i18n/locales/de/errors.json | 17 -- frontend/src/i18n/locales/de/feed.json | 60 ----- frontend/src/i18n/locales/de/header.json | 59 ----- frontend/src/i18n/locales/de/inbox.json | 24 -- frontend/src/i18n/locales/de/login.json | 12 - frontend/src/i18n/locales/de/messages.json | 35 --- frontend/src/i18n/locales/de/nav.json | 11 - .../src/i18n/locales/de/notifications.json | 15 -- frontend/src/i18n/locales/de/onboarding.json | 29 --- frontend/src/i18n/locales/de/player.json | 40 --- frontend/src/i18n/locales/de/playlists.json | 61 ----- frontend/src/i18n/locales/de/plex.json | 229 ------------------ .../src/i18n/locales/de/quotaActions.json | 18 -- frontend/src/i18n/locales/de/scheduler.json | 110 --------- frontend/src/i18n/locales/de/settings.json | 155 ------------ frontend/src/i18n/locales/de/setup.json | 50 ---- frontend/src/i18n/locales/de/sidebar.json | 76 ------ frontend/src/i18n/locales/de/stats.json | 54 ----- frontend/src/i18n/locales/de/tagManager.json | 12 - frontend/src/i18n/locales/de/time.json | 19 -- frontend/src/i18n/locales/de/users.json | 46 ---- frontend/src/i18n/locales/de/views.json | 16 -- frontend/src/i18n/locales/de/welcome.json | 80 ------ frontend/src/i18n/locales/en/welcome.json | 2 +- frontend/src/i18n/locales/hu/welcome.json | 2 +- 41 files changed, 6 insertions(+), 1951 deletions(-) delete mode 100644 frontend/src/i18n/locales/de/about.json delete mode 100644 frontend/src/i18n/locales/de/audit.json delete mode 100644 frontend/src/i18n/locales/de/auditActions.json delete mode 100644 frontend/src/i18n/locales/de/card.json delete mode 100644 frontend/src/i18n/locales/de/channel.json delete mode 100644 frontend/src/i18n/locales/de/channels.json delete mode 100644 frontend/src/i18n/locales/de/common.json delete mode 100644 frontend/src/i18n/locales/de/config.json delete mode 100644 frontend/src/i18n/locales/de/datatable.json delete mode 100644 frontend/src/i18n/locales/de/downloads.json delete mode 100644 frontend/src/i18n/locales/de/editor.json delete mode 100644 frontend/src/i18n/locales/de/errors.json delete mode 100644 frontend/src/i18n/locales/de/feed.json delete mode 100644 frontend/src/i18n/locales/de/header.json delete mode 100644 frontend/src/i18n/locales/de/inbox.json delete mode 100644 frontend/src/i18n/locales/de/login.json delete mode 100644 frontend/src/i18n/locales/de/messages.json delete mode 100644 frontend/src/i18n/locales/de/nav.json delete mode 100644 frontend/src/i18n/locales/de/notifications.json delete mode 100644 frontend/src/i18n/locales/de/onboarding.json delete mode 100644 frontend/src/i18n/locales/de/player.json delete mode 100644 frontend/src/i18n/locales/de/playlists.json delete mode 100644 frontend/src/i18n/locales/de/plex.json delete mode 100644 frontend/src/i18n/locales/de/quotaActions.json delete mode 100644 frontend/src/i18n/locales/de/scheduler.json delete mode 100644 frontend/src/i18n/locales/de/settings.json delete mode 100644 frontend/src/i18n/locales/de/setup.json delete mode 100644 frontend/src/i18n/locales/de/sidebar.json delete mode 100644 frontend/src/i18n/locales/de/stats.json delete mode 100644 frontend/src/i18n/locales/de/tagManager.json delete mode 100644 frontend/src/i18n/locales/de/time.json delete mode 100644 frontend/src/i18n/locales/de/users.json delete mode 100644 frontend/src/i18n/locales/de/views.json delete mode 100644 frontend/src/i18n/locales/de/welcome.json diff --git a/README.md b/README.md index 22ef321..ebc3b11 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,8 @@ blocker and SponsorBlock keep working. presets, per-user storage quota), trim / crop / split & join them in a built-in editor, then save to your device, share with another user, or hand out a public watch link. - **Multi-user** with per-user private state, a shared catalog, and a fair daily API-quota guard. -- **Self-hosted & private**, with a first-run web setup wizard and the interface in **English, - Hungarian and German**. +- **Self-hosted & private**, with a first-run web setup wizard and the interface in **English + and Hungarian**. ## Quick start (self-hosting) diff --git a/backend/app/auth.py b/backend/app/auth.py index 5f814d6..cdd8490 100644 --- a/backend/app/auth.py +++ b/backend/app/auth.py @@ -364,7 +364,7 @@ async def callback( prefs = dict(user.preferences or {}) if not prefs.get("language"): loc = (userinfo.get("locale") or "").split("-")[0].lower() - prefs["language"] = loc if loc in {"en", "hu", "de"} else "en" + prefs["language"] = loc if loc in {"en", "hu"} else "en" user.preferences = prefs db.flush() diff --git a/backend/app/routes/messages.py b/backend/app/routes/messages.py index de41d13..d40d3e7 100644 --- a/backend/app/routes/messages.py +++ b/backend/app/routes/messages.py @@ -51,14 +51,6 @@ WELCOME = { "jelszóval.\n\n" "Jó böngészést!" ), - "de": ( - "Willkommen bei Siftlode! 👋\n\n" - "Das ist dein privater Nachrichten-Posteingang. Unterhaltungen mit anderen Mitgliedern " - "sind Ende-zu-Ende-verschlüsselt — nur du und dein Gegenüber könnt sie lesen, nicht " - "einmal ein Admin. Um jemandem zu schreiben, richte zuerst die sichere " - "Nachrichtenübermittlung mit einem Passwort ein.\n\n" - "Viel Spaß mit deinem Feed!" - ), } diff --git a/frontend/src/components/LanguageSwitcher.tsx b/frontend/src/components/LanguageSwitcher.tsx index eaaf244..3aee33a 100644 --- a/frontend/src/components/LanguageSwitcher.tsx +++ b/frontend/src/components/LanguageSwitcher.tsx @@ -95,7 +95,7 @@ export default function LanguageSwitcher({ className="relative p-2 rounded-lg text-muted hover:text-fg hover:bg-card transition" > - {/* Current-language badge (HU/EN/DE) so the active language reads at a glance without + {/* Current-language badge (HU/EN) so the active language reads at a glance without opening the menu. The ring matches the page background to detach it from the icon. */} {current.code.toUpperCase()} diff --git a/frontend/src/i18n/index.ts b/frontend/src/i18n/index.ts index 1e07905..2d7d503 100644 --- a/frontend/src/i18n/index.ts +++ b/frontend/src/i18n/index.ts @@ -6,7 +6,6 @@ import { LS } from "../lib/storage"; export const LANGUAGES = [ { code: "en", label: "English" }, { code: "hu", label: "Magyar" }, - { code: "de", label: "Deutsch" }, ] as const; export type LangCode = (typeof LANGUAGES)[number]["code"]; const SUPPORTED = LANGUAGES.map((l) => l.code) as readonly string[]; diff --git a/frontend/src/i18n/locales/de/about.json b/frontend/src/i18n/locales/de/about.json deleted file mode 100644 index c529a67..0000000 --- a/frontend/src/i18n/locales/de/about.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "title": "Über", - "tagline": "Selbstgehosteter, mehrbenutzerfähiger Reader für deine YouTube-Abos.", - "frontend": "Frontend", - "backend": "Backend", - "database": "Datenbank", - "buildDate": "Build-Datum", - "whatsNew": "Neuigkeiten", - "releaseNotes": "Versionshinweise", - "new": "Neu", - "fixes": "Korrekturen" -} diff --git a/frontend/src/i18n/locales/de/audit.json b/frontend/src/i18n/locales/de/audit.json deleted file mode 100644 index cabffc3..0000000 --- a/frontend/src/i18n/locales/de/audit.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "title": "Audit-Protokoll", - "intro": "Ein reines Anhänge-Protokoll der Admin-Aktionen und Scheduler-Laufzusammenfassungen — wer was wann getan hat. Auf Aktionsebene protokolliert, nie pro Element.", - "empty": "Noch keine Protokolleinträge.", - "system": "System", - "clear": "Protokoll leeren", - "clearTitle": "Audit-Protokoll leeren?", - "clearConfirm": "Alle Protokolleinträge dauerhaft löschen? Das kann nicht rückgängig gemacht werden. (Ein Eintrag, der festhält, dass du es geleert hast, bleibt erhalten.)", - "cleared": "{{count}} Protokolleinträge gelöscht", - "clearFailed": "Das Protokoll konnte nicht geleert werden.", - "truncated": "Die {{returned}} neuesten von {{total}} Einträgen werden angezeigt.", - "cols": { - "when": "Wann", - "actor": "Wer", - "action": "Aktion", - "details": "Details" - } -} diff --git a/frontend/src/i18n/locales/de/auditActions.json b/frontend/src/i18n/locales/de/auditActions.json deleted file mode 100644 index 4705cfe..0000000 --- a/frontend/src/i18n/locales/de/auditActions.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "user_role_change": "Rolle geändert", - "user_suspend": "Benutzer gesperrt", - "user_unsuspend": "Benutzer reaktiviert", - "user_delete": "Benutzer gelöscht", - "invite_approve": "Zugriffsanfrage genehmigt", - "invite_deny": "Zugriffsanfrage abgelehnt", - "invite_add": "E-Mail auf Whitelist", - "demo_add": "Demo-E-Mail hinzugefügt", - "demo_remove": "Demo-E-Mail entfernt", - "demo_reset": "Demo zurückgesetzt", - "discovery_purge": "Entdeckung bereinigt", - "config_set": "Einstellung geändert", - "config_reset": "Einstellung zurückgesetzt", - "scheduler_interval": "Job-Intervall geändert", - "maintenance_tune": "Wartung angepasst", - "sync_pause": "Sync pausiert", - "sync_resume": "Sync fortgesetzt", - "job_run": "Scheduler-Job ausgeführt", - "audit_clear": "Audit-Protokoll geleert" -} diff --git a/frontend/src/i18n/locales/de/card.json b/frontend/src/i18n/locales/de/card.json deleted file mode 100644 index ff3e4e7..0000000 --- a/frontend/src/i18n/locales/de/card.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "live": "live", - "upcoming": "demnächst", - "watchedUnmark": "Angesehen — zum Aufheben klicken", - "markWatched": "Als angesehen markieren", - "savedRemove": "Gespeichert — zum Entfernen klicken", - "saveForLater": "Für später speichern", - "unhide": "Einblenden", - "hide": "Ausblenden", - "onlyThisChannel": "Nur dieser Kanal", - "resetState": "Zurücksetzen — Fortschritt/Status löschen", - "thisChannel": "Dieser Kanal", - "continueTitle": "Dort fortsetzen, wo du aufgehört hast", - "continue": "Fortsetzen", - "restartTitle": "Von vorne abspielen", - "restart": "Von vorne", - "play": "Abspielen", - "stream": "Stream", - "views": "Aufrufe" -} diff --git a/frontend/src/i18n/locales/de/channel.json b/frontend/src/i18n/locales/de/channel.json deleted file mode 100644 index a5cfe78..0000000 --- a/frontend/src/i18n/locales/de/channel.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "back": "Zurück", - "exploringBadge": "Erkunden", - "subscribe": "Abonnieren", - "subscribedState": "Abonniert", - "unsubscribe": "Abbestellen", - "unsubTitle": "Abbestellen?", - "unsubBody": "{{name}} auf YouTube nicht mehr folgen?", - "subscribed": "{{name}} abonniert", - "subscribers": "{{formatted}} Abonnenten", - "videoCount_one": "{{count}} Video", - "videoCount_other": "{{count}} Videos", - "totalViews": "{{formatted}} Aufrufe", - "joined": "Beigetreten {{date}}", - "loadingVideos": "Videos dieses Kanals werden geladen…", - "tabVideos": "Videos", - "tabAbout": "Info", - "loadMore": "Mehr von YouTube laden", - "noDescription": "Dieser Kanal hat keine Beschreibung.", - "block": "Kanal blockieren", - "unblock": "Blockierung aufheben", - "blockedBadge": "Blockiert", - "country": "Land", - "language": "Sprache", - "topics": "Themen", - "keywords": "Schlagwörter" -} diff --git a/frontend/src/i18n/locales/de/channels.json b/frontend/src/i18n/locales/de/channels.json deleted file mode 100644 index a1cc7fc..0000000 --- a/frontend/src/i18n/locales/de/channels.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "intro": "Lege die <0>Priorität eines Kanals fest, um seine Videos nach oben zu schieben, wenn du nach „Kanalpriorität“ sortierst, hänge eigene <1>Tags an, um den Feed zu filtern, oder <2>blende einen Kanal <2>aus, um ihn ohne Abbestellung aus dem Feed zu entfernen.", - "syncSubscriptions": "Abos von YouTube lesen", - "syncSubscriptionsHint": "Importiert deine Abo-Liste erneut von YouTube — fügt neu abonnierte Kanäle hinzu und entfernt abbestellte. Die Videos selbst werden weiterhin automatisch im Hintergrund synchronisiert; sie werden hierbei nicht neu geladen.", - "backfillEverything": "Alles nachladen", - "backfillEverythingHint": "Fordert das vollständige Nachladen des gesamten Katalogs für jeden abonnierten Kanal an. Ältere Videos und die Suche werden vollständig, soweit das gemeinsame Tageskontingent es zulässt — das kann eine Weile dauern.", - "tabs": { - "subscribed": "Abonnements", - "discovery": "Aus Playlists entdecken" - }, - "discovery": { - "intro": "Kanäle, die in deinen Playlists vorkommen, die du aber nicht abonniert hast. Abonniere sie, um ihnen zu folgen — ihre neuen Uploads erscheinen dann in deinem Feed (das Abonnieren kostet etwas YouTube-Kontingent; vorhandene Videos werden nicht erneut geladen).", - "loading": "Kanäle werden gesucht…", - "empty": "Keine neuen Kanäle — jeden Kanal in deinen Playlists abonnierst du bereits.", - "subscribe": "Abonnieren", - "subscribeHint": "Diesen Kanal auf YouTube abonnieren (ändert dein echtes Konto; verbraucht etwas API-Kontingent). Seine Videos kommen bei der nächsten Hintergrund-Synchronisierung.", - "needWriteHint": "Aktiviere das Bearbeiten von Playlists in den Einstellungen, um auf YouTube zu abonnieren.", - "subscribedTitle": "Auf YouTube abonniert", - "subscribedBody": "Du folgst jetzt {{name}} — neue Uploads erscheinen in deinem Feed.", - "subscribeFailed": "Abonnieren fehlgeschlagen", - "confirmSubscribe": "„{{name}}“ auf YouTube abonnieren? Das ändert dein echtes YouTube-Konto und verbraucht etwas API-Kontingent.", - "cols": { - "totalVideos": "Videos", - "inPlaylists": "In Playlists", - "inPlaylistsHint": "{{videos}} Video(s) von diesem Kanal in {{playlists}} deiner Playlist(s)." - } - }, - "filters": { - "all": "Alle", - "needsFull": "Verlauf unvollständig", - "fullySynced": "Vollständig synchronisiert", - "hidden": "Ausgeblendet" - }, - "tags": { - "yourTags": "Deine Tags", - "manage": "Tags verwalten", - "yourTagsHint": "Deine persönlichen Labels. Hänge sie unten an Kanäle an und filtere den Feed dann über die Seitenleiste nach Tag. (Getrennt von den automatischen Sprach-/Themen-Tags.)" - }, - "loading": "Kanäle werden geladen…", - "empty": "Keine Kanäle.", - "pager": { - "prev": "Zurück", - "next": "Weiter", - "page": "Seite {{page}} von {{total}}" - }, - "cols": { - "priority": "Prio", - "channel": "Kanal", - "stored": "Gespeichert", - "subs": "Abonnenten", - "lastUpload": "Letzter Upload", - "length": "Länge", - "types": "N/S/L", - "typesHint": "Videos nach Typ: Normale / Shorts / Live", - "sync": "Sync", - "tags": "Tags", - "actions": "Aktionen" - }, - "stats": { - "channels": "Kanäle", - "channelsHint": "Kanäle, die du abonniert hast.", - "recentSynced": "Aktuelle synchronisiert", - "recentSyncedHint": "Kanäle, deren neueste Uploads in der lokalen Datenbank sind und in deinem Feed erscheinen.", - "fullHistory": "Vollständiger Verlauf", - "fullHistoryHint": "Kanäle, deren gesamter Katalog geladen ist, von denen, für die du den vollständigen Verlauf angefordert hast.", - "left": "übrig", - "leftHint": "Grobe Schätzung zum Abschluss des Nachladens des vollständigen Verlaufs für {{count}} ausstehende(n) Kanal/Kanäle, basierend auf dem gemeinsamen Tageskontingent.", - "myVideos": "Meine Videos", - "myVideosHint": "Insgesamt verfügbare Videos über all deine Kanäle.", - "quotaLeft": "Kontingent übrig", - "quotaLeftHint": "Heute noch verbleibendes gemeinsames YouTube-API-Budget (wird um Mitternacht US-Pazifikzeit zurückgesetzt)." - }, - "row": { - "openOnYouTube": "Auf YouTube öffnen", - "editTags": "Tags bearbeiten", - "filterFeedByTag": "Feed nach „{{name}}“ filtern", - "priorityHint": "Deine Einstufung für diesen Kanal. Sortiere den Feed nach „Kanalpriorität“, um Kanäle mit höherer Priorität nach oben zu bringen.", - "raisePriority": "Priorität erhöhen", - "lowerPriority": "Priorität senken", - "recent": "aktuell", - "recentSyncedHint": "Neueste Uploads synchronisiert.", - "recentNotSyncedHint": "Neueste Uploads noch nicht geladen.", - "full": "vollständig", - "fullHint": "Vollständiger Verlauf geladen.", - "fullNotFetchedHint": "Das vollständige Archiv ist noch nicht geladen — nutze die Backfill-Aktion in der Spalte Aktionen.", - "fullySynced": "vollständig synchron", - "fullySyncedHint": "Aktuelle Uploads und das vollständige Archiv sind beide vorhanden.", - "backfillThis": "Diesen Kanal vollständig laden", - "backfillThisHint": "Das vollständige Archiv dieses Kanals laden (ältere Videos + komplette Suche). Erneut klicken, um die Anfrage zurückzuziehen.", - "resetBackfill": "Kanal zurücksetzen & neu laden", - "resetHint": "Admin: diesen Kanal von Grund auf neu laden (aktuelle + vollständiges Archiv), unabhängig vom aktuellen Sync-Status.", - "fullHistoryQueued": "vollständiger Verlauf in Warteschlange", - "fullHistoryComing": "vollständiger Verlauf unterwegs", - "queuedRequestedHint": "Vollständiger Verlauf angefordert — der gesamte Katalog dieses Kanals wird nachgeladen, soweit das gemeinsame Kontingent es zulässt. Klicke, um deine Anforderung abzubrechen.", - "queuedByOtherHint": "Ein anderer Abonnent hat den vollständigen Verlauf dieses Kanals bereits angefordert, sein gesamter Katalog ist also für alle unterwegs — hier gibt es nichts zu tun.", - "hiddenHint": "Ausgeblendet — die Videos dieses Kanals bleiben aus deinem Feed heraus. Klicke, um sie wieder anzuzeigen.", - "hideHint": "Blendet die Videos dieses Kanals aus deinem Feed aus. Er bleibt abonniert; dies bestellt nicht bei YouTube ab.", - "unhide": "Einblenden", - "hideFromFeed": "Aus Feed ausblenden", - "unsubscribeHint": "Bestelle diesen Kanal bei YouTube ab (ändert dein echtes Konto). Im schreibgeschützten Modus ist dies ausgeblendet — nutze stattdessen Ausblenden.", - "unsubscribeOnYoutube": "Bei YouTube abbestellen", - "thisChannel": "Dieser Kanal" - }, - "notify": { - "synced": "{{count}} Abos synchronisiert", - "syncFailed": "Abo-Synchronisierung fehlgeschlagen", - "unsubscribed": "{{name}} bei YouTube abbestellt", - "unsubscribeFailed": "Abbestellen fehlgeschlagen", - "fullHistoryRequested": "Vollständiger Verlauf für {{count}} Kanäle angefordert", - "fullHistoryFailed": "Vollständiger Verlauf konnte nicht angefordert werden", - "needYouTube": "Verbinde dein YouTube-Konto, um dies zu tun.", - "connect": "Verbinden", - "filteredByTag": "Feed nach Tag gefiltert: {{name}}", - "resetDone": "{{name}} zurückgesetzt — wird neu geladen", - "resetFailed": "Kanal konnte nicht zurückgesetzt werden" - }, - "confirmUnsubscribe": "Kanal „{{name}}“ bei YouTube abbestellen? Dies ändert dein echtes YouTube-Konto. Um ihn nur aus deinem Feed zu entfernen, blende ihn stattdessen aus.", - "confirmReset": "Kanal „{{name}}“ von Grund auf neu laden? Dies startet den Backfill neu (aktuell + vollständiger Verlauf) und verbraucht etwas API-Kontingent.", - "searchPlaceholder": "Kanäle suchen…", - "blocked": { - "title": "Blockierte Kanäle", - "hint": "Ihre Videos sind in deinem Feed, der Bibliothek und der Live-Suche ausgeblendet." - } -} diff --git a/frontend/src/i18n/locales/de/common.json b/frontend/src/i18n/locales/de/common.json deleted file mode 100644 index 854bffd..0000000 --- a/frontend/src/i18n/locales/de/common.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "privacyPolicy": "Datenschutzerklärung", - "termsOfService": "Nutzungsbedingungen", - "sourceCode": "Quellcode", - "close": "Schließen", - "cancel": "Abbrechen", - "confirm": "Bestätigen", - "confirmTitle": "Bist du sicher?", - "save": "Speichern", - "undo": "Rückgängig", - "redo": "Wiederholen", - "loading": "Wird geladen…", - "language": "Sprache", - "somethingWrong": "Etwas ist schiefgelaufen.", - "accessRequestsTitle": "Zugangsanfragen", - "accessRequestsMessage": "{{count}} ausstehend — prüfe sie auf der Benutzer-Seite.", - "accessRequestsReview": "Überprüfen", - "demoTitle": "Demo-Konto", - "demoSharedNotice": "Du bist im gemeinsamen Demo-Konto. Alles, was du hier tust — Playlists, ausgeblendete Videos, Einstellungen — ist gemeinsam und kann von anderen Demo-Besuchern gleichzeitig geändert werden.", - "backToTop": "Nach oben" -} diff --git a/frontend/src/i18n/locales/de/config.json b/frontend/src/i18n/locales/de/config.json deleted file mode 100644 index 42a0eee..0000000 --- a/frontend/src/i18n/locales/de/config.json +++ /dev/null @@ -1,204 +0,0 @@ -{ - "intro": "In der Datenbank gespeicherte Betriebseinstellungen — sie überschreiben die Datei-/Env-Standardwerte und wirken ohne erneutes Deployment. Lass ein Feld leer (oder setze es zurück), um auf den Standard zurückzufallen.", - "loading": "Konfiguration wird geladen…", - "groups": { - "access": "Zugang", - "email": "E-Mail / SMTP", - "youtube": "YouTube-API", - "google": "Google-Anmeldung", - "quota": "Kontingent", - "backfill": "Nachladen (Backfill)", - "shorts": "Shorts-Prüfung", - "batch": "Stapelgrößen", - "explore": "Kanal erkunden", - "downloads": "Downloads", - "plex": "Plex", - "audit": "Audit-Protokoll" - }, - "fields": { - "smtp_host": { - "label": "SMTP-Host", - "hint": "z. B. smtp.gmail.com" - }, - "smtp_port": { - "label": "SMTP-Port", - "hint": "Üblicherweise 587 (STARTTLS)." - }, - "smtp_user": { - "label": "SMTP-Benutzername", - "hint": "Das sendende Konto / der Login." - }, - "smtp_from": { - "label": "Absenderadresse", - "hint": "z. B. Siftlode . Standard ist der Benutzername." - }, - "smtp_password": { - "label": "SMTP-Passwort", - "hint": "App-Passwort. Verschlüsselt gespeichert; nur schreibbar — wird nie wieder angezeigt." - }, - "quota_daily_budget": { - "label": "Tägliches Kontingentbudget", - "hint": "YouTube-Data-API-Einheiten pro Tag (kostenloses Limit 10.000). Standard 9000 lässt Puffer." - }, - "backfill_quota_reserve": { - "label": "Nachlade-Kontingentreserve", - "hint": "Reservierte Einheiten, damit geplantes Nachladen interaktive Syncs / Anreicherung nie aushungert." - }, - "search_daily_limit_per_user": { - "label": "Live-Suche — Tageslimit pro Nutzer", - "hint": "Maximale Live-YouTube-Suchen pro Nutzer und Tag. Bei der API-Quelle kostet jede Suche 100 Einheiten (das gemeinsame Budget reicht also für insgesamt nur ~80-90/Tag); bei der Scrape-Quelle ist es ein reines Missbrauchslimit. 0 = Live-Suche deaktiviert." - }, - "search_source": { - "label": "Quelle der Live-Suche", - "hint": "Woher die Ergebnisse der Live-YouTube-Suche stammen. \"scrape\" nutzt YouTubes internen Endpunkt und verbraucht kein API-Kontingent (empfohlen). \"api\" nutzt das offizielle search.list (100 Einheiten/Seite). Auf \"api\" umstellen, falls das Scraping ausfällt oder blockiert wird." - }, - "backfill_recent_max_videos": { - "label": "Aktuelles Nachladen — max. Videos", - "hint": "Neueste Videos pro Kanal beim ersten Durchlauf." - }, - "backfill_recent_max_days": { - "label": "Aktuelles Nachladen — max. Alter (Tage)", - "hint": "Wie weit der erste Durchlauf pro Kanal zurückreicht." - }, - "shorts_probe_max_seconds": { - "label": "Shorts-Prüfung — max. Dauer (s)", - "hint": "Nur Videos bis zu dieser Länge werden als mögliche Shorts geprüft." - }, - "shorts_probe_batch": { - "label": "Shorts-Prüfung — Stapelgröße", - "hint": "Wie viele Videos pro Lauf klassifiziert werden." - }, - "enrich_batch_size": { - "label": "Anreicherungs-Stapelgröße", - "hint": "videos.list-IDs pro Aufruf (YouTube begrenzt auf 50)." - }, - "autotag_title_sample": { - "label": "Auto-Tag-Titelstichprobe", - "hint": "Pro Kanal abgetastete aktuelle Videotitel zur Spracherkennung." - }, - "youtube_api_key": { - "label": "YouTube-API-Schlüssel", - "hint": "Optional. Für öffentliche Lesezugriffe (Kanäle/Videos), damit gemeinsames Nachladen nicht vom OAuth eines Nutzers abhängt. Verschlüsselt gespeichert; nur schreibbar." - }, - "youtube_api_proxy": { - "label": "YouTube-API-Egress-Proxy", - "hint": "Optional. HTTP(S)-Proxy-URL für ausgehende YouTube-API-Aufrufe — über einen Host mit fester IP leiten, damit ein IP-beschränkter Schlüssel von einem Server mit dynamischer IP funktioniert. Leer = direkt." - }, - "google_client_id": { - "label": "Google-Client-ID", - "hint": "OAuth-2.0-Client-ID für die Google-Anmeldung. Verschlüsselt gespeichert; nur schreibbar. Beide Google-Felder leer lassen, um die Google-Anmeldung zu deaktivieren (E-Mail+Passwort funktioniert weiter)." - }, - "google_client_secret": { - "label": "Google-Client-Secret", - "hint": "OAuth-2.0-Client-Secret. Verschlüsselt gespeichert; nur schreibbar." - }, - "allow_registration": { - "label": "Registrierung erlauben", - "hint": "Wenn aktiviert, kann jeder eine E-Mail+Passwort-Registrierung einreichen. Für die Anmeldung sind weiterhin E-Mail-Bestätigung und Admin-Freigabe nötig." - }, - "explore_grace_days": { - "label": "Erkunden — Karenzzeit (Tage)", - "hint": "Wie lange ein erkundeter, nicht abonnierter Kanal (und seine flüchtigen Videos) behalten wird, bevor die Aufräumaufgabe ihn entfernt. Ein Abo behält ihn dauerhaft." - }, - "search_grace_days": { - "label": "Suchergebnisse — Karenzzeit (Tage)", - "hint": "Wie lange ein nicht behaltenes Live-Suchergebnis-Video (niemand hat es angesehen/gespeichert/abonniert) behalten wird, bevor die Entdeckungs-Aufräumaufgabe es entfernt." - }, - "download_total_max_bytes": { - "label": "Gesamt-Cache-Limit (Bytes)", - "hint": "Maximale Gesamtgröße über die Downloads aller Nutzer. 0 = unbegrenzt (die Festplatte ist die Grenze); bei Überschreitung entfernt die GC die am längsten ungenutzten Dateien." - }, - "download_default_max_bytes": { - "label": "Standard-Kontingent pro Nutzer (Bytes)", - "hint": "Wie viel Speicher ein Nutzer belegen darf, bevor neue Downloads blockiert werden. Eine Überschreibung pro Nutzer hat Vorrang. Standard 5 GiB." - }, - "download_default_max_concurrent": { - "label": "Gleichzeitige Downloads / Nutzer", - "hint": "Wie viele Downloads ein Nutzer gleichzeitig ausführen darf." - }, - "download_default_max_jobs": { - "label": "Job-Limit / Nutzer", - "hint": "Maximale Anzahl an Downloads, die ein Nutzer in seiner Bibliothek behalten darf." - }, - "download_retention_days": { - "label": "Aufbewahrung (Tage)", - "hint": "Ein Download läuft so viele Tage nach dem letzten Zugriff ab; die Aufräumaufgabe entfernt ihn dann (sobald nichts mehr darauf verweist) nach der Karenzzeit." - }, - "download_gc_grace_days": { - "label": "Aufräum-Karenzzeit (Tage)", - "hint": "Zusätzliche Tage, die ein abgelaufener Download vor dem Löschen behalten wird." - }, - "download_layout": { - "label": "Ablage-Layout", - "hint": "„plex“ = ein Kanal/Staffel JJJJ/…-Baum mit .nfo + Poster (in Plex indexierbar); „flat“ = ein einzelner Ordner." - }, - "download_worker_concurrency": { - "label": "Worker-Parallelität", - "hint": "Wie viele Downloads der Worker parallel verarbeitet." - }, - "sponsorblock_default": { - "label": "SponsorBlock standardmäßig", - "hint": "SponsorBlock (Sponsor-Segmente überspringen) für neu erstellte benutzerdefinierte Download-Profile aktivieren." - }, - "plex_enabled": { - "label": "Plex aktivieren", - "hint": "Zeigt die Plex-Bibliothek als Feed-Quelle mit einem umfangreichen Player. Erfordert, dass der Plex-Server von diesem Host erreichbar ist und die Medien auf einem lokalen Mount liegen (siehe Pfad-Zuordnung)." - }, - "plex_server_url": { - "label": "Plex-Server-URL", - "hint": "Von diesem Host erreichbare Basis-URL, z. B. http://192.168.1.100:32400." - }, - "plex_server_token": { - "label": "Plex-Server-Token", - "hint": "Der Admin-X-Plex-Token des Servers. Nur serverseitig genutzt (Metadaten + Bilder); wird nie an den Browser gesendet. Verschlüsselt gespeichert; nur schreibbar." - }, - "plex_path_map": { - "label": "Medien-Pfad-Zuordnung", - "hint": "Ordnet die Plex-Dateipfade dem lokalen Mount dieses Hosts zu, damit Siftlode die physische Datei abspielen kann. Durch Zeilenumbruch/Komma getrennte plexPrefix=localPrefix-Paare, z. B. /data=/plex-media. Leer = auf beiden Seiten identisch." - }, - "plex_libraries": { - "label": "Angezeigte Bibliotheken", - "hint": "Durch Kommas getrennte Plex-Sektionsschlüssel. Leer = alle Sektionen. Nutze nach dem Verbindungstest die Bibliotheksauswahl unten." - }, - "plex_sync_interval_min": { - "label": "Sync-Intervall (Minuten)", - "hint": "Wie oft die Katalog-Sync-Aufgabe die Plex-Metadaten spiegelt." - }, - "plex_max_transcodes": { - "label": "Max. gleichzeitige Transcodes", - "hint": "Obergrenze für gleichzeitige CPU-Transcodes inkompatibler Dateien (späterer Fallback). Direkt abspielbare Dateien sind nicht begrenzt." - } - }, - "save": "Speichern", - "saving": "Speichern…", - "saved": "Gespeichert", - "saveFailed": "Speichern fehlgeschlagen", - "unsaved": "Nicht gespeicherte Änderungen", - "discard": "Verwerfen", - "willReset": "wird beim Speichern auf den Standard zurückgesetzt", - "reset": "Zurücksetzen", - "resetHint": "Diese Überschreibung entfernen und auf den Datei-/Env-Standard zurückfallen.", - "resetDone": "Auf Standard zurückgesetzt", - "usingDefault": "Standard wird verwendet", - "overridden": "in der DB überschrieben", - "secretSet": "•••••••• gespeichert", - "secretEnv": "Env-Wert wird verwendet", - "secretUnset": "nicht gesetzt", - "secretsDisabled": "Setze TOKEN_ENCRYPTION_KEY, um Secrets in der Datenbank zu speichern.", - "testEmail": "Test-E-Mail senden", - "testEmailHint": "Sende eine Test-E-Mail an deine eigene Adresse mit den obigen Einstellungen, um zu prüfen, ob sie funktionieren.", - "testSent": "Test-E-Mail gesendet an {{to}}", - "testFailed": "Test-E-Mail fehlgeschlagen — prüfe die Einstellungen", - "plexTest": "Verbindung testen", - "plexTestHint": "Prüft die Plex-Server-URL + den Token und lädt die Bibliotheksliste. Speichere zuerst deine Änderungen.", - "plexTestDirty": "Speichere deine Änderungen vor dem Verbindungstest.", - "plexTestOk": "Verbunden mit {{name}}", - "plexTestFailed": "Plex-Verbindung fehlgeschlagen — prüfe URL und Token", - "plexConnected": "Verbunden mit {{name}} {{version}}", - "plexMediaOk": "Medieneinbindung OK — eine Beispieldatei wurde auf der Festplatte gefunden.", - "plexMediaMissing": "Medien unter {{path}} nicht lesbar — prüfe, ob die Plex-Medien in den Container eingebunden sind und die Pfadzuordnung stimmt. Die Wiedergabe schlägt fehl, bis dies behoben ist.", - "plexPickLibraries": "Anzuzeigende Bibliotheken:", - "plexPickHint": "Keine auszuwählen entspricht dem Anzeigen aller Bibliotheken. Nicht vergessen zu speichern.", - "plexMovies": "Filme", - "plexShows": "Serien" -} diff --git a/frontend/src/i18n/locales/de/datatable.json b/frontend/src/i18n/locales/de/datatable.json deleted file mode 100644 index 5e67f41..0000000 --- a/frontend/src/i18n/locales/de/datatable.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "filter": "Filtern", - "clear": "Löschen", - "all": "Alle", - "rowsPerPage": "Zeilen pro Seite", - "noOptions": "Keine Optionen", - "empty": "Nichts anzuzeigen.", - "pager": { - "prev": "Zurück", - "next": "Weiter", - "page": "Seite {{page}} von {{total}}", - "pageLabel": "Seite", - "ofTotal": "von {{total}}" - } -} diff --git a/frontend/src/i18n/locales/de/downloads.json b/frontend/src/i18n/locales/de/downloads.json deleted file mode 100644 index 001f96e..0000000 --- a/frontend/src/i18n/locales/de/downloads.json +++ /dev/null @@ -1,201 +0,0 @@ -{ - "navLabel": "Downloads", - "button": { - "label": "Herunterladen", - "queued": "In Warteschlange", - "downloaded": "Heruntergeladen" - }, - "dialog": { - "title": "Video herunterladen", - "profile": "Format", - "nameLabel": "Dateiname (optional)", - "namePlaceholder": "Leer lassen, um den Videotitel zu verwenden", - "submit": "Zu Downloads hinzufügen", - "added": "Zu Downloads hinzugefügt", - "view": "Öffnen", - "manage": "Formate verwalten" - }, - "page": { - "title": "Downloads", - "subtitle": "Videos auf den Server laden und dann auf dein Gerät speichern.", - "addUrl": "YouTube-Link oder Video-ID einfügen…", - "add": "Hinzufügen", - "manage": "Formate verwalten" - }, - "tabs": { - "queue": "Warteschlange", - "done": "Bibliothek", - "shared": "Mit mir geteilt", - "system": "System" - }, - "status": { - "queued": "Wartet", - "running": "Lädt", - "paused": "Pausiert", - "done": "Fertig", - "error": "Fehlgeschlagen", - "canceled": "Abgebrochen", - "expired": "Abgelaufen" - }, - "phase": { - "video": "Video wird geladen", - "audio": "Audio wird geladen", - "merging": "Zusammenführen", - "audio_extract": "Audio wird extrahiert", - "thumbnail": "Vorschaubild einbetten", - "sponsorblock": "Sponsoren entfernen", - "metadata": "Metadaten schreiben", - "processing": "Verarbeitung", - "editing": "Bearbeitung" - }, - "actions": { - "pause": "Pause", - "resume": "Fortsetzen", - "cancel": "Abbrechen", - "delete": "Entfernen", - "retry": "Erneut", - "download": "Auf mein Gerät speichern", - "rename": "Umbenennen", - "editDetails": "Details bearbeiten", - "share": "Teilen", - "edit": "Bearbeiten / schneiden", - "removeShared": "Aus meiner Liste entfernen" - }, - "empty": { - "queue": "Die Warteschlange ist leer.", - "done": "Noch keine Downloads.", - "shared": "Es wurde nichts mit dir geteilt.", - "admin": "Auf dieser Instanz gibt es noch keine Downloads." - }, - "usage": { - "title": "Dein Speicher", - "items": "{{used}} / {{max}} Elemente", - "unlimited": "Unbegrenzt" - }, - "source": { - "open": "Originalseite in neuem Tab öffnen", - "copy": "Quell-Link kopieren", - "copied": "Quell-Link in die Zwischenablage kopiert", - "copyFailed": "Link konnte nicht kopiert werden" - }, - "edit": { - "title": "Details bearbeiten", - "name": "Titel", - "channel": "Kanal", - "channelUrl": "Kanal-Link", - "extraLinks": "Weitere Links", - "addLink": "Link hinzufügen", - "removeLink": "Link entfernen", - "hint": "Nur Anzeige — die gespeicherte Datei behält ihren Namen. Kanal und Links erscheinen auch auf der Karte und auf geteilten Wiedergabeseiten.", - "save": "Speichern" - }, - "share": { - "title": "Download teilen", - "label": "E-Mail des Empfängers", - "placeholder": "user@example.com", - "submit": "Teilen", - "done": "Geteilt mit {{email}}", - "failed": "Teilen fehlgeschlagen.", - "userSection": "Mit einem Nutzer teilen", - "userHint": "Erscheint bei ihm unter „Mit mir geteilt”.", - "pickPlaceholder": "Nutzer suchen…", - "noUsers": "Keine weiteren Nutzer.", - "linkSection": "Per Link teilen", - "linkHint": "Jeder mit dem Link kann zusehen — ohne Konto.", - "noLinks": "Noch keine Links.", - "newLink": "Link erstellen", - "createLink": "Erstellen", - "allowDownload": "Download erlauben", - "expiry": "Läuft ab", - "expiryNever": "Nie", - "days": "{{n}} Tage", - "password": "Passwort (optional)", - "copy": "Link kopieren", - "copyFailed": "Kopieren fehlgeschlagen.", - "revoke": "Widerrufen", - "revokeConfirm": "Der Link funktioniert sofort nicht mehr.", - "views": "{{n}} Aufrufe", - "expiresOn": "läuft ab {{date}}", - "hasPassword": "Passwort", - "downloadable": "herunterladbar" - }, - "confirm": { - "cancelTitle": "Download abbrechen?", - "cancelBody": "Dies stoppt den Download und entfernt ihn aus deiner Warteschlange.", - "deleteTitle": "Download entfernen?", - "deleteBody": "Dies entfernt ihn aus deiner Liste. Die Datei kann bis zum Ablauf für andere Nutzer im Cache bleiben.", - "removeSharedTitle": "Aus deiner Liste entfernen?", - "removeSharedBody": "Entfernt es nur aus deiner geteilten Liste — die Datei des Besitzers wird nicht gelöscht." - }, - "profiles": { - "manage": "Formate verwalten", - "title": "Download-Formate", - "builtin": "Integriert", - "yours": "Deine Formate", - "new": "Neues Format", - "name": "Name", - "namePlaceholder": "Mein Format", - "mode": "Inhalt", - "modeAv": "Video + Audio", - "modeV": "Nur Video", - "modeA": "Nur Audio", - "quality": "Max. Qualität", - "best": "Beste", - "container": "Container", - "audioFormat": "Audioformat", - "vcodec": "Video-Codec", - "any": "Beliebig", - "embedSubs": "Untertitel einbetten", - "embedChapters": "Kapitel einbetten", - "embedThumbnail": "Vorschaubild einbetten", - "sponsorblock": "Sponsor-Segmente überspringen (SponsorBlock)", - "save": "Speichern", - "create": "Erstellen", - "delete": "Löschen", - "deleteConfirm": "Das Format „{{name}}“ löschen?" - }, - "admin": { - "storageTitle": "Speicher", - "readyFiles": "Fertige Dateien", - "totalSize": "Gesamtgröße", - "cap": "Cache-Limit", - "noCap": "kein Limit", - "perUser": "Speicher pro Nutzer", - "user": "Nutzer", - "footprint": "Belegung", - "editQuota": "Kontingent bearbeiten", - "quotaTitle": "Download-Kontingent — {{email}}", - "maxBytes": "Speicherlimit (GB)", - "maxJobs": "Max. Downloads", - "maxConcurrent": "Max. gleichzeitig", - "unlimited": "Unbegrenzt (Speicherlimit umgehen)", - "reset": "Auf Standard zurücksetzen", - "save": "Speichern", - "custom": "individuell", - "default": "Standard", - "quotaPickPlaceholder": "Kontingent für einen Nutzer festlegen…", - "noFootprint": "Noch keine Downloads." - }, - "cols": { - "title": "Titel", - "channel": "Kanal", - "format": "Format", - "size": "Größe", - "status": "Status", - "added": "Hinzugefügt", - "user": "Nutzer" - }, - "clipBadge": "Clip", - "errors": { - "quota": { - "max_bytes": "Dein Download-Speicherkontingent ist aufgebraucht ({{size}} GB). Gib zuerst Speicher frei.", - "max_jobs": "Du hast dein Download-Limit erreicht ({{limit}} Objekte). Entferne zuerst einige.", - "generic": "Download-Kontingent überschritten." - }, - "edit": { - "empty_edit": "Wähle zuerst einen Zuschneidebereich oder Ausschnitt.", - "source_not_ready": "Der Quell-Download ist noch nicht bearbeitbar.", - "generic": "Dieser Schnitt konnte nicht erstellt werden." - } - } -} diff --git a/frontend/src/i18n/locales/de/editor.json b/frontend/src/i18n/locales/de/editor.json deleted file mode 100644 index e82903c..0000000 --- a/frontend/src/i18n/locales/de/editor.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "title": "Bearbeiten: „{{name}}“", - "playPause": "Wiedergabe / Pause", - "setIn": "Start hier", - "setOut": "Ende hier", - "in": "Start", - "out": "Ende", - "selected": "Auswahl", - "cropOff": "Zuschneiden", - "cropOn": "Zuschneiden aktiv", - "cropReencode": "Zuschneiden kodiert immer neu (bildgenau).", - "split": "Aufteilen in", - "accurate": { - "label": "Genauer Schnitt", - "hint": "Bildgenau. Kodiert den Clip neu." - }, - "fast": { - "label": "Schneller Schnitt", - "hint": "Sofort, schneidet aber an Keyframes (±1–2 s)." - }, - "nameLabel": "Clip-Name (optional)", - "willReencode": "Wird neu kodiert", - "willCopy": "Sofort (Stream-Kopie)", - "create": "Clip erstellen", - "createN": "{{count}} Clips erstellen", - "created_one": "{{count}} Clip zu deinen Downloads hinzugefügt", - "created_other": "{{count}} Clips zu deinen Downloads hinzugefügt", - "failed": "Clip konnte nicht erstellt werden.", - "splitHere": "Hier teilen", - "output": { - "label": "Ausgabe", - "separate": "Einzelne Dateien", - "join": "Zu einer zusammenfügen" - }, - "segment": "Segment {{n}}", - "keep": "Behalten", - "drop": "Verwerfen", - "kept": "Behalten", - "dropped": "Verworfen", - "keptSeg": "Behaltenes Segment", - "droppedSeg": "Verworfenes Segment", - "removeCut": "Schnitt entfernen", - "keptSummary": "{{n}} behalten · {{dur}}", - "createJoin": "Zusammengefügten Clip erstellen" -} diff --git a/frontend/src/i18n/locales/de/errors.json b/frontend/src/i18n/locales/de/errors.json deleted file mode 100644 index 8b09923..0000000 --- a/frontend/src/i18n/locales/de/errors.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "title": "Nicht möglich", - "generic": "Der Server konnte die Aktion nicht ausführen. Bitte erneut versuchen.", - "server": "Serverfehler", - "ok": "OK", - "offline": { - "title": "Verbindung verloren", - "body": "Server nicht erreichbar — er startet möglicherweise gerade neu. Dieser Hinweis verschwindet, sobald die Verbindung wieder steht." - }, - "boundary": { - "title": "Etwas ist schiefgelaufen.", - "subtitle": "Die App ist auf einen unerwarteten Fehler gestoßen.", - "reload": "Neu laden", - "notifTitle": "Etwas ist kaputtgegangen", - "notifMessage": "Unerwarteter Fehler" - } -} diff --git a/frontend/src/i18n/locales/de/feed.json b/frontend/src/i18n/locales/de/feed.json deleted file mode 100644 index 8008e5a..0000000 --- a/frontend/src/i18n/locales/de/feed.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "loading": "Feed wird geladen…", - "loadError": "Feed konnte nicht geladen werden.", - "emptyTitle": "Dein Feed ist leer", - "emptyBody": "Verbinde dein YouTube-Konto, um deine Abos zu importieren und deinen Feed aufzubauen.", - "setUp": "Meinen Feed einrichten", - "browseShared": "Oder einfach die gemeinsame Bibliothek durchsuchen", - "demoEmptyTitle": "Hier in der Demo ist nichts", - "demoEmptyBody": "Das Demo-Konto hat keine eigenen Abos — aber die gesamte gemeinsame Bibliothek steht dir offen: durchsuchen, filtern und sortieren.", - "browseLibrary": "Bibliothek durchsuchen", - "noMatches": "Keine Videos entsprechen diesen Filtern.", - "videoCount_one": "{{formattedCount}} Video", - "videoCount_other": "{{formattedCount}} Videos", - "sortLabel": "Sortierung", - "dirAsc": "Aufsteigend", - "dirDesc": "Absteigend", - "sortKey": { - "date": "Datum", - "popular": "Beliebt", - "duration": "Dauer", - "title": "Name", - "subscribers": "Kanal-Abonnenten", - "priority": "Kanal-Priorität", - "shuffle": "Überraschung", - "relevance": "Relevanz" - }, - "loadingMore": "Mehr wird geladen…", - "hiddenNamed": "Ausgeblendet: „{{title}}”", - "hidden": "Video ausgeblendet", - "undo": "Rückgängig", - "markedWatchedNamed": "Als angesehen markiert: „{{title}}”", - "markedWatched": "Als angesehen markiert", - "unwatch": "Nicht mehr als angesehen", - "source": { - "label": "Quelle", - "tip": "Danach filtern, wie Videos hierher kamen: nur deine Abos/Katalog, samt deiner über Live-YouTube-Suche gefundenen Videos, oder nur diese Suchergebnisse.", - "organic": "Ohne Suchergebnisse", - "all": "Mit Suchergebnissen", - "only": "Nur Suchergebnisse", - "plex": "Plex" - }, - "yt": { - "searchFor": "Auf YouTube suchen nach „{{query}}”", - "resultsFor": "YouTube-Ergebnisse für „{{query}}”", - "quotaNote": "Live-Ergebnisse — verbraucht gemeinsames API-Kontingent", - "freeNote": "Live-Ergebnisse — kein Suchkontingent verbraucht", - "back": "Zurück zum Feed", - "searching": "Suche auf YouTube…", - "noResults": "Keine YouTube-Videos gefunden.", - "error": "YouTube-Suche fehlgeschlagen.", - "loadMore": "Mehr laden (verbraucht Kontingent)", - "loadMoreFree": "Mehr laden", - "show": "Zeigen", - "ephemeralNote": "Diese Ergebnisse sind temporär — sie verschwinden automatisch, außer du siehst dir eines an oder speicherst es.", - "clearNow": "Jetzt löschen", - "cleared_one": "{{count}} Ergebnis gelöscht", - "cleared_other": "{{count}} Ergebnisse gelöscht", - "showHint": "Wie viele Ergebnisse auf einmal — Mehr laden holt einen weiteren Block dieser Größe." - } -} diff --git a/frontend/src/i18n/locales/de/header.json b/frontend/src/i18n/locales/de/header.json deleted file mode 100644 index a8987bd..0000000 --- a/frontend/src/i18n/locales/de/header.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "feed": "Feed", - "searchPlaceholder": "Deine Abos durchsuchen…", - "clearSearch": "Suche löschen", - "searchYoutube": "Auf YouTube suchen", - "searchYoutubeTip": "Live auf YouTube nach diesem Begriff suchen (verbraucht gemeinsames API-Kontingent). Du kannst auch Enter drücken.", - "go": "Los", - "goTip": "Suchen (oder Enter drücken)", - "prevModule": "Vorheriges Modul", - "nextModule": "Nächstes Modul", - "channelManager": "Kanalverwaltung", - "usageStats": "Nutzung & Statistik", - "scheduler": "Planer", - "configuration": "Konfiguration", - "users": "Benutzer", - "scope": { - "label": "Feed-Quelle", - "my": "Meine", - "myTip": "Nur Videos aus deinen eigenen Abos", - "all": "Bibliothek", - "allTip": "Alle Videos im gemeinsamen Katalog" - }, - "account": { - "admin": "Admin", - "feed": "Feed", - "channels": "Kanäle", - "playlists": "Wiedergabelisten", - "stats": "Statistik", - "scheduler": "Planer", - "config": "Konfiguration", - "users": "Benutzer", - "settings": "Einstellungen", - "about": "Über", - "signOut": "Abmelden", - "addAccount": "Weiteres Konto hinzufügen", - "switchTo": "Zu {{name}} wechseln", - "audit": "Audit-Protokoll" - }, - "sync": { - "yours": "deine", - "total": "gesamt", - "countTooltip": "Videos aus deinen Abos im Vergleich zur Gesamtzahl im gemeinsamen Katalog, den die Abos aller Nutzer gefüllt haben.", - "paused": "pausiert", - "syncing": "{{count}} werden synchronisiert", - "backfillingHistory": "Verlauf wird geladen", - "working": "Synchronisierung…", - "recentQueued": "{{count}} in Warteschlange", - "allSynced": "alles synchronisiert", - "withoutFullHistory": "{{count}} ohne vollständigen Verlauf", - "fullHistoryTooltip": "Einige deiner Kanäle haben nur ihre neuesten Uploads. Klicke, um die Kanalverwaltung (auf diese gefiltert) zu öffnen und ihren vollständigen Katalog anzufordern.", - "pause": "Hintergrund-Synchronisierung pausieren", - "resume": "Hintergrund-Synchronisierung fortsetzen" - }, - "banner": { - "updated": "Aktualisiert auf v{{version}} — sieh, was sich geändert hat.", - "releaseNotes": "Versionshinweise", - "dismiss": "Schließen" - } -} diff --git a/frontend/src/i18n/locales/de/inbox.json b/frontend/src/i18n/locales/de/inbox.json deleted file mode 100644 index 00df770..0000000 --- a/frontend/src/i18n/locales/de/inbox.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "navLabel": "Benachrichtigungen", - "title": "Benachrichtigungen", - "subtitle": "Neuigkeiten aus deiner Bibliothek und vom System.", - "tabInbox": "Benachrichtigungen", - "empty": "Alles erledigt.", - "markAllRead": "Alle als gelesen markieren", - "clearAll": "Alle löschen", - "markRead": "Als gelesen markieren", - "dismiss": "Verwerfen", - "sectionSystem": "System", - "sectionActivity": "Aktivität", - "andMore": "und {{count}} weitere", - "openScheduler": "Zeitplaner öffnen", - "maintenance": { - "title": "Videos entfernt", - "body_one": "{{count}} gespeichertes oder in einer Playlist befindliches Video wurde entfernt, weil es auf YouTube nicht mehr verfügbar ist.", - "body_other": "{{count}} gespeicherte oder in Playlists befindliche Videos wurden entfernt, weil sie auf YouTube nicht mehr verfügbar sind." - }, - "jobDone": { - "titleOk": "{{job}} abgeschlossen", - "titleError": "{{job}} fehlgeschlagen" - } -} diff --git a/frontend/src/i18n/locales/de/login.json b/frontend/src/i18n/locales/de/login.json deleted file mode 100644 index c92df28..0000000 --- a/frontend/src/i18n/locales/de/login.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "tagline": "Ein selbstgehosteter, filterbarer Feed deiner YouTube-Abos — sortieren, taggen und den Lärm ausblenden. Melde dich mit Google an, um loszulegen; der YouTube-Zugriff ist ein optionaler, separater Schritt, den du steuerst.", - "signIn": "Mit Google anmelden", - "approved": "Du bist bereits freigeschaltet — melde dich einfach oben mit Google an.", - "requested": "Danke — deine Anfrage ist eingegangen. Wir benachrichtigen dich per E-Mail, sobald sie genehmigt ist.", - "noAccessYet": "Noch keinen Zugang?", - "denied": "Dieses Google-Konto ist nicht freigeschaltet. Fordere unten Zugang an.", - "emailPlaceholder": "du@gmail.com", - "sending": "Wird gesendet…", - "requestAccess": "Zugang anfordern", - "submitError": "Konnte nicht gesendet werden — prüfe die Adresse und versuche es erneut." -} diff --git a/frontend/src/i18n/locales/de/messages.json b/frontend/src/i18n/locales/de/messages.json deleted file mode 100644 index 2ffaf4d..0000000 --- a/frontend/src/i18n/locales/de/messages.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "tab": "Nachrichten", - "navLabel": "Nachrichten", - "popOut": "In Pop-out öffnen", - "expand": "Aufklappen", - "minimize": "Minimieren", - "close": "Schließen", - "conversations": "Unterhaltungen", - "new": "Neue Nachricht", - "newTitle": "Neue Nachricht", - "empty": "Noch keine Unterhaltungen. Starte eine über die Schaltfläche „Neue Nachricht“.", - "searchPeople": "Personen suchen…", - "noPeople": "Noch niemand zum Schreiben.", - "threadEmpty": "Noch keine Nachrichten — sag Hallo.", - "writePlaceholder": "Nachricht schreiben…", - "send": "Senden", - "encrypted": "Verschlüsselte Nachricht", - "encryptedHint": "Ende-zu-Ende-verschlüsselt", - "cantDecrypt": "Kann nicht entschlüsselt werden", - "sendFailed": "Nachricht konnte nicht gesendet werden. Bitte erneut versuchen.", - "systemReadOnly": "Dies ist eine automatische Nachricht.", - "setupTitle": "Sichere Nachrichten einrichten", - "setupBody": "Nachrichten sind Ende-zu-Ende-verschlüsselt. Wähle ein Passwort zum Schutz deines Schlüssels — er verlässt nie dein Gerät, sodass niemand (nicht einmal ein Admin) deine Unterhaltungen lesen kann.", - "setupWarn": "Wenn du dieses Passwort vergisst, kann dein Nachrichtenverlauf nicht wiederhergestellt werden.", - "setUp": "Einrichten", - "unlockTitle": "Nachrichten entsperren", - "unlockBody": "Gib dein Nachrichten-Passwort ein, um deine Unterhaltungen auf diesem Gerät zu entsperren.", - "unlock": "Entsperren", - "passphrase": "Passwort", - "passphraseConfirm": "Passwort bestätigen", - "passTooShort": "Mindestens 6 Zeichen verwenden.", - "passMismatch": "Die Passwörter stimmen nicht überein.", - "wrongPass": "Falsches Passwort.", - "setupFailed": "Sichere Nachrichten konnten nicht eingerichtet werden. Bitte erneut versuchen." -} diff --git a/frontend/src/i18n/locales/de/nav.json b/frontend/src/i18n/locales/de/nav.json deleted file mode 100644 index c3135ba..0000000 --- a/frontend/src/i18n/locales/de/nav.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "primary": "Hauptnavigation", - "collapse": "Seitenleiste einklappen", - "expand": "Seitenleiste ausklappen", - "adminSection": "Admin", - "role": { - "admin": "Admin", - "user": "Benutzer", - "demo": "Demo" - } -} diff --git a/frontend/src/i18n/locales/de/notifications.json b/frontend/src/i18n/locales/de/notifications.json deleted file mode 100644 index 5c7fdb3..0000000 --- a/frontend/src/i18n/locales/de/notifications.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "title": "Benachrichtigungen", - "clearAll": "Alle löschen", - "clear": "Löschen", - "empty": "Noch keine Benachrichtigungen.", - "actionNeeded": "Aktion erforderlich", - "dismiss": "Schließen", - "findInFeed": "Im Feed finden", - "unhide": "Einblenden", - "unwatch": "Nicht angesehen", - "openInManager": "Kanalverwaltung", - "openOnYouTube": "Auf YouTube öffnen", - "unhidden": "Eingeblendet „{{title}}”", - "unwatched": "Als nicht angesehen markiert „{{title}}”" -} diff --git a/frontend/src/i18n/locales/de/onboarding.json b/frontend/src/i18n/locales/de/onboarding.json deleted file mode 100644 index ab0238d..0000000 --- a/frontend/src/i18n/locales/de/onboarding.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "close": "Schließen", - "consent": "Google zeigt einen <0>„Google hat diese App nicht überprüft“-Bildschirm — das ist zu erwarten, da Siftlode die vollständige Überprüfung von Google nicht durchlaufen hat. Es ist sicher fortzufahren: klicke auf <1>Erweitert → <2>Weiter zu Siftlode. Du kannst den Zugriff jederzeit in deinem <3>Google-Konto widerrufen.", - "read": { - "title": "Verbinde deine YouTube-Abos", - "body": "Du bist angemeldet. Um deinen Feed aufzubauen, benötigt Siftlode <0>schreibgeschützten Zugriff auf die Kanäle, die du auf YouTube abonniert hast. Es postet, löscht oder ändert damit niemals etwas.", - "connect": "Verbinden (schreibgeschützt)", - "skip": "Vorerst überspringen" - }, - "importing": { - "title": "Dein Feed wird aufgebaut…", - "body": "Deine YouTube-Abos werden importiert. Bereits in Siftlode vorhandene Kanäle erscheinen sofort; neue werden automatisch im Hintergrund ergänzt." - }, - "write": { - "title": "Du bist verbunden", - "importFailed": "Dein YouTube-Konto ist verbunden, aber der Import der Abos ist fehlgeschlagen — du kannst es unter Einstellungen → Synchronisierung erneut versuchen. ", - "feedReady": "Dein Feed ist bereit{{channels}}. Optional kannst du Siftlode erlauben, ", - "feedReadyChannels": " ({{count}} Kanäle)", - "rationale": "Kanäle auf YouTube für dich <0>abzubestellen — dafür ist eine zusätzliche Schreibberechtigung nötig. Überspringe es, um schreibgeschützt zu bleiben; du kannst es später jederzeit in den Einstellungen aktivieren.", - "retryImport": "Import erneut versuchen", - "enableUnsubscribe": "Abbestellen aktivieren", - "keepReadOnly": "Nein danke — schreibgeschützt bleiben" - }, - "done": { - "title": "Alles erledigt", - "body": "Lese- und Abbestell-Zugriff sind beide erteilt. Du kannst beide jederzeit unter Einstellungen → Konto oder in deinem Google-Konto überprüfen oder widerrufen.", - "done": "Fertig" - } -} diff --git a/frontend/src/i18n/locales/de/player.json b/frontend/src/i18n/locales/de/player.json deleted file mode 100644 index 26ef23f..0000000 --- a/frontend/src/i18n/locales/de/player.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "loading": "Wird geladen…", - "backToOriginal": "Zurück zum ursprünglichen Video", - "back": "Zurück", - "previous": "Vorheriges", - "next": "Nächstes", - "autoAdvance": { - "label": "Auto", - "hint": "Was am Videoende folgt. Klicken zum Wechseln: Aus, Nächstes, Vorheriges, Zufall. In deinem Konto gespeichert.", - "off": "Aus", - "next": "Nächstes", - "prev": "Vorheriges", - "random": "Zufall" - }, - "loop": { - "label": "Wiederholen", - "hint": "Aktuelles Video wiederholen (Eins), ganze Liste wiederholen (Alle) oder keines (Aus). In deinem Konto gespeichert.", - "off": "Aus", - "one": "Eins", - "all": "Alle" - }, - "close": "Schließen", - "closeEsc": "Schließen (Esc)", - "description": "Beschreibung", - "noDescription": "Keine Beschreibung.", - "channel": "Kanal", - "views": "{{formattedCount}} Aufrufe", - "stream": "Stream", - "watched": "Angesehen", - "markWatched": "Als angesehen markieren", - "watchedUnmark": "Angesehen — zum Aufheben klicken", - "jumpToTime": "Zu dieser Stelle springen", - "playInApp": "Im integrierten Player abspielen", - "unavailableTitle": "Hier nicht abspielbar", - "unavailableBody": "Dieses Video ist nicht verfügbar — möglicherweise privat, entfernt oder regional gesperrt.", - "embedDisabledBody": "Der Eigentümer hat die Wiedergabe auf anderen Seiten deaktiviert (häufig bei automatisch generierten „Topic“-Musiktiteln). Auf YouTube ist es weiterhin abspielbar.", - "openOnYoutube": "Auf YouTube öffnen", - "shortcutsHint": "Klick: Wiedergabe/Pause · Scrollen: Lautstärke · F: Vollbild", - "nativeControls": "YouTube-Steuerung aktiv" -} diff --git a/frontend/src/i18n/locales/de/playlists.json b/frontend/src/i18n/locales/de/playlists.json deleted file mode 100644 index 859bb1e..0000000 --- a/frontend/src/i18n/locales/de/playlists.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "title": "Wiedergabelisten", - "watchLater": "Später ansehen", - "syncYoutube": "Von YouTube synchronisieren", - "syncedToast": "{{count}} Wiedergabelisten von YouTube synchronisiert", - "syncFailed": "Synchronisierung von YouTube fehlgeschlagen", - "ytReadonly": "Von YouTube synchronisiert — Änderungen werden zurücksynchronisiert", - "noneYet": "Noch keine Wiedergabelisten", - "newPlaylist": "Neue Liste…", - "searchPlaceholder": "Wiedergabelisten suchen…", - "noMatches": "Keine passende Wiedergabeliste.", - "itemCount_one": "{{count}} Video", - "itemCount_other": "{{count}} Videos", - "pickOne": "Wähle links eine Liste.", - "loading": "Wird geladen…", - "empty": "Diese Liste ist leer — füge Videos aus dem Feed hinzu.", - "playAll": "Alle abspielen", - "delete": "Löschen", - "rename": "Umbenennen", - "confirmDelete": "Die Wiedergabeliste „{{name}}“ löschen? Kann nicht rückgängig gemacht werden.", - "addToPlaylist": "Zur Wiedergabeliste hinzufügen", - "exportToYoutube": "Zu YouTube exportieren", - "pushToYoutube": "Mit YouTube synchronisieren", - "unsynced": "Nicht synchronisiert", - "unsyncedHint": "Du hast lokale Änderungen, die noch nicht mit YouTube synchronisiert sind.", - "openOnYoutube": "Auf YouTube öffnen", - "revert": "Von YouTube zurücksetzen", - "revertTitle": "Lokale Änderungen verwerfen?", - "revertMsg": "Dies lädt die Wiedergabeliste von YouTube neu und verwirft deine nicht synchronisierten lokalen Änderungen (Reihenfolge, Hinzufügungen, Entfernungen, Umbenennung). Fortfahren?", - "revertConfirm": "Verwerfen & neu laden", - "revertDone": "„{{name}}“ von YouTube neu geladen ✓", - "revertFailed": "Neuladen von YouTube fehlgeschlagen.", - "pushTitle": "Mit YouTube synchronisieren", - "pushConfirm": "Synchronisieren", - "pushPlanCreate": "Eine neue YouTube-Wiedergabeliste mit {{count}} Videos erstellen? (~{{units}} Kontingenteinheiten; heute noch {{left}} übrig.)", - "pushPlanUpdate": "Auf YouTube aktualisieren — {{insert}} hinzufügen, {{del}} entfernen, {{reorder}} neu anordnen? (~{{units}} Kontingenteinheiten; heute noch {{left}} übrig.)", - "pushDiverged": "{{count}} Video(s), die derzeit auf YouTube sind, werden entfernt.", - "pushNoQuota": "Nicht genug YouTube-Kontingent für heute ({{left}}) für diese Synchronisierung (~{{units}} benötigt). Versuche es morgen erneut.", - "pushUpToDate": "„{{name}}“ ist bereits mit YouTube synchronisiert.", - "pushDone": "„{{name}}“ mit YouTube synchronisiert ✓", - "pushPartial": "Synchronisiert, aber {{count}} Element(e) wurden übersprungen.", - "pushFailed": "Synchronisierung mit YouTube fehlgeschlagen.", - "deleteOnYoutubeTitle": "Auch auf YouTube löschen?", - "deleteOnYoutubeMsg": "„{{name}}“ auch aus deinem YouTube-Konto löschen? Wähle „Nur hier“, um sie auf YouTube zu behalten.", - "deleteOnYoutubeConfirm": "Auf YouTube löschen", - "deleteHereOnly": "Nur hier", - "deleteYtFailed": "Löschen auf YouTube fehlgeschlagen; nur hier entfernt.", - "sortLabel": "Sortierung", - "sortManual": "Eigene Reihenfolge", - "sortTitle": "Titel", - "sortDuration": "Dauer", - "sortChannel": "Kanal", - "dirAsc": "Aufsteigend", - "dirDesc": "Absteigend", - "groupByChannel": "Nach Kanal gruppieren", - "railSortCustom": "Eigene Reihenfolge", - "railSortName": "Name", - "railSortCount": "Anzahl", - "railSortDuration": "Gesamtlänge", - "dirtyFirst": "Nicht synchronisierte zuerst" -} diff --git a/frontend/src/i18n/locales/de/plex.json b/frontend/src/i18n/locales/de/plex.json deleted file mode 100644 index c239936..0000000 --- a/frontend/src/i18n/locales/de/plex.json +++ /dev/null @@ -1,229 +0,0 @@ -{ - "navLabel": "Plex", - "backToFeed": "YouTube-Feed", - "backToLibrary": "Zurück zur Bibliothek", - "searchPlaceholder": "Plex durchsuchen…", - "sort": { - "added": "Kürzlich hinzugefügt", - "title": "Titel", - "year": "Jahr", - "rating": "IMDb-Wertung", - "duration": "Länge", - "release": "Erscheinungsdatum" - }, - "filter": { - "scope": "Bibliothek", - "scopeOpt": { - "both": "Alle", - "movie": "Filme", - "show": "Serien" - }, - "show": "Anzeige", - "showOpt": { - "all": "Alle", - "unwatched": "Ungesehen", - "in_progress": "Läuft", - "watched": "Gesehen" - }, - "sort": "Sortierung", - "clearAll": "Filter zurücksetzen", - "active": "Aktiv", - "any": "Alle", - "rating": "IMDb-Wertung", - "genre": "Genre", - "year": "Jahr", - "duration": "Länge", - "durationOpt": { - "short": "Unter 90 Min.", - "mid": "90–120 Min.", - "long": "Über 2 Std." - }, - "added": "Zu Plex hinzugefügt", - "addedOpt": { - "24h": "24 Std.", - "1w": "1 Woche", - "1m": "1 Monat", - "6m": "6 Monate", - "1y": "1 Jahr" - }, - "contentRating": "Altersfreigabe", - "match": { - "any": "Beliebig", - "all": "Alle" - }, - "dir": { - "desc": "↓ Absteigend", - "asc": "↑ Aufsteigend" - }, - "collection": "Sammlung", - "collectionSearch": "Sammlungen suchen…", - "collectionEmpty": "Keine Sammlungen" - }, - "count": "{{count}} Titel", - "searchCount": "{{count}} Treffer", - "noMatches": "Keine Treffer.", - "noMatchesFiltered_one": "Keine Treffer — ein Filter schränkt die Ergebnisse zusätzlich ein.", - "noMatchesFiltered_other": "Keine Treffer — {{count}} Filter schränken die Ergebnisse zusätzlich ein.", - "loading": "Wird geladen…", - "empty": "Noch nichts hier. Starte eine Plex-Synchronisierung auf der Admin-Konfigurationsseite.", - "watched": "Angesehen", - "inProgress": "Läuft", - "play": "Abspielen", - "resume": "Fortsetzen", - "openShow": "Serie öffnen", - "markWatched": "Als gesehen markieren", - "markUnwatched": "Als ungesehen markieren", - "seasons": "{{count}} Staffeln", - "unified": { - "titles": "Titel", - "episodes": "Folgen" - }, - "series": { - "seasons": "Staffeln", - "backToShow": "Zurück zur Serie", - "episodeCount": "{{count}} Folgen", - "playFromStart": "Von Anfang an", - "related": "Ähnliche Serien", - "markShowWatched": "Serie als gesehen", - "markShowUnwatched": "Serie als ungesehen", - "markSeasonWatched": "Staffel als gesehen", - "markSeasonUnwatched": "Staffel als ungesehen", - "addShowCollection": "Zur Sammlung" - }, - "collEditor": { - "manage": "Sammlungen", - "title": "Sammlungen — {{title}}", - "newPlaceholder": "Name der neuen Sammlung…", - "create": "Erstellen", - "search": "Sammlungen filtern…", - "none": "Noch keine bearbeitbaren Sammlungen. Erstelle oben eine.", - "count": "{{count}} Titel", - "delete": "Löschen", - "deleteConfirm": "Sammlung „{{title}}“ aus Plex löschen? Betrifft alle Benutzer.", - "note": "Änderungen werden in Plex geschrieben und für alle Clients/Benutzer sichtbar.", - "others": "{{count}} vorhandene Sammlungen verwalten…", - "makeEditable": "Verwalten" - }, - "player": { - "loading": "Wird geladen…", - "back": "Zurück", - "skipIntro": "Intro überspringen", - "skipCredits": "Abspann überspringen", - "playPause": "Wiedergabe / Pause (Leertaste)", - "restart": "Von vorn beginnen", - "prev": "Vorherige Folge", - "next": "Nächste Folge", - "mute": "Stumm (m)", - "fullscreen": "Vollbild (f)", - "download": "Originaldatei herunterladen", - "tracks": "Audio & Untertitel", - "audio": "Audio", - "subtitles": "Untertitel", - "subOff": "Aus", - "errNotFound": "Diese Datei kann nicht abgespielt werden — die Medien sind auf dem Server nicht erreichbar (die Plex-Medieneinbindung fehlt oder die Pfadzuordnung ist falsch). Bitte den Administrator, die Plex-Konfiguration zu prüfen.", - "errTranscode": "Das Format dieser Datei erfordert eine Transkodierung, die noch nicht unterstützt wird.", - "errGeneric": "Die Wiedergabe konnte nicht gestartet werden. Bitte erneut versuchen.", - "stop": "Stopp", - "help": "Tastenkürzel", - "infoBtn": "Medieninfo", - "keys": { - "playPause": "Wiedergabe / Pause", - "seek": "10 Sekunden spulen", - "episode": "Vorherige / nächste Folge", - "volume": "Lautstärke lauter / leiser", - "mute": "Stumm", - "fullscreen": "Vollbild", - "info": "Medieninfo", - "back": "Stopp & zurück zum Feed", - "help": "Diese Hilfe anzeigen", - "audio": "Audiospur wechseln", - "subtitle": "Untertitel wechseln", - "fineSeek": "Feines Springen" - }, - "settings": "Einstellungen", - "sync": "Offsets", - "subOffset": "Untertitel-Verzögerung", - "audioOffset": "Audio-Verzögerung", - "playback": "Wiedergabe", - "seekStep": "Sprungweite", - "fineSeek": "Feines Springen (Strg)", - "autoHide": "Steuerung ausblenden", - "subStyle": "Untertitel", - "subSize": "Schriftgröße", - "subPos": "Position", - "subColor": "Textfarbe", - "subBg": "Hintergrund", - "subBgNone": "Keiner", - "clock": "Uhr", - "clockShow": "Uhr anzeigen", - "clock24h": "24-Stunden", - "clockSize": "Größe", - "clockColor": "Farbe", - "clockDate": "Datum anzeigen", - "subShadow": "Schatten", - "subShadowColor": "Schattenfarbe", - "autoSkipIntro": "Intro autom. überspringen", - "autoSkipCredits": "Abspann autom. überspringen", - "autoSkipDelay": "Auto-Skip-Verzögerung" - }, - "info": { - "title": "Medieninfo", - "customize": "Ansicht anpassen", - "prefArtBg": "Dezenter Hintergrund", - "prefCast": "Besetzung", - "prefRelated": "Ähnliche Serien", - "stripSource": { - "collection": "Sammlungen", - "imdb": "IMDb", - "tmdb": "TMDb", - "tvdb": "TVDb", - "trakt": "Trakt", - "smart": "Smart-Listen" - }, - "openImdb": "Bei IMDb öffnen", - "director": "Regie", - "cast": "Besetzung & Crew", - "markWatched": "Als gesehen markieren", - "markUnwatched": "Gesehen zurücknehmen", - "clearResume": "Fortsetzungsposition löschen", - "filterYear": "Titel aus {{year}}", - "filterRating": "Titel mit {{n}}+", - "filterActor": "Titel mit {{name}}", - "browseCollection": "Sammlung durchsuchen →" - }, - "playable": { - "direct": "Spielt direkt im Browser", - "remux": "Braucht ein leichtes Remux (keine Video-Neucodierung)", - "transcode": "Braucht Transcoding (aufwendiger)" - }, - "playlistAdd": { - "manage": "Zur Playlist", - "title": "Zur Playlist — {{title}}", - "newPlaceholder": "Name der neuen Playlist…", - "create": "Erstellen", - "none": "Noch keine Playlists. Erstelle oben eine.", - "count": "{{count}} Titel", - "groupProgress": "{{in}} / {{size}}" - }, - "playlist": { - "section": "Playlists", - "newPlaceholder": "Neue Playlist…", - "create": "Playlist erstellen", - "none": "Noch keine Playlists.", - "deleteConfirm": "Playlist „{{title}}“ löschen?", - "rename": "Umbenennen", - "playAll": "Alle abspielen", - "delete": "Playlist löschen", - "empty": "Diese Playlist ist leer. Füge Titel über deren Infoseite hinzu.", - "layoutAccordion": "Akkordeon-Ansicht", - "layoutTree": "Baum-Ansicht", - "removeShow": "Ganze Serie entfernen", - "removeSeason": "Ganze Staffel entfernen", - "episodes": "{{count}} Folgen", - "season": "Staffel {{n}}", - "seasonUnknown": "Folgen", - "addShow": "Ganze Serie zu einer Playlist", - "addSeason": "Staffel zu einer Playlist", - "addEpisode": "Folge zu einer Playlist" - } -} diff --git a/frontend/src/i18n/locales/de/quotaActions.json b/frontend/src/i18n/locales/de/quotaActions.json deleted file mode 100644 index 7896ec4..0000000 --- a/frontend/src/i18n/locales/de/quotaActions.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "subscriptions_pull": "Abonnements einlesen", - "subscriptions_resync": "Automatische Abo-Resynchronisierung", - "playlists_pull": "Playlists einlesen", - "playlists_push": "Playlists hochladen", - "playlists_delete": "Playlist löschen", - "videos_backfill_recent": "Aktuelle Uploads laden", - "videos_backfill_full": "Vollständigen Verlauf laden", - "videos_enrich": "Video-Anreicherung", - "videos_lookup": "Video-Abfrage", - "videos_search": "Live-Suche", - "channels_discover": "Kanal-Entdeckung", - "channels_subscribe": "Abonnieren", - "channels_unsubscribe": "Abo beenden", - "maintenance_revalidate": "Wartungs-Neuvalidierung", - "other": "Sonstiges", - "channels_explore": "Kanal erkunden" -} diff --git a/frontend/src/i18n/locales/de/scheduler.json b/frontend/src/i18n/locales/de/scheduler.json deleted file mode 100644 index f7618d3..0000000 --- a/frontend/src/i18n/locales/de/scheduler.json +++ /dev/null @@ -1,110 +0,0 @@ -{ - "title": "Hintergrund-Planer", - "loading": "Planer wird geladen…", - "running": "Läuft", - "paused": "Pausiert", - "notHere": "Läuft in dieser Instanz nicht", - "notHereNote": "Der Planer läuft auf dem Server. Diese Instanz zeigt die gemeinsame Warteschlange und das Kontingent, aber die Live-Job-Aktivität erscheint nur dort, wo der Planer läuft.", - "updating": "wird aktualisiert…", - "pause": "Pausieren", - "resume": "Fortsetzen", - "pauseHint": "Die gesamte Hintergrund-Synchronisierung der App pausieren oder fortsetzen (für alle Nutzer).", - "toggleFailed": "Der Planer-Status konnte nicht geändert werden", - "jobsTitle": "Jobs", - "queueTitle": "Wartende Arbeit", - "quotaTitle": "Gemeinsames API-Kontingent", - "everyMin": "alle {{count}} Min", - "runningNow": "Läuft gerade…", - "lastRun": "Letzter Lauf {{time}}", - "neverRun": "In dieser Sitzung noch nicht gelaufen", - "active": "aktiv", - "nextRun": "nächster", - "quotaUsed": "{{used}} / {{budget}} Einheiten heute verbraucht", - "quotaRemaining": "{{formatted}} übrig", - "quotaNote": "Gemeinsames YouTube-Data-API-Budget; Reset um Mitternacht US-Pazifik. Backfill tritt zurück, um Spielraum für interaktive Nutzung zu lassen.", - "minutes": "Min", - "editInterval": "Klicken, um das Intervall zu ändern", - "intervalFailed": "Intervall konnte nicht geändert werden", - "runNow": "Jetzt ausführen", - "runAll": "Alle jetzt starten", - "runAllHint": "Alle Jobs jetzt ausführen, zusätzlich zu ihren Zeitplänen.", - "runAllPausedHint": "Setze den Planer fort, um Jobs auszuführen (pausiert werden sie übersprungen).", - "triggered": "{{job}} gestartet", - "triggeredAll_one": "{{count}} Job gestartet", - "triggeredAll_other": "{{count}} Jobs gestartet", - "phase": { - "recheck": "Markierte Videos erneut prüfen", - "revalidate": "Videos erneut validieren", - "enrich": "Videos anreichern", - "backfill_recent": "Neue Uploads nachladen", - "backfill_deep": "Ganze Historie nachladen", - "probe": "Shorts klassifizieren", - "rss_poll": "Auf neue Uploads prüfen", - "subscriptions": "Abos synchronisieren", - "autotag": "Kanäle automatisch taggen", - "playlist_sync": "Playlists synchronisieren", - "working": "Arbeitet…" - }, - "maintenance": { - "title": "Wartung", - "batchLabel": "Pro Lauf erneut geprüfte Videos", - "batchHint": "Wie viele am längsten nicht geprüfte Videos der Wartungs-Job pro Lauf erneut validiert. Höher = schnellerer Gesamtzyklus, aber mehr API-Kontingent (1 Einheit je 50 Videos).", - "batchNote": "Standard: {{default}}. ~233k Videos ÷ dieser Wert = Tage für einen vollen Prüfzyklus." - }, - "dot": { - "running": "läuft gerade", - "ok": "letzter Lauf OK", - "idle": "untätig (in dieser Sitzung noch nicht gelaufen)", - "error": "letzter Lauf fehlgeschlagen", - "skipped": "übersprungen (Sync pausiert)" - }, - "jobDesc": { - "rss_poll": "Prüft den RSS-Feed jedes Kanals auf neue Uploads (kostenlos, kein Kontingent). Fällt er aus, erscheinen neue Videos erst später, wenn ein Backfill sie erfasst — der Feed hinkt YouTube hinterher.", - "enrich": "Holt Videodetails (Dauer, Aufrufe, Live-/Shorts-Flags) und prüft Live-Videos erneut, bis sie enden. Fällt er aus, bleiben Videos ohne Dauer/Statistik und beendete Streams hängen als „live“ fest.", - "backfill": "Holt neue Uploads und dann den gesamten Verlauf vorgemerkter Kanäle, innerhalb des gemeinsamen Kontingents. Fällt er aus, werden ältere Videos und der volle Verlauf nicht ergänzt.", - "autotag": "Erkennt Sprache/Themen jedes Kanals und vergibt automatische Tags. Fällt er aus, bekommen neue Kanäle keine Auto-Tags, sodass Sprach-/Themenfilter sie verfehlen.", - "shorts": "Prüft youtube.com/shorts, um Shorts zu markieren. Fällt er aus, werden Shorts im Feed nicht von normalen Videos getrennt.", - "subscriptions": "Importiert die YouTube-Abos jedes Nutzers neu. Fällt er aus, werden auf YouTube hinzugefügte oder entfernte Abos hier nicht übernommen.", - "playlist_sync": "Spiegelt die YouTube-Wiedergabelisten jedes Nutzers in die App. Fällt er aus, erscheinen Änderungen an deinen YouTube-Listen lokal nicht.", - "maintenance": "Findet Videos, die nicht mehr abspielbar sind (gelöscht, auf privat gesetzt, abgebrochene Premieren), blendet sie aus dem Feed aus und entfernt sie nach einer Schonfrist; prüft die am längsten nicht geprüften Videos erneut. Fällt er aus, bleiben tote Videos im Katalog.", - "explore_cleanup": "Entfernt Kanäle, die du erkundet, aber nie abonniert hast (und ihre Videos), nach einer Karenzzeit, damit Neugier-Stöbern den Katalog nicht überfüllt. Wenn es stoppt, bleiben verlassene erkundete Kanäle bestehen.", - "download_gc": "Löscht heruntergeladene Dateien nach Ablauf der Aufbewahrungsfrist, gibt nicht mehr belegten Speicher frei und warnt vor dem Ablauf einer geteilten Datei. Fällt er aus, häufen sich alte Downloads und Speicherplatz wird nicht freigegeben.", - "plex_sync": "Spiegelt die aktivierten Plex-Bibliotheken in den App-Katalog — die Metadaten hinter Plex-Stöbern, Suche, Filtern und Info-Seiten (Genres, Besetzung, Wertungen, Artwork). Fällt er aus, erscheinen neu hinzugefügte oder geänderte Plex-Titel erst beim nächsten Sync.", - "plex_watch_sync": "Holt kürzliche Plex-Wiedergabeänderungen (beendete Episoden, Fortsetzungspositionen) nach Siftlode und schiebt lokale Wiedergabeänderungen nach, die Plex nicht erreicht haben. Der günstige Zwei-Wege-Abgleich zwischen den vollen Reconciles. Fällt er aus, erscheinen in der Plex-App gesetzte Gesehen-/Fortsetzungsstände hier später.", - "plex_watch_reconcile": "Ein vollständiger Wiedergabe-Abgleich, der jede Bibliothek neu scannt — erfasst, was der inkrementelle Lauf nicht kann, vor allem eine auf Plex-Seite zurückgenommene Gesehen-Markierung. Schwerer, läuft daher etwa täglich. Fällt er aus, werden in Plex vorgenommene Un-Watches nicht zurückgespiegelt.", - "audit_gc": "Entfernt Audit-Protokolleinträge, die älter als das eingestellte Aufbewahrungsfenster sind (Konfiguration → Audit-Protokoll). Wenn er stoppt, wächst das Protokoll unbegrenzt — harmlos, aber unbeschränkt." - }, - "jobs": { - "rss_poll": "RSS-Abfrage (neue Uploads)", - "enrich": "Anreicherung + Live-Aktualisierung", - "backfill": "Backfill (aktuell + ganze Historie)", - "autotag": "Automatisches Tagging", - "shorts": "Shorts-Klassifizierung", - "subscriptions": "Abo-Resync", - "playlist_sync": "YouTube-Wiedergabelisten-Sync", - "maintenance": "Wartung + Validierung", - "demo_reset": "Demo-Konto-Reset", - "explore_cleanup": "Bereinigung erkundeter Kanäle", - "download_gc": "Download-Bereinigung", - "plex_sync": "Plex-Bibliothek-Sync", - "plex_watch_sync": "Plex-Wiedergabe-Sync (inkrementell)", - "plex_watch_reconcile": "Plex-Wiedergabe-Abgleich (vollständig)", - "audit_gc": "Audit-Protokoll-Bereinigung" - }, - "queue": { - "recentPending": "Zu synchronisierende Kanäle", - "recentPendingHint": "Kanäle, deren neueste Uploads noch nicht abgerufen wurden.", - "deepPending": "Ganze Historie ausstehend", - "deepPendingHint": "Für vollständigen Verlauf vorgemerkte Kanäle, die noch nicht fertig sind.", - "enrichPending": "Zu anreichernde Videos", - "enrichPendingHint": "Videos, die noch auf Details warten (Dauer, Statistiken, Live-/Short-Klassifizierung).", - "shortsPending": "Zu klassifizierende Shorts", - "shortsPendingHint": "Bereits angereicherte Videos, die noch auf die Shorts-Prüfung warten.", - "liveRefresh": "Live zu aktualisieren", - "liveRefreshHint": "Live-/bevorstehende Videos (und gerade beendete Streams ohne Dauer), bis sie sich stabilisieren." - }, - "purgeDiscovery": "Entdeckung leeren", - "purgeDiscoveryHint": "Alle nicht behaltenen Entdeckungsinhalte jetzt entfernen (von niemandem angesehene/gespeicherte Suchergebnisse und erkundete, nicht abonnierte Kanäle) — ohne Karenzzeit.", - "purgeConfirmTitle": "Entdeckungsinhalte löschen?", - "purgedDiscovery": "{{count}} Entdeckungselemente entfernt" -} diff --git a/frontend/src/i18n/locales/de/settings.json b/frontend/src/i18n/locales/de/settings.json deleted file mode 100644 index 314bcc1..0000000 --- a/frontend/src/i18n/locales/de/settings.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "title": "Einstellungen", - "tabs": { - "appearance": "Darstellung", - "notifications": "Benachrichtigungen", - "account": "Konto" - }, - "plexSync": { - "title": "Plex-Wiedergabesync", - "intro": "Halte den Status „gesehen / wird gerade angesehen“ zwischen Plex und Siftlode synchron. Beim Einschalten wird dein vorhandener Plex-Verlauf einmalig importiert (Plex gewinnt); die laufende Zwei-Wege-Synchronisierung folgt.", - "toggle": "Zwei-Wege-Sync mit Plex", - "toggleHint": "Nutzt das Plex-Konto dieses Servers. Beim Aktivieren wird dein Plex-Wiedergabeverlauf einmalig importiert.", - "importNow": "Jetzt aus Plex importieren", - "imported": "Aus Plex importiert: {{watched}} gesehen, {{in_progress}} laufend.", - "lastSync": "Letzter Import: {{when}}", - "never": "Noch nicht importiert", - "importing": "Plex-Wiedergabeverlauf wird importiert…" - }, - "save": { - "unsaved": "Nicht gespeicherte Änderungen", - "saving": "Speichern…", - "saved": "Einstellungen gespeichert", - "failed": "Speichern fehlgeschlagen", - "save": "Speichern", - "discard": "Verwerfen" - }, - "unsaved": { - "title": "Nicht gespeicherte Änderungen", - "message": "Du hast nicht gespeicherte Einstellungen. Verwerfen und verlassen?", - "discard": "Änderungen verwerfen" - }, - "appearance": { - "colorScheme": "Farbschema", - "display": "Anzeige", - "darkMode": "Dunkler Modus", - "backgroundImage": "Hintergrundbild", - "backgroundImageHint": "Zeigt ein dezentes, themenbezogenes Hintergrundbild hinter der App, damit die Glasflächen etwas zum Brechen haben. Aus = einfarbig.", - "backgroundFade": "Bildabblendung", - "listView": "Listenansicht", - "listViewHint": "Zeigt den Feed als kompakte Liste statt als Kartenraster an.", - "performanceMode": "Leistungsmodus", - "performanceModeHint": "Schaltet den durchscheinenden Glaseffekt und die weichen Schatten aus, für flüssigere Darstellung auf langsameren Geräten.", - "showHints": "Hinweise anzeigen", - "showHintsHint": "Diese kleinen Hover-Erklärungen. Schalte sie aus, sobald du dich auskennst.", - "textSize": "Schriftgröße" - }, - "notifications": { - "title": "Benachrichtigungen", - "sound": "Ton bei Hinweisen", - "soundHint": "Spielt einen kurzen Ton ab, wenn eine Benachrichtigung deine Aufmerksamkeit braucht (z. B. Fehler, Abfragen).", - "autoDismiss": "Toasts automatisch ausblenden", - "autoDismissHint": "Ausgeschaltet bleiben Pop-up-Toasts, bis du sie schließt. Eingeschaltet blenden sie sich nach der eingestellten Zeit aus.", - "dismissAfter": "Ausblenden nach", - "sendTest": "Testbenachrichtigung senden", - "testTitle": "Testbenachrichtigung", - "testMessage": "So sieht eine Benachrichtigung aus." - }, - "account": { - "title": "Konto", - "youtubeAccess": "YouTube-Zugriff", - "youtubeAccessIntro": "Die Anmeldung teilt nur deinen Namen und deine E-Mail-Adresse. Der YouTube-Zugriff wird separat erteilt, unten — jeder ist optional und jederzeit in deinem Google-Konto widerrufbar.", - "readTitle": "Abos lesen (dein Feed)", - "readGrantedHint": "Erteilt. Siftlode liest die Kanäle, denen du folgst, um deinen Feed aufzubauen. Damit ändert es nie dein YouTube-Konto.", - "readEnableHint": "Nur-Lese-Zugriff auf deine Abos — erforderlich, um deinen Feed aufzubauen. Siftlode kann damit nichts ändern.", - "writeTitle": "Abbestellen (Schreiben)", - "writeGrantedHint": "Erteilt. Du kannst Kanäle auf YouTube von innerhalb Siftlode abbestellen. Es schreibt nur, wenn du es anforderst.", - "writeEnableHint": "Ermöglicht Siftlode, Kanäle auf YouTube für dich abzubestellen. Optional — überspringe es, um im Nur-Lese-Modus zu bleiben.", - "granted": "Erteilt", - "enable": "Aktivieren", - "enableHint": "Leitet zu Google weiter, um die Berechtigung zu erteilen. Google zeigt einen Hinweis „nicht verifizierte App“ — das ist zu erwarten; klicke auf Erweitert → Weiter zu Siftlode.", - "walkMeThrough": "Führe mich durch die Einrichtung", - "dangerZone": "Gefahrenzone", - "deleteHint": "Löscht dein Konto und alle deine Daten dauerhaft — Abos, Tags, Wiedergabeverlauf, Playlists und Einstellungen. Kann nicht rückgängig gemacht werden.", - "deleteAccount": "Mein Konto löschen", - "deleteTitle": "Konto löschen?", - "deleteConfirm": "Dies löscht dein Konto und alle deine Daten (Abos, Tags, angesehen/gespeichert/ausgeblendet, Playlists, Einstellungen) dauerhaft. Es kann nicht rückgängig gemacht werden.", - "deleteConfirmButton": "Alles löschen", - "demoNotice": "Dies ist das gemeinsame Demo-Konto. Es hat keine YouTube-Verbindung — du kannst die gesamte gemeinsame Bibliothek durchsuchen, filtern und sortieren, Playlists erstellen und Dinge ausprobieren, aber nichts berührt ein echtes YouTube-Konto.", - "signInMethods": "Anmeldemethoden", - "googleLink": { - "title": "Google-Konto", - "connected": "Verbunden", - "connectedHint": "Dein Google-Konto ist verknüpft. Du kannst dich mit Google anmelden und unten den YouTube-Zugriff erteilen.", - "connectHint": "Verknüpfe ein Google-Konto, um dich mit Google anzumelden und den YouTube-Zugriff für deinen Feed zu aktivieren.", - "connect": "Google verknüpfen", - "linked": "Google-Konto verknüpft.", - "conflict": "Dieses Google-Konto ist bereits mit einem anderen Siftlode-Konto verknüpft.", - "mismatch": "Das ist ein anderes Google-Konto als das bereits hier verknüpfte. Melde dich mit dem verknüpften an.", - "error": "Das Google-Konto konnte nicht verknüpft werden. Bitte versuche es erneut." - }, - "password": { - "title": "Passwort", - "setHint": "Ein Passwort ist gesetzt. Du kannst dich mit E-Mail und Passwort anmelden.", - "unsetHint": "Noch kein Passwort — lege eines fest, um dich mit deiner E-Mail anzumelden (zusätzlich zu oder anstelle von Google).", - "set": "Passwort festlegen", - "change": "Passwort ändern", - "current": "Aktuelles Passwort", - "new": "Neues Passwort (min. 10 Zeichen)", - "save": "Passwort speichern", - "saving": "Speichern…", - "saved": "Passwort für {{email}} aktualisiert.", - "failed": "Das Passwort konnte nicht aktualisiert werden." - }, - "sessions": { - "title": "Aktive Sitzungen", - "hint": "Diese Kontoanmeldung überall sonst abmelden — praktisch, wenn du auf einem geteilten oder verlorenen Gerät angemeldet geblieben bist. Hier bleibst du angemeldet.", - "action": "Andere Sitzungen abmelden", - "confirmTitle": "Andere Sitzungen abmelden?", - "confirmBody": "Damit wirst du in allen anderen Browsern und auf allen anderen Geräten abgemeldet. Hier bleibst du angemeldet.", - "done": "Von allen anderen Sitzungen abgemeldet.", - "failed": "Andere Sitzungen konnten nicht abgemeldet werden. Bitte versuche es erneut." - } - }, - "demo": { - "title": "Demo-Zugang", - "intro": "E-Mails hier können das gemeinsame Demo-Konto direkt von der Anmeldeseite aus betreten — ohne Google-Anmeldung. Sie tippen die Adresse einfach ins Feld und werden nach einem Moment eingelassen.", - "addPlaceholder": "E-Mail für die Demo auf die Whitelist…", - "add": "Hinzufügen", - "added": "{{email}} zur Demo-Whitelist hinzugefügt", - "addFailed": "Diese E-Mail konnte nicht hinzugefügt werden", - "removeFailed": "Diese E-Mail konnte nicht entfernt werden", - "remove": "Entfernen", - "removeHint": "Diese E-Mail von der Demo-Whitelist entfernen.", - "addedBy": "hinzugefügt von {{who}}", - "reset": "Demo-Zustand zurücksetzen", - "resetHint": "Löscht die Playlists, ausgeblendeten/angesehenen/gespeicherten Videos und Einstellungen des Demo-Kontos und legt dann ein paar Beispiel-Playlists neu an.", - "resetTitle": "Demo-Zustand zurücksetzen?", - "resetConfirm": "Dies löscht alles, was das Demo-Konto angesammelt hat — Playlists, ausgeblendete/angesehene/gespeicherte Videos und Einstellungen — und legt ein paar Beispiel-Playlists neu an. Es kann nicht rückgängig gemacht werden.", - "resetDone": "Demo zurückgesetzt — {{count}} Beispiel-Playlist(s) angelegt", - "resetFailed": "Das Demo-Konto konnte nicht zurückgesetzt werden", - "removed": "Von der Demo-Whitelist entfernt", - "removeTitle": "Demo-Zugang entfernen?", - "removeConfirm": "{{email}} von der Demo-Whitelist entfernen? Der Zugang zum Demo-Konto ist dann nicht mehr möglich." - }, - "invites": { - "title": "Zugriffsanfragen", - "noPending": "Keine ausstehenden Anfragen.", - "addPlaceholder": "E-Mail direkt hinzufügen…", - "add": "Hinzufügen", - "decided": "{{count}} entschieden", - "approved": "{{email}} genehmigt — sie können sich jetzt anmelden", - "approveFailed": "Genehmigung fehlgeschlagen", - "denyFailed": "Ablehnung fehlgeschlagen", - "addedToWhitelist": "{{email}} zur Whitelist hinzugefügt", - "addFailed": "Diese E-Mail konnte nicht hinzugefügt werden", - "requested": "angefragt {{time}}", - "approveHint": "Genehmigen — setzt diese E-Mail auf die Whitelist und benachrichtigt sie per E-Mail, dass sie dabei sind.", - "approve": "Genehmigen", - "denyHint": "Diese Anfrage ablehnen.", - "deny": "Ablehnen", - "denied": "Anfrage abgelehnt", - "denyTitle": "Zugriffsanfrage ablehnen?", - "denyConfirm": "Die Zugriffsanfrage von {{email}} ablehnen?" - } -} diff --git a/frontend/src/i18n/locales/de/setup.json b/frontend/src/i18n/locales/de/setup.json deleted file mode 100644 index c869997..0000000 --- a/frontend/src/i18n/locales/de/setup.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "intro": { - "title": "Willkommen — richten wir Siftlode ein", - "body": "Ein paar schnelle Schritte, und deine Instanz ist bereit: Erstelle dein Admin-Konto und verbinde optional die Google-Anmeldung und E-Mail. All das kannst du später in den Admin-Einstellungen ändern." - }, - "admin": { - "title": "Admin-Konto erstellen", - "desc": "Dies ist das erste Konto und ein Administrator. Nach der Einrichtung meldest du dich mit dieser E-Mail und diesem Passwort an.", - "email": "E-Mail-Adresse", - "password": "Passwort (min. {{n}} Zeichen)", - "invalidEmail": "Gib eine gültige E-Mail-Adresse ein.", - "shortPassword": "Das Passwort muss mindestens {{n}} Zeichen lang sein." - }, - "google": { - "title": "Mit Google anmelden (optional)", - "desc": "Füge deine Google-OAuth-Client-ID und das Secret ein, um die Google-Anmeldung und den YouTube-Zugriff zu aktivieren. Beide leer lassen zum Überspringen — E-Mail+Passwort funktioniert auch ohne.", - "clientId": "Google-Client-ID", - "clientSecret": "Google-Client-Secret", - "bothRequired": "Gib Client-ID und Secret ein oder lass beide leer zum Überspringen.", - "skipHint": "Leer lassen, um dies später auf der Admin-Konfigurationsseite einzurichten." - }, - "smtp": { - "title": "E-Mail / SMTP (optional)", - "desc": "Richte einen SMTP-Server ein, damit Siftlode Bestätigungs-, Reset- und Benachrichtigungs-E-Mails senden kann. Host leer lassen zum Überspringen — ohne E-Mail werden Registrierungen stattdessen vom Admin automatisch freigegeben.", - "host": "SMTP-Host (z. B. smtp.gmail.com)", - "port": "Port", - "user": "Benutzername", - "from": "Absenderadresse (optional)", - "password": "SMTP-Passwort", - "testTo": "Test-E-Mail senden an…", - "test": "Test senden", - "testOk": "Test-E-Mail gesendet — sieh im Postfach nach.", - "testFail": "Senden fehlgeschlagen — prüfe die Einstellungen.", - "skipHint": "Leer lassen, um dies später auf der Admin-Konfigurationsseite einzurichten." - }, - "finish": { - "title": "Alles bereit!", - "body": "Klicke auf Fertig, um die Einrichtung abzuschließen. Der Assistent verschwindet und du gelangst zur Anmeldeseite — melde dich mit dem soeben erstellten Admin-Konto an." - }, - "nav": { - "back": "Zurück", - "next": "Weiter", - "finish": "Fertig" - }, - "token": { - "title": "Setup-Link erforderlich", - "body": "Öffne den Assistenten über den Link, der beim ersten Start in den Container-Logs ausgegeben wird (sieht aus wie …/setup?token=…). Er stellt sicher, dass nur der Betreiber diese Instanz einrichten kann." - }, - "genericError": "Etwas ist schiefgelaufen — bitte versuche es erneut." -} diff --git a/frontend/src/i18n/locales/de/sidebar.json b/frontend/src/i18n/locales/de/sidebar.json deleted file mode 100644 index 7ca94de..0000000 --- a/frontend/src/i18n/locales/de/sidebar.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "filters": "Filter", - "activeCount": "{{count}} aktiv", - "clearAll": "Alle löschen", - "resetDefaults": "Auf Standard zurücksetzen", - "customize": "Seitenleiste anpassen", - "done": "Fertig", - "editHint": "Zum Umordnen ziehen · Auge zum Ein-/Ausblenden", - "channel": "Kanal", - "loading": "Wird geladen…", - "thisChannel": "Dieser Kanal", - "channelCount": "{{count}} Kanal", - "channelCount_other": "{{count}} Kanäle", - "dragToReorder": "Zum Umordnen ziehen", - "showWidget": "Widget einblenden", - "hideWidget": "Widget ausblenden", - "expand": "Ausklappen", - "collapse": "Einklappen", - "collapsePanel": "Filter einklappen", - "expandPanel": "Filter ausklappen", - "any": "Beliebig", - "all": "Alle", - "tagModeTooltip": "Beliebige vs. alle ausgewählten Tags treffen", - "match": "Treffer", - "custom": "Benutzerdefiniert", - "from": "Von", - "to": "Bis", - "clearDates": "Daten löschen", - "reshuffle": "Neu mischen", - "noMatchingTags": "Keine passenden Tags", - "manageTags": "Verwalten", - "shareView": "Link zu dieser Ansicht kopieren", - "shareCopied": "Ansichts-Link in die Zwischenablage kopiert", - "shareFailed": "Link konnte nicht kopiert werden", - "widget": { - "savedviews": "Gespeicherte Ansichten", - "show": "Anzeigen", - "sort": "Sortierung", - "date": "Upload-Datum", - "content": "Inhaltstyp", - "language": "Sprache", - "topic": "Thema", - "tags": "Deine Tags" - }, - "show": { - "unwatched": "Ungesehen", - "in_progress": "Angefangen", - "all": "Alle", - "watched": "Angesehen", - "saved": "Gespeichert", - "hidden": "Ausgeblendet" - }, - "sort": { - "newest": "Neueste", - "oldest": "Älteste", - "views": "Meistgesehen", - "duration_desc": "Längste", - "duration_asc": "Kürzeste", - "title": "Name (A–Z)", - "subscribers": "Kanal-Abonnenten", - "priority": "Kanal-Priorität", - "shuffle": "Überrasch mich" - }, - "content": { - "normal": "Normal", - "shorts": "Shorts", - "live": "Live / Demnächst" - }, - "datePreset": { - "24h": "24 Std.", - "1week": "1 Woche", - "1month": "1 Monat", - "6months": "6 Monate", - "1year": "1 Jahr" - } -} diff --git a/frontend/src/i18n/locales/de/stats.json b/frontend/src/i18n/locales/de/stats.json deleted file mode 100644 index 3abf5ce..0000000 --- a/frontend/src/i18n/locales/de/stats.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "tabs": { - "overview": "Übersicht", - "system": "System" - }, - "title": "API-Kontingentnutzung", - "rangeDays": "{{count}} T", - "introBefore": "YouTube-Data-API-Einheiten, zugeordnet danach, wer die Nutzung ausgelöst hat. ", - "introSystem": "System", - "introAfter": " ist gemeinsame Hintergrundarbeit (geplantes Nachladen, Anreicherung, erneute Synchronisierung), die nicht an eine Person gebunden ist.", - "loading": "Wird geladen…", - "noData": "Keine Daten.", - "dailyTotal": "Tagessumme ({{count}} T · {{units}} Einheiten)", - "noUsageInRange": "Keine Nutzung in diesem Zeitraum.", - "dailyTooltip": "{{day}}: {{units}} Einheiten", - "system": "System (Hintergrund)", - "sync": { - "myStatus": "Mein Synchronisierungsstatus", - "channels": "Kanäle", - "channelsHint": "Wie viele Kanäle du abonniert hast.", - "recentSynced": "Neueste synchronisiert", - "recentSyncedHint": "Kanäle, deren neueste Uploads (~letztes Jahr) bereits in der lokalen Datenbank sind und so in deinem Feed erscheinen.", - "fullHistory": "Vollständiger Verlauf", - "fullHistoryHint": "Kanäle, deren gesamter Katalog abgerufen wurde, von denen, für die du den vollständigen Verlauf angefordert hast (pro Kanal auf der Kanäle-Seite aktivierbar).", - "fullHistoryEta": "Vollständiger Verlauf – Restzeit", - "fullHistoryEtaHint": "Grobe Schätzung, um das Nachladen des vollständigen Verlaufs von {{count}} ausstehenden Kanal/Kanälen abzuschließen, basierend auf dem gemeinsamen Tageskontingent.", - "myVideos": "Meine Videos", - "myVideosHint": "Gesamtzahl der dir verfügbaren Videos über deine abonnierten Kanäle.", - "quotaLeft": "Heute verbleibendes Kontingent", - "quotaLeftHint": "Heute verbleibendes gemeinsames YouTube-API-Budget (wird um Mitternacht US-Pazifik zurückgesetzt). Das Nachladen ordnet sich diesem unter.", - "loading": "Wird geladen…", - "apiUsage": "Deine API-Nutzung", - "today": "Heute", - "todayHint": "YouTube-API-Einheiten, die deine eigenen Aktionen heute verbraucht haben (wird um Mitternacht US-Pazifik zurückgesetzt). Die Hintergrund-Synchronisierung zählt hier nicht.", - "last7d": "Letzte 7 Tage", - "last30d": "Letzte 30 Tage", - "allTime": "Gesamt", - "byAction": "Nach Aktion (30 Tage)", - "noUsage": "Noch keine Nutzung.", - "actions": "Aktionen", - "syncSubscriptions": "Abos synchronisieren", - "syncSubscriptionsHint": "Importiert deine YouTube-Abos erneut und holt neu gefolgte Kanäle hinzu.", - "backfillEverything": "Alles nachladen", - "backfillEverythingHint": "Fordert das Nachladen des vollständigen Katalogs für jeden Kanal an, den du abonniert hast. Ältere Videos und die vollständige Suche werden gefüllt, soweit das gemeinsame Tageskontingent es zulässt.", - "synced": "{{count}} Abos synchronisiert", - "syncFailed": "Synchronisierung fehlgeschlagen", - "fullHistoryRequested": "Vollständiger Verlauf für {{count}} Kanäle angefordert", - "fullHistoryFailed": "Vollständiger Verlauf konnte nicht angefordert werden", - "admin": "Admin", - "adminPauseHint": "Pausiert oder setzt die Hintergrund-Synchronisierung für die gesamte App fort (alle Nutzer).", - "resumeBackgroundSync": "Hintergrund-Synchronisierung fortsetzen", - "pauseBackgroundSync": "Hintergrund-Synchronisierung pausieren" - } -} diff --git a/frontend/src/i18n/locales/de/tagManager.json b/frontend/src/i18n/locales/de/tagManager.json deleted file mode 100644 index 7042cf1..0000000 --- a/frontend/src/i18n/locales/de/tagManager.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "title": "Tags verwalten", - "channels": "{{count}} Kan.", - "onChannels": "Getaggte Kanäle", - "delete": "Löschen", - "deleted": "Tag „{{name}}“ gelöscht", - "deleteTitle": "Tag löschen", - "confirmDelete": "Tag „{{name}}“ löschen? Er wird von allen Kanälen entfernt, auf denen er liegt.", - "empty": "Noch keine Tags — füge unten einen hinzu.", - "newPlaceholder": "Neuer Tag-Name…", - "add": "Hinzufügen" -} diff --git a/frontend/src/i18n/locales/de/time.json b/frontend/src/i18n/locales/de/time.json deleted file mode 100644 index 80a00f7..0000000 --- a/frontend/src/i18n/locales/de/time.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "justNow": "gerade eben", - "secondsAgo": "vor {{count}} Sek.", - "minutesAgo": "vor {{count}} Min.", - "hoursAgo": "vor {{count}} Std.", - "daysAgo": "vor {{count}} T.", - "weeksAgo": "vor {{count}} Wo.", - "monthsAgo": "vor {{count}} Mon.", - "yearsAgo": "vor {{count}} J.", - "eta": { - "now": "jetzt", - "done": "fertig", - "lessThanHour": "< 1 Stunde", - "hours_one": "~{{count}} Stunde", - "hours_other": "~{{count}} Stunden", - "days_one": "~{{count}} Tag", - "days_other": "~{{count}} Tage" - } -} diff --git a/frontend/src/i18n/locales/de/users.json b/frontend/src/i18n/locales/de/users.json deleted file mode 100644 index e45c288..0000000 --- a/frontend/src/i18n/locales/de/users.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "tabs": { - "roles": "Benutzer & Rollen", - "access": "Zugriffsanfragen", - "demo": "Demo" - }, - "roles": { - "title": "Benutzer & Rollen", - "intro": "Alle, die sich anmelden können. Mache einen Benutzer zum Admin oder wieder zum normalen Benutzer.", - "empty": "Noch keine Benutzer.", - "you": "du", - "admin": "Admin", - "demo": "Demo", - "suspended": "Gesperrt", - "pending": "Wartet auf Freigabe", - "unverified": "E-Mail nicht bestätigt", - "demoLocked": "Die Rolle des Demo-Kontos kann nicht geändert werden.", - "selfLocked": "Du kannst deine eigene Rolle nicht ändern.", - "makeAdmin": "Zum Admin machen", - "makeUser": "Zum Benutzer machen", - "updated": "Rolle aktualisiert für {{email}}", - "promoteTitle": "Zum Admin machen?", - "demoteTitle": "Admin entfernen?", - "promoteConfirm": "{{email}} vollen Admin-Zugriff geben (Einstellungen, Planer, Benutzerverwaltung)?", - "demoteConfirm": "{{email}} den Admin-Zugriff entziehen? Wird zum normalen Benutzer." - }, - "suspend": { - "action": "Sperren", - "undoAction": "Entsperren", - "title": "Konto sperren?", - "undoTitle": "Konto entsperren?", - "confirm": "{{email}} sperren? Eine Anmeldung (per Passwort oder Google) ist erst nach dem Entsperren wieder möglich, und jede aktive Sitzung endet.", - "undoConfirm": "{{email}} entsperren? Eine Anmeldung ist dann wieder möglich.", - "done": "{{email}} gesperrt", - "undone": "{{email}} entsperrt", - "demoLocked": "Das Demo-Konto kann nicht gesperrt werden.", - "selfLocked": "Du kannst dein eigenes Konto nicht sperren." - }, - "delete": { - "action": "Löschen", - "locked": "Dieses Konto kann hier nicht gelöscht werden.", - "title": "Konto löschen?", - "confirm": "{{email}} und alle zugehörigen Daten endgültig löschen — Abos, Tags, Wiedergabeverlauf, Playlists und Einstellungen? Das kann nicht rückgängig gemacht werden.", - "done": "{{email}} gelöscht" - } -} diff --git a/frontend/src/i18n/locales/de/views.json b/frontend/src/i18n/locales/de/views.json deleted file mode 100644 index a176243..0000000 --- a/frontend/src/i18n/locales/de/views.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "save": "Aktuelle Ansicht speichern", - "namePlaceholder": "Name der Ansicht", - "saveAction": "Speichern", - "cancel": "Abbrechen", - "empty": "Noch keine gespeicherten Ansichten. Filtere den Feed und speichere ihn.", - "apply": "Diese Ansicht anwenden", - "makeDefault": "Als Standard festlegen (wird beim Start geladen)", - "isDefault": "Standardansicht — wird beim Start geladen", - "share": "Link zu dieser Ansicht kopieren", - "shareCopied": "Ansichtslink kopiert", - "rename": "Umbenennen", - "delete": "Löschen", - "deleteConfirm": "Die gespeicherte Ansicht „{{name}}“ löschen?", - "reorder": "Zum Neuordnen ziehen" -} diff --git a/frontend/src/i18n/locales/de/welcome.json b/frontend/src/i18n/locales/de/welcome.json deleted file mode 100644 index 191ba12..0000000 --- a/frontend/src/i18n/locales/de/welcome.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "hero": { - "title": "Deine YouTube-Abos, so wie ein Feed funktionieren sollte.", - "subtitle": "Jeder Upload der Kanäle, denen du folgst, in einem aufgeräumten, filterbaren Feed — kein Algorithmus entscheidet, was du siehst, und kein Shorts- oder Livestream-Lärm, außer du willst es.", - "trust": "Selbst gehostet und privat. Melde dich per E-Mail an oder fahre mit Google fort." - }, - "openSource": { - "title": "Kostenlos und quelloffen", - "body": "Der Code von Siftlode ist öffentlich — sieh nach, wie es funktioniert, betreibe deine eigene private Instanz oder melde ein Problem.", - "link": "Quellcode ansehen" - }, - "preview": { - "feed": "Der Feed — filtern nach Kanal, Sprache, Thema, Länge und Datum.", - "channels": "Kanal-Verwaltung", - "playlists": "Wiedergabelisten" - }, - "features": { - "heading": "Warum Siftlode", - "readable": { - "title": "Deine Abos, endlich lesbar", - "body": "Sortiere und filtere nach Kanal, Tag, Sprache, Thema, Länge oder Datum und speichere deine Einstellungen als Ansichten. Du entscheidest, was erscheint, nicht ein Algorithmus." - }, - "neverMiss": { - "title": "Verpasse keinen Upload", - "body": "Jedes Video jedes Kanals landet in einem Feed; Shorts und Livestreams bleiben standardmäßig ausgeblendet." - }, - "search": { - "title": "Durchsuche ganz YouTube", - "body": "Durchsuche YouTube direkt aus deinem Feed — Ergebnisse lassen sich abspielen, speichern und wie jedes andere Video zu Playlists hinzufügen." - }, - "playlists": { - "title": "Playlists, die in beide Richtungen synchronisieren", - "body": "Erstelle Playlists lokal und halte sie in beidseitiger Synchronisation mit YouTube." - }, - "player": { - "title": "Schau in der App, mach überall weiter", - "body": "Ein In-App-Player setzt jedes Video genau dort fort, wo du aufgehört hast — kein Empfehlungs-Strudel." - }, - "private": { - "title": "Selbst gehostet und privat", - "body": "Betreibe es selbst, mehrbenutzerfähig, auf Englisch, Ungarisch und Deutsch. Deine Daten bleiben deine." - } - }, - "auth": { - "signinTitle": "Anmelden", - "createTitle": "Konto erstellen", - "forgotTitle": "Passwort zurücksetzen", - "resetTitle": "Neues Passwort festlegen", - "email": "E-Mail", - "password": "Passwort", - "newPassword": "Neues Passwort (min. {{min}} Zeichen)", - "invalidEmail": "Gib eine gültige E-Mail-Adresse ein.", - "weakPassword": "Das Passwort muss mindestens {{min}} Zeichen lang sein.", - "genericError": "Etwas ist schiefgelaufen. Bitte versuche es erneut.", - "working": "Einen Moment…", - "signinButton": "Anmelden", - "createButton": "Konto erstellen", - "forgotButton": "Reset-Link senden", - "resetButton": "Passwort festlegen", - "createLink": "Konto erstellen", - "forgotLink": "Passwort vergessen?", - "backToSignin": "Zurück zur Anmeldung", - "or": "oder", - "google": "Mit Google fortfahren", - "tryDemo": "Demo ausprobieren", - "demoHint": "Gib eine E-Mail ein, die für das Demo-Konto freigeschaltet ist.", - "demoEmail": "Demo-E-Mail", - "demoEnter": "Eintreten", - "demoDenied": "Diese E-Mail ist nicht für die Demo freigeschaltet. Bitte den Admin, sie hinzuzufügen.", - "registerDone": "Fast geschafft — prüfe deine E-Mails auf den Bestätigungslink. Nach der Bestätigung gibt ein Admin dein Konto frei, bevor du dich anmelden kannst.", - "forgotDone": "Falls zu dieser E-Mail ein Konto gehört, haben wir einen Reset-Link gesendet. Prüfe dein Postfach.", - "resetDone": "Dein Passwort wurde festgelegt — du kannst dich jetzt anmelden.", - "verifyOk": "E-Mail bestätigt. Sobald ein Admin dein Konto freigibt, kannst du dich anmelden.", - "verifyInvalid": "Dieser Bestätigungslink ist ungültig oder abgelaufen.", - "accessRequested": "Danke — wir haben deine Anfrage erfasst. Ein Admin wird sie prüfen.", - "accessDenied": "Dieses Konto ist für diese Instanz noch nicht freigegeben — registriere dich unten oder bitte den Admin um Zugang.", - "suspended": "Dieses Konto wurde gesperrt und kann sich nicht anmelden. Falls das ein Irrtum ist, wende dich an den Betreiber (Details in deiner E-Mail).", - "deleted": "Dein Konto und alle Daten wurden endgültig gelöscht. Eine Bestätigung haben wir dir per E-Mail geschickt." - } -} diff --git a/frontend/src/i18n/locales/en/welcome.json b/frontend/src/i18n/locales/en/welcome.json index 9c8f6a9..40daa11 100644 --- a/frontend/src/i18n/locales/en/welcome.json +++ b/frontend/src/i18n/locales/en/welcome.json @@ -38,7 +38,7 @@ }, "private": { "title": "Self-hosted and private", - "body": "Run it yourself, multi-user, in English, Hungarian and German. Your data stays yours." + "body": "Run it yourself, multi-user, in English and Hungarian. Your data stays yours." } }, "auth": { diff --git a/frontend/src/i18n/locales/hu/welcome.json b/frontend/src/i18n/locales/hu/welcome.json index 216e5dd..03a4689 100644 --- a/frontend/src/i18n/locales/hu/welcome.json +++ b/frontend/src/i18n/locales/hu/welcome.json @@ -38,7 +38,7 @@ }, "private": { "title": "Self-hosted és privát", - "body": "Futtasd magad, többfelhasználós, angolul, magyarul és németül. Az adataid a tieid maradnak." + "body": "Futtasd magad, többfelhasználós, angolul és magyarul. Az adataid a tieid maradnak." } }, "auth": {