fix: eliminate cross-app consistency drift

This commit is contained in:
edde746
2026-07-13 23:13:53 +02:00
parent ec249d2ed4
commit d5f3e581df
142 changed files with 4751 additions and 1075 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../widgets/app_icon.dart';
import '../widgets/clickable_cursor.dart';
@@ -41,7 +42,7 @@ class FocusableAction {
final FocusableActionBuilder? builder;
const FocusableAction({
this.icon = Icons.circle,
this.icon = Symbols.circle_rounded,
this.iconColor,
this.iconFill = 1.0,
this.iconSize = 24,
+24 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "Шаблон за маркер на финални надписи",
"creditsPatternDescription": "Шаблон с регулярен израз за намиране на маркери за финални надписи в заглавия на глави",
"invalidRegex": "Невалиден регулярен израз",
"regex": "Регулярен израз",
"downloads": "Изтегляния",
"downloadLocationDescription": "Изберете къде да се съхранява изтегленото съдържание",
"downloadLocationDefault": "По подразбиране (хранилище на приложението)",
@@ -336,6 +337,8 @@
"clearShortcut": "Изчисти клавишната комбинация",
"noShortcutSet": "Няма зададена клавишна комбинация",
"currentShortcut": "Текуща комбинация:",
"pressToRecord": "Избери, за да запишеш клавишна комбинация",
"recordingShortcut": "Натисни клавишната комбинация сега",
"actions": {
"playPause": "Пускане/пауза",
"volumeUp": "Увеличи звука",
@@ -434,7 +437,9 @@
"hue": "Нюанс",
"saturation": "Наситеност",
"brightness": "Яркост",
"hexColor": "Шестнадесетичен цвят"
"hexColor": "Шестнадесетичен цвят",
"expandText": "Разгъни текста",
"collapseText": "Свий текста"
},
"tooltips": {
"shufflePlay": "Разбъркано възпроизвеждане",
@@ -534,6 +539,10 @@
"markedAsWatchedOffline": "Маркирано като гледано (ще се синхронизира, когато сте онлайн)",
"markedAsUnwatchedOffline": "Маркирано като негледано (ще се синхронизира, когато сте онлайн)",
"autoRemovedWatchedDownload": "Автоматично премахнато: ${title}",
"autoRemovedWatchedDownloads": {
"one": "Автоматично премахнато ${n} гледано изтегляне",
"other": "Автоматично премахнати ${n} гледани изтегляния"
},
"removedFromContinueWatching": "Премахнато от продължаване на гледането",
"errorLoading": "Грешка: ${error}",
"streamInterrupted": "Потокът прекъсна. Натиснете „Пусни“ или превъртете, за да опитате отново.",
@@ -585,6 +594,11 @@
"backgroundColor": "Цвят на фона",
"position": "Позиция",
"assOverride": "ASS презаписване",
"overrideScale": "Мащабиране",
"overrideForce": "Принудително",
"overrideStrip": "Премахване на стиловете",
"positionTop": "Горе",
"positionBottom": "Долу",
"bold": "Получер",
"italic": "Курсив",
"renderResolution": "Резолюция на изобразяване",
@@ -710,7 +724,7 @@
"searchFailed": "Търсенето е неуспешно: ${error}",
"connectionTimeout": "Изтече времето за връзка при зареждане на ${context}",
"connectionFailed": "Не може да се осъществи връзка с медиен сървър",
"failedToLoad": "Неуспешно зареждане на ${context}: ${error}",
"unableToLoad": "Не може да се зареди ${context}. Опитайте отново.",
"noClientAvailable": "Няма наличен клиент",
"pleaseEnterToken": "Моля, въведете токен",
"invalidToken": "Невалиден токен",
@@ -966,7 +980,6 @@
"deleted": "Колекцията е изтрита",
"deleteFailed": "Неуспешно изтриване на колекция",
"deleteFailedWithError": "Неуспешно изтриване на колекция: ${error}",
"failedToLoadItems": "Неуспешно зареждане на елементите в колекцията: ${error}",
"selectCollection": "Избери колекция",
"collectionName": "Име на колекция",
"enterCollectionName": "Въведете име на колекция",
@@ -1049,6 +1062,8 @@
"hostingSession": "Сесия с домакин",
"inSession": "В сесия",
"sessionCode": "Код на сесията",
"openSessionControls": "Отвори контролите за сесията „Гледане заедно“",
"copySessionCode": "Копирай кода на сесията",
"hostControlsPlayback": "Домакинът управлява възпроизвеждането",
"anyoneCanControl": "Всеки може да управлява възпроизвеждането",
"hostControls": "Контроли на домакина",
@@ -1262,6 +1277,8 @@
},
"videoSettings": {
"playbackSpeed": "Скорост на възпроизвеждане",
"normalSpeed": "Нормална",
"sleepTimerActive": "Активен (${duration})",
"zoom": "Мащаб",
"sleepTimer": "Таймер за заспиване",
"audioSync": "Синхронизация на аудио",
@@ -1359,6 +1376,8 @@
"artworkUpdated": "Обложката е обновена",
"artworkUpdateFailed": "Неуспешно обновяване на обложката",
"noArtworkAvailable": "Няма налична обложка",
"artworkOption": "Вариант за обложка ${index}",
"selectedArtworkOption": "Вариант за обложка ${index}, избран",
"notSet": "Не е зададено",
"libraryDefault": "По подразбиране за библиотеката",
"accountDefault": "По подразбиране за акаунта",
@@ -1486,6 +1505,7 @@
"title": "Активиране на Plezy в ${service}",
"body": "Посетете ${url} и въведете този код:",
"openToActivate": "Отворете ${service}, за да активирате",
"copyCode": "Копирай кода за активиране",
"waitingForAuthorization": "Изчакване на оторизация…",
"codeCopied": "Кодът е копиран"
},
@@ -1493,6 +1513,7 @@
"title": "Вход в ${service}",
"body": "Сканирайте този QR код или отворете URL-а на което и да е устройство.",
"openToSignIn": "Отворете ${service}, за да влезете",
"copyUrl": "Копирай URL адреса за вход",
"urlCopied": "URL адресът е копиран"
},
"libraryFilter": {
+24 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "Rulletekstmarkørmønster",
"creditsPatternDescription": "Regulært udtryk til at genkende rulletekstmarkører i kapiteltitler",
"invalidRegex": "Ugyldigt regulært udtryk",
"regex": "Regulært udtryk",
"downloads": "Downloads",
"downloadLocationDescription": "Vælg hvor downloadet indhold skal gemmes",
"downloadLocationDefault": "Standard (App-lagring)",
@@ -336,6 +337,8 @@
"clearShortcut": "Ryd genvej",
"noShortcutSet": "Ingen genvej angivet",
"currentShortcut": "Nuværende genvej:",
"pressToRecord": "Vælg for at registrere en genvej",
"recordingShortcut": "Tryk på genvejen nu",
"actions": {
"playPause": "Afspil/Pause",
"volumeUp": "Lydstyrke op",
@@ -434,7 +437,9 @@
"hue": "Farvetone",
"saturation": "Mætning",
"brightness": "Lysstyrke",
"hexColor": "Hexfarve"
"hexColor": "Hexfarve",
"expandText": "Udvid tekst",
"collapseText": "Fold tekst sammen"
},
"tooltips": {
"shufflePlay": "Afspil tilfældigt",
@@ -534,6 +539,10 @@
"markedAsWatchedOffline": "Markeret som set (synkroniseres online)",
"markedAsUnwatchedOffline": "Markeret som uset (synkroniseres online)",
"autoRemovedWatchedDownload": "Automatisk fjernet: ${title}",
"autoRemovedWatchedDownloads": {
"one": "Fjernede automatisk ${n} set download",
"other": "Fjernede automatisk ${n} sete downloads"
},
"removedFromContinueWatching": "Fjernet fra Fortsæt med at se",
"errorLoading": "Fejl: ${error}",
"streamInterrupted": "Streamen blev afbrudt. Tryk på afspil, eller spol for at prøve igen.",
@@ -585,6 +594,11 @@
"backgroundColor": "Baggrundsfarve",
"position": "Position",
"assOverride": "ASS-tilsidesættelse",
"overrideScale": "Skaler",
"overrideForce": "Gennemtving",
"overrideStrip": "Fjern formatering",
"positionTop": "Øverst",
"positionBottom": "Nederst",
"bold": "Fed",
"italic": "Kursiv",
"renderResolution": "Gengivelsesopløsning",
@@ -710,7 +724,7 @@
"searchFailed": "Søgning mislykkedes: ${error}",
"connectionTimeout": "Forbindelsestimeout ved indlæsning af ${context}",
"connectionFailed": "Kan ikke oprette forbindelse til medieserver",
"failedToLoad": "Kunne ikke indlæse ${context}: ${error}",
"unableToLoad": "Kunne ikke indlæse ${context}. Prøv igen.",
"noClientAvailable": "Ingen klient tilgængelig",
"pleaseEnterToken": "Indtast et token",
"invalidToken": "Ugyldigt token",
@@ -966,7 +980,6 @@
"deleted": "Samling slettet",
"deleteFailed": "Kunne ikke slette samling",
"deleteFailedWithError": "Kunne ikke slette samling: ${error}",
"failedToLoadItems": "Kunne ikke indlæse samlingselementer: ${error}",
"selectCollection": "Vælg samling",
"collectionName": "Samlingsnavn",
"enterCollectionName": "Indtast samlingsnavn",
@@ -1049,6 +1062,8 @@
"hostingSession": "Vært for session",
"inSession": "I session",
"sessionCode": "Sessionskode",
"openSessionControls": "Åbn sessionsstyring for Se sammen",
"copySessionCode": "Kopiér sessionskode",
"hostControlsPlayback": "Vært styrer afspilning",
"anyoneCanControl": "Alle kan styre afspilning",
"hostControls": "Værtskontrol",
@@ -1262,6 +1277,8 @@
},
"videoSettings": {
"playbackSpeed": "Afspilningshastighed",
"normalSpeed": "Normal",
"sleepTimerActive": "Aktiv (${duration})",
"zoom": "Zoom",
"sleepTimer": "Sove-timer",
"audioSync": "Lydsynkronisering",
@@ -1359,6 +1376,8 @@
"artworkUpdated": "Grafik opdateret",
"artworkUpdateFailed": "Kunne ikke opdatere grafik",
"noArtworkAvailable": "Ingen grafik tilgængelig",
"artworkOption": "Grafikvalg ${index}",
"selectedArtworkOption": "Grafikvalg ${index}, valgt",
"notSet": "Ikke indstillet",
"libraryDefault": "Biblioteksstandard",
"accountDefault": "Kontostandard",
@@ -1486,6 +1505,7 @@
"title": "Aktiver Plezy på ${service}",
"body": "Besøg ${url} og indtast denne kode:",
"openToActivate": "Åbn ${service} for at aktivere",
"copyCode": "Kopiér aktiveringskode",
"waitingForAuthorization": "Venter på godkendelse…",
"codeCopied": "Kode kopieret"
},
@@ -1493,6 +1513,7 @@
"title": "Log ind på ${service}",
"body": "Scan denne QR-kode, eller åbn URL'en på en enhed.",
"openToSignIn": "Åbn ${service} for at logge ind",
"copyUrl": "Kopiér login-URL",
"urlCopied": "URL kopieret"
},
"libraryFilter": {
+24 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "Abspann-Markierungsmuster",
"creditsPatternDescription": "Regulärer Ausdruck zum Erkennen von Abspann-Markierungen in Kapiteltiteln",
"invalidRegex": "Ungültiger regulärer Ausdruck",
"regex": "Regulärer Ausdruck",
"downloads": "Downloads",
"downloadLocationDescription": "Speicherort für heruntergeladene Inhalte wählen",
"downloadLocationDefault": "Standard (App-Speicher)",
@@ -336,6 +337,8 @@
"clearShortcut": "Kürzel löschen",
"noShortcutSet": "Keine Tastenkombination festgelegt",
"currentShortcut": "Aktuelle Tastenkombination:",
"pressToRecord": "Auswählen, um eine Tastenkombination aufzuzeichnen",
"recordingShortcut": "Jetzt die Tastenkombination drücken",
"actions": {
"playPause": "Wiedergabe/Pause",
"volumeUp": "Lauter",
@@ -434,7 +437,9 @@
"hue": "Farbton",
"saturation": "Sättigung",
"brightness": "Helligkeit",
"hexColor": "Hex-Farbe"
"hexColor": "Hex-Farbe",
"expandText": "Text ausklappen",
"collapseText": "Text einklappen"
},
"tooltips": {
"shufflePlay": "Zufallswiedergabe",
@@ -534,6 +539,10 @@
"markedAsWatchedOffline": "Als gesehen markiert (wird synchronisiert, wenn online)",
"markedAsUnwatchedOffline": "Als ungesehen markiert (wird synchronisiert, wenn online)",
"autoRemovedWatchedDownload": "Automatisch entfernt: ${title}",
"autoRemovedWatchedDownloads": {
"one": "Automatisch entfernt: ${n} angesehener Download",
"other": "Automatisch entfernt: ${n} angesehene Downloads"
},
"removedFromContinueWatching": "Aus Weiterschauen' entfernt",
"errorLoading": "Fehler: ${error}",
"streamInterrupted": "Der Stream wurde unterbrochen. Drücke auf Wiedergabe oder spule, um es erneut zu versuchen.",
@@ -585,6 +594,11 @@
"backgroundColor": "Hintergrundfarbe",
"position": "Position",
"assOverride": "ASS-Überschreibung",
"overrideScale": "Skalieren",
"overrideForce": "Erzwingen",
"overrideStrip": "Formatierung entfernen",
"positionTop": "Oben",
"positionBottom": "Unten",
"bold": "Fett",
"italic": "Kursiv",
"renderResolution": "Render-Auflösung",
@@ -710,7 +724,7 @@
"searchFailed": "Suche fehlgeschlagen: ${error}",
"connectionTimeout": "Zeitüberschreitung beim Laden von ${context}",
"connectionFailed": "Keine Verbindung zum Medienserver möglich",
"failedToLoad": "Fehler beim Laden von ${context}: ${error}",
"unableToLoad": "${context} konnte nicht geladen werden. Bitte erneut versuchen.",
"noClientAvailable": "Kein Client verfügbar",
"pleaseEnterToken": "Bitte Token eingeben",
"invalidToken": "Ungültiges Token",
@@ -966,7 +980,6 @@
"deleted": "Sammlung gelöscht",
"deleteFailed": "Sammlung konnte nicht gelöscht werden",
"deleteFailedWithError": "Sammlung konnte nicht gelöscht werden: ${error}",
"failedToLoadItems": "Sammlungselemente konnten nicht geladen werden: ${error}",
"selectCollection": "Sammlung auswählen",
"collectionName": "Sammlungsname",
"enterCollectionName": "Sammlungsnamen eingeben",
@@ -1049,6 +1062,8 @@
"hostingSession": "Sitzung Hosten",
"inSession": "In Sitzung",
"sessionCode": "Sitzungscode",
"openSessionControls": "Sitzungssteuerung für Gemeinsam Schauen öffnen",
"copySessionCode": "Sitzungscode kopieren",
"hostControlsPlayback": "Host steuert die Wiedergabe",
"anyoneCanControl": "Alle können die Wiedergabe steuern",
"hostControls": "Host steuert",
@@ -1262,6 +1277,8 @@
},
"videoSettings": {
"playbackSpeed": "Wiedergabegeschwindigkeit",
"normalSpeed": "Normal",
"sleepTimerActive": "Aktiv (${duration})",
"zoom": "Zoom",
"sleepTimer": "Schlaftimer",
"audioSync": "Audio-Synchronisation",
@@ -1359,6 +1376,8 @@
"artworkUpdated": "Artwork aktualisiert",
"artworkUpdateFailed": "Artwork konnte nicht aktualisiert werden",
"noArtworkAvailable": "Kein Artwork verfügbar",
"artworkOption": "Artwork-Option ${index}",
"selectedArtworkOption": "Artwork-Option ${index}, ausgewählt",
"notSet": "Nicht festgelegt",
"libraryDefault": "Bibliotheksstandard",
"accountDefault": "Kontostandard",
@@ -1486,6 +1505,7 @@
"title": "Plezy auf ${service} aktivieren",
"body": "Gehe zu ${url} und gib diesen Code ein:",
"openToActivate": "${service} zum Aktivieren öffnen",
"copyCode": "Aktivierungscode kopieren",
"waitingForAuthorization": "Warte auf Autorisierung…",
"codeCopied": "Code kopiert"
},
@@ -1493,6 +1513,7 @@
"title": "Bei ${service} anmelden",
"body": "Scanne diesen QR-Code oder öffne die URL auf einem beliebigen Gerät.",
"openToSignIn": "${service} zum Anmelden öffnen",
"copyUrl": "Anmelde-URL kopieren",
"urlCopied": "URL kopiert"
},
"libraryFilter": {
+13 -2
View File
@@ -227,6 +227,7 @@
"creditsPattern": "Credits Marker Pattern",
"creditsPatternDescription": "Regex pattern to match credits markers in chapter titles",
"invalidRegex": "Invalid regular expression",
"regex": "Regular expression",
"downloads": "Downloads",
"downloadLocationDescription": "Choose where to store downloaded content",
"downloadLocationDefault": "Default (App Storage)",
@@ -538,6 +539,10 @@
"markedAsWatchedOffline": "Marked as watched (will sync when online)",
"markedAsUnwatchedOffline": "Marked as unwatched (will sync when online)",
"autoRemovedWatchedDownload": "Auto-removed: ${title}",
"autoRemovedWatchedDownloads": {
"one": "Auto-removed ${n} watched download",
"other": "Auto-removed ${n} watched downloads"
},
"removedFromContinueWatching": "Removed from Continue Watching",
"errorLoading": "Error: ${error}",
"streamInterrupted": "The stream was interrupted. Press play or seek to retry.",
@@ -589,6 +594,11 @@
"backgroundColor": "Background Color",
"position": "Position",
"assOverride": "ASS Override",
"overrideScale": "Scale",
"overrideForce": "Force",
"overrideStrip": "Remove styling",
"positionTop": "Top",
"positionBottom": "Bottom",
"bold": "Bold",
"italic": "Italic",
"renderResolution": "Render Resolution",
@@ -714,7 +724,7 @@
"searchFailed": "Search failed: ${error}",
"connectionTimeout": "Connection timeout while loading ${context}",
"connectionFailed": "Unable to connect to media server",
"failedToLoad": "Failed to load ${context}: ${error}",
"unableToLoad": "Unable to load ${context}. Please try again.",
"noClientAvailable": "No client available",
"pleaseEnterToken": "Please enter a token",
"invalidToken": "Invalid token",
@@ -970,7 +980,6 @@
"deleted": "Collection deleted",
"deleteFailed": "Failed to delete collection",
"deleteFailedWithError": "Failed to delete collection: ${error}",
"failedToLoadItems": "Failed to load collection items: ${error}",
"selectCollection": "Select Collection",
"collectionName": "Collection Name",
"enterCollectionName": "Enter collection name",
@@ -1268,6 +1277,8 @@
},
"videoSettings": {
"playbackSpeed": "Playback Speed",
"normalSpeed": "Normal",
"sleepTimerActive": "Active (${duration})",
"zoom": "Zoom",
"sleepTimer": "Sleep Timer",
"audioSync": "Audio Sync",
+24 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "Patrón de marcador de créditos",
"creditsPatternDescription": "Expresión regular para reconocer marcadores de créditos en los títulos de capítulos",
"invalidRegex": "Expresión regular no válida",
"regex": "Expresión regular",
"downloads": "Descargas",
"downloadLocationDescription": "Elegir dónde almacenar el contenido descargado",
"downloadLocationDefault": "Predeterminado (Almacenamiento de la App)",
@@ -336,6 +337,8 @@
"clearShortcut": "Borrar atajo",
"noShortcutSet": "Sin atajo asignado",
"currentShortcut": "Atajo actual:",
"pressToRecord": "Seleccionar para grabar un atajo",
"recordingShortcut": "Presiona el atajo ahora",
"actions": {
"playPause": "Reproducir/Pausar",
"volumeUp": "Subir Volumen",
@@ -434,7 +437,9 @@
"hue": "Tono",
"saturation": "Saturación",
"brightness": "Brillo",
"hexColor": "Color hexadecimal"
"hexColor": "Color hexadecimal",
"expandText": "Expandir texto",
"collapseText": "Contraer texto"
},
"tooltips": {
"shufflePlay": "Reproducción aleatoria",
@@ -534,6 +539,10 @@
"markedAsWatchedOffline": "Marcado como visto (se sincronizará al estar en línea)",
"markedAsUnwatchedOffline": "Marcado como no visto (se sincronizará al estar en línea)",
"autoRemovedWatchedDownload": "Eliminado automáticamente: ${title}",
"autoRemovedWatchedDownloads": {
"one": "Se eliminó automáticamente ${n} descarga vista",
"other": "Se eliminaron automáticamente ${n} descargas vistas"
},
"removedFromContinueWatching": "Eliminado de Seguir Viendo",
"errorLoading": "Error: ${error}",
"streamInterrupted": "La reproducción se interrumpió. Pulsa reproducir o avanza para volver a intentarlo.",
@@ -585,6 +594,11 @@
"backgroundColor": "Color de Fondo",
"position": "Posición",
"assOverride": "Sobreescritura ASS",
"overrideScale": "Escalar",
"overrideForce": "Forzar",
"overrideStrip": "Quitar estilos",
"positionTop": "Arriba",
"positionBottom": "Abajo",
"bold": "Negrita",
"italic": "Cursiva",
"renderResolution": "Resolución de renderizado",
@@ -710,7 +724,7 @@
"searchFailed": "Error en la búsqueda: ${error}",
"connectionTimeout": "Tiempo de conexión agotado al cargar ${context}",
"connectionFailed": "No se puede conectar al servidor multimedia",
"failedToLoad": "Error al cargar ${context}: ${error}",
"unableToLoad": "No se pudo cargar ${context}. Inténtalo de nuevo.",
"noClientAvailable": "No hay cliente disponible",
"pleaseEnterToken": "Por favor, introduce un token",
"invalidToken": "Token no válido",
@@ -966,7 +980,6 @@
"deleted": "Colección eliminada",
"deleteFailed": "Error al eliminar la colección",
"deleteFailedWithError": "Error al eliminar la colección: ${error}",
"failedToLoadItems": "Error al cargar los elementos de la colección: ${error}",
"selectCollection": "Seleccionar Colección",
"collectionName": "Nombre de la Colección",
"enterCollectionName": "Introduce el nombre de la colección",
@@ -1049,6 +1062,8 @@
"hostingSession": "Anfitrión de la Sesión",
"inSession": "En Sesión",
"sessionCode": "Código de Sesión",
"openSessionControls": "Abrir los controles de la sesión de Ver Juntos",
"copySessionCode": "Copiar código de sesión",
"hostControlsPlayback": "El anfitrión controla la reproducción",
"anyoneCanControl": "Cualquiera puede controlar la reproducción",
"hostControls": "Control del anfitrión",
@@ -1262,6 +1277,8 @@
},
"videoSettings": {
"playbackSpeed": "Velocidad de reproducción",
"normalSpeed": "Normal",
"sleepTimerActive": "Activo (${duration})",
"zoom": "Zoom",
"sleepTimer": "Temporizador de apagado",
"audioSync": "Sincronización de audio",
@@ -1359,6 +1376,8 @@
"artworkUpdated": "Artwork actualizado",
"artworkUpdateFailed": "Error al actualizar el artwork",
"noArtworkAvailable": "No hay artwork disponible",
"artworkOption": "Opción de imagen ${index}",
"selectedArtworkOption": "Opción de imagen ${index}, seleccionada",
"notSet": "No establecido",
"libraryDefault": "Predeterminado de biblioteca",
"accountDefault": "Predeterminado de cuenta",
@@ -1486,6 +1505,7 @@
"title": "Activar Plezy en ${service}",
"body": "Visita ${url} e introduce este código:",
"openToActivate": "Abrir ${service} para activar",
"copyCode": "Copiar código de activación",
"waitingForAuthorization": "Esperando autorización…",
"codeCopied": "Código copiado"
},
@@ -1493,6 +1513,7 @@
"title": "Inicia sesión en ${service}",
"body": "Escanea este código QR o abre la URL en cualquier dispositivo.",
"openToSignIn": "Abrir ${service} para iniciar sesión",
"copyUrl": "Copiar URL de inicio de sesión",
"urlCopied": "URL copiada"
},
"libraryFilter": {
+24 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "Modèle de marqueur de générique",
"creditsPatternDescription": "Expression régulière pour reconnaître les marqueurs de générique dans les titres de chapitres",
"invalidRegex": "Expression régulière invalide",
"regex": "Expression régulière",
"downloads": "Téléchargement",
"downloadLocationDescription": "Choisissez où stocker le contenu téléchargé",
"downloadLocationDefault": "Par défaut (stockage de l'application)",
@@ -336,6 +337,8 @@
"clearShortcut": "Effacer le raccourci",
"noShortcutSet": "Aucun raccourci défini",
"currentShortcut": "Raccourci actuel :",
"pressToRecord": "Sélectionner pour enregistrer un raccourci",
"recordingShortcut": "Appuyez maintenant sur le raccourci",
"actions": {
"playPause": "Lecture/Pause",
"volumeUp": "Augmenter le volume",
@@ -434,7 +437,9 @@
"hue": "Teinte",
"saturation": "Saturation",
"brightness": "Luminosité",
"hexColor": "Couleur hexadécimale"
"hexColor": "Couleur hexadécimale",
"expandText": "Développer le texte",
"collapseText": "Replier le texte"
},
"tooltips": {
"shufflePlay": "Lecture aléatoire",
@@ -534,6 +539,10 @@
"markedAsWatchedOffline": "Marqué comme vu (se synchronisera lorsque vous serez en ligne)",
"markedAsUnwatchedOffline": "Marqué comme non vu (sera synchronisé lorsque vous serez en ligne)",
"autoRemovedWatchedDownload": "Supprimé automatiquement : ${title}",
"autoRemovedWatchedDownloads": {
"one": "${n} téléchargement vu supprimé automatiquement",
"other": "${n} téléchargements vus supprimés automatiquement"
},
"removedFromContinueWatching": "Supprimer de \"Continuer à regarder\"",
"errorLoading": "Erreur: ${error}",
"streamInterrupted": "La lecture a été interrompue. Appuyez sur Lecture ou avancez pour réessayer.",
@@ -585,6 +594,11 @@
"backgroundColor": "Couleur d'arrière-plan",
"position": "Position",
"assOverride": "Remplacement ASS",
"overrideScale": "Mettre à l’échelle",
"overrideForce": "Forcer",
"overrideStrip": "Supprimer le style",
"positionTop": "Haut",
"positionBottom": "Bas",
"bold": "Gras",
"italic": "Italique",
"renderResolution": "Résolution de rendu",
@@ -710,7 +724,7 @@
"searchFailed": "Recherche échouée: ${error}",
"connectionTimeout": "Délai d'attente de connexion dépassé pendant le chargement ${context}",
"connectionFailed": "Impossible de se connecter au serveur multimédia",
"failedToLoad": "Échec du chargement ${context}: ${error}",
"unableToLoad": "Impossible de charger ${context}. Réessayez.",
"noClientAvailable": "Aucun client disponible",
"pleaseEnterToken": "Veuillez saisir un token",
"invalidToken": "Token invalide",
@@ -966,7 +980,6 @@
"deleted": "Collection supprimée",
"deleteFailed": "Échec de la suppression de la collection",
"deleteFailedWithError": "Échec de la suppression de la collection: ${error}",
"failedToLoadItems": "Échec du chargement des éléments de la collection: ${error}",
"selectCollection": "Sélectionner une collection",
"collectionName": "Nom de la collection",
"enterCollectionName": "Entrez le nom de la collection",
@@ -1049,6 +1062,8 @@
"hostingSession": "Session d'hébergement",
"inSession": "En session",
"sessionCode": "Code de session",
"openSessionControls": "Ouvrir les commandes de la session Regarder ensemble",
"copySessionCode": "Copier le code de session",
"hostControlsPlayback": "L'hôte contrôle la lecture",
"anyoneCanControl": "Tout le monde peut contrôler la lecture",
"hostControls": "Commandes de l'hôte",
@@ -1262,6 +1277,8 @@
},
"videoSettings": {
"playbackSpeed": "Vitesse de lecture",
"normalSpeed": "Normale",
"sleepTimerActive": "Actif (${duration})",
"zoom": "Zoom",
"sleepTimer": "Minuterie de mise en veille",
"audioSync": "Synchronisation audio",
@@ -1359,6 +1376,8 @@
"artworkUpdated": "Artwork mis à jour",
"artworkUpdateFailed": "Échec de la mise à jour de l'artwork",
"noArtworkAvailable": "Aucun artwork disponible",
"artworkOption": "Option d'illustration ${index}",
"selectedArtworkOption": "Option d'illustration ${index}, sélectionnée",
"notSet": "Non défini",
"libraryDefault": "Par défaut de la bibliothèque",
"accountDefault": "Par défaut du compte",
@@ -1486,6 +1505,7 @@
"title": "Activer Plezy sur ${service}",
"body": "Rendez-vous sur ${url} et entrez ce code :",
"openToActivate": "Ouvrir ${service} pour activer",
"copyCode": "Copier le code d'activation",
"waitingForAuthorization": "En attente d'autorisation…",
"codeCopied": "Code copié"
},
@@ -1493,6 +1513,7 @@
"title": "Se connecter à ${service}",
"body": "Scannez ce code QR ou ouvrez l'URL sur n'importe quel appareil.",
"openToSignIn": "Ouvrir ${service} pour se connecter",
"copyUrl": "Copier l'URL de connexion",
"urlCopied": "URL copiée"
},
"libraryFilter": {
+24 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "Pattern marcatore titoli di coda",
"creditsPatternDescription": "Espressione regolare per riconoscere i marcatori dei titoli di coda nei capitoli",
"invalidRegex": "Espressione regolare non valida",
"regex": "Espressione regolare",
"downloads": "Download",
"downloadLocationDescription": "Scegli dove salvare i contenuti scaricati",
"downloadLocationDefault": "Predefinita (Archiviazione App)",
@@ -336,6 +337,8 @@
"clearShortcut": "Elimina scorciatoia",
"noShortcutSet": "Nessuna scorciatoia impostata",
"currentShortcut": "Scorciatoia attuale:",
"pressToRecord": "Seleziona per registrare una scorciatoia",
"recordingShortcut": "Premi ora la scorciatoia",
"actions": {
"playPause": "Riproduci/Pausa",
"volumeUp": "Alza volume",
@@ -434,7 +437,9 @@
"hue": "Tonalità",
"saturation": "Saturazione",
"brightness": "Luminosità",
"hexColor": "Colore esadecimale"
"hexColor": "Colore esadecimale",
"expandText": "Espandi il testo",
"collapseText": "Comprimi il testo"
},
"tooltips": {
"shufflePlay": "Riproduzione casuale",
@@ -534,6 +539,10 @@
"markedAsWatchedOffline": "Segnato come visto (sincronizzato online)",
"markedAsUnwatchedOffline": "Segnato come non visto (sincronizzato online)",
"autoRemovedWatchedDownload": "Rimosso automaticamente: ${title}",
"autoRemovedWatchedDownloads": {
"one": "Rimosso automaticamente ${n} download già visto",
"other": "Rimossi automaticamente ${n} download già visti"
},
"removedFromContinueWatching": "Rimosso da Continua a guardare",
"errorLoading": "Errore: ${error}",
"streamInterrupted": "La riproduzione si è interrotta. Premi Riproduci o scorri per riprovare.",
@@ -585,6 +594,11 @@
"backgroundColor": "Colore sfondo",
"position": "Posizione",
"assOverride": "Sovrascrittura ASS",
"overrideScale": "Ridimensiona",
"overrideForce": "Forza",
"overrideStrip": "Rimuovi stile",
"positionTop": "In alto",
"positionBottom": "In basso",
"bold": "Grassetto",
"italic": "Corsivo",
"renderResolution": "Risoluzione di rendering",
@@ -710,7 +724,7 @@
"searchFailed": "Ricerca fallita: ${error}",
"connectionTimeout": "Timeout connessione durante caricamento di ${context}",
"connectionFailed": "Impossibile connettersi al server multimediale",
"failedToLoad": "Impossibile caricare ${context}: ${error}",
"unableToLoad": "Impossibile caricare ${context}. Riprova.",
"noClientAvailable": "Nessun client disponibile",
"pleaseEnterToken": "Inserisci token",
"invalidToken": "Token non valido",
@@ -966,7 +980,6 @@
"deleted": "Raccolta eliminata",
"deleteFailed": "Impossibile eliminare la raccolta",
"deleteFailedWithError": "Impossibile eliminare la raccolta: ${error}",
"failedToLoadItems": "Impossibile caricare gli elementi della raccolta: ${error}",
"selectCollection": "Seleziona raccolta",
"collectionName": "Nome raccolta",
"enterCollectionName": "Inserisci nome raccolta",
@@ -1049,6 +1062,8 @@
"hostingSession": "Hosting Sessione",
"inSession": "In Sessione",
"sessionCode": "Codice Sessione",
"openSessionControls": "Apri i controlli della sessione di Guarda Insieme",
"copySessionCode": "Copia il codice della sessione",
"hostControlsPlayback": "L'host controlla la riproduzione",
"anyoneCanControl": "Tutti possono controllare la riproduzione",
"hostControls": "Controllo host",
@@ -1262,6 +1277,8 @@
},
"videoSettings": {
"playbackSpeed": "Velocità di riproduzione",
"normalSpeed": "Normale",
"sleepTimerActive": "Attivo (${duration})",
"zoom": "Zoom",
"sleepTimer": "Timer di spegnimento",
"audioSync": "Sincronizzazione audio",
@@ -1359,6 +1376,8 @@
"artworkUpdated": "Artwork aggiornato",
"artworkUpdateFailed": "Aggiornamento artwork non riuscito",
"noArtworkAvailable": "Nessun artwork disponibile",
"artworkOption": "Opzione artwork ${index}",
"selectedArtworkOption": "Opzione artwork ${index}, selezionata",
"notSet": "Non impostato",
"libraryDefault": "Predefinito libreria",
"accountDefault": "Predefinito account",
@@ -1486,6 +1505,7 @@
"title": "Attiva Plezy su ${service}",
"body": "Visita ${url} e inserisci questo codice:",
"openToActivate": "Apri ${service} per attivare",
"copyCode": "Copia il codice di attivazione",
"waitingForAuthorization": "In attesa di autorizzazione…",
"codeCopied": "Codice copiato"
},
@@ -1493,6 +1513,7 @@
"title": "Accedi a ${service}",
"body": "Scansiona questo codice QR o apri l'URL su qualsiasi dispositivo.",
"openToSignIn": "Apri ${service} per accedere",
"copyUrl": "Copia l'URL di accesso",
"urlCopied": "URL copiato"
},
"libraryFilter": {
+23 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "クレジットマーカーパターン",
"creditsPatternDescription": "チャプタータイトルのクレジットマーカーに一致する正規表現パターン",
"invalidRegex": "無効な正規表現",
"regex": "正規表現",
"downloads": "ダウンロード",
"downloadLocationDescription": "ダウンロードコンテンツの保存場所を選択",
"downloadLocationDefault": "デフォルト(アプリストレージ)",
@@ -336,6 +337,8 @@
"clearShortcut": "ショートカットをクリア",
"noShortcutSet": "ショートカット未設定",
"currentShortcut": "現在のショートカット:",
"pressToRecord": "選択してショートカットを記録",
"recordingShortcut": "ショートカットを押してください",
"actions": {
"playPause": "再生/一時停止",
"volumeUp": "音量を上げる",
@@ -434,7 +437,9 @@
"hue": "色相",
"saturation": "彩度",
"brightness": "明るさ",
"hexColor": "16進カラー"
"hexColor": "16進カラー",
"expandText": "テキストを展開",
"collapseText": "テキストを折りたたむ"
},
"tooltips": {
"shufflePlay": "シャッフル再生",
@@ -534,6 +539,9 @@
"markedAsWatchedOffline": "視聴済みにしました(オンライン時に同期)",
"markedAsUnwatchedOffline": "未視聴にしました(オンライン時に同期)",
"autoRemovedWatchedDownload": "自動削除: ${title}",
"autoRemovedWatchedDownloads": {
"other": "視聴済みダウンロードを${n}件自動削除しました"
},
"removedFromContinueWatching": "視聴中から削除しました",
"errorLoading": "エラー: ${error}",
"streamInterrupted": "ストリームが中断されました。再生を押すかシークして再試行してください。",
@@ -585,6 +593,11 @@
"backgroundColor": "背景色",
"position": "位置",
"assOverride": "ASSオーバーライド",
"overrideScale": "拡大縮小",
"overrideForce": "強制",
"overrideStrip": "スタイルを削除",
"positionTop": "上",
"positionBottom": "下",
"bold": "太字",
"italic": "斜体",
"renderResolution": "レンダリング解像度",
@@ -710,7 +723,7 @@
"searchFailed": "検索に失敗しました: ${error}",
"connectionTimeout": "${context}の読み込み中に接続がタイムアウトしました",
"connectionFailed": "メディアサーバーに接続できません",
"failedToLoad": "${context}読み込みに失敗しました: ${error}",
"unableToLoad": "${context}読み込めませんでした。もう一度お試しください。",
"noClientAvailable": "クライアントが利用できません",
"pleaseEnterToken": "トークンを入力してください",
"invalidToken": "無効なトークン",
@@ -965,7 +978,6 @@
"deleted": "コレクションを削除しました",
"deleteFailed": "コレクションの削除に失敗しました",
"deleteFailedWithError": "コレクションの削除に失敗しました: ${error}",
"failedToLoadItems": "コレクションアイテムの読み込みに失敗しました: ${error}",
"selectCollection": "コレクションを選択",
"collectionName": "コレクション名",
"enterCollectionName": "コレクション名を入力",
@@ -1047,6 +1059,8 @@
"hostingSession": "セッションをホスト中",
"inSession": "セッション中",
"sessionCode": "セッションコード",
"openSessionControls": "「一緒に見る」セッションのコントロールを開く",
"copySessionCode": "セッションコードをコピー",
"hostControlsPlayback": "ホストが再生を制御",
"anyoneCanControl": "全員が再生を制御可能",
"hostControls": "ホストが制御",
@@ -1260,6 +1274,8 @@
},
"videoSettings": {
"playbackSpeed": "再生速度",
"normalSpeed": "標準",
"sleepTimerActive": "作動中(${duration}",
"zoom": "ズーム",
"sleepTimer": "スリープタイマー",
"audioSync": "音声同期",
@@ -1357,6 +1373,8 @@
"artworkUpdated": "アートワークを更新しました",
"artworkUpdateFailed": "アートワークの更新に失敗しました",
"noArtworkAvailable": "アートワークがありません",
"artworkOption": "アートワークの選択肢 ${index}",
"selectedArtworkOption": "アートワークの選択肢 ${index}、選択済み",
"notSet": "未設定",
"libraryDefault": "ライブラリのデフォルト",
"accountDefault": "アカウントのデフォルト",
@@ -1484,6 +1502,7 @@
"title": "${service} で Plezy を有効化",
"body": "${url} にアクセスしてこのコードを入力:",
"openToActivate": "${service} を開いて有効化",
"copyCode": "アクティベーションコードをコピー",
"waitingForAuthorization": "認証を待っています…",
"codeCopied": "コードをコピーしました"
},
@@ -1491,6 +1510,7 @@
"title": "${service} にサインイン",
"body": "このQRコードをスキャンするか、任意のデバイスでURLを開いてください。",
"openToSignIn": "${service} を開いてサインイン",
"copyUrl": "サインインURLをコピー",
"urlCopied": "URLをコピーしました"
},
"libraryFilter": {
+23 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "크레딧 마커 패턴",
"creditsPatternDescription": "챕터 제목에서 크레딧 마커를 인식하는 정규식 패턴",
"invalidRegex": "잘못된 정규식",
"regex": "정규식",
"downloads": "다운로드",
"downloadLocationDescription": "다운로드 콘텐츠 저장 위치 선택",
"downloadLocationDefault": "기본값 (앱 저장소)",
@@ -336,6 +337,8 @@
"clearShortcut": "단축키 삭제",
"noShortcutSet": "설정된 단축키 없음",
"currentShortcut": "현재 단축키:",
"pressToRecord": "단축키를 기록하려면 선택",
"recordingShortcut": "지금 단축키를 누르세요",
"actions": {
"playPause": "재생/일시정지",
"volumeUp": "볼륨 높이기",
@@ -434,7 +437,9 @@
"hue": "색조",
"saturation": "채도",
"brightness": "밝기",
"hexColor": "16진수 색상"
"hexColor": "16진수 색상",
"expandText": "텍스트 펼치기",
"collapseText": "텍스트 접기"
},
"tooltips": {
"shufflePlay": "무작위 재생",
@@ -534,6 +539,9 @@
"markedAsWatchedOffline": "시청 완료로 표시됨 (연결 시 동기화됨)",
"markedAsUnwatchedOffline": "미시청으로 표시됨 (연결 시 동기화됨)",
"autoRemovedWatchedDownload": "자동 삭제됨: ${title}",
"autoRemovedWatchedDownloads": {
"other": "시청한 다운로드 ${n}개를 자동 삭제했습니다"
},
"removedFromContinueWatching": "계속 시청 목록에서 제거됨",
"errorLoading": "오류: ${error}",
"streamInterrupted": "스트림이 중단되었습니다. 재생을 누르거나 탐색하여 다시 시도하세요.",
@@ -585,6 +593,11 @@
"backgroundColor": "배경색",
"position": "위치",
"assOverride": "ASS 오버라이드",
"overrideScale": "크기 조정",
"overrideForce": "강제 적용",
"overrideStrip": "스타일 제거",
"positionTop": "위",
"positionBottom": "아래",
"bold": "굵게",
"italic": "기울임꼴",
"renderResolution": "렌더링 해상도",
@@ -710,7 +723,7 @@
"searchFailed": "검색 실패: ${error}",
"connectionTimeout": "${context} 로드 중 연결 시간 초과",
"connectionFailed": "미디어 서버에 연결할 수 없습니다",
"failedToLoad": "${context} 로드 실패: ${error}",
"unableToLoad": "${context}을(를) 불러올 수 없습니다. 다시 시도하세요.",
"noClientAvailable": "사용 가능한 클라이언트가 없습니다",
"pleaseEnterToken": "토큰을 입력해 주세요",
"invalidToken": "토큰이 유효하지 않습니다",
@@ -965,7 +978,6 @@
"deleted": "컬렉션 삭제됨",
"deleteFailed": "컬렉션 삭제 실패",
"deleteFailedWithError": "컬렉션 삭제 실패: ${error}",
"failedToLoadItems": "컬렉션 항목 로드 실패: ${error}",
"selectCollection": "컬렉션 선택",
"collectionName": "컬렉션 이름",
"enterCollectionName": "컬렉션 이름 입력",
@@ -1047,6 +1059,8 @@
"hostingSession": "세션 호스팅",
"inSession": "세션 중",
"sessionCode": "세션 코드",
"openSessionControls": "함께 보기 세션 제어 열기",
"copySessionCode": "세션 코드 복사",
"hostControlsPlayback": "호스트 재생 제어",
"anyoneCanControl": "누구나 재생 제어 가능",
"hostControls": "호스트 제어",
@@ -1260,6 +1274,8 @@
},
"videoSettings": {
"playbackSpeed": "재생 속도",
"normalSpeed": "보통",
"sleepTimerActive": "활성 (${duration})",
"zoom": "확대/축소",
"sleepTimer": "취침 타이머",
"audioSync": "오디오 동기화",
@@ -1357,6 +1373,8 @@
"artworkUpdated": "아트워크가 업데이트되었습니다",
"artworkUpdateFailed": "아트워크 업데이트 실패",
"noArtworkAvailable": "사용 가능한 아트워크 없음",
"artworkOption": "아트워크 옵션 ${index}",
"selectedArtworkOption": "아트워크 옵션 ${index}, 선택됨",
"notSet": "설정되지 않음",
"libraryDefault": "라이브러리 기본값",
"accountDefault": "계정 기본값",
@@ -1484,6 +1502,7 @@
"title": "${service}에서 Plezy 활성화",
"body": "${url}을 방문하여 이 코드를 입력하세요:",
"openToActivate": "활성화하려면 ${service} 열기",
"copyCode": "활성화 코드 복사",
"waitingForAuthorization": "인증을 기다리는 중…",
"codeCopied": "코드가 복사되었습니다"
},
@@ -1491,6 +1510,7 @@
"title": "${service}에 로그인",
"body": "이 QR 코드를 스캔하거나 아무 기기에서 URL을 여세요.",
"openToSignIn": "로그인하려면 ${service} 열기",
"copyUrl": "로그인 URL 복사",
"urlCopied": "URL이 복사되었습니다"
},
"libraryFilter": {
+24 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "Rulletekstmarkørmønster",
"creditsPatternDescription": "Regulært uttrykk for å gjenkjenne rulletekstmarkører i kapitteltitler",
"invalidRegex": "Ugyldig regulært uttrykk",
"regex": "Regulært uttrykk",
"downloads": "Nedlastinger",
"downloadLocationDescription": "Velg hvor nedlastet innhold skal lagres",
"downloadLocationDefault": "Standard (App-lagring)",
@@ -336,6 +337,8 @@
"clearShortcut": "Fjern snarvei",
"noShortcutSet": "Ingen snarvei satt",
"currentShortcut": "Gjeldende snarvei:",
"pressToRecord": "Velg for å registrere en snarvei",
"recordingShortcut": "Trykk på snarveien nå",
"actions": {
"playPause": "Spill av/Pause",
"volumeUp": "Volum opp",
@@ -434,7 +437,9 @@
"hue": "Fargetone",
"saturation": "Metning",
"brightness": "Lysstyrke",
"hexColor": "Heksadesimal farge"
"hexColor": "Heksadesimal farge",
"expandText": "Utvid tekst",
"collapseText": "Fold sammen tekst"
},
"tooltips": {
"shufflePlay": "Tilfeldig avspilling",
@@ -534,6 +539,10 @@
"markedAsWatchedOffline": "Merket som sett (synkroniseres når tilkoblet)",
"markedAsUnwatchedOffline": "Merket som usett (synkroniseres når tilkoblet)",
"autoRemovedWatchedDownload": "Automatisk fjernet: ${title}",
"autoRemovedWatchedDownloads": {
"one": "Fjernet automatisk ${n} sett nedlasting",
"other": "Fjernet automatisk ${n} sette nedlastinger"
},
"removedFromContinueWatching": "Fjernet fra Fortsett å se",
"errorLoading": "Feil: ${error}",
"streamInterrupted": "Avspillingen ble avbrutt. Trykk på Spill av eller spol for å prøve på nytt.",
@@ -585,6 +594,11 @@
"backgroundColor": "Bakgrunnsfarge",
"position": "Posisjon",
"assOverride": "ASS-overstyring",
"overrideScale": "Skaler",
"overrideForce": "Tving",
"overrideStrip": "Fjern formatering",
"positionTop": "Øverst",
"positionBottom": "Nederst",
"bold": "Fet",
"italic": "Kursiv",
"renderResolution": "Gjengivelsesoppløsning",
@@ -710,7 +724,7 @@
"searchFailed": "Søk mislyktes: ${error}",
"connectionTimeout": "Tidsavbrudd ved lasting av ${context}",
"connectionFailed": "Kan ikke koble til medieserver",
"failedToLoad": "Kunne ikke laste ${context}: ${error}",
"unableToLoad": "Kunne ikke laste ${context}. Prøv igjen.",
"noClientAvailable": "Ingen klient tilgjengelig",
"pleaseEnterToken": "Vennligst skriv inn et token",
"invalidToken": "Ugyldig token",
@@ -966,7 +980,6 @@
"deleted": "Samling slettet",
"deleteFailed": "Kunne ikke slette samling",
"deleteFailedWithError": "Kunne ikke slette samling: ${error}",
"failedToLoadItems": "Kunne ikke laste samlingselementer: ${error}",
"selectCollection": "Velg samling",
"collectionName": "Samlingsnavn",
"enterCollectionName": "Skriv inn samlingsnavn",
@@ -1049,6 +1062,8 @@
"hostingSession": "Er vert for økt",
"inSession": "I økt",
"sessionCode": "Øktkode",
"openSessionControls": "Åpne øktkontroller for Se sammen",
"copySessionCode": "Kopier øktkode",
"hostControlsPlayback": "Verten kontrollerer avspilling",
"anyoneCanControl": "Alle kan kontrollere avspilling",
"hostControls": "Vertskontroll",
@@ -1262,6 +1277,8 @@
},
"videoSettings": {
"playbackSpeed": "Avspillingshastighet",
"normalSpeed": "Normal",
"sleepTimerActive": "Aktiv (${duration})",
"zoom": "Zoom",
"sleepTimer": "Søvntimer",
"audioSync": "Lydsynkronisering",
@@ -1359,6 +1376,8 @@
"artworkUpdated": "Kunstverk oppdatert",
"artworkUpdateFailed": "Kunne ikke oppdatere kunstverk",
"noArtworkAvailable": "Ingen kunstverk tilgjengelig",
"artworkOption": "Alternativ for kunstverk ${index}",
"selectedArtworkOption": "Alternativ for kunstverk ${index}, valgt",
"notSet": "Ikke angitt",
"libraryDefault": "Bibliotekstandard",
"accountDefault": "Kontostandard",
@@ -1486,6 +1505,7 @@
"title": "Aktiver Plezy på ${service}",
"body": "Besøk ${url} og skriv inn denne koden:",
"openToActivate": "Åpne ${service} for å aktivere",
"copyCode": "Kopier aktiveringskode",
"waitingForAuthorization": "Venter på godkjenning…",
"codeCopied": "Kode kopiert"
},
@@ -1493,6 +1513,7 @@
"title": "Logg inn på ${service}",
"body": "Skann denne QR-koden eller åpne URL-en på en enhet.",
"openToSignIn": "Åpne ${service} for å logge inn",
"copyUrl": "Kopier URL for pålogging",
"urlCopied": "URL kopiert"
},
"libraryFilter": {
+24 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "Aftitelingmarkeringspatroon",
"creditsPatternDescription": "Reguliere expressie om aftitelingmarkeringen in hoofdstuktitels te herkennen",
"invalidRegex": "Ongeldige reguliere expressie",
"regex": "Reguliere expressie",
"downloads": "Downloads",
"downloadLocationDescription": "Kies waar gedownloade content wordt opgeslagen",
"downloadLocationDefault": "Standaard (App-opslag)",
@@ -336,6 +337,8 @@
"clearShortcut": "Wis sneltoets",
"noShortcutSet": "Geen sneltoets ingesteld",
"currentShortcut": "Huidige sneltoets:",
"pressToRecord": "Selecteer om een sneltoets op te nemen",
"recordingShortcut": "Druk nu op de sneltoets",
"actions": {
"playPause": "Afspelen/Pauzeren",
"volumeUp": "Volume omhoog",
@@ -434,7 +437,9 @@
"hue": "Tint",
"saturation": "Verzadiging",
"brightness": "Helderheid",
"hexColor": "Hexkleur"
"hexColor": "Hexkleur",
"expandText": "Tekst uitvouwen",
"collapseText": "Tekst samenvouwen"
},
"tooltips": {
"shufflePlay": "Willekeurig afspelen",
@@ -534,6 +539,10 @@
"markedAsWatchedOffline": "Gemarkeerd als gekeken (sync wanneer online)",
"markedAsUnwatchedOffline": "Gemarkeerd als ongekeken (sync wanneer online)",
"autoRemovedWatchedDownload": "Automatisch verwijderd: ${title}",
"autoRemovedWatchedDownloads": {
"one": "Automatisch ${n} bekeken download verwijderd",
"other": "Automatisch ${n} bekeken downloads verwijderd"
},
"removedFromContinueWatching": "Verwijderd uit Doorgaan met kijken",
"errorLoading": "Fout: ${error}",
"streamInterrupted": "De stream is onderbroken. Druk op afspelen of spoel om het opnieuw te proberen.",
@@ -585,6 +594,11 @@
"backgroundColor": "Achtergrondkleur",
"position": "Positie",
"assOverride": "ASS-overschrijving",
"overrideScale": "Schalen",
"overrideForce": "Forceren",
"overrideStrip": "Opmaak verwijderen",
"positionTop": "Bovenaan",
"positionBottom": "Onderaan",
"bold": "Vet",
"italic": "Cursief",
"renderResolution": "Renderresolutie",
@@ -710,7 +724,7 @@
"searchFailed": "Zoeken mislukt: ${error}",
"connectionTimeout": "Verbinding time-out tijdens laden ${context}",
"connectionFailed": "Kan geen verbinding maken met mediaserver",
"failedToLoad": "Kon ${context} niet laden: ${error}",
"unableToLoad": "Kan ${context} niet laden. Probeer het opnieuw.",
"noClientAvailable": "Geen client beschikbaar",
"pleaseEnterToken": "Voer een token in",
"invalidToken": "Ongeldig token",
@@ -966,7 +980,6 @@
"deleted": "Collectie verwijderd",
"deleteFailed": "Collectie verwijderen mislukt",
"deleteFailedWithError": "Collectie verwijderen mislukt: ${error}",
"failedToLoadItems": "Collectie-items laden mislukt: ${error}",
"selectCollection": "Selecteer collectie",
"collectionName": "Collectienaam",
"enterCollectionName": "Voer collectienaam in",
@@ -1049,6 +1062,8 @@
"hostingSession": "Sessie Hosten",
"inSession": "In Sessie",
"sessionCode": "Sessiecode",
"openSessionControls": "Sessiebesturing voor Samen Kijken openen",
"copySessionCode": "Sessiecode kopiëren",
"hostControlsPlayback": "Host bedient het afspelen",
"anyoneCanControl": "Iedereen kan het afspelen bedienen",
"hostControls": "Host bedient",
@@ -1262,6 +1277,8 @@
},
"videoSettings": {
"playbackSpeed": "Afspeelsnelheid",
"normalSpeed": "Normaal",
"sleepTimerActive": "Actief (${duration})",
"zoom": "Zoom",
"sleepTimer": "Slaaptimer",
"audioSync": "Audio synchronisatie",
@@ -1359,6 +1376,8 @@
"artworkUpdated": "Artwork bijgewerkt",
"artworkUpdateFailed": "Artwork bijwerken mislukt",
"noArtworkAvailable": "Geen artwork beschikbaar",
"artworkOption": "Artworkoptie ${index}",
"selectedArtworkOption": "Artworkoptie ${index}, geselecteerd",
"notSet": "Niet ingesteld",
"libraryDefault": "Bibliotheekstandaard",
"accountDefault": "Accountstandaard",
@@ -1486,6 +1505,7 @@
"title": "Plezy activeren op ${service}",
"body": "Ga naar ${url} en voer deze code in:",
"openToActivate": "Open ${service} om te activeren",
"copyCode": "Activeringscode kopiëren",
"waitingForAuthorization": "Wachten op autorisatie…",
"codeCopied": "Code gekopieerd"
},
@@ -1493,6 +1513,7 @@
"title": "Aanmelden bij ${service}",
"body": "Scan deze QR-code of open de URL op een apparaat.",
"openToSignIn": "${service} openen om aan te melden",
"copyUrl": "Aanmeldings-URL kopiëren",
"urlCopied": "URL gekopieerd"
},
"libraryFilter": {
+26 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "Wzorzec znacznika napisów końcowych",
"creditsPatternDescription": "Wyrażenie regularne do rozpoznawania znaczników napisów końcowych w tytułach rozdziałów",
"invalidRegex": "Nieprawidłowe wyrażenie regularne",
"regex": "Wyrażenie regularne",
"downloads": "Pobrania",
"downloadLocationDescription": "Wybierz miejsce przechowywania pobranych treści",
"downloadLocationDefault": "Domyślne (Pamięć aplikacji)",
@@ -336,6 +337,8 @@
"clearShortcut": "Wyczyść skrót",
"noShortcutSet": "Brak ustawionego skrótu",
"currentShortcut": "Bieżący skrót:",
"pressToRecord": "Wybierz, aby zapisać skrót klawiszowy",
"recordingShortcut": "Naciśnij teraz skrót klawiszowy",
"actions": {
"playPause": "Odtwórz/Pauza",
"volumeUp": "Głośniej",
@@ -434,7 +437,9 @@
"hue": "Odcień",
"saturation": "Nasycenie",
"brightness": "Jasność",
"hexColor": "Kolor szesnastkowy"
"hexColor": "Kolor szesnastkowy",
"expandText": "Rozwiń tekst",
"collapseText": "Zwiń tekst"
},
"tooltips": {
"shufflePlay": "Odtwarzanie losowe",
@@ -534,6 +539,12 @@
"markedAsWatchedOffline": "Oznaczono jako obejrzane (zsynchronizuje się po połączeniu)",
"markedAsUnwatchedOffline": "Oznaczono jako nieobejrzane (zsynchronizuje się po połączeniu)",
"autoRemovedWatchedDownload": "Automatycznie usunięto: ${title}",
"autoRemovedWatchedDownloads": {
"one": "Automatycznie usunięto ${n} obejrzane pobranie",
"few": "Automatycznie usunięto ${n} obejrzane pobrania",
"many": "Automatycznie usunięto ${n} obejrzanych pobrań",
"other": "Automatycznie usunięto ${n} obejrzanego pobrania"
},
"removedFromContinueWatching": "Usunięto z kontynuowania oglądania",
"errorLoading": "Błąd: ${error}",
"streamInterrupted": "Strumień został przerwany. Naciśnij odtwarzanie lub przewiń, aby spróbować ponownie.",
@@ -585,6 +596,11 @@
"backgroundColor": "Kolor tła",
"position": "Pozycja",
"assOverride": "Nadpisywanie ASS",
"overrideScale": "Skaluj",
"overrideForce": "Wymuś",
"overrideStrip": "Usuń style",
"positionTop": "Góra",
"positionBottom": "Dół",
"bold": "Pogrubienie",
"italic": "Kursywa",
"renderResolution": "Rozdzielczość renderowania",
@@ -710,7 +726,7 @@
"searchFailed": "Wyszukiwanie nie powiodło się: ${error}",
"connectionTimeout": "Limit czasu połączenia przy ładowaniu ${context}",
"connectionFailed": "Nie można połączyć się z serwerem multimediów",
"failedToLoad": "Nie udało się załadować ${context}: ${error}",
"unableToLoad": "Nie udało się załadować ${context}. Spróbuj ponownie.",
"noClientAvailable": "Brak dostępnego klienta",
"pleaseEnterToken": "Wprowadź token",
"invalidToken": "Nieprawidłowy token",
@@ -968,7 +984,6 @@
"deleted": "Kolekcja usunięta",
"deleteFailed": "Nie udało się usunąć kolekcji",
"deleteFailedWithError": "Nie udało się usunąć kolekcji: ${error}",
"failedToLoadItems": "Nie udało się załadować elementów kolekcji: ${error}",
"selectCollection": "Wybierz kolekcję",
"collectionName": "Nazwa kolekcji",
"enterCollectionName": "Wprowadź nazwę kolekcji",
@@ -1053,6 +1068,8 @@
"hostingSession": "Hostowanie sesji",
"inSession": "W sesji",
"sessionCode": "Kod sesji",
"openSessionControls": "Otwórz sterowanie sesją „Oglądaj razem”",
"copySessionCode": "Skopiuj kod sesji",
"hostControlsPlayback": "Host kontroluje odtwarzanie",
"anyoneCanControl": "Każdy może kontrolować odtwarzanie",
"hostControls": "Kontrola hosta",
@@ -1266,6 +1283,8 @@
},
"videoSettings": {
"playbackSpeed": "Prędkość odtwarzania",
"normalSpeed": "Normalna",
"sleepTimerActive": "Aktywny (${duration})",
"zoom": "Zoom",
"sleepTimer": "Wyłącznik czasowy",
"audioSync": "Synchronizacja audio",
@@ -1363,6 +1382,8 @@
"artworkUpdated": "Grafika zaktualizowana",
"artworkUpdateFailed": "Nie udało się zaktualizować grafiki",
"noArtworkAvailable": "Brak dostępnej grafiki",
"artworkOption": "Opcja grafiki ${index}",
"selectedArtworkOption": "Opcja grafiki ${index}, wybrana",
"notSet": "Nie ustawiono",
"libraryDefault": "Domyślne biblioteki",
"accountDefault": "Domyślne konta",
@@ -1490,6 +1511,7 @@
"title": "Aktywuj Plezy w ${service}",
"body": "Odwiedź ${url} i wpisz ten kod:",
"openToActivate": "Otwórz ${service}, aby aktywować",
"copyCode": "Skopiuj kod aktywacyjny",
"waitingForAuthorization": "Oczekiwanie na autoryzację…",
"codeCopied": "Kod skopiowany"
},
@@ -1497,6 +1519,7 @@
"title": "Zaloguj się do ${service}",
"body": "Zeskanuj ten kod QR lub otwórz URL na dowolnym urządzeniu.",
"openToSignIn": "Otwórz ${service}, aby się zalogować",
"copyUrl": "Skopiuj adres URL logowania",
"urlCopied": "URL skopiowany"
},
"libraryFilter": {
+24 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "Padrão de marcador de créditos",
"creditsPatternDescription": "Expressão regular para corresponder marcadores de créditos nos títulos dos capítulos",
"invalidRegex": "Expressão regular inválida",
"regex": "Expressão regular",
"downloads": "Downloads",
"downloadLocationDescription": "Escolha onde armazenar conteúdo baixado",
"downloadLocationDefault": "Padrão (Armazenamento do App)",
@@ -336,6 +337,8 @@
"clearShortcut": "Limpar atalho",
"noShortcutSet": "Nenhum atalho definido",
"currentShortcut": "Atalho atual:",
"pressToRecord": "Selecionar para gravar um atalho",
"recordingShortcut": "Pressione o atalho agora",
"actions": {
"playPause": "Reproduzir/Pausar",
"volumeUp": "Aumentar Volume",
@@ -434,7 +437,9 @@
"hue": "Matiz",
"saturation": "Saturação",
"brightness": "Brilho",
"hexColor": "Cor hexadecimal"
"hexColor": "Cor hexadecimal",
"expandText": "Expandir texto",
"collapseText": "Recolher texto"
},
"tooltips": {
"shufflePlay": "Reprodução aleatória",
@@ -534,6 +539,10 @@
"markedAsWatchedOffline": "Marcado como assistido (será sincronizado quando online)",
"markedAsUnwatchedOffline": "Marcado como não assistido (será sincronizado quando online)",
"autoRemovedWatchedDownload": "Removido automaticamente: ${title}",
"autoRemovedWatchedDownloads": {
"one": "Removido automaticamente ${n} download assistido",
"other": "Removidos automaticamente ${n} downloads assistidos"
},
"removedFromContinueWatching": "Removido de Continuar Assistindo",
"errorLoading": "Erro: ${error}",
"streamInterrupted": "A transmissão foi interrompida. Toque em reproduzir ou avance para tentar novamente.",
@@ -585,6 +594,11 @@
"backgroundColor": "Cor de Fundo",
"position": "Posição",
"assOverride": "Substituição ASS",
"overrideScale": "Dimensionar",
"overrideForce": "Forçar",
"overrideStrip": "Remover estilo",
"positionTop": "Superior",
"positionBottom": "Inferior",
"bold": "Negrito",
"italic": "Itálico",
"renderResolution": "Resolução de renderização",
@@ -710,7 +724,7 @@
"searchFailed": "Falha na busca: ${error}",
"connectionTimeout": "Tempo de conexão esgotado ao carregar ${context}",
"connectionFailed": "Não foi possível conectar ao servidor de mídia",
"failedToLoad": "Falha ao carregar ${context}: ${error}",
"unableToLoad": "Não foi possível carregar ${context}. Tente novamente.",
"noClientAvailable": "Nenhum cliente disponível",
"pleaseEnterToken": "Insira um token",
"invalidToken": "Token inválido",
@@ -966,7 +980,6 @@
"deleted": "Coleção excluída",
"deleteFailed": "Falha ao excluir coleção",
"deleteFailedWithError": "Falha ao excluir coleção: ${error}",
"failedToLoadItems": "Falha ao carregar itens da coleção: ${error}",
"selectCollection": "Selecionar Coleção",
"collectionName": "Nome da Coleção",
"enterCollectionName": "Insira o nome da coleção",
@@ -1049,6 +1062,8 @@
"hostingSession": "Hospedando Sessão",
"inSession": "Em Sessão",
"sessionCode": "Código da Sessão",
"openSessionControls": "Abrir os controles da sessão Assistir Juntos",
"copySessionCode": "Copiar código da sessão",
"hostControlsPlayback": "Anfitrião controla a reprodução",
"anyoneCanControl": "Qualquer pessoa pode controlar a reprodução",
"hostControls": "Controle do anfitrião",
@@ -1262,6 +1277,8 @@
},
"videoSettings": {
"playbackSpeed": "Velocidade de Reprodução",
"normalSpeed": "Normal",
"sleepTimerActive": "Ativo (${duration})",
"zoom": "Zoom",
"sleepTimer": "Timer de Sono",
"audioSync": "Sincronia de Áudio",
@@ -1359,6 +1376,8 @@
"artworkUpdated": "Arte atualizada",
"artworkUpdateFailed": "Falha ao atualizar arte",
"noArtworkAvailable": "Nenhuma arte disponível",
"artworkOption": "Opção de arte ${index}",
"selectedArtworkOption": "Opção de arte ${index}, selecionada",
"notSet": "Não definido",
"libraryDefault": "Padrão da biblioteca",
"accountDefault": "Padrão da conta",
@@ -1486,6 +1505,7 @@
"title": "Ativar Plezy no ${service}",
"body": "Acesse ${url} e insira este código:",
"openToActivate": "Abrir ${service} para ativar",
"copyCode": "Copiar código de ativação",
"waitingForAuthorization": "Aguardando autorização…",
"codeCopied": "Código copiado"
},
@@ -1493,6 +1513,7 @@
"title": "Entrar no ${service}",
"body": "Escaneie este código QR ou abra a URL em qualquer dispositivo.",
"openToSignIn": "Abrir ${service} para entrar",
"copyUrl": "Copiar URL de login",
"urlCopied": "URL copiado"
},
"libraryFilter": {
+26 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "Шаблон маркера титров",
"creditsPatternDescription": "Регулярное выражение для распознавания маркеров титров в заголовках глав",
"invalidRegex": "Недопустимое регулярное выражение",
"regex": "Регулярное выражение",
"downloads": "Загрузки",
"downloadLocationDescription": "Выберите место для хранения загруженного контента",
"downloadLocationDefault": "По умолчанию (Хранилище приложения)",
@@ -336,6 +337,8 @@
"clearShortcut": "Очистить клавишу",
"noShortcutSet": "Сочетание не задано",
"currentShortcut": "Текущее сочетание:",
"pressToRecord": "Выберите, чтобы записать сочетание клавиш",
"recordingShortcut": "Нажмите сочетание клавиш сейчас",
"actions": {
"playPause": "Воспроизведение/Пауза",
"volumeUp": "Громкость выше",
@@ -434,7 +437,9 @@
"hue": "Цветовой тон",
"saturation": "Насыщенность",
"brightness": "Яркость",
"hexColor": "Шестнадцатеричный цвет"
"hexColor": "Шестнадцатеричный цвет",
"expandText": "Развернуть текст",
"collapseText": "Свернуть текст"
},
"tooltips": {
"shufflePlay": "Случайное воспроизведение",
@@ -534,6 +539,12 @@
"markedAsWatchedOffline": "Отмечено как просмотренное (синхронизируется при подключении)",
"markedAsUnwatchedOffline": "Отмечено как непросмотренное (синхронизируется при подключении)",
"autoRemovedWatchedDownload": "Автоудалено: ${title}",
"autoRemovedWatchedDownloads": {
"one": "Автоматически удалена ${n} просмотренная загрузка",
"few": "Автоматически удалены ${n} просмотренные загрузки",
"many": "Автоматически удалено ${n} просмотренных загрузок",
"other": "Автоматически удалено ${n} просмотренной загрузки"
},
"removedFromContinueWatching": "Удалено из «Продолжить просмотр»",
"errorLoading": "Ошибка: ${error}",
"streamInterrupted": "Поток прервался. Нажмите «Воспроизвести» или перемотайте, чтобы повторить попытку.",
@@ -585,6 +596,11 @@
"backgroundColor": "Цвет фона",
"position": "Позиция",
"assOverride": "Переопределение ASS",
"overrideScale": "Масштабировать",
"overrideForce": "Принудительно",
"overrideStrip": "Удалить стили",
"positionTop": "Сверху",
"positionBottom": "Снизу",
"bold": "Жирный",
"italic": "Курсив",
"renderResolution": "Разрешение отрисовки",
@@ -710,7 +726,7 @@
"searchFailed": "Ошибка поиска: ${error}",
"connectionTimeout": "Таймаут подключения при загрузке ${context}",
"connectionFailed": "Не удалось подключиться к медиасерверу",
"failedToLoad": "Не удалось загрузить ${context}: ${error}",
"unableToLoad": "Не удалось загрузить ${context}. Повторите попытку.",
"noClientAvailable": "Клиент недоступен",
"pleaseEnterToken": "Введите токен",
"invalidToken": "Недействительный токен",
@@ -968,7 +984,6 @@
"deleted": "Коллекция удалена",
"deleteFailed": "Не удалось удалить коллекцию",
"deleteFailedWithError": "Не удалось удалить коллекцию: ${error}",
"failedToLoadItems": "Не удалось загрузить элементы коллекции: ${error}",
"selectCollection": "Выбрать коллекцию",
"collectionName": "Название коллекции",
"enterCollectionName": "Введите название коллекции",
@@ -1053,6 +1068,8 @@
"hostingSession": "Хостинг сессии",
"inSession": "В сессии",
"sessionCode": "Код сессии",
"openSessionControls": "Открыть управление сессией «Смотреть вместе»",
"copySessionCode": "Скопировать код сессии",
"hostControlsPlayback": "Хост управляет воспроизведением",
"anyoneCanControl": "Любой может управлять воспроизведением",
"hostControls": "Управление хоста",
@@ -1266,6 +1283,8 @@
},
"videoSettings": {
"playbackSpeed": "Скорость воспроизведения",
"normalSpeed": "Обычная",
"sleepTimerActive": "Активен (${duration})",
"zoom": "Масштаб",
"sleepTimer": "Таймер сна",
"audioSync": "Синхронизация аудио",
@@ -1363,6 +1382,8 @@
"artworkUpdated": "Обложка обновлена",
"artworkUpdateFailed": "Не удалось обновить обложку",
"noArtworkAvailable": "Обложки недоступны",
"artworkOption": "Вариант обложки ${index}",
"selectedArtworkOption": "Вариант обложки ${index}, выбран",
"notSet": "Не задано",
"libraryDefault": "По умолчанию библиотеки",
"accountDefault": "По умолчанию аккаунта",
@@ -1490,6 +1511,7 @@
"title": "Активируйте Plezy в ${service}",
"body": "Перейдите на ${url} и введите этот код:",
"openToActivate": "Открыть ${service} для активации",
"copyCode": "Скопировать код активации",
"waitingForAuthorization": "Ожидание авторизации…",
"codeCopied": "Код скопирован"
},
@@ -1497,6 +1519,7 @@
"title": "Войти в ${service}",
"body": "Отсканируйте этот QR-код или откройте URL на любом устройстве.",
"openToSignIn": "Открыть ${service} для входа",
"copyUrl": "Скопировать URL для входа",
"urlCopied": "URL скопирован"
},
"libraryFilter": {
+1 -1
View File
@@ -4,7 +4,7 @@
/// To regenerate, run: `dart run slang`
///
/// Locales: 16
/// Strings: 22540 (1408 per locale)
/// Strings: 22835 (1427 per locale)
// coverage:ignore-file
// ignore_for_file: type=lint, unused_import
+47 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsBg extends TranslationsSettingsEn {
@override String get creditsPattern => 'Шаблон за маркер на финални надписи';
@override String get creditsPatternDescription => 'Шаблон с регулярен израз за намиране на маркери за финални надписи в заглавия на глави';
@override String get invalidRegex => 'Невалиден регулярен израз';
@override String get regex => 'Регулярен израз';
@override String get downloads => 'Изтегляния';
@override String get downloadLocationDescription => 'Изберете къде да се съхранява изтегленото съдържание';
@override String get downloadLocationDefault => 'По подразбиране (хранилище на приложението)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysBg extends TranslationsHotkeysEn {
@override String get clearShortcut => 'Изчисти клавишната комбинация';
@override String get noShortcutSet => 'Няма зададена клавишна комбинация';
@override String get currentShortcut => 'Текуща комбинация:';
@override String get pressToRecord => 'Избери, за да запишеш клавишна комбинация';
@override String get recordingShortcut => 'Натисни клавишната комбинация сега';
@override late final _TranslationsHotkeysActionsBg actions = _TranslationsHotkeysActionsBg._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityBg extends TranslationsAccessibilityEn {
@override String get saturation => 'Наситеност';
@override String get brightness => 'Яркост';
@override String get hexColor => 'Шестнадесетичен цвят';
@override String get expandText => 'Разгъни текста';
@override String get collapseText => 'Свий текста';
}
// Path: tooltips
@@ -693,6 +698,10 @@ class _TranslationsMessagesBg extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => 'Маркирано като гледано (ще се синхронизира, когато сте онлайн)';
@override String get markedAsUnwatchedOffline => 'Маркирано като негледано (ще се синхронизира, когато сте онлайн)';
@override String autoRemovedWatchedDownload({required Object title}) => 'Автоматично премахнато: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('bg'))(n,
one: 'Автоматично премахнато ${n} гледано изтегляне',
other: 'Автоматично премахнати ${n} гледани изтегляния',
);
@override String get removedFromContinueWatching => 'Премахнато от продължаване на гледането';
@override String errorLoading({required Object error}) => 'Грешка: ${error}';
@override String get streamInterrupted => 'Потокът прекъсна. Натиснете „Пусни“ или превъртете, за да опитате отново.';
@@ -751,6 +760,11 @@ class _TranslationsSubtitlingStylingBg extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => 'Цвят на фона';
@override String get position => 'Позиция';
@override String get assOverride => 'ASS презаписване';
@override String get overrideScale => 'Мащабиране';
@override String get overrideForce => 'Принудително';
@override String get overrideStrip => 'Премахване на стиловете';
@override String get positionTop => 'Горе';
@override String get positionBottom => 'Долу';
@override String get bold => 'Получер';
@override String get italic => 'Курсив';
@override String get renderResolution => 'Резолюция на изобразяване';
@@ -918,7 +932,7 @@ class _TranslationsErrorsBg extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => 'Търсенето е неуспешно: ${error}';
@override String connectionTimeout({required Object context}) => 'Изтече времето за връзка при зареждане на ${context}';
@override String get connectionFailed => 'Не може да се осъществи връзка с медиен сървър';
@override String failedToLoad({required Object context, required Object error}) => 'Неуспешно зареждане на ${context}: ${error}';
@override String unableToLoad({required Object context}) => 'Не може да се зареди ${context}. Опитайте отново.';
@override String get noClientAvailable => 'Няма наличен клиент';
@override String get pleaseEnterToken => 'Моля, въведете токен';
@override String get invalidToken => 'Невалиден токен';
@@ -1180,7 +1194,6 @@ class _TranslationsCollectionsBg extends TranslationsCollectionsEn {
@override String get deleted => 'Колекцията е изтрита';
@override String get deleteFailed => 'Неуспешно изтриване на колекция';
@override String deleteFailedWithError({required Object error}) => 'Неуспешно изтриване на колекция: ${error}';
@override String failedToLoadItems({required Object error}) => 'Неуспешно зареждане на елементите в колекцията: ${error}';
@override String get selectCollection => 'Избери колекция';
@override String get collectionName => 'Име на колекция';
@override String get enterCollectionName => 'Въведете име на колекция';
@@ -1284,6 +1297,8 @@ class _TranslationsWatchTogetherBg extends TranslationsWatchTogetherEn {
@override String get hostingSession => 'Сесия с домакин';
@override String get inSession => 'В сесия';
@override String get sessionCode => 'Код на сесията';
@override String get openSessionControls => 'Отвори контролите за сесията „Гледане заедно“';
@override String get copySessionCode => 'Копирай кода на сесията';
@override String get hostControlsPlayback => 'Домакинът управлява възпроизвеждането';
@override String get anyoneCanControl => 'Всеки може да управлява възпроизвеждането';
@override String get hostControls => 'Контроли на домакина';
@@ -1460,6 +1475,8 @@ class _TranslationsVideoSettingsBg extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => 'Скорост на възпроизвеждане';
@override String get normalSpeed => 'Нормална';
@override String sleepTimerActive({required Object duration}) => 'Активен (${duration})';
@override String get zoom => 'Мащаб';
@override String get sleepTimer => 'Таймер за заспиване';
@override String get audioSync => 'Синхронизация на аудио';
@@ -1578,6 +1595,8 @@ class _TranslationsMetadataEditBg extends TranslationsMetadataEditEn {
@override String get artworkUpdated => 'Обложката е обновена';
@override String get artworkUpdateFailed => 'Неуспешно обновяване на обложката';
@override String get noArtworkAvailable => 'Няма налична обложка';
@override String artworkOption({required Object index}) => 'Вариант за обложка ${index}';
@override String selectedArtworkOption({required Object index}) => 'Вариант за обложка ${index}, избран';
@override String get notSet => 'Не е зададено';
@override String get libraryDefault => 'По подразбиране за библиотеката';
@override String get accountDefault => 'По подразбиране за акаунта';
@@ -2055,6 +2074,7 @@ class _TranslationsServicesDeviceCodeBg extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => 'Активиране на Plezy в ${service}';
@override String body({required Object url}) => 'Посетете ${url} и въведете този код:';
@override String openToActivate({required Object service}) => 'Отворете ${service}, за да активирате';
@override String get copyCode => 'Копирай кода за активиране';
@override String get waitingForAuthorization => 'Изчакване на оторизация…';
@override String get codeCopied => 'Кодът е копиран';
}
@@ -2069,6 +2089,7 @@ class _TranslationsServicesOauthProxyBg extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => 'Вход в ${service}';
@override String get body => 'Сканирайте този QR код или отворете URL-а на което и да е устройство.';
@override String openToSignIn({required Object service}) => 'Отворете ${service}, за да влезете';
@override String get copyUrl => 'Копирай URL адреса за вход';
@override String get urlCopied => 'URL адресът е копиран';
}
@@ -2318,6 +2339,7 @@ extension on TranslationsBg {
'settings.creditsPattern' => 'Шаблон за маркер на финални надписи',
'settings.creditsPatternDescription' => 'Шаблон с регулярен израз за намиране на маркери за финални надписи в заглавия на глави',
'settings.invalidRegex' => 'Невалиден регулярен израз',
'settings.regex' => 'Регулярен израз',
'settings.downloads' => 'Изтегляния',
'settings.downloadLocationDescription' => 'Изберете къде да се съхранява изтегленото съдържание',
'settings.downloadLocationDefault' => 'По подразбиране (хранилище на приложението)',
@@ -2423,6 +2445,8 @@ extension on TranslationsBg {
'hotkeys.clearShortcut' => 'Изчисти клавишната комбинация',
'hotkeys.noShortcutSet' => 'Няма зададена клавишна комбинация',
'hotkeys.currentShortcut' => 'Текуща комбинация:',
'hotkeys.pressToRecord' => 'Избери, за да запишеш клавишна комбинация',
'hotkeys.recordingShortcut' => 'Натисни клавишната комбинация сега',
'hotkeys.actions.playPause' => 'Пускане/пауза',
'hotkeys.actions.volumeUp' => 'Увеличи звука',
'hotkeys.actions.volumeDown' => 'Намали звука',
@@ -2512,6 +2536,8 @@ extension on TranslationsBg {
'accessibility.saturation' => 'Наситеност',
'accessibility.brightness' => 'Яркост',
'accessibility.hexColor' => 'Шестнадесетичен цвят',
'accessibility.expandText' => 'Разгъни текста',
'accessibility.collapseText' => 'Свий текста',
'tooltips.shufflePlay' => 'Разбъркано възпроизвеждане',
'tooltips.playTrailer' => 'Пусни трейлър',
'tooltips.markAsWatched' => 'Маркирай като гледано',
@@ -2602,19 +2628,20 @@ extension on TranslationsBg {
'messages.markedAsWatchedOffline' => 'Маркирано като гледано (ще се синхронизира, когато сте онлайн)',
'messages.markedAsUnwatchedOffline' => 'Маркирано като негледано (ще се синхронизира, когато сте онлайн)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Автоматично премахнато: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('bg'))(n, one: 'Автоматично премахнато ${n} гледано изтегляне', other: 'Автоматично премахнати ${n} гледани изтегляния', ),
'messages.removedFromContinueWatching' => 'Премахнато от продължаване на гледането',
'messages.errorLoading' => ({required Object error}) => 'Грешка: ${error}',
'messages.streamInterrupted' => 'Потокът прекъсна. Натиснете „Пусни“ или превъртете, за да опитате отново.',
'messages.liveStreamInterrupted' => 'Потокът на живо прекъсна. Натиснете „Пусни“, за да опитате отново.',
'messages.fileInfoNotAvailable' => 'Информацията за файла не е налична',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Грешка при зареждане на информация за файла: ${error}',
'messages.errorLoadingSeries' => 'Грешка при зареждане на сериала',
'messages.musicNotSupported' => 'Възпроизвеждането на музика все още не се поддържа',
'messages.noDescriptionAvailable' => 'Няма налично описание',
'messages.noProfilesAvailable' => 'Няма налични профили',
'messages.contactAdminForProfiles' => 'Свържете се с администратора на сървъра, за да добави профили',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => 'Не може да се определи секцията на библиотеката за този елемент',
'messages.logsCleared' => 'Логовете са изчистени',
'messages.logsCopied' => 'Логовете са копирани в клипборда',
@@ -2653,6 +2680,11 @@ extension on TranslationsBg {
'subtitlingStyling.backgroundColor' => 'Цвят на фона',
'subtitlingStyling.position' => 'Позиция',
'subtitlingStyling.assOverride' => 'ASS презаписване',
'subtitlingStyling.overrideScale' => 'Мащабиране',
'subtitlingStyling.overrideForce' => 'Принудително',
'subtitlingStyling.overrideStrip' => 'Премахване на стиловете',
'subtitlingStyling.positionTop' => 'Горе',
'subtitlingStyling.positionBottom' => 'Долу',
'subtitlingStyling.bold' => 'Получер',
'subtitlingStyling.italic' => 'Курсив',
'subtitlingStyling.renderResolution' => 'Резолюция на изобразяване',
@@ -2766,7 +2798,7 @@ extension on TranslationsBg {
'errors.searchFailed' => ({required Object error}) => 'Търсенето е неуспешно: ${error}',
'errors.connectionTimeout' => ({required Object context}) => 'Изтече времето за връзка при зареждане на ${context}',
'errors.connectionFailed' => 'Не може да се осъществи връзка с медиен сървър',
'errors.failedToLoad' => ({required Object context, required Object error}) => 'Неуспешно зареждане на ${context}: ${error}',
'errors.unableToLoad' => ({required Object context}) => 'Не може да се зареди ${context}. Опитайте отново.',
'errors.noClientAvailable' => 'Няма наличен клиент',
'errors.pleaseEnterToken' => 'Моля, въведете токен',
'errors.invalidToken' => 'Невалиден токен',
@@ -2987,7 +3019,6 @@ extension on TranslationsBg {
'collections.deleted' => 'Колекцията е изтрита',
'collections.deleteFailed' => 'Неуспешно изтриване на колекция',
'collections.deleteFailedWithError' => ({required Object error}) => 'Неуспешно изтриване на колекция: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Неуспешно зареждане на елементите в колекцията: ${error}',
'collections.selectCollection' => 'Избери колекция',
'collections.collectionName' => 'Име на колекция',
'collections.enterCollectionName' => 'Въведете име на колекция',
@@ -3061,6 +3092,8 @@ extension on TranslationsBg {
'watchTogether.hostingSession' => 'Сесия с домакин',
'watchTogether.inSession' => 'В сесия',
'watchTogether.sessionCode' => 'Код на сесията',
'watchTogether.openSessionControls' => 'Отвори контролите за сесията „Гледане заедно“',
'watchTogether.copySessionCode' => 'Копирай кода на сесията',
'watchTogether.hostControlsPlayback' => 'Домакинът управлява възпроизвеждането',
'watchTogether.anyoneCanControl' => 'Всеки може да управлява възпроизвеждането',
'watchTogether.hostControls' => 'Контроли на домакина',
@@ -3115,6 +3148,8 @@ extension on TranslationsBg {
'downloads.manage' => 'Управление',
'downloads.tvShows' => 'ТВ сериали',
'downloads.movies' => 'Филми',
_ => null,
} ?? switch (path) {
'downloads.music' => 'Музика',
'downloads.tracksQueued' => ({required Object count}) => '${count} песни в опашката за изтегляне',
'downloads.noDownloads' => 'Все още няма изтегляния',
@@ -3127,8 +3162,6 @@ extension on TranslationsBg {
'downloads.serverErrorBitrate' => 'Грешка на сървъра: файлът може да надвишава лимита за отдалечен битрейт',
'downloads.episodesQueued' => ({required Object count}) => '${count} епизода са добавени в опашката за изтегляне',
'downloads.downloadDeleted' => 'Изтеглянето е изтрито',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => 'Да се изтрие ли "${title}" от това устройство?',
'downloads.cancelledDownloadTitle' => 'Отменено изтегляне',
'downloads.cancelledDownloadMessage' => 'Това изтегляне беше отменено. Какво искате да направите?',
@@ -3260,6 +3293,8 @@ extension on TranslationsBg {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => 'Връзката е загубена след ${attempts} опита',
'companionRemote.errors.connectionLost' => 'Връзката е загубена',
'videoSettings.playbackSpeed' => 'Скорост на възпроизвеждане',
'videoSettings.normalSpeed' => 'Нормална',
'videoSettings.sleepTimerActive' => ({required Object duration}) => 'Активен (${duration})',
'videoSettings.zoom' => 'Мащаб',
'videoSettings.sleepTimer' => 'Таймер за заспиване',
'videoSettings.audioSync' => 'Синхронизация на аудио',
@@ -3351,6 +3386,8 @@ extension on TranslationsBg {
'metadataEdit.artworkUpdated' => 'Обложката е обновена',
'metadataEdit.artworkUpdateFailed' => 'Неуспешно обновяване на обложката',
'metadataEdit.noArtworkAvailable' => 'Няма налична обложка',
'metadataEdit.artworkOption' => ({required Object index}) => 'Вариант за обложка ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => 'Вариант за обложка ${index}, избран',
'metadataEdit.notSet' => 'Не е зададено',
'metadataEdit.libraryDefault' => 'По подразбиране за библиотеката',
'metadataEdit.accountDefault' => 'По подразбиране за акаунта',
@@ -3465,11 +3502,13 @@ extension on TranslationsBg {
'services.deviceCode.title' => ({required Object service}) => 'Активиране на Plezy в ${service}',
'services.deviceCode.body' => ({required Object url}) => 'Посетете ${url} и въведете този код:',
'services.deviceCode.openToActivate' => ({required Object service}) => 'Отворете ${service}, за да активирате',
'services.deviceCode.copyCode' => 'Копирай кода за активиране',
'services.deviceCode.waitingForAuthorization' => 'Изчакване на оторизация…',
'services.deviceCode.codeCopied' => 'Кодът е копиран',
'services.oauthProxy.title' => ({required Object service}) => 'Вход в ${service}',
'services.oauthProxy.body' => 'Сканирайте този QR код или отворете URL-а на което и да е устройство.',
'services.oauthProxy.openToSignIn' => ({required Object service}) => 'Отворете ${service}, за да влезете',
'services.oauthProxy.copyUrl' => 'Копирай URL адреса за вход',
'services.oauthProxy.urlCopied' => 'URL адресът е копиран',
'services.libraryFilter.title' => 'Филтър на библиотеките',
'services.libraryFilter.subtitleAllSyncing' => 'Синхронизират се всички библиотеки',
+47 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsDa extends TranslationsSettingsEn {
@override String get creditsPattern => 'Rulletekstmarkørmønster';
@override String get creditsPatternDescription => 'Regulært udtryk til at genkende rulletekstmarkører i kapiteltitler';
@override String get invalidRegex => 'Ugyldigt regulært udtryk';
@override String get regex => 'Regulært udtryk';
@override String get downloads => 'Downloads';
@override String get downloadLocationDescription => 'Vælg hvor downloadet indhold skal gemmes';
@override String get downloadLocationDefault => 'Standard (App-lagring)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysDa extends TranslationsHotkeysEn {
@override String get clearShortcut => 'Ryd genvej';
@override String get noShortcutSet => 'Ingen genvej angivet';
@override String get currentShortcut => 'Nuværende genvej:';
@override String get pressToRecord => 'Vælg for at registrere en genvej';
@override String get recordingShortcut => 'Tryk på genvejen nu';
@override late final _TranslationsHotkeysActionsDa actions = _TranslationsHotkeysActionsDa._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityDa extends TranslationsAccessibilityEn {
@override String get saturation => 'Mætning';
@override String get brightness => 'Lysstyrke';
@override String get hexColor => 'Hexfarve';
@override String get expandText => 'Udvid tekst';
@override String get collapseText => 'Fold tekst sammen';
}
// Path: tooltips
@@ -693,6 +698,10 @@ class _TranslationsMessagesDa extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => 'Markeret som set (synkroniseres online)';
@override String get markedAsUnwatchedOffline => 'Markeret som uset (synkroniseres online)';
@override String autoRemovedWatchedDownload({required Object title}) => 'Automatisk fjernet: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('da'))(n,
one: 'Fjernede automatisk ${n} set download',
other: 'Fjernede automatisk ${n} sete downloads',
);
@override String get removedFromContinueWatching => 'Fjernet fra Fortsæt med at se';
@override String errorLoading({required Object error}) => 'Fejl: ${error}';
@override String get streamInterrupted => 'Streamen blev afbrudt. Tryk på afspil, eller spol for at prøve igen.';
@@ -751,6 +760,11 @@ class _TranslationsSubtitlingStylingDa extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => 'Baggrundsfarve';
@override String get position => 'Position';
@override String get assOverride => 'ASS-tilsidesættelse';
@override String get overrideScale => 'Skaler';
@override String get overrideForce => 'Gennemtving';
@override String get overrideStrip => 'Fjern formatering';
@override String get positionTop => 'Øverst';
@override String get positionBottom => 'Nederst';
@override String get bold => 'Fed';
@override String get italic => 'Kursiv';
@override String get renderResolution => 'Gengivelsesopløsning';
@@ -918,7 +932,7 @@ class _TranslationsErrorsDa extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => 'Søgning mislykkedes: ${error}';
@override String connectionTimeout({required Object context}) => 'Forbindelsestimeout ved indlæsning af ${context}';
@override String get connectionFailed => 'Kan ikke oprette forbindelse til medieserver';
@override String failedToLoad({required Object context, required Object error}) => 'Kunne ikke indlæse ${context}: ${error}';
@override String unableToLoad({required Object context}) => 'Kunne ikke indlæse ${context}. Prøv igen.';
@override String get noClientAvailable => 'Ingen klient tilgængelig';
@override String get pleaseEnterToken => 'Indtast et token';
@override String get invalidToken => 'Ugyldigt token';
@@ -1180,7 +1194,6 @@ class _TranslationsCollectionsDa extends TranslationsCollectionsEn {
@override String get deleted => 'Samling slettet';
@override String get deleteFailed => 'Kunne ikke slette samling';
@override String deleteFailedWithError({required Object error}) => 'Kunne ikke slette samling: ${error}';
@override String failedToLoadItems({required Object error}) => 'Kunne ikke indlæse samlingselementer: ${error}';
@override String get selectCollection => 'Vælg samling';
@override String get collectionName => 'Samlingsnavn';
@override String get enterCollectionName => 'Indtast samlingsnavn';
@@ -1284,6 +1297,8 @@ class _TranslationsWatchTogetherDa extends TranslationsWatchTogetherEn {
@override String get hostingSession => 'Vært for session';
@override String get inSession => 'I session';
@override String get sessionCode => 'Sessionskode';
@override String get openSessionControls => 'Åbn sessionsstyring for Se sammen';
@override String get copySessionCode => 'Kopiér sessionskode';
@override String get hostControlsPlayback => 'Vært styrer afspilning';
@override String get anyoneCanControl => 'Alle kan styre afspilning';
@override String get hostControls => 'Værtskontrol';
@@ -1460,6 +1475,8 @@ class _TranslationsVideoSettingsDa extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => 'Afspilningshastighed';
@override String get normalSpeed => 'Normal';
@override String sleepTimerActive({required Object duration}) => 'Aktiv (${duration})';
@override String get zoom => 'Zoom';
@override String get sleepTimer => 'Sove-timer';
@override String get audioSync => 'Lydsynkronisering';
@@ -1578,6 +1595,8 @@ class _TranslationsMetadataEditDa extends TranslationsMetadataEditEn {
@override String get artworkUpdated => 'Grafik opdateret';
@override String get artworkUpdateFailed => 'Kunne ikke opdatere grafik';
@override String get noArtworkAvailable => 'Ingen grafik tilgængelig';
@override String artworkOption({required Object index}) => 'Grafikvalg ${index}';
@override String selectedArtworkOption({required Object index}) => 'Grafikvalg ${index}, valgt';
@override String get notSet => 'Ikke indstillet';
@override String get libraryDefault => 'Biblioteksstandard';
@override String get accountDefault => 'Kontostandard';
@@ -2055,6 +2074,7 @@ class _TranslationsServicesDeviceCodeDa extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => 'Aktiver Plezy på ${service}';
@override String body({required Object url}) => 'Besøg ${url} og indtast denne kode:';
@override String openToActivate({required Object service}) => 'Åbn ${service} for at aktivere';
@override String get copyCode => 'Kopiér aktiveringskode';
@override String get waitingForAuthorization => 'Venter på godkendelse…';
@override String get codeCopied => 'Kode kopieret';
}
@@ -2069,6 +2089,7 @@ class _TranslationsServicesOauthProxyDa extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => 'Log ind på ${service}';
@override String get body => 'Scan denne QR-kode, eller åbn URL\'en på en enhed.';
@override String openToSignIn({required Object service}) => 'Åbn ${service} for at logge ind';
@override String get copyUrl => 'Kopiér login-URL';
@override String get urlCopied => 'URL kopieret';
}
@@ -2318,6 +2339,7 @@ extension on TranslationsDa {
'settings.creditsPattern' => 'Rulletekstmarkørmønster',
'settings.creditsPatternDescription' => 'Regulært udtryk til at genkende rulletekstmarkører i kapiteltitler',
'settings.invalidRegex' => 'Ugyldigt regulært udtryk',
'settings.regex' => 'Regulært udtryk',
'settings.downloads' => 'Downloads',
'settings.downloadLocationDescription' => 'Vælg hvor downloadet indhold skal gemmes',
'settings.downloadLocationDefault' => 'Standard (App-lagring)',
@@ -2423,6 +2445,8 @@ extension on TranslationsDa {
'hotkeys.clearShortcut' => 'Ryd genvej',
'hotkeys.noShortcutSet' => 'Ingen genvej angivet',
'hotkeys.currentShortcut' => 'Nuværende genvej:',
'hotkeys.pressToRecord' => 'Vælg for at registrere en genvej',
'hotkeys.recordingShortcut' => 'Tryk på genvejen nu',
'hotkeys.actions.playPause' => 'Afspil/Pause',
'hotkeys.actions.volumeUp' => 'Lydstyrke op',
'hotkeys.actions.volumeDown' => 'Lydstyrke ned',
@@ -2512,6 +2536,8 @@ extension on TranslationsDa {
'accessibility.saturation' => 'Mætning',
'accessibility.brightness' => 'Lysstyrke',
'accessibility.hexColor' => 'Hexfarve',
'accessibility.expandText' => 'Udvid tekst',
'accessibility.collapseText' => 'Fold tekst sammen',
'tooltips.shufflePlay' => 'Afspil tilfældigt',
'tooltips.playTrailer' => 'Afspil trailer',
'tooltips.markAsWatched' => 'Markér som set',
@@ -2602,19 +2628,20 @@ extension on TranslationsDa {
'messages.markedAsWatchedOffline' => 'Markeret som set (synkroniseres online)',
'messages.markedAsUnwatchedOffline' => 'Markeret som uset (synkroniseres online)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Automatisk fjernet: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('da'))(n, one: 'Fjernede automatisk ${n} set download', other: 'Fjernede automatisk ${n} sete downloads', ),
'messages.removedFromContinueWatching' => 'Fjernet fra Fortsæt med at se',
'messages.errorLoading' => ({required Object error}) => 'Fejl: ${error}',
'messages.streamInterrupted' => 'Streamen blev afbrudt. Tryk på afspil, eller spol for at prøve igen.',
'messages.liveStreamInterrupted' => 'Livestreamen blev afbrudt. Tryk på afspil for at prøve igen.',
'messages.fileInfoNotAvailable' => 'Filinfo ikke tilgængelig',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Fejl ved indlæsning af filinfo: ${error}',
'messages.errorLoadingSeries' => 'Fejl ved indlæsning af serie',
'messages.musicNotSupported' => 'Musikafspilning understøttes endnu ikke',
'messages.noDescriptionAvailable' => 'Ingen beskrivelse tilgængelig',
'messages.noProfilesAvailable' => 'Ingen profiler tilgængelige',
'messages.contactAdminForProfiles' => 'Kontakt din serveradministrator for at tilføje profiler',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => 'Kan ikke bestemme biblioteksafdeling for dette element',
'messages.logsCleared' => 'Logs ryddet',
'messages.logsCopied' => 'Logs kopieret til udklipsholder',
@@ -2653,6 +2680,11 @@ extension on TranslationsDa {
'subtitlingStyling.backgroundColor' => 'Baggrundsfarve',
'subtitlingStyling.position' => 'Position',
'subtitlingStyling.assOverride' => 'ASS-tilsidesættelse',
'subtitlingStyling.overrideScale' => 'Skaler',
'subtitlingStyling.overrideForce' => 'Gennemtving',
'subtitlingStyling.overrideStrip' => 'Fjern formatering',
'subtitlingStyling.positionTop' => 'Øverst',
'subtitlingStyling.positionBottom' => 'Nederst',
'subtitlingStyling.bold' => 'Fed',
'subtitlingStyling.italic' => 'Kursiv',
'subtitlingStyling.renderResolution' => 'Gengivelsesopløsning',
@@ -2766,7 +2798,7 @@ extension on TranslationsDa {
'errors.searchFailed' => ({required Object error}) => 'Søgning mislykkedes: ${error}',
'errors.connectionTimeout' => ({required Object context}) => 'Forbindelsestimeout ved indlæsning af ${context}',
'errors.connectionFailed' => 'Kan ikke oprette forbindelse til medieserver',
'errors.failedToLoad' => ({required Object context, required Object error}) => 'Kunne ikke indlæse ${context}: ${error}',
'errors.unableToLoad' => ({required Object context}) => 'Kunne ikke indlæse ${context}. Prøv igen.',
'errors.noClientAvailable' => 'Ingen klient tilgængelig',
'errors.pleaseEnterToken' => 'Indtast et token',
'errors.invalidToken' => 'Ugyldigt token',
@@ -2987,7 +3019,6 @@ extension on TranslationsDa {
'collections.deleted' => 'Samling slettet',
'collections.deleteFailed' => 'Kunne ikke slette samling',
'collections.deleteFailedWithError' => ({required Object error}) => 'Kunne ikke slette samling: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Kunne ikke indlæse samlingselementer: ${error}',
'collections.selectCollection' => 'Vælg samling',
'collections.collectionName' => 'Samlingsnavn',
'collections.enterCollectionName' => 'Indtast samlingsnavn',
@@ -3061,6 +3092,8 @@ extension on TranslationsDa {
'watchTogether.hostingSession' => 'Vært for session',
'watchTogether.inSession' => 'I session',
'watchTogether.sessionCode' => 'Sessionskode',
'watchTogether.openSessionControls' => 'Åbn sessionsstyring for Se sammen',
'watchTogether.copySessionCode' => 'Kopiér sessionskode',
'watchTogether.hostControlsPlayback' => 'Vært styrer afspilning',
'watchTogether.anyoneCanControl' => 'Alle kan styre afspilning',
'watchTogether.hostControls' => 'Værtskontrol',
@@ -3115,6 +3148,8 @@ extension on TranslationsDa {
'downloads.manage' => 'Administrer',
'downloads.tvShows' => 'TV-serier',
'downloads.movies' => 'Film',
_ => null,
} ?? switch (path) {
'downloads.music' => 'Musik',
'downloads.tracksQueued' => ({required Object count}) => '${count} numre i kø til download',
'downloads.noDownloads' => 'Ingen downloads endnu',
@@ -3127,8 +3162,6 @@ extension on TranslationsDa {
'downloads.serverErrorBitrate' => 'Serverfejl: filen overskrider muligvis grænsen for ekstern bitrate',
'downloads.episodesQueued' => ({required Object count}) => '${count} episoder i downloadkø',
'downloads.downloadDeleted' => 'Download slettet',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => 'Slet "${title}" fra denne enhed?',
'downloads.cancelledDownloadTitle' => 'Annulleret download',
'downloads.cancelledDownloadMessage' => 'Denne download blev annulleret. Hvad vil du gøre?',
@@ -3260,6 +3293,8 @@ extension on TranslationsDa {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => 'Forbindelse mistet efter ${attempts} forsøg',
'companionRemote.errors.connectionLost' => 'Forbindelse mistet',
'videoSettings.playbackSpeed' => 'Afspilningshastighed',
'videoSettings.normalSpeed' => 'Normal',
'videoSettings.sleepTimerActive' => ({required Object duration}) => 'Aktiv (${duration})',
'videoSettings.zoom' => 'Zoom',
'videoSettings.sleepTimer' => 'Sove-timer',
'videoSettings.audioSync' => 'Lydsynkronisering',
@@ -3351,6 +3386,8 @@ extension on TranslationsDa {
'metadataEdit.artworkUpdated' => 'Grafik opdateret',
'metadataEdit.artworkUpdateFailed' => 'Kunne ikke opdatere grafik',
'metadataEdit.noArtworkAvailable' => 'Ingen grafik tilgængelig',
'metadataEdit.artworkOption' => ({required Object index}) => 'Grafikvalg ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => 'Grafikvalg ${index}, valgt',
'metadataEdit.notSet' => 'Ikke indstillet',
'metadataEdit.libraryDefault' => 'Biblioteksstandard',
'metadataEdit.accountDefault' => 'Kontostandard',
@@ -3465,11 +3502,13 @@ extension on TranslationsDa {
'services.deviceCode.title' => ({required Object service}) => 'Aktiver Plezy på ${service}',
'services.deviceCode.body' => ({required Object url}) => 'Besøg ${url} og indtast denne kode:',
'services.deviceCode.openToActivate' => ({required Object service}) => 'Åbn ${service} for at aktivere',
'services.deviceCode.copyCode' => 'Kopiér aktiveringskode',
'services.deviceCode.waitingForAuthorization' => 'Venter på godkendelse…',
'services.deviceCode.codeCopied' => 'Kode kopieret',
'services.oauthProxy.title' => ({required Object service}) => 'Log ind på ${service}',
'services.oauthProxy.body' => 'Scan denne QR-kode, eller åbn URL\'en på en enhed.',
'services.oauthProxy.openToSignIn' => ({required Object service}) => 'Åbn ${service} for at logge ind',
'services.oauthProxy.copyUrl' => 'Kopiér login-URL',
'services.oauthProxy.urlCopied' => 'URL kopieret',
'services.libraryFilter.title' => 'Bibliotekfilter',
'services.libraryFilter.subtitleAllSyncing' => 'Synkroniserer alle biblioteker',
+47 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsDe extends TranslationsSettingsEn {
@override String get creditsPattern => 'Abspann-Markierungsmuster';
@override String get creditsPatternDescription => 'Regulärer Ausdruck zum Erkennen von Abspann-Markierungen in Kapiteltiteln';
@override String get invalidRegex => 'Ungültiger regulärer Ausdruck';
@override String get regex => 'Regulärer Ausdruck';
@override String get downloads => 'Downloads';
@override String get downloadLocationDescription => 'Speicherort für heruntergeladene Inhalte wählen';
@override String get downloadLocationDefault => 'Standard (App-Speicher)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysDe extends TranslationsHotkeysEn {
@override String get clearShortcut => 'Kürzel löschen';
@override String get noShortcutSet => 'Keine Tastenkombination festgelegt';
@override String get currentShortcut => 'Aktuelle Tastenkombination:';
@override String get pressToRecord => 'Auswählen, um eine Tastenkombination aufzuzeichnen';
@override String get recordingShortcut => 'Jetzt die Tastenkombination drücken';
@override late final _TranslationsHotkeysActionsDe actions = _TranslationsHotkeysActionsDe._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityDe extends TranslationsAccessibilityEn {
@override String get saturation => 'Sättigung';
@override String get brightness => 'Helligkeit';
@override String get hexColor => 'Hex-Farbe';
@override String get expandText => 'Text ausklappen';
@override String get collapseText => 'Text einklappen';
}
// Path: tooltips
@@ -693,6 +698,10 @@ class _TranslationsMessagesDe extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => 'Als gesehen markiert (wird synchronisiert, wenn online)';
@override String get markedAsUnwatchedOffline => 'Als ungesehen markiert (wird synchronisiert, wenn online)';
@override String autoRemovedWatchedDownload({required Object title}) => 'Automatisch entfernt: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('de'))(n,
one: 'Automatisch entfernt: ${n} angesehener Download',
other: 'Automatisch entfernt: ${n} angesehene Downloads',
);
@override String get removedFromContinueWatching => 'Aus Weiterschauen\' entfernt';
@override String errorLoading({required Object error}) => 'Fehler: ${error}';
@override String get streamInterrupted => 'Der Stream wurde unterbrochen. Drücke auf Wiedergabe oder spule, um es erneut zu versuchen.';
@@ -751,6 +760,11 @@ class _TranslationsSubtitlingStylingDe extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => 'Hintergrundfarbe';
@override String get position => 'Position';
@override String get assOverride => 'ASS-Überschreibung';
@override String get overrideScale => 'Skalieren';
@override String get overrideForce => 'Erzwingen';
@override String get overrideStrip => 'Formatierung entfernen';
@override String get positionTop => 'Oben';
@override String get positionBottom => 'Unten';
@override String get bold => 'Fett';
@override String get italic => 'Kursiv';
@override String get renderResolution => 'Render-Auflösung';
@@ -918,7 +932,7 @@ class _TranslationsErrorsDe extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => 'Suche fehlgeschlagen: ${error}';
@override String connectionTimeout({required Object context}) => 'Zeitüberschreitung beim Laden von ${context}';
@override String get connectionFailed => 'Keine Verbindung zum Medienserver möglich';
@override String failedToLoad({required Object context, required Object error}) => 'Fehler beim Laden von ${context}: ${error}';
@override String unableToLoad({required Object context}) => '${context} konnte nicht geladen werden. Bitte erneut versuchen.';
@override String get noClientAvailable => 'Kein Client verfügbar';
@override String get pleaseEnterToken => 'Bitte Token eingeben';
@override String get invalidToken => 'Ungültiges Token';
@@ -1180,7 +1194,6 @@ class _TranslationsCollectionsDe extends TranslationsCollectionsEn {
@override String get deleted => 'Sammlung gelöscht';
@override String get deleteFailed => 'Sammlung konnte nicht gelöscht werden';
@override String deleteFailedWithError({required Object error}) => 'Sammlung konnte nicht gelöscht werden: ${error}';
@override String failedToLoadItems({required Object error}) => 'Sammlungselemente konnten nicht geladen werden: ${error}';
@override String get selectCollection => 'Sammlung auswählen';
@override String get collectionName => 'Sammlungsname';
@override String get enterCollectionName => 'Sammlungsnamen eingeben';
@@ -1284,6 +1297,8 @@ class _TranslationsWatchTogetherDe extends TranslationsWatchTogetherEn {
@override String get hostingSession => 'Sitzung Hosten';
@override String get inSession => 'In Sitzung';
@override String get sessionCode => 'Sitzungscode';
@override String get openSessionControls => 'Sitzungssteuerung für Gemeinsam Schauen öffnen';
@override String get copySessionCode => 'Sitzungscode kopieren';
@override String get hostControlsPlayback => 'Host steuert die Wiedergabe';
@override String get anyoneCanControl => 'Alle können die Wiedergabe steuern';
@override String get hostControls => 'Host steuert';
@@ -1460,6 +1475,8 @@ class _TranslationsVideoSettingsDe extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => 'Wiedergabegeschwindigkeit';
@override String get normalSpeed => 'Normal';
@override String sleepTimerActive({required Object duration}) => 'Aktiv (${duration})';
@override String get zoom => 'Zoom';
@override String get sleepTimer => 'Schlaftimer';
@override String get audioSync => 'Audio-Synchronisation';
@@ -1578,6 +1595,8 @@ class _TranslationsMetadataEditDe extends TranslationsMetadataEditEn {
@override String get artworkUpdated => 'Artwork aktualisiert';
@override String get artworkUpdateFailed => 'Artwork konnte nicht aktualisiert werden';
@override String get noArtworkAvailable => 'Kein Artwork verfügbar';
@override String artworkOption({required Object index}) => 'Artwork-Option ${index}';
@override String selectedArtworkOption({required Object index}) => 'Artwork-Option ${index}, ausgewählt';
@override String get notSet => 'Nicht festgelegt';
@override String get libraryDefault => 'Bibliotheksstandard';
@override String get accountDefault => 'Kontostandard';
@@ -2055,6 +2074,7 @@ class _TranslationsServicesDeviceCodeDe extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => 'Plezy auf ${service} aktivieren';
@override String body({required Object url}) => 'Gehe zu ${url} und gib diesen Code ein:';
@override String openToActivate({required Object service}) => '${service} zum Aktivieren öffnen';
@override String get copyCode => 'Aktivierungscode kopieren';
@override String get waitingForAuthorization => 'Warte auf Autorisierung…';
@override String get codeCopied => 'Code kopiert';
}
@@ -2069,6 +2089,7 @@ class _TranslationsServicesOauthProxyDe extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => 'Bei ${service} anmelden';
@override String get body => 'Scanne diesen QR-Code oder öffne die URL auf einem beliebigen Gerät.';
@override String openToSignIn({required Object service}) => '${service} zum Anmelden öffnen';
@override String get copyUrl => 'Anmelde-URL kopieren';
@override String get urlCopied => 'URL kopiert';
}
@@ -2318,6 +2339,7 @@ extension on TranslationsDe {
'settings.creditsPattern' => 'Abspann-Markierungsmuster',
'settings.creditsPatternDescription' => 'Regulärer Ausdruck zum Erkennen von Abspann-Markierungen in Kapiteltiteln',
'settings.invalidRegex' => 'Ungültiger regulärer Ausdruck',
'settings.regex' => 'Regulärer Ausdruck',
'settings.downloads' => 'Downloads',
'settings.downloadLocationDescription' => 'Speicherort für heruntergeladene Inhalte wählen',
'settings.downloadLocationDefault' => 'Standard (App-Speicher)',
@@ -2423,6 +2445,8 @@ extension on TranslationsDe {
'hotkeys.clearShortcut' => 'Kürzel löschen',
'hotkeys.noShortcutSet' => 'Keine Tastenkombination festgelegt',
'hotkeys.currentShortcut' => 'Aktuelle Tastenkombination:',
'hotkeys.pressToRecord' => 'Auswählen, um eine Tastenkombination aufzuzeichnen',
'hotkeys.recordingShortcut' => 'Jetzt die Tastenkombination drücken',
'hotkeys.actions.playPause' => 'Wiedergabe/Pause',
'hotkeys.actions.volumeUp' => 'Lauter',
'hotkeys.actions.volumeDown' => 'Leiser',
@@ -2512,6 +2536,8 @@ extension on TranslationsDe {
'accessibility.saturation' => 'Sättigung',
'accessibility.brightness' => 'Helligkeit',
'accessibility.hexColor' => 'Hex-Farbe',
'accessibility.expandText' => 'Text ausklappen',
'accessibility.collapseText' => 'Text einklappen',
'tooltips.shufflePlay' => 'Zufallswiedergabe',
'tooltips.playTrailer' => 'Trailer abspielen',
'tooltips.markAsWatched' => 'Als gesehen markieren',
@@ -2602,19 +2628,20 @@ extension on TranslationsDe {
'messages.markedAsWatchedOffline' => 'Als gesehen markiert (wird synchronisiert, wenn online)',
'messages.markedAsUnwatchedOffline' => 'Als ungesehen markiert (wird synchronisiert, wenn online)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Automatisch entfernt: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('de'))(n, one: 'Automatisch entfernt: ${n} angesehener Download', other: 'Automatisch entfernt: ${n} angesehene Downloads', ),
'messages.removedFromContinueWatching' => 'Aus Weiterschauen\' entfernt',
'messages.errorLoading' => ({required Object error}) => 'Fehler: ${error}',
'messages.streamInterrupted' => 'Der Stream wurde unterbrochen. Drücke auf Wiedergabe oder spule, um es erneut zu versuchen.',
'messages.liveStreamInterrupted' => 'Der Livestream wurde unterbrochen. Drücke auf Wiedergabe, um es erneut zu versuchen.',
'messages.fileInfoNotAvailable' => 'Dateiinfo nicht verfügbar',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Fehler beim Laden der Dateiinfo: ${error}',
'messages.errorLoadingSeries' => 'Fehler beim Laden der Serie',
'messages.musicNotSupported' => 'Musikwiedergabe wird noch nicht unterstützt',
'messages.noDescriptionAvailable' => 'Keine Beschreibung verfügbar',
'messages.noProfilesAvailable' => 'Keine Profile verfügbar',
'messages.contactAdminForProfiles' => 'Kontaktiere deinen Serveradministrator, um Profile hinzuzufügen',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => 'Bibliotheksbereich für dieses Element kann nicht ermittelt werden',
'messages.logsCleared' => 'Protokolle gelöscht',
'messages.logsCopied' => 'Protokolle in Zwischenablage kopiert',
@@ -2653,6 +2680,11 @@ extension on TranslationsDe {
'subtitlingStyling.backgroundColor' => 'Hintergrundfarbe',
'subtitlingStyling.position' => 'Position',
'subtitlingStyling.assOverride' => 'ASS-Überschreibung',
'subtitlingStyling.overrideScale' => 'Skalieren',
'subtitlingStyling.overrideForce' => 'Erzwingen',
'subtitlingStyling.overrideStrip' => 'Formatierung entfernen',
'subtitlingStyling.positionTop' => 'Oben',
'subtitlingStyling.positionBottom' => 'Unten',
'subtitlingStyling.bold' => 'Fett',
'subtitlingStyling.italic' => 'Kursiv',
'subtitlingStyling.renderResolution' => 'Render-Auflösung',
@@ -2766,7 +2798,7 @@ extension on TranslationsDe {
'errors.searchFailed' => ({required Object error}) => 'Suche fehlgeschlagen: ${error}',
'errors.connectionTimeout' => ({required Object context}) => 'Zeitüberschreitung beim Laden von ${context}',
'errors.connectionFailed' => 'Keine Verbindung zum Medienserver möglich',
'errors.failedToLoad' => ({required Object context, required Object error}) => 'Fehler beim Laden von ${context}: ${error}',
'errors.unableToLoad' => ({required Object context}) => '${context} konnte nicht geladen werden. Bitte erneut versuchen.',
'errors.noClientAvailable' => 'Kein Client verfügbar',
'errors.pleaseEnterToken' => 'Bitte Token eingeben',
'errors.invalidToken' => 'Ungültiges Token',
@@ -2987,7 +3019,6 @@ extension on TranslationsDe {
'collections.deleted' => 'Sammlung gelöscht',
'collections.deleteFailed' => 'Sammlung konnte nicht gelöscht werden',
'collections.deleteFailedWithError' => ({required Object error}) => 'Sammlung konnte nicht gelöscht werden: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Sammlungselemente konnten nicht geladen werden: ${error}',
'collections.selectCollection' => 'Sammlung auswählen',
'collections.collectionName' => 'Sammlungsname',
'collections.enterCollectionName' => 'Sammlungsnamen eingeben',
@@ -3061,6 +3092,8 @@ extension on TranslationsDe {
'watchTogether.hostingSession' => 'Sitzung Hosten',
'watchTogether.inSession' => 'In Sitzung',
'watchTogether.sessionCode' => 'Sitzungscode',
'watchTogether.openSessionControls' => 'Sitzungssteuerung für Gemeinsam Schauen öffnen',
'watchTogether.copySessionCode' => 'Sitzungscode kopieren',
'watchTogether.hostControlsPlayback' => 'Host steuert die Wiedergabe',
'watchTogether.anyoneCanControl' => 'Alle können die Wiedergabe steuern',
'watchTogether.hostControls' => 'Host steuert',
@@ -3115,6 +3148,8 @@ extension on TranslationsDe {
'downloads.manage' => 'Verwalten',
'downloads.tvShows' => 'Serien',
'downloads.movies' => 'Filme',
_ => null,
} ?? switch (path) {
'downloads.music' => 'Musik',
'downloads.tracksQueued' => ({required Object count}) => '${count} Titel zum Download in Warteschlange',
'downloads.noDownloads' => 'Noch keine Downloads',
@@ -3127,8 +3162,6 @@ extension on TranslationsDe {
'downloads.serverErrorBitrate' => 'Serverfehler: Datei überschreitet möglicherweise das Remote-Bitrate-Limit',
'downloads.episodesQueued' => ({required Object count}) => '${count} Episoden zum Download hinzugefügt',
'downloads.downloadDeleted' => 'Download gelöscht',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => '"${title}" von diesem Gerät löschen?',
'downloads.cancelledDownloadTitle' => 'Abgebrochener Download',
'downloads.cancelledDownloadMessage' => 'Dieser Download wurde abgebrochen. Was möchtest du tun?',
@@ -3260,6 +3293,8 @@ extension on TranslationsDe {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => 'Verbindung nach ${attempts} Versuchen verloren',
'companionRemote.errors.connectionLost' => 'Verbindung verloren',
'videoSettings.playbackSpeed' => 'Wiedergabegeschwindigkeit',
'videoSettings.normalSpeed' => 'Normal',
'videoSettings.sleepTimerActive' => ({required Object duration}) => 'Aktiv (${duration})',
'videoSettings.zoom' => 'Zoom',
'videoSettings.sleepTimer' => 'Schlaftimer',
'videoSettings.audioSync' => 'Audio-Synchronisation',
@@ -3351,6 +3386,8 @@ extension on TranslationsDe {
'metadataEdit.artworkUpdated' => 'Artwork aktualisiert',
'metadataEdit.artworkUpdateFailed' => 'Artwork konnte nicht aktualisiert werden',
'metadataEdit.noArtworkAvailable' => 'Kein Artwork verfügbar',
'metadataEdit.artworkOption' => ({required Object index}) => 'Artwork-Option ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => 'Artwork-Option ${index}, ausgewählt',
'metadataEdit.notSet' => 'Nicht festgelegt',
'metadataEdit.libraryDefault' => 'Bibliotheksstandard',
'metadataEdit.accountDefault' => 'Kontostandard',
@@ -3465,11 +3502,13 @@ extension on TranslationsDe {
'services.deviceCode.title' => ({required Object service}) => 'Plezy auf ${service} aktivieren',
'services.deviceCode.body' => ({required Object url}) => 'Gehe zu ${url} und gib diesen Code ein:',
'services.deviceCode.openToActivate' => ({required Object service}) => '${service} zum Aktivieren öffnen',
'services.deviceCode.copyCode' => 'Aktivierungscode kopieren',
'services.deviceCode.waitingForAuthorization' => 'Warte auf Autorisierung…',
'services.deviceCode.codeCopied' => 'Code kopiert',
'services.oauthProxy.title' => ({required Object service}) => 'Bei ${service} anmelden',
'services.oauthProxy.body' => 'Scanne diesen QR-Code oder öffne die URL auf einem beliebigen Gerät.',
'services.oauthProxy.openToSignIn' => ({required Object service}) => '${service} zum Anmelden öffnen',
'services.oauthProxy.copyUrl' => 'Anmelde-URL kopieren',
'services.oauthProxy.urlCopied' => 'URL kopiert',
'services.libraryFilter.title' => 'Bibliothekfilter',
'services.libraryFilter.subtitleAllSyncing' => 'Alle Bibliotheken werden synchronisiert',
+46 -11
View File
@@ -794,6 +794,9 @@ class TranslationsSettingsEn {
/// en: 'Invalid regular expression'
String get invalidRegex => 'Invalid regular expression';
/// en: 'Regular expression'
String get regex => 'Regular expression';
/// en: 'Downloads'
String get downloads => 'Downloads';
@@ -1647,6 +1650,12 @@ class TranslationsMessagesEn {
/// en: 'Auto-removed: ${title}'
String autoRemovedWatchedDownload({required Object title}) => 'Auto-removed: ${title}';
/// en: '(one) {Auto-removed ${n} watched download} (other) {Auto-removed ${n} watched downloads}'
String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n,
one: 'Auto-removed ${n} watched download',
other: 'Auto-removed ${n} watched downloads',
);
/// en: 'Removed from Continue Watching'
String get removedFromContinueWatching => 'Removed from Continue Watching';
@@ -1803,6 +1812,21 @@ class TranslationsSubtitlingStylingEn {
/// en: 'ASS Override'
String get assOverride => 'ASS Override';
/// en: 'Scale'
String get overrideScale => 'Scale';
/// en: 'Force'
String get overrideForce => 'Force';
/// en: 'Remove styling'
String get overrideStrip => 'Remove styling';
/// en: 'Top'
String get positionTop => 'Top';
/// en: 'Bottom'
String get positionBottom => 'Bottom';
/// en: 'Bold'
String get bold => 'Bold';
@@ -2196,8 +2220,8 @@ class TranslationsErrorsEn {
/// en: 'Unable to connect to media server'
String get connectionFailed => 'Unable to connect to media server';
/// en: 'Failed to load ${context}: ${error}'
String failedToLoad({required Object context, required Object error}) => 'Failed to load ${context}: ${error}';
/// en: 'Unable to load ${context}. Please try again.'
String unableToLoad({required Object context}) => 'Unable to load ${context}. Please try again.';
/// en: 'No client available'
String get noClientAvailable => 'No client available';
@@ -2786,9 +2810,6 @@ class TranslationsCollectionsEn {
/// en: 'Failed to delete collection: ${error}'
String deleteFailedWithError({required Object error}) => 'Failed to delete collection: ${error}';
/// en: 'Failed to load collection items: ${error}'
String failedToLoadItems({required Object error}) => 'Failed to load collection items: ${error}';
/// en: 'Select Collection'
String get selectCollection => 'Select Collection';
@@ -3493,6 +3514,12 @@ class TranslationsVideoSettingsEn {
/// en: 'Playback Speed'
String get playbackSpeed => 'Playback Speed';
/// en: 'Normal'
String get normalSpeed => 'Normal';
/// en: 'Active (${duration})'
String sleepTimerActive({required Object duration}) => 'Active (${duration})';
/// en: 'Zoom'
String get zoom => 'Zoom';
@@ -5167,6 +5194,7 @@ extension on Translations {
'settings.creditsPattern' => 'Credits Marker Pattern',
'settings.creditsPatternDescription' => 'Regex pattern to match credits markers in chapter titles',
'settings.invalidRegex' => 'Invalid regular expression',
'settings.regex' => 'Regular expression',
'settings.downloads' => 'Downloads',
'settings.downloadLocationDescription' => 'Choose where to store downloaded content',
'settings.downloadLocationDefault' => 'Default (App Storage)',
@@ -5455,15 +5483,16 @@ extension on Translations {
'messages.markedAsWatchedOffline' => 'Marked as watched (will sync when online)',
'messages.markedAsUnwatchedOffline' => 'Marked as unwatched (will sync when online)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Auto-removed: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n, one: 'Auto-removed ${n} watched download', other: 'Auto-removed ${n} watched downloads', ),
'messages.removedFromContinueWatching' => 'Removed from Continue Watching',
'messages.errorLoading' => ({required Object error}) => 'Error: ${error}',
'messages.streamInterrupted' => 'The stream was interrupted. Press play or seek to retry.',
'messages.liveStreamInterrupted' => 'The live stream was interrupted. Press play to retry.',
'messages.fileInfoNotAvailable' => 'File information not available',
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Error loading file info: ${error}',
'messages.errorLoadingSeries' => 'Error loading series',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Error loading file info: ${error}',
'messages.errorLoadingSeries' => 'Error loading series',
'messages.musicNotSupported' => 'Music playback is not yet supported',
'messages.noDescriptionAvailable' => 'No description available',
'messages.noProfilesAvailable' => 'No profiles available',
@@ -5506,6 +5535,11 @@ extension on Translations {
'subtitlingStyling.backgroundColor' => 'Background Color',
'subtitlingStyling.position' => 'Position',
'subtitlingStyling.assOverride' => 'ASS Override',
'subtitlingStyling.overrideScale' => 'Scale',
'subtitlingStyling.overrideForce' => 'Force',
'subtitlingStyling.overrideStrip' => 'Remove styling',
'subtitlingStyling.positionTop' => 'Top',
'subtitlingStyling.positionBottom' => 'Bottom',
'subtitlingStyling.bold' => 'Bold',
'subtitlingStyling.italic' => 'Italic',
'subtitlingStyling.renderResolution' => 'Render Resolution',
@@ -5619,7 +5653,7 @@ extension on Translations {
'errors.searchFailed' => ({required Object error}) => 'Search failed: ${error}',
'errors.connectionTimeout' => ({required Object context}) => 'Connection timeout while loading ${context}',
'errors.connectionFailed' => 'Unable to connect to media server',
'errors.failedToLoad' => ({required Object context, required Object error}) => 'Failed to load ${context}: ${error}',
'errors.unableToLoad' => ({required Object context}) => 'Unable to load ${context}. Please try again.',
'errors.noClientAvailable' => 'No client available',
'errors.pleaseEnterToken' => 'Please enter a token',
'errors.invalidToken' => 'Invalid token',
@@ -5840,7 +5874,6 @@ extension on Translations {
'collections.deleted' => 'Collection deleted',
'collections.deleteFailed' => 'Failed to delete collection',
'collections.deleteFailedWithError' => ({required Object error}) => 'Failed to delete collection: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Failed to load collection items: ${error}',
'collections.selectCollection' => 'Select Collection',
'collections.collectionName' => 'Collection Name',
'collections.enterCollectionName' => 'Enter collection name',
@@ -5970,14 +6003,14 @@ extension on Translations {
'downloads.manage' => 'Manage',
'downloads.tvShows' => 'TV Shows',
'downloads.movies' => 'Movies',
_ => null,
} ?? switch (path) {
'downloads.music' => 'Music',
'downloads.tracksQueued' => ({required Object count}) => '${count} tracks queued for download',
'downloads.noDownloads' => 'No downloads yet',
'downloads.noDownloadsDescription' => 'Downloaded content will appear here for offline viewing',
'downloads.downloadNow' => 'Download',
'downloads.deleteDownload' => 'Delete download',
_ => null,
} ?? switch (path) {
'downloads.retryDownload' => 'Retry download',
'downloads.downloadQueued' => 'Download queued',
'downloads.downloadResumed' => 'Download resumed',
@@ -6115,6 +6148,8 @@ extension on Translations {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => 'Connection lost after ${attempts} attempts',
'companionRemote.errors.connectionLost' => 'Connection lost',
'videoSettings.playbackSpeed' => 'Playback Speed',
'videoSettings.normalSpeed' => 'Normal',
'videoSettings.sleepTimerActive' => ({required Object duration}) => 'Active (${duration})',
'videoSettings.zoom' => 'Zoom',
'videoSettings.sleepTimer' => 'Sleep Timer',
'videoSettings.audioSync' => 'Audio Sync',
+47 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsEs extends TranslationsSettingsEn {
@override String get creditsPattern => 'Patrón de marcador de créditos';
@override String get creditsPatternDescription => 'Expresión regular para reconocer marcadores de créditos en los títulos de capítulos';
@override String get invalidRegex => 'Expresión regular no válida';
@override String get regex => 'Expresión regular';
@override String get downloads => 'Descargas';
@override String get downloadLocationDescription => 'Elegir dónde almacenar el contenido descargado';
@override String get downloadLocationDefault => 'Predeterminado (Almacenamiento de la App)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysEs extends TranslationsHotkeysEn {
@override String get clearShortcut => 'Borrar atajo';
@override String get noShortcutSet => 'Sin atajo asignado';
@override String get currentShortcut => 'Atajo actual:';
@override String get pressToRecord => 'Seleccionar para grabar un atajo';
@override String get recordingShortcut => 'Presiona el atajo ahora';
@override late final _TranslationsHotkeysActionsEs actions = _TranslationsHotkeysActionsEs._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityEs extends TranslationsAccessibilityEn {
@override String get saturation => 'Saturación';
@override String get brightness => 'Brillo';
@override String get hexColor => 'Color hexadecimal';
@override String get expandText => 'Expandir texto';
@override String get collapseText => 'Contraer texto';
}
// Path: tooltips
@@ -693,6 +698,10 @@ class _TranslationsMessagesEs extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => 'Marcado como visto (se sincronizará al estar en línea)';
@override String get markedAsUnwatchedOffline => 'Marcado como no visto (se sincronizará al estar en línea)';
@override String autoRemovedWatchedDownload({required Object title}) => 'Eliminado automáticamente: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('es'))(n,
one: 'Se eliminó automáticamente ${n} descarga vista',
other: 'Se eliminaron automáticamente ${n} descargas vistas',
);
@override String get removedFromContinueWatching => 'Eliminado de Seguir Viendo';
@override String errorLoading({required Object error}) => 'Error: ${error}';
@override String get streamInterrupted => 'La reproducción se interrumpió. Pulsa reproducir o avanza para volver a intentarlo.';
@@ -751,6 +760,11 @@ class _TranslationsSubtitlingStylingEs extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => 'Color de Fondo';
@override String get position => 'Posición';
@override String get assOverride => 'Sobreescritura ASS';
@override String get overrideScale => 'Escalar';
@override String get overrideForce => 'Forzar';
@override String get overrideStrip => 'Quitar estilos';
@override String get positionTop => 'Arriba';
@override String get positionBottom => 'Abajo';
@override String get bold => 'Negrita';
@override String get italic => 'Cursiva';
@override String get renderResolution => 'Resolución de renderizado';
@@ -918,7 +932,7 @@ class _TranslationsErrorsEs extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => 'Error en la búsqueda: ${error}';
@override String connectionTimeout({required Object context}) => 'Tiempo de conexión agotado al cargar ${context}';
@override String get connectionFailed => 'No se puede conectar al servidor multimedia';
@override String failedToLoad({required Object context, required Object error}) => 'Error al cargar ${context}: ${error}';
@override String unableToLoad({required Object context}) => 'No se pudo cargar ${context}. Inténtalo de nuevo.';
@override String get noClientAvailable => 'No hay cliente disponible';
@override String get pleaseEnterToken => 'Por favor, introduce un token';
@override String get invalidToken => 'Token no válido';
@@ -1180,7 +1194,6 @@ class _TranslationsCollectionsEs extends TranslationsCollectionsEn {
@override String get deleted => 'Colección eliminada';
@override String get deleteFailed => 'Error al eliminar la colección';
@override String deleteFailedWithError({required Object error}) => 'Error al eliminar la colección: ${error}';
@override String failedToLoadItems({required Object error}) => 'Error al cargar los elementos de la colección: ${error}';
@override String get selectCollection => 'Seleccionar Colección';
@override String get collectionName => 'Nombre de la Colección';
@override String get enterCollectionName => 'Introduce el nombre de la colección';
@@ -1284,6 +1297,8 @@ class _TranslationsWatchTogetherEs extends TranslationsWatchTogetherEn {
@override String get hostingSession => 'Anfitrión de la Sesión';
@override String get inSession => 'En Sesión';
@override String get sessionCode => 'Código de Sesión';
@override String get openSessionControls => 'Abrir los controles de la sesión de Ver Juntos';
@override String get copySessionCode => 'Copiar código de sesión';
@override String get hostControlsPlayback => 'El anfitrión controla la reproducción';
@override String get anyoneCanControl => 'Cualquiera puede controlar la reproducción';
@override String get hostControls => 'Control del anfitrión';
@@ -1460,6 +1475,8 @@ class _TranslationsVideoSettingsEs extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => 'Velocidad de reproducción';
@override String get normalSpeed => 'Normal';
@override String sleepTimerActive({required Object duration}) => 'Activo (${duration})';
@override String get zoom => 'Zoom';
@override String get sleepTimer => 'Temporizador de apagado';
@override String get audioSync => 'Sincronización de audio';
@@ -1578,6 +1595,8 @@ class _TranslationsMetadataEditEs extends TranslationsMetadataEditEn {
@override String get artworkUpdated => 'Artwork actualizado';
@override String get artworkUpdateFailed => 'Error al actualizar el artwork';
@override String get noArtworkAvailable => 'No hay artwork disponible';
@override String artworkOption({required Object index}) => 'Opción de imagen ${index}';
@override String selectedArtworkOption({required Object index}) => 'Opción de imagen ${index}, seleccionada';
@override String get notSet => 'No establecido';
@override String get libraryDefault => 'Predeterminado de biblioteca';
@override String get accountDefault => 'Predeterminado de cuenta';
@@ -2055,6 +2074,7 @@ class _TranslationsServicesDeviceCodeEs extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => 'Activar Plezy en ${service}';
@override String body({required Object url}) => 'Visita ${url} e introduce este código:';
@override String openToActivate({required Object service}) => 'Abrir ${service} para activar';
@override String get copyCode => 'Copiar código de activación';
@override String get waitingForAuthorization => 'Esperando autorización…';
@override String get codeCopied => 'Código copiado';
}
@@ -2069,6 +2089,7 @@ class _TranslationsServicesOauthProxyEs extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => 'Inicia sesión en ${service}';
@override String get body => 'Escanea este código QR o abre la URL en cualquier dispositivo.';
@override String openToSignIn({required Object service}) => 'Abrir ${service} para iniciar sesión';
@override String get copyUrl => 'Copiar URL de inicio de sesión';
@override String get urlCopied => 'URL copiada';
}
@@ -2318,6 +2339,7 @@ extension on TranslationsEs {
'settings.creditsPattern' => 'Patrón de marcador de créditos',
'settings.creditsPatternDescription' => 'Expresión regular para reconocer marcadores de créditos en los títulos de capítulos',
'settings.invalidRegex' => 'Expresión regular no válida',
'settings.regex' => 'Expresión regular',
'settings.downloads' => 'Descargas',
'settings.downloadLocationDescription' => 'Elegir dónde almacenar el contenido descargado',
'settings.downloadLocationDefault' => 'Predeterminado (Almacenamiento de la App)',
@@ -2423,6 +2445,8 @@ extension on TranslationsEs {
'hotkeys.clearShortcut' => 'Borrar atajo',
'hotkeys.noShortcutSet' => 'Sin atajo asignado',
'hotkeys.currentShortcut' => 'Atajo actual:',
'hotkeys.pressToRecord' => 'Seleccionar para grabar un atajo',
'hotkeys.recordingShortcut' => 'Presiona el atajo ahora',
'hotkeys.actions.playPause' => 'Reproducir/Pausar',
'hotkeys.actions.volumeUp' => 'Subir Volumen',
'hotkeys.actions.volumeDown' => 'Bajar Volumen',
@@ -2512,6 +2536,8 @@ extension on TranslationsEs {
'accessibility.saturation' => 'Saturación',
'accessibility.brightness' => 'Brillo',
'accessibility.hexColor' => 'Color hexadecimal',
'accessibility.expandText' => 'Expandir texto',
'accessibility.collapseText' => 'Contraer texto',
'tooltips.shufflePlay' => 'Reproducción aleatoria',
'tooltips.playTrailer' => 'Reproducir tráiler',
'tooltips.markAsWatched' => 'Marcar como visto',
@@ -2602,19 +2628,20 @@ extension on TranslationsEs {
'messages.markedAsWatchedOffline' => 'Marcado como visto (se sincronizará al estar en línea)',
'messages.markedAsUnwatchedOffline' => 'Marcado como no visto (se sincronizará al estar en línea)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Eliminado automáticamente: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('es'))(n, one: 'Se eliminó automáticamente ${n} descarga vista', other: 'Se eliminaron automáticamente ${n} descargas vistas', ),
'messages.removedFromContinueWatching' => 'Eliminado de Seguir Viendo',
'messages.errorLoading' => ({required Object error}) => 'Error: ${error}',
'messages.streamInterrupted' => 'La reproducción se interrumpió. Pulsa reproducir o avanza para volver a intentarlo.',
'messages.liveStreamInterrupted' => 'La transmisión en vivo se interrumpió. Pulsa reproducir para volver a intentarlo.',
'messages.fileInfoNotAvailable' => 'Información de archivo no disponible',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Error al cargar info de archivo: ${error}',
'messages.errorLoadingSeries' => 'Error al cargar la serie',
'messages.musicNotSupported' => 'La reproducción de música aún no está soportada',
'messages.noDescriptionAvailable' => 'No hay descripción disponible',
'messages.noProfilesAvailable' => 'No hay perfiles disponibles',
'messages.contactAdminForProfiles' => 'Contacta a tu administrador del servidor para añadir perfiles',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => 'No se puede determinar la sección de biblioteca para este elemento',
'messages.logsCleared' => 'Logs borrados',
'messages.logsCopied' => 'Logs copiados al portapapeles',
@@ -2653,6 +2680,11 @@ extension on TranslationsEs {
'subtitlingStyling.backgroundColor' => 'Color de Fondo',
'subtitlingStyling.position' => 'Posición',
'subtitlingStyling.assOverride' => 'Sobreescritura ASS',
'subtitlingStyling.overrideScale' => 'Escalar',
'subtitlingStyling.overrideForce' => 'Forzar',
'subtitlingStyling.overrideStrip' => 'Quitar estilos',
'subtitlingStyling.positionTop' => 'Arriba',
'subtitlingStyling.positionBottom' => 'Abajo',
'subtitlingStyling.bold' => 'Negrita',
'subtitlingStyling.italic' => 'Cursiva',
'subtitlingStyling.renderResolution' => 'Resolución de renderizado',
@@ -2766,7 +2798,7 @@ extension on TranslationsEs {
'errors.searchFailed' => ({required Object error}) => 'Error en la búsqueda: ${error}',
'errors.connectionTimeout' => ({required Object context}) => 'Tiempo de conexión agotado al cargar ${context}',
'errors.connectionFailed' => 'No se puede conectar al servidor multimedia',
'errors.failedToLoad' => ({required Object context, required Object error}) => 'Error al cargar ${context}: ${error}',
'errors.unableToLoad' => ({required Object context}) => 'No se pudo cargar ${context}. Inténtalo de nuevo.',
'errors.noClientAvailable' => 'No hay cliente disponible',
'errors.pleaseEnterToken' => 'Por favor, introduce un token',
'errors.invalidToken' => 'Token no válido',
@@ -2987,7 +3019,6 @@ extension on TranslationsEs {
'collections.deleted' => 'Colección eliminada',
'collections.deleteFailed' => 'Error al eliminar la colección',
'collections.deleteFailedWithError' => ({required Object error}) => 'Error al eliminar la colección: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Error al cargar los elementos de la colección: ${error}',
'collections.selectCollection' => 'Seleccionar Colección',
'collections.collectionName' => 'Nombre de la Colección',
'collections.enterCollectionName' => 'Introduce el nombre de la colección',
@@ -3061,6 +3092,8 @@ extension on TranslationsEs {
'watchTogether.hostingSession' => 'Anfitrión de la Sesión',
'watchTogether.inSession' => 'En Sesión',
'watchTogether.sessionCode' => 'Código de Sesión',
'watchTogether.openSessionControls' => 'Abrir los controles de la sesión de Ver Juntos',
'watchTogether.copySessionCode' => 'Copiar código de sesión',
'watchTogether.hostControlsPlayback' => 'El anfitrión controla la reproducción',
'watchTogether.anyoneCanControl' => 'Cualquiera puede controlar la reproducción',
'watchTogether.hostControls' => 'Control del anfitrión',
@@ -3115,6 +3148,8 @@ extension on TranslationsEs {
'downloads.manage' => 'Gestionar',
'downloads.tvShows' => 'Series de TV',
'downloads.movies' => 'Películas',
_ => null,
} ?? switch (path) {
'downloads.music' => 'Música',
'downloads.tracksQueued' => ({required Object count}) => '${count} canciones en cola para descargar',
'downloads.noDownloads' => 'No hay descargas aún',
@@ -3127,8 +3162,6 @@ extension on TranslationsEs {
'downloads.serverErrorBitrate' => 'Error del servidor: el archivo puede superar el límite de bitrate remoto',
'downloads.episodesQueued' => ({required Object count}) => '${count} episodios en cola para descargar',
'downloads.downloadDeleted' => 'Descarga eliminada',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => '¿Eliminar "${title}" de este dispositivo?',
'downloads.cancelledDownloadTitle' => 'Descarga cancelada',
'downloads.cancelledDownloadMessage' => 'Esta descarga se canceló. ¿Qué quieres hacer?',
@@ -3260,6 +3293,8 @@ extension on TranslationsEs {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => 'Conexión perdida tras ${attempts} intentos',
'companionRemote.errors.connectionLost' => 'Conexión perdida',
'videoSettings.playbackSpeed' => 'Velocidad de reproducción',
'videoSettings.normalSpeed' => 'Normal',
'videoSettings.sleepTimerActive' => ({required Object duration}) => 'Activo (${duration})',
'videoSettings.zoom' => 'Zoom',
'videoSettings.sleepTimer' => 'Temporizador de apagado',
'videoSettings.audioSync' => 'Sincronización de audio',
@@ -3351,6 +3386,8 @@ extension on TranslationsEs {
'metadataEdit.artworkUpdated' => 'Artwork actualizado',
'metadataEdit.artworkUpdateFailed' => 'Error al actualizar el artwork',
'metadataEdit.noArtworkAvailable' => 'No hay artwork disponible',
'metadataEdit.artworkOption' => ({required Object index}) => 'Opción de imagen ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => 'Opción de imagen ${index}, seleccionada',
'metadataEdit.notSet' => 'No establecido',
'metadataEdit.libraryDefault' => 'Predeterminado de biblioteca',
'metadataEdit.accountDefault' => 'Predeterminado de cuenta',
@@ -3465,11 +3502,13 @@ extension on TranslationsEs {
'services.deviceCode.title' => ({required Object service}) => 'Activar Plezy en ${service}',
'services.deviceCode.body' => ({required Object url}) => 'Visita ${url} e introduce este código:',
'services.deviceCode.openToActivate' => ({required Object service}) => 'Abrir ${service} para activar',
'services.deviceCode.copyCode' => 'Copiar código de activación',
'services.deviceCode.waitingForAuthorization' => 'Esperando autorización…',
'services.deviceCode.codeCopied' => 'Código copiado',
'services.oauthProxy.title' => ({required Object service}) => 'Inicia sesión en ${service}',
'services.oauthProxy.body' => 'Escanea este código QR o abre la URL en cualquier dispositivo.',
'services.oauthProxy.openToSignIn' => ({required Object service}) => 'Abrir ${service} para iniciar sesión',
'services.oauthProxy.copyUrl' => 'Copiar URL de inicio de sesión',
'services.oauthProxy.urlCopied' => 'URL copiada',
'services.libraryFilter.title' => 'Filtro de bibliotecas',
'services.libraryFilter.subtitleAllSyncing' => 'Sincronizando todas las bibliotecas',
+47 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsFr extends TranslationsSettingsEn {
@override String get creditsPattern => 'Modèle de marqueur de générique';
@override String get creditsPatternDescription => 'Expression régulière pour reconnaître les marqueurs de générique dans les titres de chapitres';
@override String get invalidRegex => 'Expression régulière invalide';
@override String get regex => 'Expression régulière';
@override String get downloads => 'Téléchargement';
@override String get downloadLocationDescription => 'Choisissez où stocker le contenu téléchargé';
@override String get downloadLocationDefault => 'Par défaut (stockage de l\'application)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysFr extends TranslationsHotkeysEn {
@override String get clearShortcut => 'Effacer le raccourci';
@override String get noShortcutSet => 'Aucun raccourci défini';
@override String get currentShortcut => 'Raccourci actuel :';
@override String get pressToRecord => 'Sélectionner pour enregistrer un raccourci';
@override String get recordingShortcut => 'Appuyez maintenant sur le raccourci';
@override late final _TranslationsHotkeysActionsFr actions = _TranslationsHotkeysActionsFr._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityFr extends TranslationsAccessibilityEn {
@override String get saturation => 'Saturation';
@override String get brightness => 'Luminosité';
@override String get hexColor => 'Couleur hexadécimale';
@override String get expandText => 'Développer le texte';
@override String get collapseText => 'Replier le texte';
}
// Path: tooltips
@@ -693,6 +698,10 @@ class _TranslationsMessagesFr extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => 'Marqué comme vu (se synchronisera lorsque vous serez en ligne)';
@override String get markedAsUnwatchedOffline => 'Marqué comme non vu (sera synchronisé lorsque vous serez en ligne)';
@override String autoRemovedWatchedDownload({required Object title}) => 'Supprimé automatiquement : ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('fr'))(n,
one: '${n} téléchargement vu supprimé automatiquement',
other: '${n} téléchargements vus supprimés automatiquement',
);
@override String get removedFromContinueWatching => 'Supprimer de "Continuer à regarder"';
@override String errorLoading({required Object error}) => 'Erreur: ${error}';
@override String get streamInterrupted => 'La lecture a été interrompue. Appuyez sur Lecture ou avancez pour réessayer.';
@@ -751,6 +760,11 @@ class _TranslationsSubtitlingStylingFr extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => 'Couleur d\'arrière-plan';
@override String get position => 'Position';
@override String get assOverride => 'Remplacement ASS';
@override String get overrideScale => 'Mettre à l’échelle';
@override String get overrideForce => 'Forcer';
@override String get overrideStrip => 'Supprimer le style';
@override String get positionTop => 'Haut';
@override String get positionBottom => 'Bas';
@override String get bold => 'Gras';
@override String get italic => 'Italique';
@override String get renderResolution => 'Résolution de rendu';
@@ -918,7 +932,7 @@ class _TranslationsErrorsFr extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => 'Recherche échouée: ${error}';
@override String connectionTimeout({required Object context}) => 'Délai d\'attente de connexion dépassé pendant le chargement ${context}';
@override String get connectionFailed => 'Impossible de se connecter au serveur multimédia';
@override String failedToLoad({required Object context, required Object error}) => 'Échec du chargement ${context}: ${error}';
@override String unableToLoad({required Object context}) => 'Impossible de charger ${context}. Réessayez.';
@override String get noClientAvailable => 'Aucun client disponible';
@override String get pleaseEnterToken => 'Veuillez saisir un token';
@override String get invalidToken => 'Token invalide';
@@ -1180,7 +1194,6 @@ class _TranslationsCollectionsFr extends TranslationsCollectionsEn {
@override String get deleted => 'Collection supprimée';
@override String get deleteFailed => 'Échec de la suppression de la collection';
@override String deleteFailedWithError({required Object error}) => 'Échec de la suppression de la collection: ${error}';
@override String failedToLoadItems({required Object error}) => 'Échec du chargement des éléments de la collection: ${error}';
@override String get selectCollection => 'Sélectionner une collection';
@override String get collectionName => 'Nom de la collection';
@override String get enterCollectionName => 'Entrez le nom de la collection';
@@ -1284,6 +1297,8 @@ class _TranslationsWatchTogetherFr extends TranslationsWatchTogetherEn {
@override String get hostingSession => 'Session d\'hébergement';
@override String get inSession => 'En session';
@override String get sessionCode => 'Code de session';
@override String get openSessionControls => 'Ouvrir les commandes de la session Regarder ensemble';
@override String get copySessionCode => 'Copier le code de session';
@override String get hostControlsPlayback => 'L\'hôte contrôle la lecture';
@override String get anyoneCanControl => 'Tout le monde peut contrôler la lecture';
@override String get hostControls => 'Commandes de l\'hôte';
@@ -1460,6 +1475,8 @@ class _TranslationsVideoSettingsFr extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => 'Vitesse de lecture';
@override String get normalSpeed => 'Normale';
@override String sleepTimerActive({required Object duration}) => 'Actif (${duration})';
@override String get zoom => 'Zoom';
@override String get sleepTimer => 'Minuterie de mise en veille';
@override String get audioSync => 'Synchronisation audio';
@@ -1578,6 +1595,8 @@ class _TranslationsMetadataEditFr extends TranslationsMetadataEditEn {
@override String get artworkUpdated => 'Artwork mis à jour';
@override String get artworkUpdateFailed => 'Échec de la mise à jour de l\'artwork';
@override String get noArtworkAvailable => 'Aucun artwork disponible';
@override String artworkOption({required Object index}) => 'Option d\'illustration ${index}';
@override String selectedArtworkOption({required Object index}) => 'Option d\'illustration ${index}, sélectionnée';
@override String get notSet => 'Non défini';
@override String get libraryDefault => 'Par défaut de la bibliothèque';
@override String get accountDefault => 'Par défaut du compte';
@@ -2055,6 +2074,7 @@ class _TranslationsServicesDeviceCodeFr extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => 'Activer Plezy sur ${service}';
@override String body({required Object url}) => 'Rendez-vous sur ${url} et entrez ce code :';
@override String openToActivate({required Object service}) => 'Ouvrir ${service} pour activer';
@override String get copyCode => 'Copier le code d\'activation';
@override String get waitingForAuthorization => 'En attente d\'autorisation…';
@override String get codeCopied => 'Code copié';
}
@@ -2069,6 +2089,7 @@ class _TranslationsServicesOauthProxyFr extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => 'Se connecter à ${service}';
@override String get body => 'Scannez ce code QR ou ouvrez l\'URL sur n\'importe quel appareil.';
@override String openToSignIn({required Object service}) => 'Ouvrir ${service} pour se connecter';
@override String get copyUrl => 'Copier l\'URL de connexion';
@override String get urlCopied => 'URL copiée';
}
@@ -2318,6 +2339,7 @@ extension on TranslationsFr {
'settings.creditsPattern' => 'Modèle de marqueur de générique',
'settings.creditsPatternDescription' => 'Expression régulière pour reconnaître les marqueurs de générique dans les titres de chapitres',
'settings.invalidRegex' => 'Expression régulière invalide',
'settings.regex' => 'Expression régulière',
'settings.downloads' => 'Téléchargement',
'settings.downloadLocationDescription' => 'Choisissez où stocker le contenu téléchargé',
'settings.downloadLocationDefault' => 'Par défaut (stockage de l\'application)',
@@ -2423,6 +2445,8 @@ extension on TranslationsFr {
'hotkeys.clearShortcut' => 'Effacer le raccourci',
'hotkeys.noShortcutSet' => 'Aucun raccourci défini',
'hotkeys.currentShortcut' => 'Raccourci actuel :',
'hotkeys.pressToRecord' => 'Sélectionner pour enregistrer un raccourci',
'hotkeys.recordingShortcut' => 'Appuyez maintenant sur le raccourci',
'hotkeys.actions.playPause' => 'Lecture/Pause',
'hotkeys.actions.volumeUp' => 'Augmenter le volume',
'hotkeys.actions.volumeDown' => 'Baisser le volume',
@@ -2512,6 +2536,8 @@ extension on TranslationsFr {
'accessibility.saturation' => 'Saturation',
'accessibility.brightness' => 'Luminosité',
'accessibility.hexColor' => 'Couleur hexadécimale',
'accessibility.expandText' => 'Développer le texte',
'accessibility.collapseText' => 'Replier le texte',
'tooltips.shufflePlay' => 'Lecture aléatoire',
'tooltips.playTrailer' => 'Lire la bande-annonce',
'tooltips.markAsWatched' => 'Marqué comme vu',
@@ -2602,19 +2628,20 @@ extension on TranslationsFr {
'messages.markedAsWatchedOffline' => 'Marqué comme vu (se synchronisera lorsque vous serez en ligne)',
'messages.markedAsUnwatchedOffline' => 'Marqué comme non vu (sera synchronisé lorsque vous serez en ligne)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Supprimé automatiquement : ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('fr'))(n, one: '${n} téléchargement vu supprimé automatiquement', other: '${n} téléchargements vus supprimés automatiquement', ),
'messages.removedFromContinueWatching' => 'Supprimer de "Continuer à regarder"',
'messages.errorLoading' => ({required Object error}) => 'Erreur: ${error}',
'messages.streamInterrupted' => 'La lecture a été interrompue. Appuyez sur Lecture ou avancez pour réessayer.',
'messages.liveStreamInterrupted' => 'Le direct a été interrompu. Appuyez sur Lecture pour réessayer.',
'messages.fileInfoNotAvailable' => 'Informations sur le fichier non disponibles',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Erreur lors du chargement des informations sur le fichier: ${error}',
'messages.errorLoadingSeries' => 'Erreur lors du chargement de la série',
'messages.musicNotSupported' => 'La lecture de musique n\'est pas encore prise en charge',
'messages.noDescriptionAvailable' => 'Aucune description disponible',
'messages.noProfilesAvailable' => 'Aucun profil disponible',
'messages.contactAdminForProfiles' => 'Contactez votre administrateur serveur pour ajouter des profils',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => 'Impossible de déterminer la section de la bibliothèque pour cet élément',
'messages.logsCleared' => 'Logs effacés',
'messages.logsCopied' => 'Logs copiés dans le presse-papier',
@@ -2653,6 +2680,11 @@ extension on TranslationsFr {
'subtitlingStyling.backgroundColor' => 'Couleur d\'arrière-plan',
'subtitlingStyling.position' => 'Position',
'subtitlingStyling.assOverride' => 'Remplacement ASS',
'subtitlingStyling.overrideScale' => 'Mettre à l’échelle',
'subtitlingStyling.overrideForce' => 'Forcer',
'subtitlingStyling.overrideStrip' => 'Supprimer le style',
'subtitlingStyling.positionTop' => 'Haut',
'subtitlingStyling.positionBottom' => 'Bas',
'subtitlingStyling.bold' => 'Gras',
'subtitlingStyling.italic' => 'Italique',
'subtitlingStyling.renderResolution' => 'Résolution de rendu',
@@ -2766,7 +2798,7 @@ extension on TranslationsFr {
'errors.searchFailed' => ({required Object error}) => 'Recherche échouée: ${error}',
'errors.connectionTimeout' => ({required Object context}) => 'Délai d\'attente de connexion dépassé pendant le chargement ${context}',
'errors.connectionFailed' => 'Impossible de se connecter au serveur multimédia',
'errors.failedToLoad' => ({required Object context, required Object error}) => 'Échec du chargement ${context}: ${error}',
'errors.unableToLoad' => ({required Object context}) => 'Impossible de charger ${context}. Réessayez.',
'errors.noClientAvailable' => 'Aucun client disponible',
'errors.pleaseEnterToken' => 'Veuillez saisir un token',
'errors.invalidToken' => 'Token invalide',
@@ -2987,7 +3019,6 @@ extension on TranslationsFr {
'collections.deleted' => 'Collection supprimée',
'collections.deleteFailed' => 'Échec de la suppression de la collection',
'collections.deleteFailedWithError' => ({required Object error}) => 'Échec de la suppression de la collection: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Échec du chargement des éléments de la collection: ${error}',
'collections.selectCollection' => 'Sélectionner une collection',
'collections.collectionName' => 'Nom de la collection',
'collections.enterCollectionName' => 'Entrez le nom de la collection',
@@ -3061,6 +3092,8 @@ extension on TranslationsFr {
'watchTogether.hostingSession' => 'Session d\'hébergement',
'watchTogether.inSession' => 'En session',
'watchTogether.sessionCode' => 'Code de session',
'watchTogether.openSessionControls' => 'Ouvrir les commandes de la session Regarder ensemble',
'watchTogether.copySessionCode' => 'Copier le code de session',
'watchTogether.hostControlsPlayback' => 'L\'hôte contrôle la lecture',
'watchTogether.anyoneCanControl' => 'Tout le monde peut contrôler la lecture',
'watchTogether.hostControls' => 'Commandes de l\'hôte',
@@ -3115,6 +3148,8 @@ extension on TranslationsFr {
'downloads.manage' => 'Gérer',
'downloads.tvShows' => 'Show TV',
'downloads.movies' => 'Films',
_ => null,
} ?? switch (path) {
'downloads.music' => 'Musique',
'downloads.tracksQueued' => ({required Object count}) => '${count} titres en file d\'attente de téléchargement',
'downloads.noDownloads' => 'Aucun téléchargement pour le moment',
@@ -3127,8 +3162,6 @@ extension on TranslationsFr {
'downloads.serverErrorBitrate' => 'Erreur serveur : le fichier peut dépasser la limite de bitrate distant',
'downloads.episodesQueued' => ({required Object count}) => '${count} épisodes en attente de téléchargement',
'downloads.downloadDeleted' => 'Télécharger supprimé',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => 'Supprimer "${title}" de cet appareil ?',
'downloads.cancelledDownloadTitle' => 'Téléchargement annulé',
'downloads.cancelledDownloadMessage' => 'Ce téléchargement a été annulé. Que voulez-vous faire ?',
@@ -3260,6 +3293,8 @@ extension on TranslationsFr {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => 'Connexion perdue après ${attempts} tentatives',
'companionRemote.errors.connectionLost' => 'Connexion perdue',
'videoSettings.playbackSpeed' => 'Vitesse de lecture',
'videoSettings.normalSpeed' => 'Normale',
'videoSettings.sleepTimerActive' => ({required Object duration}) => 'Actif (${duration})',
'videoSettings.zoom' => 'Zoom',
'videoSettings.sleepTimer' => 'Minuterie de mise en veille',
'videoSettings.audioSync' => 'Synchronisation audio',
@@ -3351,6 +3386,8 @@ extension on TranslationsFr {
'metadataEdit.artworkUpdated' => 'Artwork mis à jour',
'metadataEdit.artworkUpdateFailed' => 'Échec de la mise à jour de l\'artwork',
'metadataEdit.noArtworkAvailable' => 'Aucun artwork disponible',
'metadataEdit.artworkOption' => ({required Object index}) => 'Option d\'illustration ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => 'Option d\'illustration ${index}, sélectionnée',
'metadataEdit.notSet' => 'Non défini',
'metadataEdit.libraryDefault' => 'Par défaut de la bibliothèque',
'metadataEdit.accountDefault' => 'Par défaut du compte',
@@ -3465,11 +3502,13 @@ extension on TranslationsFr {
'services.deviceCode.title' => ({required Object service}) => 'Activer Plezy sur ${service}',
'services.deviceCode.body' => ({required Object url}) => 'Rendez-vous sur ${url} et entrez ce code :',
'services.deviceCode.openToActivate' => ({required Object service}) => 'Ouvrir ${service} pour activer',
'services.deviceCode.copyCode' => 'Copier le code d\'activation',
'services.deviceCode.waitingForAuthorization' => 'En attente d\'autorisation…',
'services.deviceCode.codeCopied' => 'Code copié',
'services.oauthProxy.title' => ({required Object service}) => 'Se connecter à ${service}',
'services.oauthProxy.body' => 'Scannez ce code QR ou ouvrez l\'URL sur n\'importe quel appareil.',
'services.oauthProxy.openToSignIn' => ({required Object service}) => 'Ouvrir ${service} pour se connecter',
'services.oauthProxy.copyUrl' => 'Copier l\'URL de connexion',
'services.oauthProxy.urlCopied' => 'URL copiée',
'services.libraryFilter.title' => 'Filtre de bibliothèques',
'services.libraryFilter.subtitleAllSyncing' => 'Synchronisation de toutes les bibliothèques',
+47 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsIt extends TranslationsSettingsEn {
@override String get creditsPattern => 'Pattern marcatore titoli di coda';
@override String get creditsPatternDescription => 'Espressione regolare per riconoscere i marcatori dei titoli di coda nei capitoli';
@override String get invalidRegex => 'Espressione regolare non valida';
@override String get regex => 'Espressione regolare';
@override String get downloads => 'Download';
@override String get downloadLocationDescription => 'Scegli dove salvare i contenuti scaricati';
@override String get downloadLocationDefault => 'Predefinita (Archiviazione App)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysIt extends TranslationsHotkeysEn {
@override String get clearShortcut => 'Elimina scorciatoia';
@override String get noShortcutSet => 'Nessuna scorciatoia impostata';
@override String get currentShortcut => 'Scorciatoia attuale:';
@override String get pressToRecord => 'Seleziona per registrare una scorciatoia';
@override String get recordingShortcut => 'Premi ora la scorciatoia';
@override late final _TranslationsHotkeysActionsIt actions = _TranslationsHotkeysActionsIt._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityIt extends TranslationsAccessibilityEn {
@override String get saturation => 'Saturazione';
@override String get brightness => 'Luminosità';
@override String get hexColor => 'Colore esadecimale';
@override String get expandText => 'Espandi il testo';
@override String get collapseText => 'Comprimi il testo';
}
// Path: tooltips
@@ -693,6 +698,10 @@ class _TranslationsMessagesIt extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => 'Segnato come visto (sincronizzato online)';
@override String get markedAsUnwatchedOffline => 'Segnato come non visto (sincronizzato online)';
@override String autoRemovedWatchedDownload({required Object title}) => 'Rimosso automaticamente: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('it'))(n,
one: 'Rimosso automaticamente ${n} download già visto',
other: 'Rimossi automaticamente ${n} download già visti',
);
@override String get removedFromContinueWatching => 'Rimosso da Continua a guardare';
@override String errorLoading({required Object error}) => 'Errore: ${error}';
@override String get streamInterrupted => 'La riproduzione si è interrotta. Premi Riproduci o scorri per riprovare.';
@@ -751,6 +760,11 @@ class _TranslationsSubtitlingStylingIt extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => 'Colore sfondo';
@override String get position => 'Posizione';
@override String get assOverride => 'Sovrascrittura ASS';
@override String get overrideScale => 'Ridimensiona';
@override String get overrideForce => 'Forza';
@override String get overrideStrip => 'Rimuovi stile';
@override String get positionTop => 'In alto';
@override String get positionBottom => 'In basso';
@override String get bold => 'Grassetto';
@override String get italic => 'Corsivo';
@override String get renderResolution => 'Risoluzione di rendering';
@@ -918,7 +932,7 @@ class _TranslationsErrorsIt extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => 'Ricerca fallita: ${error}';
@override String connectionTimeout({required Object context}) => 'Timeout connessione durante caricamento di ${context}';
@override String get connectionFailed => 'Impossibile connettersi al server multimediale';
@override String failedToLoad({required Object context, required Object error}) => 'Impossibile caricare ${context}: ${error}';
@override String unableToLoad({required Object context}) => 'Impossibile caricare ${context}. Riprova.';
@override String get noClientAvailable => 'Nessun client disponibile';
@override String get pleaseEnterToken => 'Inserisci token';
@override String get invalidToken => 'Token non valido';
@@ -1180,7 +1194,6 @@ class _TranslationsCollectionsIt extends TranslationsCollectionsEn {
@override String get deleted => 'Raccolta eliminata';
@override String get deleteFailed => 'Impossibile eliminare la raccolta';
@override String deleteFailedWithError({required Object error}) => 'Impossibile eliminare la raccolta: ${error}';
@override String failedToLoadItems({required Object error}) => 'Impossibile caricare gli elementi della raccolta: ${error}';
@override String get selectCollection => 'Seleziona raccolta';
@override String get collectionName => 'Nome raccolta';
@override String get enterCollectionName => 'Inserisci nome raccolta';
@@ -1284,6 +1297,8 @@ class _TranslationsWatchTogetherIt extends TranslationsWatchTogetherEn {
@override String get hostingSession => 'Hosting Sessione';
@override String get inSession => 'In Sessione';
@override String get sessionCode => 'Codice Sessione';
@override String get openSessionControls => 'Apri i controlli della sessione di Guarda Insieme';
@override String get copySessionCode => 'Copia il codice della sessione';
@override String get hostControlsPlayback => 'L\'host controlla la riproduzione';
@override String get anyoneCanControl => 'Tutti possono controllare la riproduzione';
@override String get hostControls => 'Controllo host';
@@ -1460,6 +1475,8 @@ class _TranslationsVideoSettingsIt extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => 'Velocità di riproduzione';
@override String get normalSpeed => 'Normale';
@override String sleepTimerActive({required Object duration}) => 'Attivo (${duration})';
@override String get zoom => 'Zoom';
@override String get sleepTimer => 'Timer di spegnimento';
@override String get audioSync => 'Sincronizzazione audio';
@@ -1578,6 +1595,8 @@ class _TranslationsMetadataEditIt extends TranslationsMetadataEditEn {
@override String get artworkUpdated => 'Artwork aggiornato';
@override String get artworkUpdateFailed => 'Aggiornamento artwork non riuscito';
@override String get noArtworkAvailable => 'Nessun artwork disponibile';
@override String artworkOption({required Object index}) => 'Opzione artwork ${index}';
@override String selectedArtworkOption({required Object index}) => 'Opzione artwork ${index}, selezionata';
@override String get notSet => 'Non impostato';
@override String get libraryDefault => 'Predefinito libreria';
@override String get accountDefault => 'Predefinito account';
@@ -2055,6 +2074,7 @@ class _TranslationsServicesDeviceCodeIt extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => 'Attiva Plezy su ${service}';
@override String body({required Object url}) => 'Visita ${url} e inserisci questo codice:';
@override String openToActivate({required Object service}) => 'Apri ${service} per attivare';
@override String get copyCode => 'Copia il codice di attivazione';
@override String get waitingForAuthorization => 'In attesa di autorizzazione…';
@override String get codeCopied => 'Codice copiato';
}
@@ -2069,6 +2089,7 @@ class _TranslationsServicesOauthProxyIt extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => 'Accedi a ${service}';
@override String get body => 'Scansiona questo codice QR o apri l\'URL su qualsiasi dispositivo.';
@override String openToSignIn({required Object service}) => 'Apri ${service} per accedere';
@override String get copyUrl => 'Copia l\'URL di accesso';
@override String get urlCopied => 'URL copiato';
}
@@ -2318,6 +2339,7 @@ extension on TranslationsIt {
'settings.creditsPattern' => 'Pattern marcatore titoli di coda',
'settings.creditsPatternDescription' => 'Espressione regolare per riconoscere i marcatori dei titoli di coda nei capitoli',
'settings.invalidRegex' => 'Espressione regolare non valida',
'settings.regex' => 'Espressione regolare',
'settings.downloads' => 'Download',
'settings.downloadLocationDescription' => 'Scegli dove salvare i contenuti scaricati',
'settings.downloadLocationDefault' => 'Predefinita (Archiviazione App)',
@@ -2423,6 +2445,8 @@ extension on TranslationsIt {
'hotkeys.clearShortcut' => 'Elimina scorciatoia',
'hotkeys.noShortcutSet' => 'Nessuna scorciatoia impostata',
'hotkeys.currentShortcut' => 'Scorciatoia attuale:',
'hotkeys.pressToRecord' => 'Seleziona per registrare una scorciatoia',
'hotkeys.recordingShortcut' => 'Premi ora la scorciatoia',
'hotkeys.actions.playPause' => 'Riproduci/Pausa',
'hotkeys.actions.volumeUp' => 'Alza volume',
'hotkeys.actions.volumeDown' => 'Abbassa volume',
@@ -2512,6 +2536,8 @@ extension on TranslationsIt {
'accessibility.saturation' => 'Saturazione',
'accessibility.brightness' => 'Luminosità',
'accessibility.hexColor' => 'Colore esadecimale',
'accessibility.expandText' => 'Espandi il testo',
'accessibility.collapseText' => 'Comprimi il testo',
'tooltips.shufflePlay' => 'Riproduzione casuale',
'tooltips.playTrailer' => 'Riproduci trailer',
'tooltips.markAsWatched' => 'Segna come visto',
@@ -2602,19 +2628,20 @@ extension on TranslationsIt {
'messages.markedAsWatchedOffline' => 'Segnato come visto (sincronizzato online)',
'messages.markedAsUnwatchedOffline' => 'Segnato come non visto (sincronizzato online)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Rimosso automaticamente: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('it'))(n, one: 'Rimosso automaticamente ${n} download già visto', other: 'Rimossi automaticamente ${n} download già visti', ),
'messages.removedFromContinueWatching' => 'Rimosso da Continua a guardare',
'messages.errorLoading' => ({required Object error}) => 'Errore: ${error}',
'messages.streamInterrupted' => 'La riproduzione si è interrotta. Premi Riproduci o scorri per riprovare.',
'messages.liveStreamInterrupted' => 'La diretta si è interrotta. Premi Riproduci per riprovare.',
'messages.fileInfoNotAvailable' => 'Informazioni sul file non disponibili',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Errore caricamento informazioni sul file: ${error}',
'messages.errorLoadingSeries' => 'Errore caricamento serie',
'messages.musicNotSupported' => 'La riproduzione musicale non è ancora supportata',
'messages.noDescriptionAvailable' => 'Nessuna descrizione disponibile',
'messages.noProfilesAvailable' => 'Nessun profilo disponibile',
'messages.contactAdminForProfiles' => 'Contatta l\'amministratore del server per aggiungere profili',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => 'Impossibile determinare la sezione della libreria per questo elemento',
'messages.logsCleared' => 'Log eliminati',
'messages.logsCopied' => 'Log copiati negli appunti',
@@ -2653,6 +2680,11 @@ extension on TranslationsIt {
'subtitlingStyling.backgroundColor' => 'Colore sfondo',
'subtitlingStyling.position' => 'Posizione',
'subtitlingStyling.assOverride' => 'Sovrascrittura ASS',
'subtitlingStyling.overrideScale' => 'Ridimensiona',
'subtitlingStyling.overrideForce' => 'Forza',
'subtitlingStyling.overrideStrip' => 'Rimuovi stile',
'subtitlingStyling.positionTop' => 'In alto',
'subtitlingStyling.positionBottom' => 'In basso',
'subtitlingStyling.bold' => 'Grassetto',
'subtitlingStyling.italic' => 'Corsivo',
'subtitlingStyling.renderResolution' => 'Risoluzione di rendering',
@@ -2766,7 +2798,7 @@ extension on TranslationsIt {
'errors.searchFailed' => ({required Object error}) => 'Ricerca fallita: ${error}',
'errors.connectionTimeout' => ({required Object context}) => 'Timeout connessione durante caricamento di ${context}',
'errors.connectionFailed' => 'Impossibile connettersi al server multimediale',
'errors.failedToLoad' => ({required Object context, required Object error}) => 'Impossibile caricare ${context}: ${error}',
'errors.unableToLoad' => ({required Object context}) => 'Impossibile caricare ${context}. Riprova.',
'errors.noClientAvailable' => 'Nessun client disponibile',
'errors.pleaseEnterToken' => 'Inserisci token',
'errors.invalidToken' => 'Token non valido',
@@ -2987,7 +3019,6 @@ extension on TranslationsIt {
'collections.deleted' => 'Raccolta eliminata',
'collections.deleteFailed' => 'Impossibile eliminare la raccolta',
'collections.deleteFailedWithError' => ({required Object error}) => 'Impossibile eliminare la raccolta: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Impossibile caricare gli elementi della raccolta: ${error}',
'collections.selectCollection' => 'Seleziona raccolta',
'collections.collectionName' => 'Nome raccolta',
'collections.enterCollectionName' => 'Inserisci nome raccolta',
@@ -3061,6 +3092,8 @@ extension on TranslationsIt {
'watchTogether.hostingSession' => 'Hosting Sessione',
'watchTogether.inSession' => 'In Sessione',
'watchTogether.sessionCode' => 'Codice Sessione',
'watchTogether.openSessionControls' => 'Apri i controlli della sessione di Guarda Insieme',
'watchTogether.copySessionCode' => 'Copia il codice della sessione',
'watchTogether.hostControlsPlayback' => 'L\'host controlla la riproduzione',
'watchTogether.anyoneCanControl' => 'Tutti possono controllare la riproduzione',
'watchTogether.hostControls' => 'Controllo host',
@@ -3115,6 +3148,8 @@ extension on TranslationsIt {
'downloads.manage' => 'Gestisci',
'downloads.tvShows' => 'Serie TV',
'downloads.movies' => 'Film',
_ => null,
} ?? switch (path) {
'downloads.music' => 'Musica',
'downloads.tracksQueued' => ({required Object count}) => '${count} brani in coda per il download',
'downloads.noDownloads' => 'Nessun download',
@@ -3127,8 +3162,6 @@ extension on TranslationsIt {
'downloads.serverErrorBitrate' => 'Errore server: il file può superare il limite di bitrate remoto',
'downloads.episodesQueued' => ({required Object count}) => '${count} episodi in coda per il download',
'downloads.downloadDeleted' => 'Download eliminato',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => 'Eliminare "${title}" da questo dispositivo?',
'downloads.cancelledDownloadTitle' => 'Download annullato',
'downloads.cancelledDownloadMessage' => 'Questo download è stato annullato. Cosa vuoi fare?',
@@ -3260,6 +3293,8 @@ extension on TranslationsIt {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => 'Connessione persa dopo ${attempts} tentativi',
'companionRemote.errors.connectionLost' => 'Connessione persa',
'videoSettings.playbackSpeed' => 'Velocità di riproduzione',
'videoSettings.normalSpeed' => 'Normale',
'videoSettings.sleepTimerActive' => ({required Object duration}) => 'Attivo (${duration})',
'videoSettings.zoom' => 'Zoom',
'videoSettings.sleepTimer' => 'Timer di spegnimento',
'videoSettings.audioSync' => 'Sincronizzazione audio',
@@ -3351,6 +3386,8 @@ extension on TranslationsIt {
'metadataEdit.artworkUpdated' => 'Artwork aggiornato',
'metadataEdit.artworkUpdateFailed' => 'Aggiornamento artwork non riuscito',
'metadataEdit.noArtworkAvailable' => 'Nessun artwork disponibile',
'metadataEdit.artworkOption' => ({required Object index}) => 'Opzione artwork ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => 'Opzione artwork ${index}, selezionata',
'metadataEdit.notSet' => 'Non impostato',
'metadataEdit.libraryDefault' => 'Predefinito libreria',
'metadataEdit.accountDefault' => 'Predefinito account',
@@ -3465,11 +3502,13 @@ extension on TranslationsIt {
'services.deviceCode.title' => ({required Object service}) => 'Attiva Plezy su ${service}',
'services.deviceCode.body' => ({required Object url}) => 'Visita ${url} e inserisci questo codice:',
'services.deviceCode.openToActivate' => ({required Object service}) => 'Apri ${service} per attivare',
'services.deviceCode.copyCode' => 'Copia il codice di attivazione',
'services.deviceCode.waitingForAuthorization' => 'In attesa di autorizzazione…',
'services.deviceCode.codeCopied' => 'Codice copiato',
'services.oauthProxy.title' => ({required Object service}) => 'Accedi a ${service}',
'services.oauthProxy.body' => 'Scansiona questo codice QR o apri l\'URL su qualsiasi dispositivo.',
'services.oauthProxy.openToSignIn' => ({required Object service}) => 'Apri ${service} per accedere',
'services.oauthProxy.copyUrl' => 'Copia l\'URL di accesso',
'services.oauthProxy.urlCopied' => 'URL copiato',
'services.libraryFilter.title' => 'Filtro librerie',
'services.libraryFilter.subtitleAllSyncing' => 'Sincronizzazione di tutte le librerie',
+46 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsJa extends TranslationsSettingsEn {
@override String get creditsPattern => 'クレジットマーカーパターン';
@override String get creditsPatternDescription => 'チャプタータイトルのクレジットマーカーに一致する正規表現パターン';
@override String get invalidRegex => '無効な正規表現';
@override String get regex => '正規表現';
@override String get downloads => 'ダウンロード';
@override String get downloadLocationDescription => 'ダウンロードコンテンツの保存場所を選択';
@override String get downloadLocationDefault => 'デフォルト(アプリストレージ)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysJa extends TranslationsHotkeysEn {
@override String get clearShortcut => 'ショートカットをクリア';
@override String get noShortcutSet => 'ショートカット未設定';
@override String get currentShortcut => '現在のショートカット:';
@override String get pressToRecord => '選択してショートカットを記録';
@override String get recordingShortcut => 'ショートカットを押してください';
@override late final _TranslationsHotkeysActionsJa actions = _TranslationsHotkeysActionsJa._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityJa extends TranslationsAccessibilityEn {
@override String get saturation => '彩度';
@override String get brightness => '明るさ';
@override String get hexColor => '16進カラー';
@override String get expandText => 'テキストを展開';
@override String get collapseText => 'テキストを折りたたむ';
}
// Path: tooltips
@@ -693,6 +698,9 @@ class _TranslationsMessagesJa extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => '視聴済みにしました(オンライン時に同期)';
@override String get markedAsUnwatchedOffline => '未視聴にしました(オンライン時に同期)';
@override String autoRemovedWatchedDownload({required Object title}) => '自動削除: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ja'))(n,
other: '視聴済みダウンロードを${n}件自動削除しました',
);
@override String get removedFromContinueWatching => '視聴中から削除しました';
@override String errorLoading({required Object error}) => 'エラー: ${error}';
@override String get streamInterrupted => 'ストリームが中断されました。再生を押すかシークして再試行してください。';
@@ -751,6 +759,11 @@ class _TranslationsSubtitlingStylingJa extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => '背景色';
@override String get position => '位置';
@override String get assOverride => 'ASSオーバーライド';
@override String get overrideScale => '拡大縮小';
@override String get overrideForce => '強制';
@override String get overrideStrip => 'スタイルを削除';
@override String get positionTop => '';
@override String get positionBottom => '';
@override String get bold => '太字';
@override String get italic => '斜体';
@override String get renderResolution => 'レンダリング解像度';
@@ -918,7 +931,7 @@ class _TranslationsErrorsJa extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => '検索に失敗しました: ${error}';
@override String connectionTimeout({required Object context}) => '${context}の読み込み中に接続がタイムアウトしました';
@override String get connectionFailed => 'メディアサーバーに接続できません';
@override String failedToLoad({required Object context, required Object error}) => '${context}読み込みに失敗しました: ${error}';
@override String unableToLoad({required Object context}) => '${context}読み込めませんでした。もう一度お試しください。';
@override String get noClientAvailable => 'クライアントが利用できません';
@override String get pleaseEnterToken => 'トークンを入力してください';
@override String get invalidToken => '無効なトークン';
@@ -1179,7 +1192,6 @@ class _TranslationsCollectionsJa extends TranslationsCollectionsEn {
@override String get deleted => 'コレクションを削除しました';
@override String get deleteFailed => 'コレクションの削除に失敗しました';
@override String deleteFailedWithError({required Object error}) => 'コレクションの削除に失敗しました: ${error}';
@override String failedToLoadItems({required Object error}) => 'コレクションアイテムの読み込みに失敗しました: ${error}';
@override String get selectCollection => 'コレクションを選択';
@override String get collectionName => 'コレクション名';
@override String get enterCollectionName => 'コレクション名を入力';
@@ -1282,6 +1294,8 @@ class _TranslationsWatchTogetherJa extends TranslationsWatchTogetherEn {
@override String get hostingSession => 'セッションをホスト中';
@override String get inSession => 'セッション中';
@override String get sessionCode => 'セッションコード';
@override String get openSessionControls => '「一緒に見る」セッションのコントロールを開く';
@override String get copySessionCode => 'セッションコードをコピー';
@override String get hostControlsPlayback => 'ホストが再生を制御';
@override String get anyoneCanControl => '全員が再生を制御可能';
@override String get hostControls => 'ホストが制御';
@@ -1458,6 +1472,8 @@ class _TranslationsVideoSettingsJa extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => '再生速度';
@override String get normalSpeed => '標準';
@override String sleepTimerActive({required Object duration}) => '作動中(${duration}';
@override String get zoom => 'ズーム';
@override String get sleepTimer => 'スリープタイマー';
@override String get audioSync => '音声同期';
@@ -1576,6 +1592,8 @@ class _TranslationsMetadataEditJa extends TranslationsMetadataEditEn {
@override String get artworkUpdated => 'アートワークを更新しました';
@override String get artworkUpdateFailed => 'アートワークの更新に失敗しました';
@override String get noArtworkAvailable => 'アートワークがありません';
@override String artworkOption({required Object index}) => 'アートワークの選択肢 ${index}';
@override String selectedArtworkOption({required Object index}) => 'アートワークの選択肢 ${index}、選択済み';
@override String get notSet => '未設定';
@override String get libraryDefault => 'ライブラリのデフォルト';
@override String get accountDefault => 'アカウントのデフォルト';
@@ -2053,6 +2071,7 @@ class _TranslationsServicesDeviceCodeJa extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => '${service} で Plezy を有効化';
@override String body({required Object url}) => '${url} にアクセスしてこのコードを入力:';
@override String openToActivate({required Object service}) => '${service} を開いて有効化';
@override String get copyCode => 'アクティベーションコードをコピー';
@override String get waitingForAuthorization => '認証を待っています…';
@override String get codeCopied => 'コードをコピーしました';
}
@@ -2067,6 +2086,7 @@ class _TranslationsServicesOauthProxyJa extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => '${service} にサインイン';
@override String get body => 'このQRコードをスキャンするか、任意のデバイスでURLを開いてください。';
@override String openToSignIn({required Object service}) => '${service} を開いてサインイン';
@override String get copyUrl => 'サインインURLをコピー';
@override String get urlCopied => 'URLをコピーしました';
}
@@ -2316,6 +2336,7 @@ extension on TranslationsJa {
'settings.creditsPattern' => 'クレジットマーカーパターン',
'settings.creditsPatternDescription' => 'チャプタータイトルのクレジットマーカーに一致する正規表現パターン',
'settings.invalidRegex' => '無効な正規表現',
'settings.regex' => '正規表現',
'settings.downloads' => 'ダウンロード',
'settings.downloadLocationDescription' => 'ダウンロードコンテンツの保存場所を選択',
'settings.downloadLocationDefault' => 'デフォルト(アプリストレージ)',
@@ -2421,6 +2442,8 @@ extension on TranslationsJa {
'hotkeys.clearShortcut' => 'ショートカットをクリア',
'hotkeys.noShortcutSet' => 'ショートカット未設定',
'hotkeys.currentShortcut' => '現在のショートカット:',
'hotkeys.pressToRecord' => '選択してショートカットを記録',
'hotkeys.recordingShortcut' => 'ショートカットを押してください',
'hotkeys.actions.playPause' => '再生/一時停止',
'hotkeys.actions.volumeUp' => '音量を上げる',
'hotkeys.actions.volumeDown' => '音量を下げる',
@@ -2510,6 +2533,8 @@ extension on TranslationsJa {
'accessibility.saturation' => '彩度',
'accessibility.brightness' => '明るさ',
'accessibility.hexColor' => '16進カラー',
'accessibility.expandText' => 'テキストを展開',
'accessibility.collapseText' => 'テキストを折りたたむ',
'tooltips.shufflePlay' => 'シャッフル再生',
'tooltips.playTrailer' => '予告編を再生',
'tooltips.markAsWatched' => '視聴済みにする',
@@ -2600,19 +2625,20 @@ extension on TranslationsJa {
'messages.markedAsWatchedOffline' => '視聴済みにしました(オンライン時に同期)',
'messages.markedAsUnwatchedOffline' => '未視聴にしました(オンライン時に同期)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => '自動削除: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ja'))(n, other: '視聴済みダウンロードを${n}件自動削除しました', ),
'messages.removedFromContinueWatching' => '視聴中から削除しました',
'messages.errorLoading' => ({required Object error}) => 'エラー: ${error}',
'messages.streamInterrupted' => 'ストリームが中断されました。再生を押すかシークして再試行してください。',
'messages.liveStreamInterrupted' => 'ライブストリームが中断されました。再生を押して再試行してください。',
'messages.fileInfoNotAvailable' => 'ファイル情報が利用できません',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'ファイル情報の読み込みエラー: ${error}',
'messages.errorLoadingSeries' => 'シリーズの読み込みエラー',
'messages.musicNotSupported' => '音楽の再生はまだサポートされていません',
'messages.noDescriptionAvailable' => '説明はありません',
'messages.noProfilesAvailable' => '利用可能なプロフィールがありません',
'messages.contactAdminForProfiles' => 'プロファイルを追加するにはサーバー管理者に連絡してください',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => 'このアイテムのライブラリセクションを判別できません',
'messages.logsCleared' => 'ログをクリアしました',
'messages.logsCopied' => 'ログをクリップボードにコピーしました',
@@ -2651,6 +2677,11 @@ extension on TranslationsJa {
'subtitlingStyling.backgroundColor' => '背景色',
'subtitlingStyling.position' => '位置',
'subtitlingStyling.assOverride' => 'ASSオーバーライド',
'subtitlingStyling.overrideScale' => '拡大縮小',
'subtitlingStyling.overrideForce' => '強制',
'subtitlingStyling.overrideStrip' => 'スタイルを削除',
'subtitlingStyling.positionTop' => '',
'subtitlingStyling.positionBottom' => '',
'subtitlingStyling.bold' => '太字',
'subtitlingStyling.italic' => '斜体',
'subtitlingStyling.renderResolution' => 'レンダリング解像度',
@@ -2764,7 +2795,7 @@ extension on TranslationsJa {
'errors.searchFailed' => ({required Object error}) => '検索に失敗しました: ${error}',
'errors.connectionTimeout' => ({required Object context}) => '${context}の読み込み中に接続がタイムアウトしました',
'errors.connectionFailed' => 'メディアサーバーに接続できません',
'errors.failedToLoad' => ({required Object context, required Object error}) => '${context}読み込みに失敗しました: ${error}',
'errors.unableToLoad' => ({required Object context}) => '${context}読み込めませんでした。もう一度お試しください。',
'errors.noClientAvailable' => 'クライアントが利用できません',
'errors.pleaseEnterToken' => 'トークンを入力してください',
'errors.invalidToken' => '無効なトークン',
@@ -2985,7 +3016,6 @@ extension on TranslationsJa {
'collections.deleted' => 'コレクションを削除しました',
'collections.deleteFailed' => 'コレクションの削除に失敗しました',
'collections.deleteFailedWithError' => ({required Object error}) => 'コレクションの削除に失敗しました: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'コレクションアイテムの読み込みに失敗しました: ${error}',
'collections.selectCollection' => 'コレクションを選択',
'collections.collectionName' => 'コレクション名',
'collections.enterCollectionName' => 'コレクション名を入力',
@@ -3059,6 +3089,8 @@ extension on TranslationsJa {
'watchTogether.hostingSession' => 'セッションをホスト中',
'watchTogether.inSession' => 'セッション中',
'watchTogether.sessionCode' => 'セッションコード',
'watchTogether.openSessionControls' => '「一緒に見る」セッションのコントロールを開く',
'watchTogether.copySessionCode' => 'セッションコードをコピー',
'watchTogether.hostControlsPlayback' => 'ホストが再生を制御',
'watchTogether.anyoneCanControl' => '全員が再生を制御可能',
'watchTogether.hostControls' => 'ホストが制御',
@@ -3113,6 +3145,8 @@ extension on TranslationsJa {
'downloads.manage' => '管理',
'downloads.tvShows' => 'テレビ番組',
'downloads.movies' => '映画',
_ => null,
} ?? switch (path) {
'downloads.music' => '音楽',
'downloads.tracksQueued' => ({required Object count}) => '${count} 曲をダウンロード待機中',
'downloads.noDownloads' => 'ダウンロードなし',
@@ -3125,8 +3159,6 @@ extension on TranslationsJa {
'downloads.serverErrorBitrate' => 'サーバーエラー: ファイルがリモートビットレート制限を超えている可能性があります',
'downloads.episodesQueued' => ({required Object count}) => '${count}エピソードをダウンロードキューに追加しました',
'downloads.downloadDeleted' => 'ダウンロードを削除しました',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => 'このデバイスから「${title}」を削除しますか?',
'downloads.cancelledDownloadTitle' => 'キャンセルされたダウンロード',
'downloads.cancelledDownloadMessage' => 'このダウンロードはキャンセルされました。どうしますか?',
@@ -3258,6 +3290,8 @@ extension on TranslationsJa {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => '${attempts}回試行後に接続が切断されました',
'companionRemote.errors.connectionLost' => '接続が切断されました',
'videoSettings.playbackSpeed' => '再生速度',
'videoSettings.normalSpeed' => '標準',
'videoSettings.sleepTimerActive' => ({required Object duration}) => '作動中(${duration}',
'videoSettings.zoom' => 'ズーム',
'videoSettings.sleepTimer' => 'スリープタイマー',
'videoSettings.audioSync' => '音声同期',
@@ -3349,6 +3383,8 @@ extension on TranslationsJa {
'metadataEdit.artworkUpdated' => 'アートワークを更新しました',
'metadataEdit.artworkUpdateFailed' => 'アートワークの更新に失敗しました',
'metadataEdit.noArtworkAvailable' => 'アートワークがありません',
'metadataEdit.artworkOption' => ({required Object index}) => 'アートワークの選択肢 ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => 'アートワークの選択肢 ${index}、選択済み',
'metadataEdit.notSet' => '未設定',
'metadataEdit.libraryDefault' => 'ライブラリのデフォルト',
'metadataEdit.accountDefault' => 'アカウントのデフォルト',
@@ -3463,11 +3499,13 @@ extension on TranslationsJa {
'services.deviceCode.title' => ({required Object service}) => '${service} で Plezy を有効化',
'services.deviceCode.body' => ({required Object url}) => '${url} にアクセスしてこのコードを入力:',
'services.deviceCode.openToActivate' => ({required Object service}) => '${service} を開いて有効化',
'services.deviceCode.copyCode' => 'アクティベーションコードをコピー',
'services.deviceCode.waitingForAuthorization' => '認証を待っています…',
'services.deviceCode.codeCopied' => 'コードをコピーしました',
'services.oauthProxy.title' => ({required Object service}) => '${service} にサインイン',
'services.oauthProxy.body' => 'このQRコードをスキャンするか、任意のデバイスでURLを開いてください。',
'services.oauthProxy.openToSignIn' => ({required Object service}) => '${service} を開いてサインイン',
'services.oauthProxy.copyUrl' => 'サインインURLをコピー',
'services.oauthProxy.urlCopied' => 'URLをコピーしました',
'services.libraryFilter.title' => 'ライブラリフィルター',
'services.libraryFilter.subtitleAllSyncing' => 'すべてのライブラリを同期中',
+46 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsKo extends TranslationsSettingsEn {
@override String get creditsPattern => '크레딧 마커 패턴';
@override String get creditsPatternDescription => '챕터 제목에서 크레딧 마커를 인식하는 정규식 패턴';
@override String get invalidRegex => '잘못된 정규식';
@override String get regex => '정규식';
@override String get downloads => '다운로드';
@override String get downloadLocationDescription => '다운로드 콘텐츠 저장 위치 선택';
@override String get downloadLocationDefault => '기본값 (앱 저장소)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysKo extends TranslationsHotkeysEn {
@override String get clearShortcut => '단축키 삭제';
@override String get noShortcutSet => '설정된 단축키 없음';
@override String get currentShortcut => '현재 단축키:';
@override String get pressToRecord => '단축키를 기록하려면 선택';
@override String get recordingShortcut => '지금 단축키를 누르세요';
@override late final _TranslationsHotkeysActionsKo actions = _TranslationsHotkeysActionsKo._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityKo extends TranslationsAccessibilityEn {
@override String get saturation => '채도';
@override String get brightness => '밝기';
@override String get hexColor => '16진수 색상';
@override String get expandText => '텍스트 펼치기';
@override String get collapseText => '텍스트 접기';
}
// Path: tooltips
@@ -693,6 +698,9 @@ class _TranslationsMessagesKo extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => '시청 완료로 표시됨 (연결 시 동기화됨)';
@override String get markedAsUnwatchedOffline => '미시청으로 표시됨 (연결 시 동기화됨)';
@override String autoRemovedWatchedDownload({required Object title}) => '자동 삭제됨: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ko'))(n,
other: '시청한 다운로드 ${n}개를 자동 삭제했습니다',
);
@override String get removedFromContinueWatching => '계속 시청 목록에서 제거됨';
@override String errorLoading({required Object error}) => '오류: ${error}';
@override String get streamInterrupted => '스트림이 중단되었습니다. 재생을 누르거나 탐색하여 다시 시도하세요.';
@@ -751,6 +759,11 @@ class _TranslationsSubtitlingStylingKo extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => '배경색';
@override String get position => '위치';
@override String get assOverride => 'ASS 오버라이드';
@override String get overrideScale => '크기 조정';
@override String get overrideForce => '강제 적용';
@override String get overrideStrip => '스타일 제거';
@override String get positionTop => '';
@override String get positionBottom => '아래';
@override String get bold => '굵게';
@override String get italic => '기울임꼴';
@override String get renderResolution => '렌더링 해상도';
@@ -918,7 +931,7 @@ class _TranslationsErrorsKo extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => '검색 실패: ${error}';
@override String connectionTimeout({required Object context}) => '${context} 로드 중 연결 시간 초과';
@override String get connectionFailed => '미디어 서버에 연결할 수 없습니다';
@override String failedToLoad({required Object context, required Object error}) => '${context} 로드 실패: ${error}';
@override String unableToLoad({required Object context}) => '${context}을(를) 불러올 수 없습니다. 다시 시도하세요.';
@override String get noClientAvailable => '사용 가능한 클라이언트가 없습니다';
@override String get pleaseEnterToken => '토큰을 입력해 주세요';
@override String get invalidToken => '토큰이 유효하지 않습니다';
@@ -1179,7 +1192,6 @@ class _TranslationsCollectionsKo extends TranslationsCollectionsEn {
@override String get deleted => '컬렉션 삭제됨';
@override String get deleteFailed => '컬렉션 삭제 실패';
@override String deleteFailedWithError({required Object error}) => '컬렉션 삭제 실패: ${error}';
@override String failedToLoadItems({required Object error}) => '컬렉션 항목 로드 실패: ${error}';
@override String get selectCollection => '컬렉션 선택';
@override String get collectionName => '컬렉션 이름';
@override String get enterCollectionName => '컬렉션 이름 입력';
@@ -1282,6 +1294,8 @@ class _TranslationsWatchTogetherKo extends TranslationsWatchTogetherEn {
@override String get hostingSession => '세션 호스팅';
@override String get inSession => '세션 중';
@override String get sessionCode => '세션 코드';
@override String get openSessionControls => '함께 보기 세션 제어 열기';
@override String get copySessionCode => '세션 코드 복사';
@override String get hostControlsPlayback => '호스트 재생 제어';
@override String get anyoneCanControl => '누구나 재생 제어 가능';
@override String get hostControls => '호스트 제어';
@@ -1458,6 +1472,8 @@ class _TranslationsVideoSettingsKo extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => '재생 속도';
@override String get normalSpeed => '보통';
@override String sleepTimerActive({required Object duration}) => '활성 (${duration})';
@override String get zoom => '확대/축소';
@override String get sleepTimer => '취침 타이머';
@override String get audioSync => '오디오 동기화';
@@ -1576,6 +1592,8 @@ class _TranslationsMetadataEditKo extends TranslationsMetadataEditEn {
@override String get artworkUpdated => '아트워크가 업데이트되었습니다';
@override String get artworkUpdateFailed => '아트워크 업데이트 실패';
@override String get noArtworkAvailable => '사용 가능한 아트워크 없음';
@override String artworkOption({required Object index}) => '아트워크 옵션 ${index}';
@override String selectedArtworkOption({required Object index}) => '아트워크 옵션 ${index}, 선택됨';
@override String get notSet => '설정되지 않음';
@override String get libraryDefault => '라이브러리 기본값';
@override String get accountDefault => '계정 기본값';
@@ -2053,6 +2071,7 @@ class _TranslationsServicesDeviceCodeKo extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => '${service}에서 Plezy 활성화';
@override String body({required Object url}) => '${url}을 방문하여 이 코드를 입력하세요:';
@override String openToActivate({required Object service}) => '활성화하려면 ${service} 열기';
@override String get copyCode => '활성화 코드 복사';
@override String get waitingForAuthorization => '인증을 기다리는 중…';
@override String get codeCopied => '코드가 복사되었습니다';
}
@@ -2067,6 +2086,7 @@ class _TranslationsServicesOauthProxyKo extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => '${service}에 로그인';
@override String get body => '이 QR 코드를 스캔하거나 아무 기기에서 URL을 여세요.';
@override String openToSignIn({required Object service}) => '로그인하려면 ${service} 열기';
@override String get copyUrl => '로그인 URL 복사';
@override String get urlCopied => 'URL이 복사되었습니다';
}
@@ -2316,6 +2336,7 @@ extension on TranslationsKo {
'settings.creditsPattern' => '크레딧 마커 패턴',
'settings.creditsPatternDescription' => '챕터 제목에서 크레딧 마커를 인식하는 정규식 패턴',
'settings.invalidRegex' => '잘못된 정규식',
'settings.regex' => '정규식',
'settings.downloads' => '다운로드',
'settings.downloadLocationDescription' => '다운로드 콘텐츠 저장 위치 선택',
'settings.downloadLocationDefault' => '기본값 (앱 저장소)',
@@ -2421,6 +2442,8 @@ extension on TranslationsKo {
'hotkeys.clearShortcut' => '단축키 삭제',
'hotkeys.noShortcutSet' => '설정된 단축키 없음',
'hotkeys.currentShortcut' => '현재 단축키:',
'hotkeys.pressToRecord' => '단축키를 기록하려면 선택',
'hotkeys.recordingShortcut' => '지금 단축키를 누르세요',
'hotkeys.actions.playPause' => '재생/일시정지',
'hotkeys.actions.volumeUp' => '볼륨 높이기',
'hotkeys.actions.volumeDown' => '볼륨 낮추기',
@@ -2510,6 +2533,8 @@ extension on TranslationsKo {
'accessibility.saturation' => '채도',
'accessibility.brightness' => '밝기',
'accessibility.hexColor' => '16진수 색상',
'accessibility.expandText' => '텍스트 펼치기',
'accessibility.collapseText' => '텍스트 접기',
'tooltips.shufflePlay' => '무작위 재생',
'tooltips.playTrailer' => '예고편 재생',
'tooltips.markAsWatched' => '시청 완료로 표시',
@@ -2600,19 +2625,20 @@ extension on TranslationsKo {
'messages.markedAsWatchedOffline' => '시청 완료로 표시됨 (연결 시 동기화됨)',
'messages.markedAsUnwatchedOffline' => '미시청으로 표시됨 (연결 시 동기화됨)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => '자동 삭제됨: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ko'))(n, other: '시청한 다운로드 ${n}개를 자동 삭제했습니다', ),
'messages.removedFromContinueWatching' => '계속 시청 목록에서 제거됨',
'messages.errorLoading' => ({required Object error}) => '오류: ${error}',
'messages.streamInterrupted' => '스트림이 중단되었습니다. 재생을 누르거나 탐색하여 다시 시도하세요.',
'messages.liveStreamInterrupted' => '라이브 스트림이 중단되었습니다. 재생을 눌러 다시 시도하세요.',
'messages.fileInfoNotAvailable' => '파일 정보가 없습니다',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => '파일 정보 로딩 중 오류: ${error}',
'messages.errorLoadingSeries' => '시리즈 로딩 중 오류',
'messages.musicNotSupported' => '음악 재생 미지원',
'messages.noDescriptionAvailable' => '설명이 없습니다',
'messages.noProfilesAvailable' => '사용 가능한 프로필이 없습니다',
'messages.contactAdminForProfiles' => '프로필을 추가하려면 서버 관리자에게 문의하세요',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => '이 항목의 라이브러리 섹션을 확인할 수 없습니다',
'messages.logsCleared' => '로그가 삭제 되었습니다',
'messages.logsCopied' => '로그가 클립보드에 복사 되었습니다',
@@ -2651,6 +2677,11 @@ extension on TranslationsKo {
'subtitlingStyling.backgroundColor' => '배경색',
'subtitlingStyling.position' => '위치',
'subtitlingStyling.assOverride' => 'ASS 오버라이드',
'subtitlingStyling.overrideScale' => '크기 조정',
'subtitlingStyling.overrideForce' => '강제 적용',
'subtitlingStyling.overrideStrip' => '스타일 제거',
'subtitlingStyling.positionTop' => '',
'subtitlingStyling.positionBottom' => '아래',
'subtitlingStyling.bold' => '굵게',
'subtitlingStyling.italic' => '기울임꼴',
'subtitlingStyling.renderResolution' => '렌더링 해상도',
@@ -2764,7 +2795,7 @@ extension on TranslationsKo {
'errors.searchFailed' => ({required Object error}) => '검색 실패: ${error}',
'errors.connectionTimeout' => ({required Object context}) => '${context} 로드 중 연결 시간 초과',
'errors.connectionFailed' => '미디어 서버에 연결할 수 없습니다',
'errors.failedToLoad' => ({required Object context, required Object error}) => '${context} 로드 실패: ${error}',
'errors.unableToLoad' => ({required Object context}) => '${context}을(를) 불러올 수 없습니다. 다시 시도하세요.',
'errors.noClientAvailable' => '사용 가능한 클라이언트가 없습니다',
'errors.pleaseEnterToken' => '토큰을 입력해 주세요',
'errors.invalidToken' => '토큰이 유효하지 않습니다',
@@ -2985,7 +3016,6 @@ extension on TranslationsKo {
'collections.deleted' => '컬렉션 삭제됨',
'collections.deleteFailed' => '컬렉션 삭제 실패',
'collections.deleteFailedWithError' => ({required Object error}) => '컬렉션 삭제 실패: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => '컬렉션 항목 로드 실패: ${error}',
'collections.selectCollection' => '컬렉션 선택',
'collections.collectionName' => '컬렉션 이름',
'collections.enterCollectionName' => '컬렉션 이름 입력',
@@ -3059,6 +3089,8 @@ extension on TranslationsKo {
'watchTogether.hostingSession' => '세션 호스팅',
'watchTogether.inSession' => '세션 중',
'watchTogether.sessionCode' => '세션 코드',
'watchTogether.openSessionControls' => '함께 보기 세션 제어 열기',
'watchTogether.copySessionCode' => '세션 코드 복사',
'watchTogether.hostControlsPlayback' => '호스트 재생 제어',
'watchTogether.anyoneCanControl' => '누구나 재생 제어 가능',
'watchTogether.hostControls' => '호스트 제어',
@@ -3113,6 +3145,8 @@ extension on TranslationsKo {
'downloads.manage' => '관리',
'downloads.tvShows' => 'TV 프로그램',
'downloads.movies' => '영화',
_ => null,
} ?? switch (path) {
'downloads.music' => '음악',
'downloads.tracksQueued' => ({required Object count}) => '${count}곡 다운로드 대기 중',
'downloads.noDownloads' => '다운로드 없음',
@@ -3125,8 +3159,6 @@ extension on TranslationsKo {
'downloads.serverErrorBitrate' => '서버 오류: 파일이 원격 비트레이트 제한을 초과할 수 있습니다',
'downloads.episodesQueued' => ({required Object count}) => '${count} 에피소드가 다운로드 대기열에 추가 되었습니다',
'downloads.downloadDeleted' => '다운로드 삭제됨',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => '이 기기에서 "${title}"을(를) 삭제할까요?',
'downloads.cancelledDownloadTitle' => '취소된 다운로드',
'downloads.cancelledDownloadMessage' => '이 다운로드가 취소되었습니다. 어떻게 하시겠습니까?',
@@ -3258,6 +3290,8 @@ extension on TranslationsKo {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => '${attempts}회 시도 후 연결이 끊어졌습니다',
'companionRemote.errors.connectionLost' => '연결이 끊어졌습니다',
'videoSettings.playbackSpeed' => '재생 속도',
'videoSettings.normalSpeed' => '보통',
'videoSettings.sleepTimerActive' => ({required Object duration}) => '활성 (${duration})',
'videoSettings.zoom' => '확대/축소',
'videoSettings.sleepTimer' => '취침 타이머',
'videoSettings.audioSync' => '오디오 동기화',
@@ -3349,6 +3383,8 @@ extension on TranslationsKo {
'metadataEdit.artworkUpdated' => '아트워크가 업데이트되었습니다',
'metadataEdit.artworkUpdateFailed' => '아트워크 업데이트 실패',
'metadataEdit.noArtworkAvailable' => '사용 가능한 아트워크 없음',
'metadataEdit.artworkOption' => ({required Object index}) => '아트워크 옵션 ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => '아트워크 옵션 ${index}, 선택됨',
'metadataEdit.notSet' => '설정되지 않음',
'metadataEdit.libraryDefault' => '라이브러리 기본값',
'metadataEdit.accountDefault' => '계정 기본값',
@@ -3463,11 +3499,13 @@ extension on TranslationsKo {
'services.deviceCode.title' => ({required Object service}) => '${service}에서 Plezy 활성화',
'services.deviceCode.body' => ({required Object url}) => '${url}을 방문하여 이 코드를 입력하세요:',
'services.deviceCode.openToActivate' => ({required Object service}) => '활성화하려면 ${service} 열기',
'services.deviceCode.copyCode' => '활성화 코드 복사',
'services.deviceCode.waitingForAuthorization' => '인증을 기다리는 중…',
'services.deviceCode.codeCopied' => '코드가 복사되었습니다',
'services.oauthProxy.title' => ({required Object service}) => '${service}에 로그인',
'services.oauthProxy.body' => '이 QR 코드를 스캔하거나 아무 기기에서 URL을 여세요.',
'services.oauthProxy.openToSignIn' => ({required Object service}) => '로그인하려면 ${service} 열기',
'services.oauthProxy.copyUrl' => '로그인 URL 복사',
'services.oauthProxy.urlCopied' => 'URL이 복사되었습니다',
'services.libraryFilter.title' => '라이브러리 필터',
'services.libraryFilter.subtitleAllSyncing' => '모든 라이브러리 동기화 중',
+47 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsNb extends TranslationsSettingsEn {
@override String get creditsPattern => 'Rulletekstmarkørmønster';
@override String get creditsPatternDescription => 'Regulært uttrykk for å gjenkjenne rulletekstmarkører i kapitteltitler';
@override String get invalidRegex => 'Ugyldig regulært uttrykk';
@override String get regex => 'Regulært uttrykk';
@override String get downloads => 'Nedlastinger';
@override String get downloadLocationDescription => 'Velg hvor nedlastet innhold skal lagres';
@override String get downloadLocationDefault => 'Standard (App-lagring)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysNb extends TranslationsHotkeysEn {
@override String get clearShortcut => 'Fjern snarvei';
@override String get noShortcutSet => 'Ingen snarvei satt';
@override String get currentShortcut => 'Gjeldende snarvei:';
@override String get pressToRecord => 'Velg for å registrere en snarvei';
@override String get recordingShortcut => 'Trykk på snarveien nå';
@override late final _TranslationsHotkeysActionsNb actions = _TranslationsHotkeysActionsNb._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityNb extends TranslationsAccessibilityEn {
@override String get saturation => 'Metning';
@override String get brightness => 'Lysstyrke';
@override String get hexColor => 'Heksadesimal farge';
@override String get expandText => 'Utvid tekst';
@override String get collapseText => 'Fold sammen tekst';
}
// Path: tooltips
@@ -693,6 +698,10 @@ class _TranslationsMessagesNb extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => 'Merket som sett (synkroniseres når tilkoblet)';
@override String get markedAsUnwatchedOffline => 'Merket som usett (synkroniseres når tilkoblet)';
@override String autoRemovedWatchedDownload({required Object title}) => 'Automatisk fjernet: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('nb'))(n,
one: 'Fjernet automatisk ${n} sett nedlasting',
other: 'Fjernet automatisk ${n} sette nedlastinger',
);
@override String get removedFromContinueWatching => 'Fjernet fra Fortsett å se';
@override String errorLoading({required Object error}) => 'Feil: ${error}';
@override String get streamInterrupted => 'Avspillingen ble avbrutt. Trykk på Spill av eller spol for å prøve på nytt.';
@@ -751,6 +760,11 @@ class _TranslationsSubtitlingStylingNb extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => 'Bakgrunnsfarge';
@override String get position => 'Posisjon';
@override String get assOverride => 'ASS-overstyring';
@override String get overrideScale => 'Skaler';
@override String get overrideForce => 'Tving';
@override String get overrideStrip => 'Fjern formatering';
@override String get positionTop => 'Øverst';
@override String get positionBottom => 'Nederst';
@override String get bold => 'Fet';
@override String get italic => 'Kursiv';
@override String get renderResolution => 'Gjengivelsesoppløsning';
@@ -918,7 +932,7 @@ class _TranslationsErrorsNb extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => 'Søk mislyktes: ${error}';
@override String connectionTimeout({required Object context}) => 'Tidsavbrudd ved lasting av ${context}';
@override String get connectionFailed => 'Kan ikke koble til medieserver';
@override String failedToLoad({required Object context, required Object error}) => 'Kunne ikke laste ${context}: ${error}';
@override String unableToLoad({required Object context}) => 'Kunne ikke laste ${context}. Prøv igjen.';
@override String get noClientAvailable => 'Ingen klient tilgjengelig';
@override String get pleaseEnterToken => 'Vennligst skriv inn et token';
@override String get invalidToken => 'Ugyldig token';
@@ -1180,7 +1194,6 @@ class _TranslationsCollectionsNb extends TranslationsCollectionsEn {
@override String get deleted => 'Samling slettet';
@override String get deleteFailed => 'Kunne ikke slette samling';
@override String deleteFailedWithError({required Object error}) => 'Kunne ikke slette samling: ${error}';
@override String failedToLoadItems({required Object error}) => 'Kunne ikke laste samlingselementer: ${error}';
@override String get selectCollection => 'Velg samling';
@override String get collectionName => 'Samlingsnavn';
@override String get enterCollectionName => 'Skriv inn samlingsnavn';
@@ -1284,6 +1297,8 @@ class _TranslationsWatchTogetherNb extends TranslationsWatchTogetherEn {
@override String get hostingSession => 'Er vert for økt';
@override String get inSession => 'I økt';
@override String get sessionCode => 'Øktkode';
@override String get openSessionControls => 'Åpne øktkontroller for Se sammen';
@override String get copySessionCode => 'Kopier øktkode';
@override String get hostControlsPlayback => 'Verten kontrollerer avspilling';
@override String get anyoneCanControl => 'Alle kan kontrollere avspilling';
@override String get hostControls => 'Vertskontroll';
@@ -1460,6 +1475,8 @@ class _TranslationsVideoSettingsNb extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => 'Avspillingshastighet';
@override String get normalSpeed => 'Normal';
@override String sleepTimerActive({required Object duration}) => 'Aktiv (${duration})';
@override String get zoom => 'Zoom';
@override String get sleepTimer => 'Søvntimer';
@override String get audioSync => 'Lydsynkronisering';
@@ -1578,6 +1595,8 @@ class _TranslationsMetadataEditNb extends TranslationsMetadataEditEn {
@override String get artworkUpdated => 'Kunstverk oppdatert';
@override String get artworkUpdateFailed => 'Kunne ikke oppdatere kunstverk';
@override String get noArtworkAvailable => 'Ingen kunstverk tilgjengelig';
@override String artworkOption({required Object index}) => 'Alternativ for kunstverk ${index}';
@override String selectedArtworkOption({required Object index}) => 'Alternativ for kunstverk ${index}, valgt';
@override String get notSet => 'Ikke angitt';
@override String get libraryDefault => 'Bibliotekstandard';
@override String get accountDefault => 'Kontostandard';
@@ -2055,6 +2074,7 @@ class _TranslationsServicesDeviceCodeNb extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => 'Aktiver Plezy på ${service}';
@override String body({required Object url}) => 'Besøk ${url} og skriv inn denne koden:';
@override String openToActivate({required Object service}) => 'Åpne ${service} for å aktivere';
@override String get copyCode => 'Kopier aktiveringskode';
@override String get waitingForAuthorization => 'Venter på godkjenning…';
@override String get codeCopied => 'Kode kopiert';
}
@@ -2069,6 +2089,7 @@ class _TranslationsServicesOauthProxyNb extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => 'Logg inn på ${service}';
@override String get body => 'Skann denne QR-koden eller åpne URL-en på en enhet.';
@override String openToSignIn({required Object service}) => 'Åpne ${service} for å logge inn';
@override String get copyUrl => 'Kopier URL for pålogging';
@override String get urlCopied => 'URL kopiert';
}
@@ -2318,6 +2339,7 @@ extension on TranslationsNb {
'settings.creditsPattern' => 'Rulletekstmarkørmønster',
'settings.creditsPatternDescription' => 'Regulært uttrykk for å gjenkjenne rulletekstmarkører i kapitteltitler',
'settings.invalidRegex' => 'Ugyldig regulært uttrykk',
'settings.regex' => 'Regulært uttrykk',
'settings.downloads' => 'Nedlastinger',
'settings.downloadLocationDescription' => 'Velg hvor nedlastet innhold skal lagres',
'settings.downloadLocationDefault' => 'Standard (App-lagring)',
@@ -2423,6 +2445,8 @@ extension on TranslationsNb {
'hotkeys.clearShortcut' => 'Fjern snarvei',
'hotkeys.noShortcutSet' => 'Ingen snarvei satt',
'hotkeys.currentShortcut' => 'Gjeldende snarvei:',
'hotkeys.pressToRecord' => 'Velg for å registrere en snarvei',
'hotkeys.recordingShortcut' => 'Trykk på snarveien nå',
'hotkeys.actions.playPause' => 'Spill av/Pause',
'hotkeys.actions.volumeUp' => 'Volum opp',
'hotkeys.actions.volumeDown' => 'Volum ned',
@@ -2512,6 +2536,8 @@ extension on TranslationsNb {
'accessibility.saturation' => 'Metning',
'accessibility.brightness' => 'Lysstyrke',
'accessibility.hexColor' => 'Heksadesimal farge',
'accessibility.expandText' => 'Utvid tekst',
'accessibility.collapseText' => 'Fold sammen tekst',
'tooltips.shufflePlay' => 'Tilfeldig avspilling',
'tooltips.playTrailer' => 'Spill trailer',
'tooltips.markAsWatched' => 'Merk som sett',
@@ -2602,19 +2628,20 @@ extension on TranslationsNb {
'messages.markedAsWatchedOffline' => 'Merket som sett (synkroniseres når tilkoblet)',
'messages.markedAsUnwatchedOffline' => 'Merket som usett (synkroniseres når tilkoblet)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Automatisk fjernet: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('nb'))(n, one: 'Fjernet automatisk ${n} sett nedlasting', other: 'Fjernet automatisk ${n} sette nedlastinger', ),
'messages.removedFromContinueWatching' => 'Fjernet fra Fortsett å se',
'messages.errorLoading' => ({required Object error}) => 'Feil: ${error}',
'messages.streamInterrupted' => 'Avspillingen ble avbrutt. Trykk på Spill av eller spol for å prøve på nytt.',
'messages.liveStreamInterrupted' => 'Direktesendingen ble avbrutt. Trykk på Spill av for å prøve på nytt.',
'messages.fileInfoNotAvailable' => 'Filinformasjon ikke tilgjengelig',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Feil ved lasting av filinformasjon: ${error}',
'messages.errorLoadingSeries' => 'Feil ved lasting av serie',
'messages.musicNotSupported' => 'Musikkavspilling støttes ikke ennå',
'messages.noDescriptionAvailable' => 'Ingen beskrivelse tilgjengelig',
'messages.noProfilesAvailable' => 'Ingen profiler tilgjengelige',
'messages.contactAdminForProfiles' => 'Kontakt serveradministratoren din for å legge til profiler',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => 'Kan ikke fastslå bibliotekseksjonen for dette elementet',
'messages.logsCleared' => 'Logger tømt',
'messages.logsCopied' => 'Logger kopiert til utklippstavle',
@@ -2653,6 +2680,11 @@ extension on TranslationsNb {
'subtitlingStyling.backgroundColor' => 'Bakgrunnsfarge',
'subtitlingStyling.position' => 'Posisjon',
'subtitlingStyling.assOverride' => 'ASS-overstyring',
'subtitlingStyling.overrideScale' => 'Skaler',
'subtitlingStyling.overrideForce' => 'Tving',
'subtitlingStyling.overrideStrip' => 'Fjern formatering',
'subtitlingStyling.positionTop' => 'Øverst',
'subtitlingStyling.positionBottom' => 'Nederst',
'subtitlingStyling.bold' => 'Fet',
'subtitlingStyling.italic' => 'Kursiv',
'subtitlingStyling.renderResolution' => 'Gjengivelsesoppløsning',
@@ -2766,7 +2798,7 @@ extension on TranslationsNb {
'errors.searchFailed' => ({required Object error}) => 'Søk mislyktes: ${error}',
'errors.connectionTimeout' => ({required Object context}) => 'Tidsavbrudd ved lasting av ${context}',
'errors.connectionFailed' => 'Kan ikke koble til medieserver',
'errors.failedToLoad' => ({required Object context, required Object error}) => 'Kunne ikke laste ${context}: ${error}',
'errors.unableToLoad' => ({required Object context}) => 'Kunne ikke laste ${context}. Prøv igjen.',
'errors.noClientAvailable' => 'Ingen klient tilgjengelig',
'errors.pleaseEnterToken' => 'Vennligst skriv inn et token',
'errors.invalidToken' => 'Ugyldig token',
@@ -2987,7 +3019,6 @@ extension on TranslationsNb {
'collections.deleted' => 'Samling slettet',
'collections.deleteFailed' => 'Kunne ikke slette samling',
'collections.deleteFailedWithError' => ({required Object error}) => 'Kunne ikke slette samling: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Kunne ikke laste samlingselementer: ${error}',
'collections.selectCollection' => 'Velg samling',
'collections.collectionName' => 'Samlingsnavn',
'collections.enterCollectionName' => 'Skriv inn samlingsnavn',
@@ -3061,6 +3092,8 @@ extension on TranslationsNb {
'watchTogether.hostingSession' => 'Er vert for økt',
'watchTogether.inSession' => 'I økt',
'watchTogether.sessionCode' => 'Øktkode',
'watchTogether.openSessionControls' => 'Åpne øktkontroller for Se sammen',
'watchTogether.copySessionCode' => 'Kopier øktkode',
'watchTogether.hostControlsPlayback' => 'Verten kontrollerer avspilling',
'watchTogether.anyoneCanControl' => 'Alle kan kontrollere avspilling',
'watchTogether.hostControls' => 'Vertskontroll',
@@ -3115,6 +3148,8 @@ extension on TranslationsNb {
'downloads.manage' => 'Administrer',
'downloads.tvShows' => 'TV-serier',
'downloads.movies' => 'Filmer',
_ => null,
} ?? switch (path) {
'downloads.music' => 'Musikk',
'downloads.tracksQueued' => ({required Object count}) => '${count} spor i nedlastingskø',
'downloads.noDownloads' => 'Ingen nedlastinger ennå',
@@ -3127,8 +3162,6 @@ extension on TranslationsNb {
'downloads.serverErrorBitrate' => 'Serverfeil: filen kan overskride grensen for ekstern bitrate',
'downloads.episodesQueued' => ({required Object count}) => '${count} episoder i nedlastingskø',
'downloads.downloadDeleted' => 'Nedlasting slettet',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => 'Slette "${title}" fra denne enheten?',
'downloads.cancelledDownloadTitle' => 'Avbrutt nedlasting',
'downloads.cancelledDownloadMessage' => 'Denne nedlastingen ble avbrutt. Hva vil du gjøre?',
@@ -3260,6 +3293,8 @@ extension on TranslationsNb {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => 'Tilkobling mistet etter ${attempts} forsøk',
'companionRemote.errors.connectionLost' => 'Tilkobling mistet',
'videoSettings.playbackSpeed' => 'Avspillingshastighet',
'videoSettings.normalSpeed' => 'Normal',
'videoSettings.sleepTimerActive' => ({required Object duration}) => 'Aktiv (${duration})',
'videoSettings.zoom' => 'Zoom',
'videoSettings.sleepTimer' => 'Søvntimer',
'videoSettings.audioSync' => 'Lydsynkronisering',
@@ -3351,6 +3386,8 @@ extension on TranslationsNb {
'metadataEdit.artworkUpdated' => 'Kunstverk oppdatert',
'metadataEdit.artworkUpdateFailed' => 'Kunne ikke oppdatere kunstverk',
'metadataEdit.noArtworkAvailable' => 'Ingen kunstverk tilgjengelig',
'metadataEdit.artworkOption' => ({required Object index}) => 'Alternativ for kunstverk ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => 'Alternativ for kunstverk ${index}, valgt',
'metadataEdit.notSet' => 'Ikke angitt',
'metadataEdit.libraryDefault' => 'Bibliotekstandard',
'metadataEdit.accountDefault' => 'Kontostandard',
@@ -3465,11 +3502,13 @@ extension on TranslationsNb {
'services.deviceCode.title' => ({required Object service}) => 'Aktiver Plezy på ${service}',
'services.deviceCode.body' => ({required Object url}) => 'Besøk ${url} og skriv inn denne koden:',
'services.deviceCode.openToActivate' => ({required Object service}) => 'Åpne ${service} for å aktivere',
'services.deviceCode.copyCode' => 'Kopier aktiveringskode',
'services.deviceCode.waitingForAuthorization' => 'Venter på godkjenning…',
'services.deviceCode.codeCopied' => 'Kode kopiert',
'services.oauthProxy.title' => ({required Object service}) => 'Logg inn på ${service}',
'services.oauthProxy.body' => 'Skann denne QR-koden eller åpne URL-en på en enhet.',
'services.oauthProxy.openToSignIn' => ({required Object service}) => 'Åpne ${service} for å logge inn',
'services.oauthProxy.copyUrl' => 'Kopier URL for pålogging',
'services.oauthProxy.urlCopied' => 'URL kopiert',
'services.libraryFilter.title' => 'Biblioteksfilter',
'services.libraryFilter.subtitleAllSyncing' => 'Synkroniserer alle biblioteker',
+47 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsNl extends TranslationsSettingsEn {
@override String get creditsPattern => 'Aftitelingmarkeringspatroon';
@override String get creditsPatternDescription => 'Reguliere expressie om aftitelingmarkeringen in hoofdstuktitels te herkennen';
@override String get invalidRegex => 'Ongeldige reguliere expressie';
@override String get regex => 'Reguliere expressie';
@override String get downloads => 'Downloads';
@override String get downloadLocationDescription => 'Kies waar gedownloade content wordt opgeslagen';
@override String get downloadLocationDefault => 'Standaard (App-opslag)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysNl extends TranslationsHotkeysEn {
@override String get clearShortcut => 'Wis sneltoets';
@override String get noShortcutSet => 'Geen sneltoets ingesteld';
@override String get currentShortcut => 'Huidige sneltoets:';
@override String get pressToRecord => 'Selecteer om een sneltoets op te nemen';
@override String get recordingShortcut => 'Druk nu op de sneltoets';
@override late final _TranslationsHotkeysActionsNl actions = _TranslationsHotkeysActionsNl._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityNl extends TranslationsAccessibilityEn {
@override String get saturation => 'Verzadiging';
@override String get brightness => 'Helderheid';
@override String get hexColor => 'Hexkleur';
@override String get expandText => 'Tekst uitvouwen';
@override String get collapseText => 'Tekst samenvouwen';
}
// Path: tooltips
@@ -693,6 +698,10 @@ class _TranslationsMessagesNl extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => 'Gemarkeerd als gekeken (sync wanneer online)';
@override String get markedAsUnwatchedOffline => 'Gemarkeerd als ongekeken (sync wanneer online)';
@override String autoRemovedWatchedDownload({required Object title}) => 'Automatisch verwijderd: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('nl'))(n,
one: 'Automatisch ${n} bekeken download verwijderd',
other: 'Automatisch ${n} bekeken downloads verwijderd',
);
@override String get removedFromContinueWatching => 'Verwijderd uit Doorgaan met kijken';
@override String errorLoading({required Object error}) => 'Fout: ${error}';
@override String get streamInterrupted => 'De stream is onderbroken. Druk op afspelen of spoel om het opnieuw te proberen.';
@@ -751,6 +760,11 @@ class _TranslationsSubtitlingStylingNl extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => 'Achtergrondkleur';
@override String get position => 'Positie';
@override String get assOverride => 'ASS-overschrijving';
@override String get overrideScale => 'Schalen';
@override String get overrideForce => 'Forceren';
@override String get overrideStrip => 'Opmaak verwijderen';
@override String get positionTop => 'Bovenaan';
@override String get positionBottom => 'Onderaan';
@override String get bold => 'Vet';
@override String get italic => 'Cursief';
@override String get renderResolution => 'Renderresolutie';
@@ -918,7 +932,7 @@ class _TranslationsErrorsNl extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => 'Zoeken mislukt: ${error}';
@override String connectionTimeout({required Object context}) => 'Verbinding time-out tijdens laden ${context}';
@override String get connectionFailed => 'Kan geen verbinding maken met mediaserver';
@override String failedToLoad({required Object context, required Object error}) => 'Kon ${context} niet laden: ${error}';
@override String unableToLoad({required Object context}) => 'Kan ${context} niet laden. Probeer het opnieuw.';
@override String get noClientAvailable => 'Geen client beschikbaar';
@override String get pleaseEnterToken => 'Voer een token in';
@override String get invalidToken => 'Ongeldig token';
@@ -1180,7 +1194,6 @@ class _TranslationsCollectionsNl extends TranslationsCollectionsEn {
@override String get deleted => 'Collectie verwijderd';
@override String get deleteFailed => 'Collectie verwijderen mislukt';
@override String deleteFailedWithError({required Object error}) => 'Collectie verwijderen mislukt: ${error}';
@override String failedToLoadItems({required Object error}) => 'Collectie-items laden mislukt: ${error}';
@override String get selectCollection => 'Selecteer collectie';
@override String get collectionName => 'Collectienaam';
@override String get enterCollectionName => 'Voer collectienaam in';
@@ -1284,6 +1297,8 @@ class _TranslationsWatchTogetherNl extends TranslationsWatchTogetherEn {
@override String get hostingSession => 'Sessie Hosten';
@override String get inSession => 'In Sessie';
@override String get sessionCode => 'Sessiecode';
@override String get openSessionControls => 'Sessiebesturing voor Samen Kijken openen';
@override String get copySessionCode => 'Sessiecode kopiëren';
@override String get hostControlsPlayback => 'Host bedient het afspelen';
@override String get anyoneCanControl => 'Iedereen kan het afspelen bedienen';
@override String get hostControls => 'Host bedient';
@@ -1460,6 +1475,8 @@ class _TranslationsVideoSettingsNl extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => 'Afspeelsnelheid';
@override String get normalSpeed => 'Normaal';
@override String sleepTimerActive({required Object duration}) => 'Actief (${duration})';
@override String get zoom => 'Zoom';
@override String get sleepTimer => 'Slaaptimer';
@override String get audioSync => 'Audio synchronisatie';
@@ -1578,6 +1595,8 @@ class _TranslationsMetadataEditNl extends TranslationsMetadataEditEn {
@override String get artworkUpdated => 'Artwork bijgewerkt';
@override String get artworkUpdateFailed => 'Artwork bijwerken mislukt';
@override String get noArtworkAvailable => 'Geen artwork beschikbaar';
@override String artworkOption({required Object index}) => 'Artworkoptie ${index}';
@override String selectedArtworkOption({required Object index}) => 'Artworkoptie ${index}, geselecteerd';
@override String get notSet => 'Niet ingesteld';
@override String get libraryDefault => 'Bibliotheekstandaard';
@override String get accountDefault => 'Accountstandaard';
@@ -2055,6 +2074,7 @@ class _TranslationsServicesDeviceCodeNl extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => 'Plezy activeren op ${service}';
@override String body({required Object url}) => 'Ga naar ${url} en voer deze code in:';
@override String openToActivate({required Object service}) => 'Open ${service} om te activeren';
@override String get copyCode => 'Activeringscode kopiëren';
@override String get waitingForAuthorization => 'Wachten op autorisatie…';
@override String get codeCopied => 'Code gekopieerd';
}
@@ -2069,6 +2089,7 @@ class _TranslationsServicesOauthProxyNl extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => 'Aanmelden bij ${service}';
@override String get body => 'Scan deze QR-code of open de URL op een apparaat.';
@override String openToSignIn({required Object service}) => '${service} openen om aan te melden';
@override String get copyUrl => 'Aanmeldings-URL kopiëren';
@override String get urlCopied => 'URL gekopieerd';
}
@@ -2318,6 +2339,7 @@ extension on TranslationsNl {
'settings.creditsPattern' => 'Aftitelingmarkeringspatroon',
'settings.creditsPatternDescription' => 'Reguliere expressie om aftitelingmarkeringen in hoofdstuktitels te herkennen',
'settings.invalidRegex' => 'Ongeldige reguliere expressie',
'settings.regex' => 'Reguliere expressie',
'settings.downloads' => 'Downloads',
'settings.downloadLocationDescription' => 'Kies waar gedownloade content wordt opgeslagen',
'settings.downloadLocationDefault' => 'Standaard (App-opslag)',
@@ -2423,6 +2445,8 @@ extension on TranslationsNl {
'hotkeys.clearShortcut' => 'Wis sneltoets',
'hotkeys.noShortcutSet' => 'Geen sneltoets ingesteld',
'hotkeys.currentShortcut' => 'Huidige sneltoets:',
'hotkeys.pressToRecord' => 'Selecteer om een sneltoets op te nemen',
'hotkeys.recordingShortcut' => 'Druk nu op de sneltoets',
'hotkeys.actions.playPause' => 'Afspelen/Pauzeren',
'hotkeys.actions.volumeUp' => 'Volume omhoog',
'hotkeys.actions.volumeDown' => 'Volume omlaag',
@@ -2512,6 +2536,8 @@ extension on TranslationsNl {
'accessibility.saturation' => 'Verzadiging',
'accessibility.brightness' => 'Helderheid',
'accessibility.hexColor' => 'Hexkleur',
'accessibility.expandText' => 'Tekst uitvouwen',
'accessibility.collapseText' => 'Tekst samenvouwen',
'tooltips.shufflePlay' => 'Willekeurig afspelen',
'tooltips.playTrailer' => 'Trailer afspelen',
'tooltips.markAsWatched' => 'Markeer als gekeken',
@@ -2602,19 +2628,20 @@ extension on TranslationsNl {
'messages.markedAsWatchedOffline' => 'Gemarkeerd als gekeken (sync wanneer online)',
'messages.markedAsUnwatchedOffline' => 'Gemarkeerd als ongekeken (sync wanneer online)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Automatisch verwijderd: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('nl'))(n, one: 'Automatisch ${n} bekeken download verwijderd', other: 'Automatisch ${n} bekeken downloads verwijderd', ),
'messages.removedFromContinueWatching' => 'Verwijderd uit Doorgaan met kijken',
'messages.errorLoading' => ({required Object error}) => 'Fout: ${error}',
'messages.streamInterrupted' => 'De stream is onderbroken. Druk op afspelen of spoel om het opnieuw te proberen.',
'messages.liveStreamInterrupted' => 'De livestream is onderbroken. Druk op afspelen om het opnieuw te proberen.',
'messages.fileInfoNotAvailable' => 'Bestand informatie niet beschikbaar',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Fout bij laden bestand info: ${error}',
'messages.errorLoadingSeries' => 'Fout bij laden serie',
'messages.musicNotSupported' => 'Muziek afspelen wordt nog niet ondersteund',
'messages.noDescriptionAvailable' => 'Geen beschrijving beschikbaar',
'messages.noProfilesAvailable' => 'Geen profielen beschikbaar',
'messages.contactAdminForProfiles' => 'Neem contact op met je serverbeheerder om profielen toe te voegen',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => 'Kan bibliotheeksectie voor dit item niet bepalen',
'messages.logsCleared' => 'Logs gewist',
'messages.logsCopied' => 'Logs gekopieerd naar klembord',
@@ -2653,6 +2680,11 @@ extension on TranslationsNl {
'subtitlingStyling.backgroundColor' => 'Achtergrondkleur',
'subtitlingStyling.position' => 'Positie',
'subtitlingStyling.assOverride' => 'ASS-overschrijving',
'subtitlingStyling.overrideScale' => 'Schalen',
'subtitlingStyling.overrideForce' => 'Forceren',
'subtitlingStyling.overrideStrip' => 'Opmaak verwijderen',
'subtitlingStyling.positionTop' => 'Bovenaan',
'subtitlingStyling.positionBottom' => 'Onderaan',
'subtitlingStyling.bold' => 'Vet',
'subtitlingStyling.italic' => 'Cursief',
'subtitlingStyling.renderResolution' => 'Renderresolutie',
@@ -2766,7 +2798,7 @@ extension on TranslationsNl {
'errors.searchFailed' => ({required Object error}) => 'Zoeken mislukt: ${error}',
'errors.connectionTimeout' => ({required Object context}) => 'Verbinding time-out tijdens laden ${context}',
'errors.connectionFailed' => 'Kan geen verbinding maken met mediaserver',
'errors.failedToLoad' => ({required Object context, required Object error}) => 'Kon ${context} niet laden: ${error}',
'errors.unableToLoad' => ({required Object context}) => 'Kan ${context} niet laden. Probeer het opnieuw.',
'errors.noClientAvailable' => 'Geen client beschikbaar',
'errors.pleaseEnterToken' => 'Voer een token in',
'errors.invalidToken' => 'Ongeldig token',
@@ -2987,7 +3019,6 @@ extension on TranslationsNl {
'collections.deleted' => 'Collectie verwijderd',
'collections.deleteFailed' => 'Collectie verwijderen mislukt',
'collections.deleteFailedWithError' => ({required Object error}) => 'Collectie verwijderen mislukt: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Collectie-items laden mislukt: ${error}',
'collections.selectCollection' => 'Selecteer collectie',
'collections.collectionName' => 'Collectienaam',
'collections.enterCollectionName' => 'Voer collectienaam in',
@@ -3061,6 +3092,8 @@ extension on TranslationsNl {
'watchTogether.hostingSession' => 'Sessie Hosten',
'watchTogether.inSession' => 'In Sessie',
'watchTogether.sessionCode' => 'Sessiecode',
'watchTogether.openSessionControls' => 'Sessiebesturing voor Samen Kijken openen',
'watchTogether.copySessionCode' => 'Sessiecode kopiëren',
'watchTogether.hostControlsPlayback' => 'Host bedient het afspelen',
'watchTogether.anyoneCanControl' => 'Iedereen kan het afspelen bedienen',
'watchTogether.hostControls' => 'Host bedient',
@@ -3115,6 +3148,8 @@ extension on TranslationsNl {
'downloads.manage' => 'Beheren',
'downloads.tvShows' => 'Series',
'downloads.movies' => 'Films',
_ => null,
} ?? switch (path) {
'downloads.music' => 'Muziek',
'downloads.tracksQueued' => ({required Object count}) => '${count} nummers in wachtrij voor download',
'downloads.noDownloads' => 'Nog geen downloads',
@@ -3127,8 +3162,6 @@ extension on TranslationsNl {
'downloads.serverErrorBitrate' => 'Serverfout: bestand overschrijdt mogelijk de externe bitrate-limiet',
'downloads.episodesQueued' => ({required Object count}) => '${count} afleveringen in wachtrij voor download',
'downloads.downloadDeleted' => 'Download verwijderd',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => '"${title}" van dit apparaat verwijderen?',
'downloads.cancelledDownloadTitle' => 'Geannuleerde download',
'downloads.cancelledDownloadMessage' => 'Deze download is geannuleerd. Wat wil je doen?',
@@ -3260,6 +3293,8 @@ extension on TranslationsNl {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => 'Verbinding verloren na ${attempts} pogingen',
'companionRemote.errors.connectionLost' => 'Verbinding verloren',
'videoSettings.playbackSpeed' => 'Afspeelsnelheid',
'videoSettings.normalSpeed' => 'Normaal',
'videoSettings.sleepTimerActive' => ({required Object duration}) => 'Actief (${duration})',
'videoSettings.zoom' => 'Zoom',
'videoSettings.sleepTimer' => 'Slaaptimer',
'videoSettings.audioSync' => 'Audio synchronisatie',
@@ -3351,6 +3386,8 @@ extension on TranslationsNl {
'metadataEdit.artworkUpdated' => 'Artwork bijgewerkt',
'metadataEdit.artworkUpdateFailed' => 'Artwork bijwerken mislukt',
'metadataEdit.noArtworkAvailable' => 'Geen artwork beschikbaar',
'metadataEdit.artworkOption' => ({required Object index}) => 'Artworkoptie ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => 'Artworkoptie ${index}, geselecteerd',
'metadataEdit.notSet' => 'Niet ingesteld',
'metadataEdit.libraryDefault' => 'Bibliotheekstandaard',
'metadataEdit.accountDefault' => 'Accountstandaard',
@@ -3465,11 +3502,13 @@ extension on TranslationsNl {
'services.deviceCode.title' => ({required Object service}) => 'Plezy activeren op ${service}',
'services.deviceCode.body' => ({required Object url}) => 'Ga naar ${url} en voer deze code in:',
'services.deviceCode.openToActivate' => ({required Object service}) => 'Open ${service} om te activeren',
'services.deviceCode.copyCode' => 'Activeringscode kopiëren',
'services.deviceCode.waitingForAuthorization' => 'Wachten op autorisatie…',
'services.deviceCode.codeCopied' => 'Code gekopieerd',
'services.oauthProxy.title' => ({required Object service}) => 'Aanmelden bij ${service}',
'services.oauthProxy.body' => 'Scan deze QR-code of open de URL op een apparaat.',
'services.oauthProxy.openToSignIn' => ({required Object service}) => '${service} openen om aan te melden',
'services.oauthProxy.copyUrl' => 'Aanmeldings-URL kopiëren',
'services.oauthProxy.urlCopied' => 'URL gekopieerd',
'services.libraryFilter.title' => 'Bibliotheekfilter',
'services.libraryFilter.subtitleAllSyncing' => 'Alle bibliotheken synchroniseren',
+49 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsPl extends TranslationsSettingsEn {
@override String get creditsPattern => 'Wzorzec znacznika napisów końcowych';
@override String get creditsPatternDescription => 'Wyrażenie regularne do rozpoznawania znaczników napisów końcowych w tytułach rozdziałów';
@override String get invalidRegex => 'Nieprawidłowe wyrażenie regularne';
@override String get regex => 'Wyrażenie regularne';
@override String get downloads => 'Pobrania';
@override String get downloadLocationDescription => 'Wybierz miejsce przechowywania pobranych treści';
@override String get downloadLocationDefault => 'Domyślne (Pamięć aplikacji)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysPl extends TranslationsHotkeysEn {
@override String get clearShortcut => 'Wyczyść skrót';
@override String get noShortcutSet => 'Brak ustawionego skrótu';
@override String get currentShortcut => 'Bieżący skrót:';
@override String get pressToRecord => 'Wybierz, aby zapisać skrót klawiszowy';
@override String get recordingShortcut => 'Naciśnij teraz skrót klawiszowy';
@override late final _TranslationsHotkeysActionsPl actions = _TranslationsHotkeysActionsPl._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityPl extends TranslationsAccessibilityEn {
@override String get saturation => 'Nasycenie';
@override String get brightness => 'Jasność';
@override String get hexColor => 'Kolor szesnastkowy';
@override String get expandText => 'Rozwiń tekst';
@override String get collapseText => 'Zwiń tekst';
}
// Path: tooltips
@@ -693,6 +698,12 @@ class _TranslationsMessagesPl extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => 'Oznaczono jako obejrzane (zsynchronizuje się po połączeniu)';
@override String get markedAsUnwatchedOffline => 'Oznaczono jako nieobejrzane (zsynchronizuje się po połączeniu)';
@override String autoRemovedWatchedDownload({required Object title}) => 'Automatycznie usunięto: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('pl'))(n,
one: 'Automatycznie usunięto ${n} obejrzane pobranie',
few: 'Automatycznie usunięto ${n} obejrzane pobrania',
many: 'Automatycznie usunięto ${n} obejrzanych pobrań',
other: 'Automatycznie usunięto ${n} obejrzanego pobrania',
);
@override String get removedFromContinueWatching => 'Usunięto z kontynuowania oglądania';
@override String errorLoading({required Object error}) => 'Błąd: ${error}';
@override String get streamInterrupted => 'Strumień został przerwany. Naciśnij odtwarzanie lub przewiń, aby spróbować ponownie.';
@@ -751,6 +762,11 @@ class _TranslationsSubtitlingStylingPl extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => 'Kolor tła';
@override String get position => 'Pozycja';
@override String get assOverride => 'Nadpisywanie ASS';
@override String get overrideScale => 'Skaluj';
@override String get overrideForce => 'Wymuś';
@override String get overrideStrip => 'Usuń style';
@override String get positionTop => 'Góra';
@override String get positionBottom => 'Dół';
@override String get bold => 'Pogrubienie';
@override String get italic => 'Kursywa';
@override String get renderResolution => 'Rozdzielczość renderowania';
@@ -918,7 +934,7 @@ class _TranslationsErrorsPl extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => 'Wyszukiwanie nie powiodło się: ${error}';
@override String connectionTimeout({required Object context}) => 'Limit czasu połączenia przy ładowaniu ${context}';
@override String get connectionFailed => 'Nie można połączyć się z serwerem multimediów';
@override String failedToLoad({required Object context, required Object error}) => 'Nie udało się załadować ${context}: ${error}';
@override String unableToLoad({required Object context}) => 'Nie udało się załadować ${context}. Spróbuj ponownie.';
@override String get noClientAvailable => 'Brak dostępnego klienta';
@override String get pleaseEnterToken => 'Wprowadź token';
@override String get invalidToken => 'Nieprawidłowy token';
@@ -1182,7 +1198,6 @@ class _TranslationsCollectionsPl extends TranslationsCollectionsEn {
@override String get deleted => 'Kolekcja usunięta';
@override String get deleteFailed => 'Nie udało się usunąć kolekcji';
@override String deleteFailedWithError({required Object error}) => 'Nie udało się usunąć kolekcji: ${error}';
@override String failedToLoadItems({required Object error}) => 'Nie udało się załadować elementów kolekcji: ${error}';
@override String get selectCollection => 'Wybierz kolekcję';
@override String get collectionName => 'Nazwa kolekcji';
@override String get enterCollectionName => 'Wprowadź nazwę kolekcji';
@@ -1288,6 +1303,8 @@ class _TranslationsWatchTogetherPl extends TranslationsWatchTogetherEn {
@override String get hostingSession => 'Hostowanie sesji';
@override String get inSession => 'W sesji';
@override String get sessionCode => 'Kod sesji';
@override String get openSessionControls => 'Otwórz sterowanie sesją „Oglądaj razem”';
@override String get copySessionCode => 'Skopiuj kod sesji';
@override String get hostControlsPlayback => 'Host kontroluje odtwarzanie';
@override String get anyoneCanControl => 'Każdy może kontrolować odtwarzanie';
@override String get hostControls => 'Kontrola hosta';
@@ -1464,6 +1481,8 @@ class _TranslationsVideoSettingsPl extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => 'Prędkość odtwarzania';
@override String get normalSpeed => 'Normalna';
@override String sleepTimerActive({required Object duration}) => 'Aktywny (${duration})';
@override String get zoom => 'Zoom';
@override String get sleepTimer => 'Wyłącznik czasowy';
@override String get audioSync => 'Synchronizacja audio';
@@ -1582,6 +1601,8 @@ class _TranslationsMetadataEditPl extends TranslationsMetadataEditEn {
@override String get artworkUpdated => 'Grafika zaktualizowana';
@override String get artworkUpdateFailed => 'Nie udało się zaktualizować grafiki';
@override String get noArtworkAvailable => 'Brak dostępnej grafiki';
@override String artworkOption({required Object index}) => 'Opcja grafiki ${index}';
@override String selectedArtworkOption({required Object index}) => 'Opcja grafiki ${index}, wybrana';
@override String get notSet => 'Nie ustawiono';
@override String get libraryDefault => 'Domyślne biblioteki';
@override String get accountDefault => 'Domyślne konta';
@@ -2059,6 +2080,7 @@ class _TranslationsServicesDeviceCodePl extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => 'Aktywuj Plezy w ${service}';
@override String body({required Object url}) => 'Odwiedź ${url} i wpisz ten kod:';
@override String openToActivate({required Object service}) => 'Otwórz ${service}, aby aktywować';
@override String get copyCode => 'Skopiuj kod aktywacyjny';
@override String get waitingForAuthorization => 'Oczekiwanie na autoryzację…';
@override String get codeCopied => 'Kod skopiowany';
}
@@ -2073,6 +2095,7 @@ class _TranslationsServicesOauthProxyPl extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => 'Zaloguj się do ${service}';
@override String get body => 'Zeskanuj ten kod QR lub otwórz URL na dowolnym urządzeniu.';
@override String openToSignIn({required Object service}) => 'Otwórz ${service}, aby się zalogować';
@override String get copyUrl => 'Skopiuj adres URL logowania';
@override String get urlCopied => 'URL skopiowany';
}
@@ -2322,6 +2345,7 @@ extension on TranslationsPl {
'settings.creditsPattern' => 'Wzorzec znacznika napisów końcowych',
'settings.creditsPatternDescription' => 'Wyrażenie regularne do rozpoznawania znaczników napisów końcowych w tytułach rozdziałów',
'settings.invalidRegex' => 'Nieprawidłowe wyrażenie regularne',
'settings.regex' => 'Wyrażenie regularne',
'settings.downloads' => 'Pobrania',
'settings.downloadLocationDescription' => 'Wybierz miejsce przechowywania pobranych treści',
'settings.downloadLocationDefault' => 'Domyślne (Pamięć aplikacji)',
@@ -2427,6 +2451,8 @@ extension on TranslationsPl {
'hotkeys.clearShortcut' => 'Wyczyść skrót',
'hotkeys.noShortcutSet' => 'Brak ustawionego skrótu',
'hotkeys.currentShortcut' => 'Bieżący skrót:',
'hotkeys.pressToRecord' => 'Wybierz, aby zapisać skrót klawiszowy',
'hotkeys.recordingShortcut' => 'Naciśnij teraz skrót klawiszowy',
'hotkeys.actions.playPause' => 'Odtwórz/Pauza',
'hotkeys.actions.volumeUp' => 'Głośniej',
'hotkeys.actions.volumeDown' => 'Ciszej',
@@ -2516,6 +2542,8 @@ extension on TranslationsPl {
'accessibility.saturation' => 'Nasycenie',
'accessibility.brightness' => 'Jasność',
'accessibility.hexColor' => 'Kolor szesnastkowy',
'accessibility.expandText' => 'Rozwiń tekst',
'accessibility.collapseText' => 'Zwiń tekst',
'tooltips.shufflePlay' => 'Odtwarzanie losowe',
'tooltips.playTrailer' => 'Odtwórz zwiastun',
'tooltips.markAsWatched' => 'Oznacz jako obejrzane',
@@ -2606,19 +2634,20 @@ extension on TranslationsPl {
'messages.markedAsWatchedOffline' => 'Oznaczono jako obejrzane (zsynchronizuje się po połączeniu)',
'messages.markedAsUnwatchedOffline' => 'Oznaczono jako nieobejrzane (zsynchronizuje się po połączeniu)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Automatycznie usunięto: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('pl'))(n, one: 'Automatycznie usunięto ${n} obejrzane pobranie', few: 'Automatycznie usunięto ${n} obejrzane pobrania', many: 'Automatycznie usunięto ${n} obejrzanych pobrań', other: 'Automatycznie usunięto ${n} obejrzanego pobrania', ),
'messages.removedFromContinueWatching' => 'Usunięto z kontynuowania oglądania',
'messages.errorLoading' => ({required Object error}) => 'Błąd: ${error}',
'messages.streamInterrupted' => 'Strumień został przerwany. Naciśnij odtwarzanie lub przewiń, aby spróbować ponownie.',
'messages.liveStreamInterrupted' => 'Transmisja na żywo została przerwana. Naciśnij odtwarzanie, aby spróbować ponownie.',
'messages.fileInfoNotAvailable' => 'Informacje o pliku niedostępne',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Błąd ładowania informacji o pliku: ${error}',
'messages.errorLoadingSeries' => 'Błąd ładowania serialu',
'messages.musicNotSupported' => 'Odtwarzanie muzyki nie jest jeszcze obsługiwane',
'messages.noDescriptionAvailable' => 'Brak dostępnego opisu',
'messages.noProfilesAvailable' => 'Brak dostępnych profili',
'messages.contactAdminForProfiles' => 'Skontaktuj się z administratorem serwera, aby dodać profile',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => 'Nie można określić sekcji biblioteki dla tego elementu',
'messages.logsCleared' => 'Logi wyczyszczone',
'messages.logsCopied' => 'Logi skopiowane do schowka',
@@ -2657,6 +2686,11 @@ extension on TranslationsPl {
'subtitlingStyling.backgroundColor' => 'Kolor tła',
'subtitlingStyling.position' => 'Pozycja',
'subtitlingStyling.assOverride' => 'Nadpisywanie ASS',
'subtitlingStyling.overrideScale' => 'Skaluj',
'subtitlingStyling.overrideForce' => 'Wymuś',
'subtitlingStyling.overrideStrip' => 'Usuń style',
'subtitlingStyling.positionTop' => 'Góra',
'subtitlingStyling.positionBottom' => 'Dół',
'subtitlingStyling.bold' => 'Pogrubienie',
'subtitlingStyling.italic' => 'Kursywa',
'subtitlingStyling.renderResolution' => 'Rozdzielczość renderowania',
@@ -2770,7 +2804,7 @@ extension on TranslationsPl {
'errors.searchFailed' => ({required Object error}) => 'Wyszukiwanie nie powiodło się: ${error}',
'errors.connectionTimeout' => ({required Object context}) => 'Limit czasu połączenia przy ładowaniu ${context}',
'errors.connectionFailed' => 'Nie można połączyć się z serwerem multimediów',
'errors.failedToLoad' => ({required Object context, required Object error}) => 'Nie udało się załadować ${context}: ${error}',
'errors.unableToLoad' => ({required Object context}) => 'Nie udało się załadować ${context}. Spróbuj ponownie.',
'errors.noClientAvailable' => 'Brak dostępnego klienta',
'errors.pleaseEnterToken' => 'Wprowadź token',
'errors.invalidToken' => 'Nieprawidłowy token',
@@ -2991,7 +3025,6 @@ extension on TranslationsPl {
'collections.deleted' => 'Kolekcja usunięta',
'collections.deleteFailed' => 'Nie udało się usunąć kolekcji',
'collections.deleteFailedWithError' => ({required Object error}) => 'Nie udało się usunąć kolekcji: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Nie udało się załadować elementów kolekcji: ${error}',
'collections.selectCollection' => 'Wybierz kolekcję',
'collections.collectionName' => 'Nazwa kolekcji',
'collections.enterCollectionName' => 'Wprowadź nazwę kolekcji',
@@ -3065,6 +3098,8 @@ extension on TranslationsPl {
'watchTogether.hostingSession' => 'Hostowanie sesji',
'watchTogether.inSession' => 'W sesji',
'watchTogether.sessionCode' => 'Kod sesji',
'watchTogether.openSessionControls' => 'Otwórz sterowanie sesją „Oglądaj razem”',
'watchTogether.copySessionCode' => 'Skopiuj kod sesji',
'watchTogether.hostControlsPlayback' => 'Host kontroluje odtwarzanie',
'watchTogether.anyoneCanControl' => 'Każdy może kontrolować odtwarzanie',
'watchTogether.hostControls' => 'Kontrola hosta',
@@ -3119,6 +3154,8 @@ extension on TranslationsPl {
'downloads.manage' => 'Zarządzaj',
'downloads.tvShows' => 'Seriale TV',
'downloads.movies' => 'Filmy',
_ => null,
} ?? switch (path) {
'downloads.music' => 'Muzyka',
'downloads.tracksQueued' => ({required Object count}) => '${count} utworów w kolejce do pobrania',
'downloads.noDownloads' => 'Brak pobrań',
@@ -3131,8 +3168,6 @@ extension on TranslationsPl {
'downloads.serverErrorBitrate' => 'Błąd serwera: plik może przekraczać zdalny limit bitrate',
'downloads.episodesQueued' => ({required Object count}) => '${count} odcinków w kolejce pobierania',
'downloads.downloadDeleted' => 'Pobranie usunięte',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => 'Usunąć "${title}" z tego urządzenia?',
'downloads.cancelledDownloadTitle' => 'Anulowane pobieranie',
'downloads.cancelledDownloadMessage' => 'To pobieranie zostało anulowane. Co chcesz zrobić?',
@@ -3264,6 +3299,8 @@ extension on TranslationsPl {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => 'Połączenie utracone po ${attempts} próbach',
'companionRemote.errors.connectionLost' => 'Połączenie utracone',
'videoSettings.playbackSpeed' => 'Prędkość odtwarzania',
'videoSettings.normalSpeed' => 'Normalna',
'videoSettings.sleepTimerActive' => ({required Object duration}) => 'Aktywny (${duration})',
'videoSettings.zoom' => 'Zoom',
'videoSettings.sleepTimer' => 'Wyłącznik czasowy',
'videoSettings.audioSync' => 'Synchronizacja audio',
@@ -3355,6 +3392,8 @@ extension on TranslationsPl {
'metadataEdit.artworkUpdated' => 'Grafika zaktualizowana',
'metadataEdit.artworkUpdateFailed' => 'Nie udało się zaktualizować grafiki',
'metadataEdit.noArtworkAvailable' => 'Brak dostępnej grafiki',
'metadataEdit.artworkOption' => ({required Object index}) => 'Opcja grafiki ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => 'Opcja grafiki ${index}, wybrana',
'metadataEdit.notSet' => 'Nie ustawiono',
'metadataEdit.libraryDefault' => 'Domyślne biblioteki',
'metadataEdit.accountDefault' => 'Domyślne konta',
@@ -3469,11 +3508,13 @@ extension on TranslationsPl {
'services.deviceCode.title' => ({required Object service}) => 'Aktywuj Plezy w ${service}',
'services.deviceCode.body' => ({required Object url}) => 'Odwiedź ${url} i wpisz ten kod:',
'services.deviceCode.openToActivate' => ({required Object service}) => 'Otwórz ${service}, aby aktywować',
'services.deviceCode.copyCode' => 'Skopiuj kod aktywacyjny',
'services.deviceCode.waitingForAuthorization' => 'Oczekiwanie na autoryzację…',
'services.deviceCode.codeCopied' => 'Kod skopiowany',
'services.oauthProxy.title' => ({required Object service}) => 'Zaloguj się do ${service}',
'services.oauthProxy.body' => 'Zeskanuj ten kod QR lub otwórz URL na dowolnym urządzeniu.',
'services.oauthProxy.openToSignIn' => ({required Object service}) => 'Otwórz ${service}, aby się zalogować',
'services.oauthProxy.copyUrl' => 'Skopiuj adres URL logowania',
'services.oauthProxy.urlCopied' => 'URL skopiowany',
'services.libraryFilter.title' => 'Filtr bibliotek',
'services.libraryFilter.subtitleAllSyncing' => 'Synchronizowanie wszystkich bibliotek',
+47 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsPt extends TranslationsSettingsEn {
@override String get creditsPattern => 'Padrão de marcador de créditos';
@override String get creditsPatternDescription => 'Expressão regular para corresponder marcadores de créditos nos títulos dos capítulos';
@override String get invalidRegex => 'Expressão regular inválida';
@override String get regex => 'Expressão regular';
@override String get downloads => 'Downloads';
@override String get downloadLocationDescription => 'Escolha onde armazenar conteúdo baixado';
@override String get downloadLocationDefault => 'Padrão (Armazenamento do App)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysPt extends TranslationsHotkeysEn {
@override String get clearShortcut => 'Limpar atalho';
@override String get noShortcutSet => 'Nenhum atalho definido';
@override String get currentShortcut => 'Atalho atual:';
@override String get pressToRecord => 'Selecionar para gravar um atalho';
@override String get recordingShortcut => 'Pressione o atalho agora';
@override late final _TranslationsHotkeysActionsPt actions = _TranslationsHotkeysActionsPt._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityPt extends TranslationsAccessibilityEn {
@override String get saturation => 'Saturação';
@override String get brightness => 'Brilho';
@override String get hexColor => 'Cor hexadecimal';
@override String get expandText => 'Expandir texto';
@override String get collapseText => 'Recolher texto';
}
// Path: tooltips
@@ -693,6 +698,10 @@ class _TranslationsMessagesPt extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => 'Marcado como assistido (será sincronizado quando online)';
@override String get markedAsUnwatchedOffline => 'Marcado como não assistido (será sincronizado quando online)';
@override String autoRemovedWatchedDownload({required Object title}) => 'Removido automaticamente: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('pt'))(n,
one: 'Removido automaticamente ${n} download assistido',
other: 'Removidos automaticamente ${n} downloads assistidos',
);
@override String get removedFromContinueWatching => 'Removido de Continuar Assistindo';
@override String errorLoading({required Object error}) => 'Erro: ${error}';
@override String get streamInterrupted => 'A transmissão foi interrompida. Toque em reproduzir ou avance para tentar novamente.';
@@ -751,6 +760,11 @@ class _TranslationsSubtitlingStylingPt extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => 'Cor de Fundo';
@override String get position => 'Posição';
@override String get assOverride => 'Substituição ASS';
@override String get overrideScale => 'Dimensionar';
@override String get overrideForce => 'Forçar';
@override String get overrideStrip => 'Remover estilo';
@override String get positionTop => 'Superior';
@override String get positionBottom => 'Inferior';
@override String get bold => 'Negrito';
@override String get italic => 'Itálico';
@override String get renderResolution => 'Resolução de renderização';
@@ -918,7 +932,7 @@ class _TranslationsErrorsPt extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => 'Falha na busca: ${error}';
@override String connectionTimeout({required Object context}) => 'Tempo de conexão esgotado ao carregar ${context}';
@override String get connectionFailed => 'Não foi possível conectar ao servidor de mídia';
@override String failedToLoad({required Object context, required Object error}) => 'Falha ao carregar ${context}: ${error}';
@override String unableToLoad({required Object context}) => 'Não foi possível carregar ${context}. Tente novamente.';
@override String get noClientAvailable => 'Nenhum cliente disponível';
@override String get pleaseEnterToken => 'Insira um token';
@override String get invalidToken => 'Token inválido';
@@ -1180,7 +1194,6 @@ class _TranslationsCollectionsPt extends TranslationsCollectionsEn {
@override String get deleted => 'Coleção excluída';
@override String get deleteFailed => 'Falha ao excluir coleção';
@override String deleteFailedWithError({required Object error}) => 'Falha ao excluir coleção: ${error}';
@override String failedToLoadItems({required Object error}) => 'Falha ao carregar itens da coleção: ${error}';
@override String get selectCollection => 'Selecionar Coleção';
@override String get collectionName => 'Nome da Coleção';
@override String get enterCollectionName => 'Insira o nome da coleção';
@@ -1284,6 +1297,8 @@ class _TranslationsWatchTogetherPt extends TranslationsWatchTogetherEn {
@override String get hostingSession => 'Hospedando Sessão';
@override String get inSession => 'Em Sessão';
@override String get sessionCode => 'Código da Sessão';
@override String get openSessionControls => 'Abrir os controles da sessão Assistir Juntos';
@override String get copySessionCode => 'Copiar código da sessão';
@override String get hostControlsPlayback => 'Anfitrião controla a reprodução';
@override String get anyoneCanControl => 'Qualquer pessoa pode controlar a reprodução';
@override String get hostControls => 'Controle do anfitrião';
@@ -1460,6 +1475,8 @@ class _TranslationsVideoSettingsPt extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => 'Velocidade de Reprodução';
@override String get normalSpeed => 'Normal';
@override String sleepTimerActive({required Object duration}) => 'Ativo (${duration})';
@override String get zoom => 'Zoom';
@override String get sleepTimer => 'Timer de Sono';
@override String get audioSync => 'Sincronia de Áudio';
@@ -1578,6 +1595,8 @@ class _TranslationsMetadataEditPt extends TranslationsMetadataEditEn {
@override String get artworkUpdated => 'Arte atualizada';
@override String get artworkUpdateFailed => 'Falha ao atualizar arte';
@override String get noArtworkAvailable => 'Nenhuma arte disponível';
@override String artworkOption({required Object index}) => 'Opção de arte ${index}';
@override String selectedArtworkOption({required Object index}) => 'Opção de arte ${index}, selecionada';
@override String get notSet => 'Não definido';
@override String get libraryDefault => 'Padrão da biblioteca';
@override String get accountDefault => 'Padrão da conta';
@@ -2055,6 +2074,7 @@ class _TranslationsServicesDeviceCodePt extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => 'Ativar Plezy no ${service}';
@override String body({required Object url}) => 'Acesse ${url} e insira este código:';
@override String openToActivate({required Object service}) => 'Abrir ${service} para ativar';
@override String get copyCode => 'Copiar código de ativação';
@override String get waitingForAuthorization => 'Aguardando autorização…';
@override String get codeCopied => 'Código copiado';
}
@@ -2069,6 +2089,7 @@ class _TranslationsServicesOauthProxyPt extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => 'Entrar no ${service}';
@override String get body => 'Escaneie este código QR ou abra a URL em qualquer dispositivo.';
@override String openToSignIn({required Object service}) => 'Abrir ${service} para entrar';
@override String get copyUrl => 'Copiar URL de login';
@override String get urlCopied => 'URL copiado';
}
@@ -2318,6 +2339,7 @@ extension on TranslationsPt {
'settings.creditsPattern' => 'Padrão de marcador de créditos',
'settings.creditsPatternDescription' => 'Expressão regular para corresponder marcadores de créditos nos títulos dos capítulos',
'settings.invalidRegex' => 'Expressão regular inválida',
'settings.regex' => 'Expressão regular',
'settings.downloads' => 'Downloads',
'settings.downloadLocationDescription' => 'Escolha onde armazenar conteúdo baixado',
'settings.downloadLocationDefault' => 'Padrão (Armazenamento do App)',
@@ -2423,6 +2445,8 @@ extension on TranslationsPt {
'hotkeys.clearShortcut' => 'Limpar atalho',
'hotkeys.noShortcutSet' => 'Nenhum atalho definido',
'hotkeys.currentShortcut' => 'Atalho atual:',
'hotkeys.pressToRecord' => 'Selecionar para gravar um atalho',
'hotkeys.recordingShortcut' => 'Pressione o atalho agora',
'hotkeys.actions.playPause' => 'Reproduzir/Pausar',
'hotkeys.actions.volumeUp' => 'Aumentar Volume',
'hotkeys.actions.volumeDown' => 'Diminuir Volume',
@@ -2512,6 +2536,8 @@ extension on TranslationsPt {
'accessibility.saturation' => 'Saturação',
'accessibility.brightness' => 'Brilho',
'accessibility.hexColor' => 'Cor hexadecimal',
'accessibility.expandText' => 'Expandir texto',
'accessibility.collapseText' => 'Recolher texto',
'tooltips.shufflePlay' => 'Reprodução aleatória',
'tooltips.playTrailer' => 'Reproduzir trailer',
'tooltips.markAsWatched' => 'Marcar como assistido',
@@ -2602,19 +2628,20 @@ extension on TranslationsPt {
'messages.markedAsWatchedOffline' => 'Marcado como assistido (será sincronizado quando online)',
'messages.markedAsUnwatchedOffline' => 'Marcado como não assistido (será sincronizado quando online)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Removido automaticamente: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('pt'))(n, one: 'Removido automaticamente ${n} download assistido', other: 'Removidos automaticamente ${n} downloads assistidos', ),
'messages.removedFromContinueWatching' => 'Removido de Continuar Assistindo',
'messages.errorLoading' => ({required Object error}) => 'Erro: ${error}',
'messages.streamInterrupted' => 'A transmissão foi interrompida. Toque em reproduzir ou avance para tentar novamente.',
'messages.liveStreamInterrupted' => 'A transmissão ao vivo foi interrompida. Toque em reproduzir para tentar novamente.',
'messages.fileInfoNotAvailable' => 'Informações do arquivo não disponíveis',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Erro ao carregar info do arquivo: ${error}',
'messages.errorLoadingSeries' => 'Erro ao carregar série',
'messages.musicNotSupported' => 'Reprodução de música ainda não é suportada',
'messages.noDescriptionAvailable' => 'Nenhuma descrição disponível',
'messages.noProfilesAvailable' => 'Nenhum perfil disponível',
'messages.contactAdminForProfiles' => 'Contate o administrador do servidor para adicionar perfis',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => 'Não é possível determinar a secção da biblioteca para este item',
'messages.logsCleared' => 'Logs limpos',
'messages.logsCopied' => 'Logs copiados para a área de transferência',
@@ -2653,6 +2680,11 @@ extension on TranslationsPt {
'subtitlingStyling.backgroundColor' => 'Cor de Fundo',
'subtitlingStyling.position' => 'Posição',
'subtitlingStyling.assOverride' => 'Substituição ASS',
'subtitlingStyling.overrideScale' => 'Dimensionar',
'subtitlingStyling.overrideForce' => 'Forçar',
'subtitlingStyling.overrideStrip' => 'Remover estilo',
'subtitlingStyling.positionTop' => 'Superior',
'subtitlingStyling.positionBottom' => 'Inferior',
'subtitlingStyling.bold' => 'Negrito',
'subtitlingStyling.italic' => 'Itálico',
'subtitlingStyling.renderResolution' => 'Resolução de renderização',
@@ -2766,7 +2798,7 @@ extension on TranslationsPt {
'errors.searchFailed' => ({required Object error}) => 'Falha na busca: ${error}',
'errors.connectionTimeout' => ({required Object context}) => 'Tempo de conexão esgotado ao carregar ${context}',
'errors.connectionFailed' => 'Não foi possível conectar ao servidor de mídia',
'errors.failedToLoad' => ({required Object context, required Object error}) => 'Falha ao carregar ${context}: ${error}',
'errors.unableToLoad' => ({required Object context}) => 'Não foi possível carregar ${context}. Tente novamente.',
'errors.noClientAvailable' => 'Nenhum cliente disponível',
'errors.pleaseEnterToken' => 'Insira um token',
'errors.invalidToken' => 'Token inválido',
@@ -2987,7 +3019,6 @@ extension on TranslationsPt {
'collections.deleted' => 'Coleção excluída',
'collections.deleteFailed' => 'Falha ao excluir coleção',
'collections.deleteFailedWithError' => ({required Object error}) => 'Falha ao excluir coleção: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Falha ao carregar itens da coleção: ${error}',
'collections.selectCollection' => 'Selecionar Coleção',
'collections.collectionName' => 'Nome da Coleção',
'collections.enterCollectionName' => 'Insira o nome da coleção',
@@ -3061,6 +3092,8 @@ extension on TranslationsPt {
'watchTogether.hostingSession' => 'Hospedando Sessão',
'watchTogether.inSession' => 'Em Sessão',
'watchTogether.sessionCode' => 'Código da Sessão',
'watchTogether.openSessionControls' => 'Abrir os controles da sessão Assistir Juntos',
'watchTogether.copySessionCode' => 'Copiar código da sessão',
'watchTogether.hostControlsPlayback' => 'Anfitrião controla a reprodução',
'watchTogether.anyoneCanControl' => 'Qualquer pessoa pode controlar a reprodução',
'watchTogether.hostControls' => 'Controle do anfitrião',
@@ -3115,6 +3148,8 @@ extension on TranslationsPt {
'downloads.manage' => 'Gerenciar',
'downloads.tvShows' => 'Séries de TV',
'downloads.movies' => 'Filmes',
_ => null,
} ?? switch (path) {
'downloads.music' => 'Música',
'downloads.tracksQueued' => ({required Object count}) => '${count} faixas na fila para download',
'downloads.noDownloads' => 'Nenhum download ainda',
@@ -3127,8 +3162,6 @@ extension on TranslationsPt {
'downloads.serverErrorBitrate' => 'Erro do servidor: o arquivo pode exceder o limite remoto de bitrate',
'downloads.episodesQueued' => ({required Object count}) => '${count} episódios na fila de download',
'downloads.downloadDeleted' => 'Download excluído',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => 'Excluir "${title}" deste dispositivo?',
'downloads.cancelledDownloadTitle' => 'Download cancelado',
'downloads.cancelledDownloadMessage' => 'Este download foi cancelado. O que você deseja fazer?',
@@ -3260,6 +3293,8 @@ extension on TranslationsPt {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => 'Conexão perdida após ${attempts} tentativas',
'companionRemote.errors.connectionLost' => 'Conexão perdida',
'videoSettings.playbackSpeed' => 'Velocidade de Reprodução',
'videoSettings.normalSpeed' => 'Normal',
'videoSettings.sleepTimerActive' => ({required Object duration}) => 'Ativo (${duration})',
'videoSettings.zoom' => 'Zoom',
'videoSettings.sleepTimer' => 'Timer de Sono',
'videoSettings.audioSync' => 'Sincronia de Áudio',
@@ -3351,6 +3386,8 @@ extension on TranslationsPt {
'metadataEdit.artworkUpdated' => 'Arte atualizada',
'metadataEdit.artworkUpdateFailed' => 'Falha ao atualizar arte',
'metadataEdit.noArtworkAvailable' => 'Nenhuma arte disponível',
'metadataEdit.artworkOption' => ({required Object index}) => 'Opção de arte ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => 'Opção de arte ${index}, selecionada',
'metadataEdit.notSet' => 'Não definido',
'metadataEdit.libraryDefault' => 'Padrão da biblioteca',
'metadataEdit.accountDefault' => 'Padrão da conta',
@@ -3465,11 +3502,13 @@ extension on TranslationsPt {
'services.deviceCode.title' => ({required Object service}) => 'Ativar Plezy no ${service}',
'services.deviceCode.body' => ({required Object url}) => 'Acesse ${url} e insira este código:',
'services.deviceCode.openToActivate' => ({required Object service}) => 'Abrir ${service} para ativar',
'services.deviceCode.copyCode' => 'Copiar código de ativação',
'services.deviceCode.waitingForAuthorization' => 'Aguardando autorização…',
'services.deviceCode.codeCopied' => 'Código copiado',
'services.oauthProxy.title' => ({required Object service}) => 'Entrar no ${service}',
'services.oauthProxy.body' => 'Escaneie este código QR ou abra a URL em qualquer dispositivo.',
'services.oauthProxy.openToSignIn' => ({required Object service}) => 'Abrir ${service} para entrar',
'services.oauthProxy.copyUrl' => 'Copiar URL de login',
'services.oauthProxy.urlCopied' => 'URL copiado',
'services.libraryFilter.title' => 'Filtro de bibliotecas',
'services.libraryFilter.subtitleAllSyncing' => 'Sincronizando todas as bibliotecas',
+49 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsRu extends TranslationsSettingsEn {
@override String get creditsPattern => 'Шаблон маркера титров';
@override String get creditsPatternDescription => 'Регулярное выражение для распознавания маркеров титров в заголовках глав';
@override String get invalidRegex => 'Недопустимое регулярное выражение';
@override String get regex => 'Регулярное выражение';
@override String get downloads => 'Загрузки';
@override String get downloadLocationDescription => 'Выберите место для хранения загруженного контента';
@override String get downloadLocationDefault => 'По умолчанию (Хранилище приложения)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysRu extends TranslationsHotkeysEn {
@override String get clearShortcut => 'Очистить клавишу';
@override String get noShortcutSet => 'Сочетание не задано';
@override String get currentShortcut => 'Текущее сочетание:';
@override String get pressToRecord => 'Выберите, чтобы записать сочетание клавиш';
@override String get recordingShortcut => 'Нажмите сочетание клавиш сейчас';
@override late final _TranslationsHotkeysActionsRu actions = _TranslationsHotkeysActionsRu._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityRu extends TranslationsAccessibilityEn {
@override String get saturation => 'Насыщенность';
@override String get brightness => 'Яркость';
@override String get hexColor => 'Шестнадцатеричный цвет';
@override String get expandText => 'Развернуть текст';
@override String get collapseText => 'Свернуть текст';
}
// Path: tooltips
@@ -693,6 +698,12 @@ class _TranslationsMessagesRu extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => 'Отмечено как просмотренное (синхронизируется при подключении)';
@override String get markedAsUnwatchedOffline => 'Отмечено как непросмотренное (синхронизируется при подключении)';
@override String autoRemovedWatchedDownload({required Object title}) => 'Автоудалено: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n,
one: 'Автоматически удалена ${n} просмотренная загрузка',
few: 'Автоматически удалены ${n} просмотренные загрузки',
many: 'Автоматически удалено ${n} просмотренных загрузок',
other: 'Автоматически удалено ${n} просмотренной загрузки',
);
@override String get removedFromContinueWatching => 'Удалено из «Продолжить просмотр»';
@override String errorLoading({required Object error}) => 'Ошибка: ${error}';
@override String get streamInterrupted => 'Поток прервался. Нажмите «Воспроизвести» или перемотайте, чтобы повторить попытку.';
@@ -751,6 +762,11 @@ class _TranslationsSubtitlingStylingRu extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => 'Цвет фона';
@override String get position => 'Позиция';
@override String get assOverride => 'Переопределение ASS';
@override String get overrideScale => 'Масштабировать';
@override String get overrideForce => 'Принудительно';
@override String get overrideStrip => 'Удалить стили';
@override String get positionTop => 'Сверху';
@override String get positionBottom => 'Снизу';
@override String get bold => 'Жирный';
@override String get italic => 'Курсив';
@override String get renderResolution => 'Разрешение отрисовки';
@@ -918,7 +934,7 @@ class _TranslationsErrorsRu extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => 'Ошибка поиска: ${error}';
@override String connectionTimeout({required Object context}) => 'Таймаут подключения при загрузке ${context}';
@override String get connectionFailed => 'Не удалось подключиться к медиасерверу';
@override String failedToLoad({required Object context, required Object error}) => 'Не удалось загрузить ${context}: ${error}';
@override String unableToLoad({required Object context}) => 'Не удалось загрузить ${context}. Повторите попытку.';
@override String get noClientAvailable => 'Клиент недоступен';
@override String get pleaseEnterToken => 'Введите токен';
@override String get invalidToken => 'Недействительный токен';
@@ -1182,7 +1198,6 @@ class _TranslationsCollectionsRu extends TranslationsCollectionsEn {
@override String get deleted => 'Коллекция удалена';
@override String get deleteFailed => 'Не удалось удалить коллекцию';
@override String deleteFailedWithError({required Object error}) => 'Не удалось удалить коллекцию: ${error}';
@override String failedToLoadItems({required Object error}) => 'Не удалось загрузить элементы коллекции: ${error}';
@override String get selectCollection => 'Выбрать коллекцию';
@override String get collectionName => 'Название коллекции';
@override String get enterCollectionName => 'Введите название коллекции';
@@ -1288,6 +1303,8 @@ class _TranslationsWatchTogetherRu extends TranslationsWatchTogetherEn {
@override String get hostingSession => 'Хостинг сессии';
@override String get inSession => 'В сессии';
@override String get sessionCode => 'Код сессии';
@override String get openSessionControls => 'Открыть управление сессией «Смотреть вместе»';
@override String get copySessionCode => 'Скопировать код сессии';
@override String get hostControlsPlayback => 'Хост управляет воспроизведением';
@override String get anyoneCanControl => 'Любой может управлять воспроизведением';
@override String get hostControls => 'Управление хоста';
@@ -1464,6 +1481,8 @@ class _TranslationsVideoSettingsRu extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => 'Скорость воспроизведения';
@override String get normalSpeed => 'Обычная';
@override String sleepTimerActive({required Object duration}) => 'Активен (${duration})';
@override String get zoom => 'Масштаб';
@override String get sleepTimer => 'Таймер сна';
@override String get audioSync => 'Синхронизация аудио';
@@ -1582,6 +1601,8 @@ class _TranslationsMetadataEditRu extends TranslationsMetadataEditEn {
@override String get artworkUpdated => 'Обложка обновлена';
@override String get artworkUpdateFailed => 'Не удалось обновить обложку';
@override String get noArtworkAvailable => 'Обложки недоступны';
@override String artworkOption({required Object index}) => 'Вариант обложки ${index}';
@override String selectedArtworkOption({required Object index}) => 'Вариант обложки ${index}, выбран';
@override String get notSet => 'Не задано';
@override String get libraryDefault => 'По умолчанию библиотеки';
@override String get accountDefault => 'По умолчанию аккаунта';
@@ -2059,6 +2080,7 @@ class _TranslationsServicesDeviceCodeRu extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => 'Активируйте Plezy в ${service}';
@override String body({required Object url}) => 'Перейдите на ${url} и введите этот код:';
@override String openToActivate({required Object service}) => 'Открыть ${service} для активации';
@override String get copyCode => 'Скопировать код активации';
@override String get waitingForAuthorization => 'Ожидание авторизации…';
@override String get codeCopied => 'Код скопирован';
}
@@ -2073,6 +2095,7 @@ class _TranslationsServicesOauthProxyRu extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => 'Войти в ${service}';
@override String get body => 'Отсканируйте этот QR-код или откройте URL на любом устройстве.';
@override String openToSignIn({required Object service}) => 'Открыть ${service} для входа';
@override String get copyUrl => 'Скопировать URL для входа';
@override String get urlCopied => 'URL скопирован';
}
@@ -2322,6 +2345,7 @@ extension on TranslationsRu {
'settings.creditsPattern' => 'Шаблон маркера титров',
'settings.creditsPatternDescription' => 'Регулярное выражение для распознавания маркеров титров в заголовках глав',
'settings.invalidRegex' => 'Недопустимое регулярное выражение',
'settings.regex' => 'Регулярное выражение',
'settings.downloads' => 'Загрузки',
'settings.downloadLocationDescription' => 'Выберите место для хранения загруженного контента',
'settings.downloadLocationDefault' => 'По умолчанию (Хранилище приложения)',
@@ -2427,6 +2451,8 @@ extension on TranslationsRu {
'hotkeys.clearShortcut' => 'Очистить клавишу',
'hotkeys.noShortcutSet' => 'Сочетание не задано',
'hotkeys.currentShortcut' => 'Текущее сочетание:',
'hotkeys.pressToRecord' => 'Выберите, чтобы записать сочетание клавиш',
'hotkeys.recordingShortcut' => 'Нажмите сочетание клавиш сейчас',
'hotkeys.actions.playPause' => 'Воспроизведение/Пауза',
'hotkeys.actions.volumeUp' => 'Громкость выше',
'hotkeys.actions.volumeDown' => 'Громкость ниже',
@@ -2516,6 +2542,8 @@ extension on TranslationsRu {
'accessibility.saturation' => 'Насыщенность',
'accessibility.brightness' => 'Яркость',
'accessibility.hexColor' => 'Шестнадцатеричный цвет',
'accessibility.expandText' => 'Развернуть текст',
'accessibility.collapseText' => 'Свернуть текст',
'tooltips.shufflePlay' => 'Случайное воспроизведение',
'tooltips.playTrailer' => 'Воспроизвести трейлер',
'tooltips.markAsWatched' => 'Отметить как просмотренное',
@@ -2606,19 +2634,20 @@ extension on TranslationsRu {
'messages.markedAsWatchedOffline' => 'Отмечено как просмотренное (синхронизируется при подключении)',
'messages.markedAsUnwatchedOffline' => 'Отмечено как непросмотренное (синхронизируется при подключении)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Автоудалено: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n, one: 'Автоматически удалена ${n} просмотренная загрузка', few: 'Автоматически удалены ${n} просмотренные загрузки', many: 'Автоматически удалено ${n} просмотренных загрузок', other: 'Автоматически удалено ${n} просмотренной загрузки', ),
'messages.removedFromContinueWatching' => 'Удалено из «Продолжить просмотр»',
'messages.errorLoading' => ({required Object error}) => 'Ошибка: ${error}',
'messages.streamInterrupted' => 'Поток прервался. Нажмите «Воспроизвести» или перемотайте, чтобы повторить попытку.',
'messages.liveStreamInterrupted' => 'Прямая трансляция прервалась. Нажмите «Воспроизвести», чтобы повторить попытку.',
'messages.fileInfoNotAvailable' => 'Информация о файле недоступна',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Ошибка загрузки информации о файле: ${error}',
'messages.errorLoadingSeries' => 'Ошибка загрузки сериала',
'messages.musicNotSupported' => 'Воспроизведение музыки пока не поддерживается',
'messages.noDescriptionAvailable' => 'Описание недоступно',
'messages.noProfilesAvailable' => 'Профили недоступны',
'messages.contactAdminForProfiles' => 'Обратитесь к администратору сервера для добавления профилей',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => 'Не удаётся определить раздел библиотеки для этого элемента',
'messages.logsCleared' => 'Логи очищены',
'messages.logsCopied' => 'Логи скопированы в буфер обмена',
@@ -2657,6 +2686,11 @@ extension on TranslationsRu {
'subtitlingStyling.backgroundColor' => 'Цвет фона',
'subtitlingStyling.position' => 'Позиция',
'subtitlingStyling.assOverride' => 'Переопределение ASS',
'subtitlingStyling.overrideScale' => 'Масштабировать',
'subtitlingStyling.overrideForce' => 'Принудительно',
'subtitlingStyling.overrideStrip' => 'Удалить стили',
'subtitlingStyling.positionTop' => 'Сверху',
'subtitlingStyling.positionBottom' => 'Снизу',
'subtitlingStyling.bold' => 'Жирный',
'subtitlingStyling.italic' => 'Курсив',
'subtitlingStyling.renderResolution' => 'Разрешение отрисовки',
@@ -2770,7 +2804,7 @@ extension on TranslationsRu {
'errors.searchFailed' => ({required Object error}) => 'Ошибка поиска: ${error}',
'errors.connectionTimeout' => ({required Object context}) => 'Таймаут подключения при загрузке ${context}',
'errors.connectionFailed' => 'Не удалось подключиться к медиасерверу',
'errors.failedToLoad' => ({required Object context, required Object error}) => 'Не удалось загрузить ${context}: ${error}',
'errors.unableToLoad' => ({required Object context}) => 'Не удалось загрузить ${context}. Повторите попытку.',
'errors.noClientAvailable' => 'Клиент недоступен',
'errors.pleaseEnterToken' => 'Введите токен',
'errors.invalidToken' => 'Недействительный токен',
@@ -2991,7 +3025,6 @@ extension on TranslationsRu {
'collections.deleted' => 'Коллекция удалена',
'collections.deleteFailed' => 'Не удалось удалить коллекцию',
'collections.deleteFailedWithError' => ({required Object error}) => 'Не удалось удалить коллекцию: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Не удалось загрузить элементы коллекции: ${error}',
'collections.selectCollection' => 'Выбрать коллекцию',
'collections.collectionName' => 'Название коллекции',
'collections.enterCollectionName' => 'Введите название коллекции',
@@ -3065,6 +3098,8 @@ extension on TranslationsRu {
'watchTogether.hostingSession' => 'Хостинг сессии',
'watchTogether.inSession' => 'В сессии',
'watchTogether.sessionCode' => 'Код сессии',
'watchTogether.openSessionControls' => 'Открыть управление сессией «Смотреть вместе»',
'watchTogether.copySessionCode' => 'Скопировать код сессии',
'watchTogether.hostControlsPlayback' => 'Хост управляет воспроизведением',
'watchTogether.anyoneCanControl' => 'Любой может управлять воспроизведением',
'watchTogether.hostControls' => 'Управление хоста',
@@ -3119,6 +3154,8 @@ extension on TranslationsRu {
'downloads.manage' => 'Управление',
'downloads.tvShows' => 'Сериалы',
'downloads.movies' => 'Фильмы',
_ => null,
} ?? switch (path) {
'downloads.music' => 'Музыка',
'downloads.tracksQueued' => ({required Object count}) => '${count} треков в очереди на загрузку',
'downloads.noDownloads' => 'Загрузок пока нет',
@@ -3131,8 +3168,6 @@ extension on TranslationsRu {
'downloads.serverErrorBitrate' => 'Ошибка сервера: файл может превышать удаленный лимит bitrate',
'downloads.episodesQueued' => ({required Object count}) => '${count} эпизодов поставлено в очередь загрузки',
'downloads.downloadDeleted' => 'Загрузка удалена',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => 'Удалить "${title}" с этого устройства?',
'downloads.cancelledDownloadTitle' => 'Загрузка отменена',
'downloads.cancelledDownloadMessage' => 'Эта загрузка была отменена. Что вы хотите сделать?',
@@ -3264,6 +3299,8 @@ extension on TranslationsRu {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => 'Соединение потеряно после ${attempts} попыток',
'companionRemote.errors.connectionLost' => 'Соединение потеряно',
'videoSettings.playbackSpeed' => 'Скорость воспроизведения',
'videoSettings.normalSpeed' => 'Обычная',
'videoSettings.sleepTimerActive' => ({required Object duration}) => 'Активен (${duration})',
'videoSettings.zoom' => 'Масштаб',
'videoSettings.sleepTimer' => 'Таймер сна',
'videoSettings.audioSync' => 'Синхронизация аудио',
@@ -3355,6 +3392,8 @@ extension on TranslationsRu {
'metadataEdit.artworkUpdated' => 'Обложка обновлена',
'metadataEdit.artworkUpdateFailed' => 'Не удалось обновить обложку',
'metadataEdit.noArtworkAvailable' => 'Обложки недоступны',
'metadataEdit.artworkOption' => ({required Object index}) => 'Вариант обложки ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => 'Вариант обложки ${index}, выбран',
'metadataEdit.notSet' => 'Не задано',
'metadataEdit.libraryDefault' => 'По умолчанию библиотеки',
'metadataEdit.accountDefault' => 'По умолчанию аккаунта',
@@ -3469,11 +3508,13 @@ extension on TranslationsRu {
'services.deviceCode.title' => ({required Object service}) => 'Активируйте Plezy в ${service}',
'services.deviceCode.body' => ({required Object url}) => 'Перейдите на ${url} и введите этот код:',
'services.deviceCode.openToActivate' => ({required Object service}) => 'Открыть ${service} для активации',
'services.deviceCode.copyCode' => 'Скопировать код активации',
'services.deviceCode.waitingForAuthorization' => 'Ожидание авторизации…',
'services.deviceCode.codeCopied' => 'Код скопирован',
'services.oauthProxy.title' => ({required Object service}) => 'Войти в ${service}',
'services.oauthProxy.body' => 'Отсканируйте этот QR-код или откройте URL на любом устройстве.',
'services.oauthProxy.openToSignIn' => ({required Object service}) => 'Открыть ${service} для входа',
'services.oauthProxy.copyUrl' => 'Скопировать URL для входа',
'services.oauthProxy.urlCopied' => 'URL скопирован',
'services.libraryFilter.title' => 'Фильтр библиотек',
'services.libraryFilter.subtitleAllSyncing' => 'Синхронизация всех библиотек',
+47 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsSv extends TranslationsSettingsEn {
@override String get creditsPattern => 'Eftertextmarkörsmönster';
@override String get creditsPatternDescription => 'Reguljärt uttryck för att matcha eftertextmarkörer i kapiteltitlar';
@override String get invalidRegex => 'Ogiltigt reguljärt uttryck';
@override String get regex => 'Reguljärt uttryck';
@override String get downloads => 'Nedladdningar';
@override String get downloadLocationDescription => 'Välj var nedladdat innehåll ska lagras';
@override String get downloadLocationDefault => 'Standard (App-lagring)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysSv extends TranslationsHotkeysEn {
@override String get clearShortcut => 'Rensa genväg';
@override String get noShortcutSet => 'Ingen genväg angiven';
@override String get currentShortcut => 'Aktuell genväg:';
@override String get pressToRecord => 'Välj för att registrera en genväg';
@override String get recordingShortcut => 'Tryck på genvägen nu';
@override late final _TranslationsHotkeysActionsSv actions = _TranslationsHotkeysActionsSv._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilitySv extends TranslationsAccessibilityEn {
@override String get saturation => 'Mättnad';
@override String get brightness => 'Ljusstyrka';
@override String get hexColor => 'Hexfärg';
@override String get expandText => 'Expandera text';
@override String get collapseText => 'Fäll ihop text';
}
// Path: tooltips
@@ -693,6 +698,10 @@ class _TranslationsMessagesSv extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => 'Markerad som sedd (synkroniseras när online)';
@override String get markedAsUnwatchedOffline => 'Markerad som osedd (synkroniseras när online)';
@override String autoRemovedWatchedDownload({required Object title}) => 'Automatiskt borttagen: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('sv'))(n,
one: 'Tog automatiskt bort ${n} sedd nedladdning',
other: 'Tog automatiskt bort ${n} sedda nedladdningar',
);
@override String get removedFromContinueWatching => 'Borttagen från Fortsätt titta';
@override String errorLoading({required Object error}) => 'Fel: ${error}';
@override String get streamInterrupted => 'Uppspelningen avbröts. Tryck på play eller spola för att försöka igen.';
@@ -751,6 +760,11 @@ class _TranslationsSubtitlingStylingSv extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => 'Bakgrundsfärg';
@override String get position => 'Position';
@override String get assOverride => 'ASS-åsidosättning';
@override String get overrideScale => 'Skala';
@override String get overrideForce => 'Tvinga';
@override String get overrideStrip => 'Ta bort formatering';
@override String get positionTop => 'Överst';
@override String get positionBottom => 'Nederst';
@override String get bold => 'Fet';
@override String get italic => 'Kursiv';
@override String get renderResolution => 'Renderingsupplösning';
@@ -918,7 +932,7 @@ class _TranslationsErrorsSv extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => 'Sökning misslyckades: ${error}';
@override String connectionTimeout({required Object context}) => 'Anslutnings-timeout vid laddning ${context}';
@override String get connectionFailed => 'Kan inte ansluta till mediaserver';
@override String failedToLoad({required Object context, required Object error}) => 'Misslyckades att ladda ${context}: ${error}';
@override String unableToLoad({required Object context}) => 'Det gick inte att läsa in ${context}. Försök igen.';
@override String get noClientAvailable => 'Ingen klient tillgänglig';
@override String get pleaseEnterToken => 'Vänligen ange en token';
@override String get invalidToken => 'Ogiltig token';
@@ -1180,7 +1194,6 @@ class _TranslationsCollectionsSv extends TranslationsCollectionsEn {
@override String get deleted => 'Samling borttagen';
@override String get deleteFailed => 'Det gick inte att ta bort samlingen';
@override String deleteFailedWithError({required Object error}) => 'Det gick inte att ta bort samlingen: ${error}';
@override String failedToLoadItems({required Object error}) => 'Det gick inte att läsa in samlingsobjekt: ${error}';
@override String get selectCollection => 'Välj samling';
@override String get collectionName => 'Samlingsnamn';
@override String get enterCollectionName => 'Ange samlingsnamn';
@@ -1284,6 +1297,8 @@ class _TranslationsWatchTogetherSv extends TranslationsWatchTogetherEn {
@override String get hostingSession => 'Värd för Session';
@override String get inSession => 'I Session';
@override String get sessionCode => 'Sessionskod';
@override String get openSessionControls => 'Öppna sessionskontroller för Titta Tillsammans';
@override String get copySessionCode => 'Kopiera sessionskod';
@override String get hostControlsPlayback => 'Värden styr uppspelningen';
@override String get anyoneCanControl => 'Alla kan styra uppspelningen';
@override String get hostControls => 'Värd styr';
@@ -1460,6 +1475,8 @@ class _TranslationsVideoSettingsSv extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => 'Uppspelningshastighet';
@override String get normalSpeed => 'Normal';
@override String sleepTimerActive({required Object duration}) => 'Aktiv (${duration})';
@override String get zoom => 'Zoom';
@override String get sleepTimer => 'Sovtimer';
@override String get audioSync => 'Ljudsynkronisering';
@@ -1578,6 +1595,8 @@ class _TranslationsMetadataEditSv extends TranslationsMetadataEditEn {
@override String get artworkUpdated => 'Artwork uppdaterad';
@override String get artworkUpdateFailed => 'Kunde inte uppdatera artwork';
@override String get noArtworkAvailable => 'Ingen artwork tillgänglig';
@override String artworkOption({required Object index}) => 'Bildalternativ ${index}';
@override String selectedArtworkOption({required Object index}) => 'Bildalternativ ${index}, valt';
@override String get notSet => 'Inte angiven';
@override String get libraryDefault => 'Biblioteksstandard';
@override String get accountDefault => 'Kontostandard';
@@ -2055,6 +2074,7 @@ class _TranslationsServicesDeviceCodeSv extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => 'Aktivera Plezy på ${service}';
@override String body({required Object url}) => 'Besök ${url} och ange den här koden:';
@override String openToActivate({required Object service}) => 'Öppna ${service} för att aktivera';
@override String get copyCode => 'Kopiera aktiveringskod';
@override String get waitingForAuthorization => 'Väntar på auktorisering…';
@override String get codeCopied => 'Kod kopierad';
}
@@ -2069,6 +2089,7 @@ class _TranslationsServicesOauthProxySv extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => 'Logga in på ${service}';
@override String get body => 'Skanna den här QR-koden eller öppna URL:en på valfri enhet.';
@override String openToSignIn({required Object service}) => 'Öppna ${service} för att logga in';
@override String get copyUrl => 'Kopiera inloggnings-URL';
@override String get urlCopied => 'URL kopierad';
}
@@ -2318,6 +2339,7 @@ extension on TranslationsSv {
'settings.creditsPattern' => 'Eftertextmarkörsmönster',
'settings.creditsPatternDescription' => 'Reguljärt uttryck för att matcha eftertextmarkörer i kapiteltitlar',
'settings.invalidRegex' => 'Ogiltigt reguljärt uttryck',
'settings.regex' => 'Reguljärt uttryck',
'settings.downloads' => 'Nedladdningar',
'settings.downloadLocationDescription' => 'Välj var nedladdat innehåll ska lagras',
'settings.downloadLocationDefault' => 'Standard (App-lagring)',
@@ -2423,6 +2445,8 @@ extension on TranslationsSv {
'hotkeys.clearShortcut' => 'Rensa genväg',
'hotkeys.noShortcutSet' => 'Ingen genväg angiven',
'hotkeys.currentShortcut' => 'Aktuell genväg:',
'hotkeys.pressToRecord' => 'Välj för att registrera en genväg',
'hotkeys.recordingShortcut' => 'Tryck på genvägen nu',
'hotkeys.actions.playPause' => 'Spela/Pausa',
'hotkeys.actions.volumeUp' => 'Höj volym',
'hotkeys.actions.volumeDown' => 'Sänk volym',
@@ -2512,6 +2536,8 @@ extension on TranslationsSv {
'accessibility.saturation' => 'Mättnad',
'accessibility.brightness' => 'Ljusstyrka',
'accessibility.hexColor' => 'Hexfärg',
'accessibility.expandText' => 'Expandera text',
'accessibility.collapseText' => 'Fäll ihop text',
'tooltips.shufflePlay' => 'Blanda uppspelning',
'tooltips.playTrailer' => 'Spela trailer',
'tooltips.markAsWatched' => 'Markera som sedd',
@@ -2602,19 +2628,20 @@ extension on TranslationsSv {
'messages.markedAsWatchedOffline' => 'Markerad som sedd (synkroniseras när online)',
'messages.markedAsUnwatchedOffline' => 'Markerad som osedd (synkroniseras när online)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Automatiskt borttagen: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('sv'))(n, one: 'Tog automatiskt bort ${n} sedd nedladdning', other: 'Tog automatiskt bort ${n} sedda nedladdningar', ),
'messages.removedFromContinueWatching' => 'Borttagen från Fortsätt titta',
'messages.errorLoading' => ({required Object error}) => 'Fel: ${error}',
'messages.streamInterrupted' => 'Uppspelningen avbröts. Tryck på play eller spola för att försöka igen.',
'messages.liveStreamInterrupted' => 'Livestreamen avbröts. Tryck på play för att försöka igen.',
'messages.fileInfoNotAvailable' => 'Filinformation inte tillgänglig',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => 'Fel vid laddning av filinformation: ${error}',
'messages.errorLoadingSeries' => 'Fel vid laddning av serie',
'messages.musicNotSupported' => 'Musikuppspelning stöds inte ännu',
'messages.noDescriptionAvailable' => 'Ingen beskrivning tillgänglig',
'messages.noProfilesAvailable' => 'Inga profiler tillgängliga',
'messages.contactAdminForProfiles' => 'Kontakta din serveradministratör för att lägga till profiler',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => 'Kan inte avgöra biblioteksavdelningen för detta objekt',
'messages.logsCleared' => 'Loggar rensade',
'messages.logsCopied' => 'Loggar kopierade till urklipp',
@@ -2653,6 +2680,11 @@ extension on TranslationsSv {
'subtitlingStyling.backgroundColor' => 'Bakgrundsfärg',
'subtitlingStyling.position' => 'Position',
'subtitlingStyling.assOverride' => 'ASS-åsidosättning',
'subtitlingStyling.overrideScale' => 'Skala',
'subtitlingStyling.overrideForce' => 'Tvinga',
'subtitlingStyling.overrideStrip' => 'Ta bort formatering',
'subtitlingStyling.positionTop' => 'Överst',
'subtitlingStyling.positionBottom' => 'Nederst',
'subtitlingStyling.bold' => 'Fet',
'subtitlingStyling.italic' => 'Kursiv',
'subtitlingStyling.renderResolution' => 'Renderingsupplösning',
@@ -2766,7 +2798,7 @@ extension on TranslationsSv {
'errors.searchFailed' => ({required Object error}) => 'Sökning misslyckades: ${error}',
'errors.connectionTimeout' => ({required Object context}) => 'Anslutnings-timeout vid laddning ${context}',
'errors.connectionFailed' => 'Kan inte ansluta till mediaserver',
'errors.failedToLoad' => ({required Object context, required Object error}) => 'Misslyckades att ladda ${context}: ${error}',
'errors.unableToLoad' => ({required Object context}) => 'Det gick inte att läsa in ${context}. Försök igen.',
'errors.noClientAvailable' => 'Ingen klient tillgänglig',
'errors.pleaseEnterToken' => 'Vänligen ange en token',
'errors.invalidToken' => 'Ogiltig token',
@@ -2987,7 +3019,6 @@ extension on TranslationsSv {
'collections.deleted' => 'Samling borttagen',
'collections.deleteFailed' => 'Det gick inte att ta bort samlingen',
'collections.deleteFailedWithError' => ({required Object error}) => 'Det gick inte att ta bort samlingen: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Det gick inte att läsa in samlingsobjekt: ${error}',
'collections.selectCollection' => 'Välj samling',
'collections.collectionName' => 'Samlingsnamn',
'collections.enterCollectionName' => 'Ange samlingsnamn',
@@ -3061,6 +3092,8 @@ extension on TranslationsSv {
'watchTogether.hostingSession' => 'Värd för Session',
'watchTogether.inSession' => 'I Session',
'watchTogether.sessionCode' => 'Sessionskod',
'watchTogether.openSessionControls' => 'Öppna sessionskontroller för Titta Tillsammans',
'watchTogether.copySessionCode' => 'Kopiera sessionskod',
'watchTogether.hostControlsPlayback' => 'Värden styr uppspelningen',
'watchTogether.anyoneCanControl' => 'Alla kan styra uppspelningen',
'watchTogether.hostControls' => 'Värd styr',
@@ -3115,6 +3148,8 @@ extension on TranslationsSv {
'downloads.manage' => 'Hantera',
'downloads.tvShows' => 'TV-serier',
'downloads.movies' => 'Filmer',
_ => null,
} ?? switch (path) {
'downloads.music' => 'Musik',
'downloads.tracksQueued' => ({required Object count}) => '${count} låtar i nedladdningskö',
'downloads.noDownloads' => 'Inga nedladdningar ännu',
@@ -3127,8 +3162,6 @@ extension on TranslationsSv {
'downloads.serverErrorBitrate' => 'Serverfel: filen kan överskrida gränsen för fjärrbitrate',
'downloads.episodesQueued' => ({required Object count}) => '${count} avsnitt köade för nedladdning',
'downloads.downloadDeleted' => 'Nedladdning borttagen',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => 'Ta bort "${title}" från den här enheten?',
'downloads.cancelledDownloadTitle' => 'Avbruten nedladdning',
'downloads.cancelledDownloadMessage' => 'Den här nedladdningen avbröts. Vad vill du göra?',
@@ -3260,6 +3293,8 @@ extension on TranslationsSv {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => 'Anslutning förlorad efter ${attempts} försök',
'companionRemote.errors.connectionLost' => 'Anslutning förlorad',
'videoSettings.playbackSpeed' => 'Uppspelningshastighet',
'videoSettings.normalSpeed' => 'Normal',
'videoSettings.sleepTimerActive' => ({required Object duration}) => 'Aktiv (${duration})',
'videoSettings.zoom' => 'Zoom',
'videoSettings.sleepTimer' => 'Sovtimer',
'videoSettings.audioSync' => 'Ljudsynkronisering',
@@ -3351,6 +3386,8 @@ extension on TranslationsSv {
'metadataEdit.artworkUpdated' => 'Artwork uppdaterad',
'metadataEdit.artworkUpdateFailed' => 'Kunde inte uppdatera artwork',
'metadataEdit.noArtworkAvailable' => 'Ingen artwork tillgänglig',
'metadataEdit.artworkOption' => ({required Object index}) => 'Bildalternativ ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => 'Bildalternativ ${index}, valt',
'metadataEdit.notSet' => 'Inte angiven',
'metadataEdit.libraryDefault' => 'Biblioteksstandard',
'metadataEdit.accountDefault' => 'Kontostandard',
@@ -3465,11 +3502,13 @@ extension on TranslationsSv {
'services.deviceCode.title' => ({required Object service}) => 'Aktivera Plezy på ${service}',
'services.deviceCode.body' => ({required Object url}) => 'Besök ${url} och ange den här koden:',
'services.deviceCode.openToActivate' => ({required Object service}) => 'Öppna ${service} för att aktivera',
'services.deviceCode.copyCode' => 'Kopiera aktiveringskod',
'services.deviceCode.waitingForAuthorization' => 'Väntar på auktorisering…',
'services.deviceCode.codeCopied' => 'Kod kopierad',
'services.oauthProxy.title' => ({required Object service}) => 'Logga in på ${service}',
'services.oauthProxy.body' => 'Skanna den här QR-koden eller öppna URL:en på valfri enhet.',
'services.oauthProxy.openToSignIn' => ({required Object service}) => 'Öppna ${service} för att logga in',
'services.oauthProxy.copyUrl' => 'Kopiera inloggnings-URL',
'services.oauthProxy.urlCopied' => 'URL kopierad',
'services.libraryFilter.title' => 'Biblioteksfilter',
'services.libraryFilter.subtitleAllSyncing' => 'Synkroniserar alla bibliotek',
+46 -8
View File
@@ -358,6 +358,7 @@ class _TranslationsSettingsZh extends TranslationsSettingsEn {
@override String get creditsPattern => '片尾标记模式';
@override String get creditsPatternDescription => '用于匹配章节标题中片尾标记的正则表达式';
@override String get invalidRegex => '无效的正则表达式';
@override String get regex => '正则表达式';
@override String get downloads => '下载';
@override String get downloadLocationDescription => '选择下载内容的存储位置';
@override String get downloadLocationDefault => '默认(应用存储)';
@@ -481,6 +482,8 @@ class _TranslationsHotkeysZh extends TranslationsHotkeysEn {
@override String get clearShortcut => '清除快捷键';
@override String get noShortcutSet => '未设置快捷键';
@override String get currentShortcut => '当前快捷键:';
@override String get pressToRecord => '选择以录制快捷键';
@override String get recordingShortcut => '现在请按下快捷键';
@override late final _TranslationsHotkeysActionsZh actions = _TranslationsHotkeysActionsZh._(_root);
}
@@ -582,6 +585,8 @@ class _TranslationsAccessibilityZh extends TranslationsAccessibilityEn {
@override String get saturation => '饱和度';
@override String get brightness => '亮度';
@override String get hexColor => '十六进制颜色';
@override String get expandText => '展开文本';
@override String get collapseText => '折叠文本';
}
// Path: tooltips
@@ -693,6 +698,9 @@ class _TranslationsMessagesZh extends TranslationsMessagesEn {
@override String get markedAsWatchedOffline => '已标记为已观看 (将在联网时同步)';
@override String get markedAsUnwatchedOffline => '已标记为未观看 (将在联网时同步)';
@override String autoRemovedWatchedDownload({required Object title}) => '已自动移除: ${title}';
@override String autoRemovedWatchedDownloads({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n,
other: '已自动移除 ${n} 个看过的下载',
);
@override String get removedFromContinueWatching => '已从继续观看中移除';
@override String errorLoading({required Object error}) => '错误: ${error}';
@override String get streamInterrupted => '视频流已中断。按播放键或拖动进度条重试。';
@@ -751,6 +759,11 @@ class _TranslationsSubtitlingStylingZh extends TranslationsSubtitlingStylingEn {
@override String get backgroundColor => '背景颜色';
@override String get position => '位置';
@override String get assOverride => 'ASS 样式覆盖';
@override String get overrideScale => '缩放';
@override String get overrideForce => '强制';
@override String get overrideStrip => '移除样式';
@override String get positionTop => '顶部';
@override String get positionBottom => '底部';
@override String get bold => '粗体';
@override String get italic => '斜体';
@override String get renderResolution => '渲染分辨率';
@@ -918,7 +931,7 @@ class _TranslationsErrorsZh extends TranslationsErrorsEn {
@override String searchFailed({required Object error}) => '搜索失败: ${error}';
@override String connectionTimeout({required Object context}) => '加载 ${context} 时连接超时';
@override String get connectionFailed => '无法连接到媒体服务器';
@override String failedToLoad({required Object context, required Object error}) => '无法加载 ${context}: ${error}';
@override String unableToLoad({required Object context}) => '无法加载${context}。请重试。';
@override String get noClientAvailable => '没有可用客户端';
@override String get pleaseEnterToken => '请输入一个令牌';
@override String get invalidToken => '令牌无效';
@@ -1179,7 +1192,6 @@ class _TranslationsCollectionsZh extends TranslationsCollectionsEn {
@override String get deleted => '已删除合集';
@override String get deleteFailed => '删除合集失败';
@override String deleteFailedWithError({required Object error}) => '删除合集失败:${error}';
@override String failedToLoadItems({required Object error}) => '加载合集项目失败:${error}';
@override String get selectCollection => '选择合集';
@override String get collectionName => '合集名称';
@override String get enterCollectionName => '输入合集名称';
@@ -1282,6 +1294,8 @@ class _TranslationsWatchTogetherZh extends TranslationsWatchTogetherEn {
@override String get hostingSession => '主持会话';
@override String get inSession => '在会话中';
@override String get sessionCode => '会话代码';
@override String get openSessionControls => '打开“一起看”会话控制';
@override String get copySessionCode => '复制会话代码';
@override String get hostControlsPlayback => '主持人控制播放';
@override String get anyoneCanControl => '任何人都可以控制播放';
@override String get hostControls => '主持人控制';
@@ -1458,6 +1472,8 @@ class _TranslationsVideoSettingsZh extends TranslationsVideoSettingsEn {
// Translations
@override String get playbackSpeed => '播放速度';
@override String get normalSpeed => '正常';
@override String sleepTimerActive({required Object duration}) => '运行中(${duration}';
@override String get zoom => '缩放';
@override String get sleepTimer => '睡眠定时器';
@override String get audioSync => '音频同步';
@@ -1576,6 +1592,8 @@ class _TranslationsMetadataEditZh extends TranslationsMetadataEditEn {
@override String get artworkUpdated => '封面图片已更新';
@override String get artworkUpdateFailed => '封面图片更新失败';
@override String get noArtworkAvailable => '没有可用的封面图片';
@override String artworkOption({required Object index}) => '封面图片选项 ${index}';
@override String selectedArtworkOption({required Object index}) => '封面图片选项 ${index},已选择';
@override String get notSet => '未设置';
@override String get libraryDefault => '媒体库默认';
@override String get accountDefault => '账户默认';
@@ -2053,6 +2071,7 @@ class _TranslationsServicesDeviceCodeZh extends TranslationsServicesDeviceCodeEn
@override String title({required Object service}) => '${service} 上激活 Plezy';
@override String body({required Object url}) => '访问 ${url} 并输入此代码:';
@override String openToActivate({required Object service}) => '打开 ${service} 以激活';
@override String get copyCode => '复制激活代码';
@override String get waitingForAuthorization => '等待授权…';
@override String get codeCopied => '代码已复制';
}
@@ -2067,6 +2086,7 @@ class _TranslationsServicesOauthProxyZh extends TranslationsServicesOauthProxyEn
@override String title({required Object service}) => '登录 ${service}';
@override String get body => '扫描此二维码,或在任意设备上打开 URL。';
@override String openToSignIn({required Object service}) => '打开 ${service} 以登录';
@override String get copyUrl => '复制登录 URL';
@override String get urlCopied => 'URL 已复制';
}
@@ -2316,6 +2336,7 @@ extension on TranslationsZh {
'settings.creditsPattern' => '片尾标记模式',
'settings.creditsPatternDescription' => '用于匹配章节标题中片尾标记的正则表达式',
'settings.invalidRegex' => '无效的正则表达式',
'settings.regex' => '正则表达式',
'settings.downloads' => '下载',
'settings.downloadLocationDescription' => '选择下载内容的存储位置',
'settings.downloadLocationDefault' => '默认(应用存储)',
@@ -2421,6 +2442,8 @@ extension on TranslationsZh {
'hotkeys.clearShortcut' => '清除快捷键',
'hotkeys.noShortcutSet' => '未设置快捷键',
'hotkeys.currentShortcut' => '当前快捷键:',
'hotkeys.pressToRecord' => '选择以录制快捷键',
'hotkeys.recordingShortcut' => '现在请按下快捷键',
'hotkeys.actions.playPause' => '播放/暂停',
'hotkeys.actions.volumeUp' => '增大音量',
'hotkeys.actions.volumeDown' => '减小音量',
@@ -2510,6 +2533,8 @@ extension on TranslationsZh {
'accessibility.saturation' => '饱和度',
'accessibility.brightness' => '亮度',
'accessibility.hexColor' => '十六进制颜色',
'accessibility.expandText' => '展开文本',
'accessibility.collapseText' => '折叠文本',
'tooltips.shufflePlay' => '随机播放',
'tooltips.playTrailer' => '播放预告片',
'tooltips.markAsWatched' => '标记为已观看',
@@ -2600,19 +2625,20 @@ extension on TranslationsZh {
'messages.markedAsWatchedOffline' => '已标记为已观看 (将在联网时同步)',
'messages.markedAsUnwatchedOffline' => '已标记为未观看 (将在联网时同步)',
'messages.autoRemovedWatchedDownload' => ({required Object title}) => '已自动移除: ${title}',
'messages.autoRemovedWatchedDownloads' => ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n, other: '已自动移除 ${n} 个看过的下载', ),
'messages.removedFromContinueWatching' => '已从继续观看中移除',
'messages.errorLoading' => ({required Object error}) => '错误: ${error}',
'messages.streamInterrupted' => '视频流已中断。按播放键或拖动进度条重试。',
'messages.liveStreamInterrupted' => '直播流已中断。按播放键重试。',
'messages.fileInfoNotAvailable' => '文件信息不可用',
_ => null,
} ?? switch (path) {
'messages.errorLoadingFileInfo' => ({required Object error}) => '加载文件信息时出错: ${error}',
'messages.errorLoadingSeries' => '加载系列时出错',
'messages.musicNotSupported' => '尚不支持播放音乐',
'messages.noDescriptionAvailable' => '暂无描述',
'messages.noProfilesAvailable' => '没有可用的用户',
'messages.contactAdminForProfiles' => '请联系服务器管理员添加用户配置',
_ => null,
} ?? switch (path) {
'messages.unableToDetermineLibrarySection' => '无法确定此项目的库分区',
'messages.logsCleared' => '日志已清除',
'messages.logsCopied' => '日志已复制到剪贴板',
@@ -2651,6 +2677,11 @@ extension on TranslationsZh {
'subtitlingStyling.backgroundColor' => '背景颜色',
'subtitlingStyling.position' => '位置',
'subtitlingStyling.assOverride' => 'ASS 样式覆盖',
'subtitlingStyling.overrideScale' => '缩放',
'subtitlingStyling.overrideForce' => '强制',
'subtitlingStyling.overrideStrip' => '移除样式',
'subtitlingStyling.positionTop' => '顶部',
'subtitlingStyling.positionBottom' => '底部',
'subtitlingStyling.bold' => '粗体',
'subtitlingStyling.italic' => '斜体',
'subtitlingStyling.renderResolution' => '渲染分辨率',
@@ -2764,7 +2795,7 @@ extension on TranslationsZh {
'errors.searchFailed' => ({required Object error}) => '搜索失败: ${error}',
'errors.connectionTimeout' => ({required Object context}) => '加载 ${context} 时连接超时',
'errors.connectionFailed' => '无法连接到媒体服务器',
'errors.failedToLoad' => ({required Object context, required Object error}) => '无法加载 ${context}: ${error}',
'errors.unableToLoad' => ({required Object context}) => '无法加载${context}。请重试。',
'errors.noClientAvailable' => '没有可用客户端',
'errors.pleaseEnterToken' => '请输入一个令牌',
'errors.invalidToken' => '令牌无效',
@@ -2985,7 +3016,6 @@ extension on TranslationsZh {
'collections.deleted' => '已删除合集',
'collections.deleteFailed' => '删除合集失败',
'collections.deleteFailedWithError' => ({required Object error}) => '删除合集失败:${error}',
'collections.failedToLoadItems' => ({required Object error}) => '加载合集项目失败:${error}',
'collections.selectCollection' => '选择合集',
'collections.collectionName' => '合集名称',
'collections.enterCollectionName' => '输入合集名称',
@@ -3059,6 +3089,8 @@ extension on TranslationsZh {
'watchTogether.hostingSession' => '主持会话',
'watchTogether.inSession' => '在会话中',
'watchTogether.sessionCode' => '会话代码',
'watchTogether.openSessionControls' => '打开“一起看”会话控制',
'watchTogether.copySessionCode' => '复制会话代码',
'watchTogether.hostControlsPlayback' => '主持人控制播放',
'watchTogether.anyoneCanControl' => '任何人都可以控制播放',
'watchTogether.hostControls' => '主持人控制',
@@ -3113,6 +3145,8 @@ extension on TranslationsZh {
'downloads.manage' => '管理',
'downloads.tvShows' => '电视剧',
'downloads.movies' => '电影',
_ => null,
} ?? switch (path) {
'downloads.music' => '音乐',
'downloads.tracksQueued' => ({required Object count}) => '${count} 首曲目已加入下载队列',
'downloads.noDownloads' => '暂无下载',
@@ -3125,8 +3159,6 @@ extension on TranslationsZh {
'downloads.serverErrorBitrate' => '服务器错误:文件可能超过远程比特率限制',
'downloads.episodesQueued' => ({required Object count}) => '${count} 集已加入下载队列',
'downloads.downloadDeleted' => '下载已删除',
_ => null,
} ?? switch (path) {
'downloads.deleteConfirm' => ({required Object title}) => '要从此设备删除“${title}”吗?',
'downloads.cancelledDownloadTitle' => '已取消的下载',
'downloads.cancelledDownloadMessage' => '此下载已取消。你想怎么做?',
@@ -3258,6 +3290,8 @@ extension on TranslationsZh {
'companionRemote.errors.connectionLostAfterAttempts' => ({required Object attempts}) => '${attempts} 次尝试后连接丢失',
'companionRemote.errors.connectionLost' => '连接丢失',
'videoSettings.playbackSpeed' => '播放速度',
'videoSettings.normalSpeed' => '正常',
'videoSettings.sleepTimerActive' => ({required Object duration}) => '运行中(${duration}',
'videoSettings.zoom' => '缩放',
'videoSettings.sleepTimer' => '睡眠定时器',
'videoSettings.audioSync' => '音频同步',
@@ -3349,6 +3383,8 @@ extension on TranslationsZh {
'metadataEdit.artworkUpdated' => '封面图片已更新',
'metadataEdit.artworkUpdateFailed' => '封面图片更新失败',
'metadataEdit.noArtworkAvailable' => '没有可用的封面图片',
'metadataEdit.artworkOption' => ({required Object index}) => '封面图片选项 ${index}',
'metadataEdit.selectedArtworkOption' => ({required Object index}) => '封面图片选项 ${index},已选择',
'metadataEdit.notSet' => '未设置',
'metadataEdit.libraryDefault' => '媒体库默认',
'metadataEdit.accountDefault' => '账户默认',
@@ -3463,11 +3499,13 @@ extension on TranslationsZh {
'services.deviceCode.title' => ({required Object service}) => '${service} 上激活 Plezy',
'services.deviceCode.body' => ({required Object url}) => '访问 ${url} 并输入此代码:',
'services.deviceCode.openToActivate' => ({required Object service}) => '打开 ${service} 以激活',
'services.deviceCode.copyCode' => '复制激活代码',
'services.deviceCode.waitingForAuthorization' => '等待授权…',
'services.deviceCode.codeCopied' => '代码已复制',
'services.oauthProxy.title' => ({required Object service}) => '登录 ${service}',
'services.oauthProxy.body' => '扫描此二维码,或在任意设备上打开 URL。',
'services.oauthProxy.openToSignIn' => ({required Object service}) => '打开 ${service} 以登录',
'services.oauthProxy.copyUrl' => '复制登录 URL',
'services.oauthProxy.urlCopied' => 'URL 已复制',
'services.libraryFilter.title' => '媒体库筛选',
'services.libraryFilter.subtitleAllSyncing' => '同步所有媒体库',
+24 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "Eftertextmarkörsmönster",
"creditsPatternDescription": "Reguljärt uttryck för att matcha eftertextmarkörer i kapiteltitlar",
"invalidRegex": "Ogiltigt reguljärt uttryck",
"regex": "Reguljärt uttryck",
"downloads": "Nedladdningar",
"downloadLocationDescription": "Välj var nedladdat innehåll ska lagras",
"downloadLocationDefault": "Standard (App-lagring)",
@@ -336,6 +337,8 @@
"clearShortcut": "Rensa genväg",
"noShortcutSet": "Ingen genväg angiven",
"currentShortcut": "Aktuell genväg:",
"pressToRecord": "Välj för att registrera en genväg",
"recordingShortcut": "Tryck på genvägen nu",
"actions": {
"playPause": "Spela/Pausa",
"volumeUp": "Höj volym",
@@ -434,7 +437,9 @@
"hue": "Nyans",
"saturation": "Mättnad",
"brightness": "Ljusstyrka",
"hexColor": "Hexfärg"
"hexColor": "Hexfärg",
"expandText": "Expandera text",
"collapseText": "Fäll ihop text"
},
"tooltips": {
"shufflePlay": "Blanda uppspelning",
@@ -534,6 +539,10 @@
"markedAsWatchedOffline": "Markerad som sedd (synkroniseras när online)",
"markedAsUnwatchedOffline": "Markerad som osedd (synkroniseras när online)",
"autoRemovedWatchedDownload": "Automatiskt borttagen: ${title}",
"autoRemovedWatchedDownloads": {
"one": "Tog automatiskt bort ${n} sedd nedladdning",
"other": "Tog automatiskt bort ${n} sedda nedladdningar"
},
"removedFromContinueWatching": "Borttagen från Fortsätt titta",
"errorLoading": "Fel: ${error}",
"streamInterrupted": "Uppspelningen avbröts. Tryck på play eller spola för att försöka igen.",
@@ -585,6 +594,11 @@
"backgroundColor": "Bakgrundsfärg",
"position": "Position",
"assOverride": "ASS-åsidosättning",
"overrideScale": "Skala",
"overrideForce": "Tvinga",
"overrideStrip": "Ta bort formatering",
"positionTop": "Överst",
"positionBottom": "Nederst",
"bold": "Fet",
"italic": "Kursiv",
"renderResolution": "Renderingsupplösning",
@@ -710,7 +724,7 @@
"searchFailed": "Sökning misslyckades: ${error}",
"connectionTimeout": "Anslutnings-timeout vid laddning ${context}",
"connectionFailed": "Kan inte ansluta till mediaserver",
"failedToLoad": "Misslyckades att ladda ${context}: ${error}",
"unableToLoad": "Det gick inte att läsa in ${context}. Försök igen.",
"noClientAvailable": "Ingen klient tillgänglig",
"pleaseEnterToken": "Vänligen ange en token",
"invalidToken": "Ogiltig token",
@@ -966,7 +980,6 @@
"deleted": "Samling borttagen",
"deleteFailed": "Det gick inte att ta bort samlingen",
"deleteFailedWithError": "Det gick inte att ta bort samlingen: ${error}",
"failedToLoadItems": "Det gick inte att läsa in samlingsobjekt: ${error}",
"selectCollection": "Välj samling",
"collectionName": "Samlingsnamn",
"enterCollectionName": "Ange samlingsnamn",
@@ -1049,6 +1062,8 @@
"hostingSession": "Värd för Session",
"inSession": "I Session",
"sessionCode": "Sessionskod",
"openSessionControls": "Öppna sessionskontroller för Titta Tillsammans",
"copySessionCode": "Kopiera sessionskod",
"hostControlsPlayback": "Värden styr uppspelningen",
"anyoneCanControl": "Alla kan styra uppspelningen",
"hostControls": "Värd styr",
@@ -1262,6 +1277,8 @@
},
"videoSettings": {
"playbackSpeed": "Uppspelningshastighet",
"normalSpeed": "Normal",
"sleepTimerActive": "Aktiv (${duration})",
"zoom": "Zoom",
"sleepTimer": "Sovtimer",
"audioSync": "Ljudsynkronisering",
@@ -1359,6 +1376,8 @@
"artworkUpdated": "Artwork uppdaterad",
"artworkUpdateFailed": "Kunde inte uppdatera artwork",
"noArtworkAvailable": "Ingen artwork tillgänglig",
"artworkOption": "Bildalternativ ${index}",
"selectedArtworkOption": "Bildalternativ ${index}, valt",
"notSet": "Inte angiven",
"libraryDefault": "Biblioteksstandard",
"accountDefault": "Kontostandard",
@@ -1486,6 +1505,7 @@
"title": "Aktivera Plezy på ${service}",
"body": "Besök ${url} och ange den här koden:",
"openToActivate": "Öppna ${service} för att aktivera",
"copyCode": "Kopiera aktiveringskod",
"waitingForAuthorization": "Väntar på auktorisering…",
"codeCopied": "Kod kopierad"
},
@@ -1493,6 +1513,7 @@
"title": "Logga in på ${service}",
"body": "Skanna den här QR-koden eller öppna URL:en på valfri enhet.",
"openToSignIn": "Öppna ${service} för att logga in",
"copyUrl": "Kopiera inloggnings-URL",
"urlCopied": "URL kopierad"
},
"libraryFilter": {
+23 -3
View File
@@ -227,6 +227,7 @@
"creditsPattern": "片尾标记模式",
"creditsPatternDescription": "用于匹配章节标题中片尾标记的正则表达式",
"invalidRegex": "无效的正则表达式",
"regex": "正则表达式",
"downloads": "下载",
"downloadLocationDescription": "选择下载内容的存储位置",
"downloadLocationDefault": "默认(应用存储)",
@@ -336,6 +337,8 @@
"clearShortcut": "清除快捷键",
"noShortcutSet": "未设置快捷键",
"currentShortcut": "当前快捷键:",
"pressToRecord": "选择以录制快捷键",
"recordingShortcut": "现在请按下快捷键",
"actions": {
"playPause": "播放/暂停",
"volumeUp": "增大音量",
@@ -434,7 +437,9 @@
"hue": "色相",
"saturation": "饱和度",
"brightness": "亮度",
"hexColor": "十六进制颜色"
"hexColor": "十六进制颜色",
"expandText": "展开文本",
"collapseText": "折叠文本"
},
"tooltips": {
"shufflePlay": "随机播放",
@@ -534,6 +539,9 @@
"markedAsWatchedOffline": "已标记为已观看 (将在联网时同步)",
"markedAsUnwatchedOffline": "已标记为未观看 (将在联网时同步)",
"autoRemovedWatchedDownload": "已自动移除: ${title}",
"autoRemovedWatchedDownloads": {
"other": "已自动移除 ${n} 个看过的下载"
},
"removedFromContinueWatching": "已从继续观看中移除",
"errorLoading": "错误: ${error}",
"streamInterrupted": "视频流已中断。按播放键或拖动进度条重试。",
@@ -585,6 +593,11 @@
"backgroundColor": "背景颜色",
"position": "位置",
"assOverride": "ASS 样式覆盖",
"overrideScale": "缩放",
"overrideForce": "强制",
"overrideStrip": "移除样式",
"positionTop": "顶部",
"positionBottom": "底部",
"bold": "粗体",
"italic": "斜体",
"renderResolution": "渲染分辨率",
@@ -710,7 +723,7 @@
"searchFailed": "搜索失败: ${error}",
"connectionTimeout": "加载 ${context} 时连接超时",
"connectionFailed": "无法连接到媒体服务器",
"failedToLoad": "无法加载 ${context}: ${error}",
"unableToLoad": "无法加载${context}。请重试。",
"noClientAvailable": "没有可用客户端",
"pleaseEnterToken": "请输入一个令牌",
"invalidToken": "令牌无效",
@@ -965,7 +978,6 @@
"deleted": "已删除合集",
"deleteFailed": "删除合集失败",
"deleteFailedWithError": "删除合集失败:${error}",
"failedToLoadItems": "加载合集项目失败:${error}",
"selectCollection": "选择合集",
"collectionName": "合集名称",
"enterCollectionName": "输入合集名称",
@@ -1047,6 +1059,8 @@
"hostingSession": "主持会话",
"inSession": "在会话中",
"sessionCode": "会话代码",
"openSessionControls": "打开“一起看”会话控制",
"copySessionCode": "复制会话代码",
"hostControlsPlayback": "主持人控制播放",
"anyoneCanControl": "任何人都可以控制播放",
"hostControls": "主持人控制",
@@ -1260,6 +1274,8 @@
},
"videoSettings": {
"playbackSpeed": "播放速度",
"normalSpeed": "正常",
"sleepTimerActive": "运行中(${duration}",
"zoom": "缩放",
"sleepTimer": "睡眠定时器",
"audioSync": "音频同步",
@@ -1357,6 +1373,8 @@
"artworkUpdated": "封面图片已更新",
"artworkUpdateFailed": "封面图片更新失败",
"noArtworkAvailable": "没有可用的封面图片",
"artworkOption": "封面图片选项 ${index}",
"selectedArtworkOption": "封面图片选项 ${index},已选择",
"notSet": "未设置",
"libraryDefault": "媒体库默认",
"accountDefault": "账户默认",
@@ -1484,6 +1502,7 @@
"title": "在 ${service} 上激活 Plezy",
"body": "访问 ${url} 并输入此代码:",
"openToActivate": "打开 ${service} 以激活",
"copyCode": "复制激活代码",
"waitingForAuthorization": "等待授权…",
"codeCopied": "代码已复制"
},
@@ -1491,6 +1510,7 @@
"title": "登录 ${service}",
"body": "扫描此二维码,或在任意设备上打开 URL。",
"openToSignIn": "打开 ${service} 以登录",
"copyUrl": "复制登录 URL",
"urlCopied": "URL 已复制"
},
"libraryFilter": {
+5 -3
View File
@@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
// ignore: depend_on_referenced_packages
import 'package:shared_preferences_foundation/shared_preferences_foundation.dart';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';
import 'package:window_manager/window_manager.dart';
@@ -68,6 +69,7 @@ import 'utils/media_server_http_client.dart';
import 'utils/orientation_helper.dart';
import 'utils/watch_state_notifier.dart';
import 'i18n/strings.g.dart';
import 'widgets/app_icon.dart';
import 'focus/input_mode_tracker.dart';
import 'focus/key_event_utils.dart';
import 'package:intl/date_symbol_data_local.dart';
@@ -678,7 +680,7 @@ class _MainAppState extends State<MainApp> with WidgetsBindingObserver {
if (deleted.isNotEmpty) {
final msg = deleted.length == 1
? t.messages.autoRemovedWatchedDownload(title: deleted.first)
: t.messages.autoRemovedWatchedDownload(title: '${deleted.length} items');
: t.messages.autoRemovedWatchedDownloads(n: deleted.length);
showMainSnackBar(msg);
}
}
@@ -1404,9 +1406,9 @@ class _SetupScreenState extends State<SetupScreen> with MountedSetStateMixin {
child: CircularProgressIndicator(strokeWidth: 1.5, color: coralColor),
);
} else if (connected) {
statusIcon = const Icon(Icons.check_circle, size: 14, color: successColor);
statusIcon = const AppIcon(Symbols.check_circle_rounded, size: 14, color: successColor);
} else {
statusIcon = const Icon(Icons.cancel, size: 14, color: failColor);
statusIcon = const AppIcon(Symbols.cancel_rounded, size: 14, color: failColor);
}
return Padding(
key: ValueKey(entry.key),
+2 -1
View File
@@ -3,7 +3,7 @@ import '../media/media_sort.dart';
import '../services/api_cache.dart';
import '../services/playback_initialization_types.dart';
import '../utils/app_logger.dart';
import '../utils/media_server_http_client.dart' show AbortController, MediaServerResponse;
import '../utils/media_server_http_client.dart' show AbortController, MediaServerResponse, throwIfHttpError;
import '../utils/external_ids.dart';
import '../utils/watch_state_notifier.dart';
import 'download_resolution.dart';
@@ -732,6 +732,7 @@ mixin MediaServerCacheMixin implements MediaServerClient {
}
try {
final response = await networkCall();
throwIfHttpError(response);
if (cacheResponse) {
await _putCacheResponse(cacheKey, response.data);
}
+12 -24
View File
@@ -9,11 +9,6 @@ import '../utils/provider_extensions.dart';
/// and replacing items in lists, while allowing each screen to customize
/// which lists should be updated.
mixin ItemUpdatable<T extends StatefulWidget> on State<T> {
/// Override to enable backend-aware item refresh. [updateItem] resolves
/// the right [MediaServerClient] for the item's server. When null,
/// [updateItem] is a no-op.
String? get itemServerId => null;
/// Updates a single item in the screen's list(s) after watch state changes
///
/// Fetches the latest metadata with images (including clearLogo) and
@@ -21,17 +16,18 @@ mixin ItemUpdatable<T extends StatefulWidget> on State<T> {
///
/// If the fetch fails, the error is silently caught and the item will
/// be updated on the next full refresh.
Future<void> updateItem(String itemId) async {
Future<void> updateItem(MediaItem source) async {
if (!mounted) return;
final serverId = source.serverId;
if (serverId == null) return;
try {
final serverId = itemServerId;
if (serverId == null) return;
final updatedItem = await context.tryGetMediaClientForServer(ServerId(serverId))?.fetchItem(itemId);
final updatedItem = await context.tryGetMediaClientForServer(ServerId(serverId))?.fetchItem(source.id);
if (updatedItem != null) {
if (!mounted) return;
setState(() {
updateItemInLists(itemId, updatedItem);
updateItemInLists(source.globalKey, updatedItem);
});
}
} catch (e) {
@@ -39,20 +35,12 @@ mixin ItemUpdatable<T extends StatefulWidget> on State<T> {
}
}
/// Override this method to specify which list(s) should be updated
/// Override this method to specify which list(s) should be updated.
///
/// This method is called within [setState], so you should directly
/// modify your list(s) without calling setState again.
/// This method is called within [setState], so implementations should
/// directly modify their lists without calling [setState] again.
///
/// Example:
/// ```dart
/// @override
/// void updateItemInLists(String itemId, MediaItem updatedItem) {
/// final index = _items.indexWhere((item) => item.id == itemId);
/// if (index != -1) {
/// _items[index] = updatedItem;
/// }
/// }
/// ```
void updateItemInLists(String itemId, MediaItem updatedItem);
/// [sourceGlobalKey] is the server-qualified identity of the item that
/// initiated the refresh.
void updateItemInLists(String sourceGlobalKey, MediaItem updatedItem);
}
+12 -23
View File
@@ -421,43 +421,32 @@ class DiscoverProvider extends ChangeNotifier with DisposableChangeNotifierMixin
return fetched.items;
}
/// Refetch a single item (post-edit refresh from a hub row) and swap it
/// into whichever lists contain it. Items can come from any registered
/// server, so the owning server is resolved by scanning the visible lists.
Future<void> updateItem(String itemId) async {
/// Refetch a single item (post-edit refresh from a hub row) through its
/// source server and swap it into whichever lists contain that qualified
/// identity.
Future<void> updateItem(MediaItem source) async {
final serverId = source.serverId;
if (serverId == null) return;
try {
final serverId = _serverIdForItem(itemId);
if (serverId == null) return;
final updated = await _multiServer.getClientForServer(ServerId(serverId))?.fetchItem(itemId);
final updated = await _multiServer.getClientForServer(ServerId(serverId))?.fetchItem(source.id);
if (updated == null || isDisposed) return;
_updateItemInLists(itemId, updated);
_updateItemInLists(source.globalKey, updated);
safeNotifyListeners();
} catch (_) {
// Silently fail — the item will refresh on the next full reload.
}
}
String? _serverIdForItem(String itemId) {
for (final item in _onDeck) {
if (item.id == itemId) return item.serverId;
}
for (final hub in _hubs) {
for (final item in hub.items) {
if (item.id == itemId) return item.serverId;
}
}
return null;
}
void _updateItemInLists(String itemId, MediaItem updatedItem) {
final onDeckIndex = _onDeck.indexWhere((item) => item.id == itemId);
void _updateItemInLists(String sourceGlobalKey, MediaItem updatedItem) {
final onDeckIndex = _onDeck.indexWhere((item) => item.globalKey == sourceGlobalKey);
if (onDeckIndex != -1) {
_onDeck = List.of(_onDeck)..[onDeckIndex] = updatedItem;
}
for (var i = 0; i < _hubs.length; i++) {
final hub = _hubs[i];
final itemIndex = hub.items.indexWhere((item) => item.id == itemId);
final itemIndex = hub.items.indexWhere((item) => item.globalKey == sourceGlobalKey);
if (itemIndex != -1) {
final newItems = List<MediaItem>.from(hub.items);
newItems[itemIndex] = updatedItem;
-13
View File
@@ -112,19 +112,6 @@ class ThemeProvider extends ChangeNotifier with DisposableChangeNotifierMixin, W
_themeChannel.invokeMethod('setSplashTheme', {'mode': name});
}
String get themeModeDisplayName {
switch (_themeMode) {
case settings.ThemeMode.light:
return 'Light';
case settings.ThemeMode.dark:
return 'Dark';
case settings.ThemeMode.oled:
return 'OLED';
case settings.ThemeMode.system:
return 'System';
}
}
IconData get themeModeIcon {
switch (_themeMode) {
case settings.ThemeMode.light:
+2 -5
View File
@@ -60,9 +60,6 @@ class _ActorMediaScreenState extends BaseMediaListDetailScreen<ActorMediaScreen>
serverName: widget.serverName,
);
@override
String? get itemServerId => widget.serverId;
@override
String get title => widget.actorName;
@@ -87,9 +84,9 @@ class _ActorMediaScreenState extends BaseMediaListDetailScreen<ActorMediaScreen>
}
@override
void updateItemInLists(String itemId, MediaItem updatedItem) {
void updateItemInLists(String sourceGlobalKey, MediaItem updatedItem) {
for (final entry in loadedItems.entries) {
if (entry.value.id == itemId) {
if (entry.value.globalKey == sourceGlobalKey) {
loadedItems[entry.key] = updatedItem;
return;
}
+6 -10
View File
@@ -11,6 +11,7 @@ import '../utils/provider_extensions.dart';
import '../services/media_list_playback_launcher.dart';
import '../widgets/loading_indicator_box.dart';
import '../utils/app_logger.dart';
import '../utils/error_message_utils.dart';
import '../utils/snackbar_helper.dart';
import '../mixins/refreshable.dart';
import '../mixins/item_updatable.dart';
@@ -98,8 +99,8 @@ abstract class BaseMediaListDetailScreen<T extends StatefulWidget> extends State
}
@override
void updateItemInLists(String itemId, MediaItem updatedItem) {
final index = items.indexWhere((it) => it.id == itemId);
void updateItemInLists(String sourceGlobalKey, MediaItem updatedItem) {
final index = items.indexWhere((item) => item.globalKey == sourceGlobalKey);
if (index != -1) {
items[index] = updatedItem;
}
@@ -169,11 +170,6 @@ mixin StandardItemLoader<T extends StatefulWidget> on BaseMediaListDetailScreen<
/// Fetch items from the API (must be implemented by subclass)
Future<List<MediaItem>> fetchItems();
/// Get error message for failed load (can be overridden)
String getLoadErrorMessage(Object error) {
return 'Failed to load items: ${error.toString()}';
}
/// Get log message for successful load (can be overridden)
String getLoadSuccessMessage(int itemCount) {
return 'Loaded $itemCount items';
@@ -200,11 +196,11 @@ mixin StandardItemLoader<T extends StatefulWidget> on BaseMediaListDetailScreen<
}
appLogger.d(getLoadSuccessMessage(newItems.length));
} catch (e) {
appLogger.e('Failed to load items', error: e);
} catch (e, stackTrace) {
final message = localizedLoadErrorMessage(e, stackTrace, context: title);
if (mounted) {
setState(() {
errorMessage = getLoadErrorMessage(e);
errorMessage = message;
isLoading = false;
});
}
+7 -8
View File
@@ -9,6 +9,7 @@ import '../mixins/paginated_item_loader.dart';
import '../providers/download_provider.dart';
import '../utils/app_logger.dart';
import '../utils/dialogs.dart';
import '../utils/error_message_utils.dart';
import '../utils/download_utils.dart';
import '../utils/platform_detector.dart';
import '../utils/media_server_http_client.dart';
@@ -40,9 +41,6 @@ class _CollectionDetailScreenState extends BaseMediaListDetailScreen<CollectionD
@override
MediaItem get mediaItem => widget.collection;
@override
String? get itemServerId => widget.collection.serverId;
@override
String get title => widget.collection.title!;
@@ -72,12 +70,12 @@ class _CollectionDetailScreenState extends BaseMediaListDetailScreen<CollectionD
}
@override
void updateItemInLists(String itemId, MediaItem updatedItem) {
void updateItemInLists(String sourceGlobalKey, MediaItem updatedItem) {
// Search [loadedItems] (not the flat [items] snapshot, which only has
// the first page) so refreshing an item at a scrolled-in position updates
// the grid in place.
for (final entry in loadedItems.entries) {
if (entry.value.id == itemId) {
if (entry.value.globalKey == sourceGlobalKey) {
loadedItems[entry.key] = updatedItem;
return;
}
@@ -86,6 +84,7 @@ class _CollectionDetailScreenState extends BaseMediaListDetailScreen<CollectionD
@override
Future<void> loadItems() async {
String? loadErrorMessage;
await loadInitialPaginatedItems(
pageSize: _pageSize,
resetViewState: () {
@@ -97,8 +96,8 @@ class _CollectionDetailScreenState extends BaseMediaListDetailScreen<CollectionD
items = loaded;
isLoading = false;
},
applyError: (error, _) {
errorMessage = t.collections.failedToLoadItems(error: error.toString());
applyError: (error, stackTrace) {
errorMessage = loadErrorMessage ?? t.errors.unableToLoad(context: t.collections.collection);
isLoading = false;
},
onLoaded: (loadedCount, totalCount) {
@@ -106,7 +105,7 @@ class _CollectionDetailScreenState extends BaseMediaListDetailScreen<CollectionD
autoFocusFirstItemAfterLoad();
},
onError: (error, stackTrace) {
appLogger.e('Failed to load collection items', error: error, stackTrace: stackTrace);
loadErrorMessage = localizedLoadErrorMessage(error, stackTrace, context: t.collections.collection);
},
);
}
@@ -2,6 +2,7 @@ import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import '../../focus/focusable_text_field.dart';
@@ -15,6 +16,7 @@ import '../../theme/mono_tokens.dart';
import '../../utils/app_logger.dart';
import '../../utils/dialogs.dart';
import '../../widgets/companion_remote/discovery_view.dart';
import '../../widgets/app_icon.dart';
import '../../widgets/overlay_sheet.dart';
import '../../widgets/pill_input_decoration.dart';
@@ -39,7 +41,7 @@ class _MobileRemoteScreenState extends State<MobileRemoteScreen> {
builder: (context, provider, child) {
if (provider.isConnected) {
return IconButton(
icon: const Icon(Icons.link_off),
icon: const AppIcon(Symbols.link_off_rounded),
onPressed: () async {
final confirmed = await showConfirmDialog(
context,
@@ -74,7 +76,7 @@ class _MobileRemoteScreenState extends State<MobileRemoteScreen> {
const SizedBox(height: 8),
Text(
t.companionRemote.remote.attemptOf(current: provider.reconnectAttempts),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: Colors.grey),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: tokens(context).textMuted),
),
const SizedBox(height: 32),
Row(
@@ -161,7 +163,7 @@ class _RemoteControlContentState extends State<_RemoteControlContent> {
color: Theme.of(context).colorScheme.primaryContainer,
child: Row(
children: [
Icon(Icons.computer, color: Theme.of(context).colorScheme.onPrimaryContainer),
AppIcon(Symbols.computer_rounded, color: Theme.of(context).colorScheme.onPrimaryContainer),
const SizedBox(width: 12),
Expanded(
child: Column(
@@ -202,17 +204,17 @@ class _RemoteControlContentState extends State<_RemoteControlContent> {
ButtonSegment(
value: 0,
label: Text(t.companionRemote.remote.tabRemote),
icon: const Icon(Icons.navigation),
icon: const AppIcon(Symbols.navigation_rounded),
),
ButtonSegment(
value: 1,
label: Text(t.companionRemote.remote.tabPlay),
icon: const Icon(Icons.play_arrow),
icon: const AppIcon(Symbols.play_arrow_rounded),
),
ButtonSegment(
value: 2,
label: Text(t.companionRemote.remote.tabMore),
icon: const Icon(Icons.flash_on),
icon: const AppIcon(Symbols.flash_on_rounded),
),
],
selected: {_selectedTab},
@@ -243,17 +245,17 @@ class _RemoteControlContentState extends State<_RemoteControlContent> {
mainAxisAlignment: .spaceEvenly,
children: [
_RemoteButton(
icon: Icons.home,
icon: Symbols.home_rounded,
label: t.common.home,
onPressed: () => _sendCommand(RemoteCommandType.home),
),
_RemoteButton(
icon: Icons.arrow_back,
icon: Symbols.arrow_back_rounded,
label: t.common.back,
onPressed: () => _sendCommand(RemoteCommandType.back),
),
_RemoteButton(
icon: Icons.menu,
icon: Symbols.menu_rounded,
label: t.companionRemote.remote.menu,
onPressed: () => _sendCommand(RemoteCommandType.contextMenu),
),
@@ -271,27 +273,27 @@ class _RemoteControlContentState extends State<_RemoteControlContent> {
alignment: WrapAlignment.center,
children: [
_RemoteChip(
icon: Icons.explore,
icon: Symbols.explore_rounded,
label: t.companionRemote.remote.tabDiscover,
onPressed: () => _sendCommand(RemoteCommandType.tabDiscover),
),
_RemoteChip(
icon: Icons.video_library,
icon: Symbols.video_library_rounded,
label: t.companionRemote.remote.tabLibraries,
onPressed: () => _sendCommand(RemoteCommandType.tabLibraries),
),
_RemoteChip(
icon: Icons.search,
icon: Symbols.search_rounded,
label: t.companionRemote.remote.tabSearch,
onPressed: () => _showSearchSheet(switchToSearchTab: true),
),
_RemoteChip(
icon: Icons.download,
icon: Symbols.download_rounded,
label: t.companionRemote.remote.tabDownloads,
onPressed: () => _sendCommand(RemoteCommandType.tabDownloads),
),
_RemoteChip(
icon: Icons.settings,
icon: Symbols.settings_rounded,
label: t.companionRemote.remote.tabSettings,
onPressed: () => _sendCommand(RemoteCommandType.tabSettings),
),
@@ -310,13 +312,13 @@ class _RemoteControlContentState extends State<_RemoteControlContent> {
mainAxisAlignment: .center,
children: [
_RemoteButton(
icon: Icons.skip_previous,
icon: Symbols.skip_previous_rounded,
label: t.companionRemote.remote.previous,
onPressed: () => _sendCommand(RemoteCommandType.previousTrack),
),
const SizedBox(width: 16),
_RemoteButton(
icon: Icons.play_arrow,
icon: Symbols.play_arrow_rounded,
label: t.companionRemote.remote.playPause,
size: 64,
iconSize: 36,
@@ -324,7 +326,7 @@ class _RemoteControlContentState extends State<_RemoteControlContent> {
),
const SizedBox(width: 16),
_RemoteButton(
icon: Icons.skip_next,
icon: Symbols.skip_next_rounded,
label: t.companionRemote.remote.next,
onPressed: () => _sendCommand(RemoteCommandType.nextTrack),
),
@@ -335,19 +337,19 @@ class _RemoteControlContentState extends State<_RemoteControlContent> {
mainAxisAlignment: .center,
children: [
_RemoteButton(
icon: Icons.replay_10,
icon: Symbols.replay_10_rounded,
label: t.companionRemote.remote.seekBack,
onPressed: () => _sendCommand(RemoteCommandType.seekBackward),
),
const SizedBox(width: 16),
_RemoteButton(
icon: Icons.stop,
icon: Symbols.stop_rounded,
label: t.companionRemote.remote.stop,
onPressed: () => _sendCommand(RemoteCommandType.stop),
),
const SizedBox(width: 16),
_RemoteButton(
icon: Icons.forward_10,
icon: Symbols.forward_10_rounded,
label: t.companionRemote.remote.seekForward,
onPressed: () => _sendCommand(RemoteCommandType.seekForward),
),
@@ -360,19 +362,19 @@ class _RemoteControlContentState extends State<_RemoteControlContent> {
mainAxisAlignment: .center,
children: [
_RemoteButton(
icon: Icons.volume_off,
icon: Symbols.volume_off_rounded,
label: t.common.mute,
onPressed: () => _sendCommand(RemoteCommandType.volumeMute),
),
const SizedBox(width: 16),
_RemoteButton(
icon: Icons.volume_down,
icon: Symbols.volume_down_rounded,
label: t.companionRemote.remote.volumeDown,
onPressed: () => _sendCommand(RemoteCommandType.volumeDown),
),
const SizedBox(width: 16),
_RemoteButton(
icon: Icons.volume_up,
icon: Symbols.volume_up_rounded,
label: t.companionRemote.remote.volumeUp,
onPressed: () => _sendCommand(RemoteCommandType.volumeUp),
),
@@ -393,20 +395,21 @@ class _RemoteControlContentState extends State<_RemoteControlContent> {
runSpacing: 12,
alignment: WrapAlignment.center,
children: [
if (!isPlayerActive) _RemoteCard(icon: Icons.search, label: t.common.search, onPressed: _showSearchSheet),
if (!isPlayerActive)
_RemoteCard(icon: Symbols.search_rounded, label: t.common.search, onPressed: _showSearchSheet),
if (isPlayerActive) ...[
_RemoteCard(
icon: Icons.fullscreen,
icon: Symbols.fullscreen_rounded,
label: t.companionRemote.remote.fullscreen,
onPressed: () => _sendCommand(RemoteCommandType.fullscreen),
),
_RemoteCard(
icon: Icons.subtitles,
icon: Symbols.subtitles_rounded,
label: t.companionRemote.remote.subtitles,
onPressed: () => _sendCommand(RemoteCommandType.subtitles),
),
_RemoteCard(
icon: Icons.audiotrack,
icon: Symbols.audiotrack_rounded,
label: t.companionRemote.remote.audio,
onPressed: () => _sendCommand(RemoteCommandType.audioTracks),
),
@@ -442,22 +445,22 @@ class _DPad extends StatelessWidget {
),
_DPadZone(
startAngle: -135,
icon: Icons.keyboard_arrow_up,
icon: Symbols.keyboard_arrow_up_rounded,
onTap: () => onCommand(RemoteCommandType.dpadUp),
),
_DPadZone(
startAngle: -45,
icon: Icons.keyboard_arrow_right,
icon: Symbols.keyboard_arrow_right_rounded,
onTap: () => onCommand(RemoteCommandType.dpadRight),
),
_DPadZone(
startAngle: 45,
icon: Icons.keyboard_arrow_down,
icon: Symbols.keyboard_arrow_down_rounded,
onTap: () => onCommand(RemoteCommandType.dpadDown),
),
_DPadZone(
startAngle: 135,
icon: Icons.keyboard_arrow_left,
icon: Symbols.keyboard_arrow_left_rounded,
onTap: () => onCommand(RemoteCommandType.dpadLeft),
),
Center(child: _DPadCenter(onTap: () => onCommand(RemoteCommandType.select))),
@@ -512,7 +515,7 @@ class _DPadZoneState extends State<_DPadZone> {
duration: tokens(context).fast,
color: _pressed ? colors.primary.withValues(alpha: 0.8) : colors.primary,
alignment: widget.iconAlignment,
child: Icon(widget.icon, size: 28, color: colors.onPrimary),
child: AppIcon(widget.icon, size: 28, color: colors.onPrimary),
),
),
),
@@ -626,7 +629,7 @@ class _RemoteButton extends StatelessWidget {
onPressed();
},
style: FilledButton.styleFrom(padding: .zero, shape: const CircleBorder()),
child: Icon(icon, size: iconSize),
child: AppIcon(icon, size: iconSize),
),
),
const SizedBox(height: 4),
@@ -650,7 +653,7 @@ class _RemoteChip extends StatelessWidget {
HapticFeedback.lightImpact();
onPressed();
},
icon: Icon(icon, size: 18),
icon: AppIcon(icon, size: 18),
label: Text(label),
);
}
@@ -689,8 +692,11 @@ class _SearchBottomSheetState extends State<_SearchBottomSheet> with ControllerD
decoration: pillInputDecoration(
context,
hintText: t.companionRemote.remote.searchHint,
prefixIcon: const Icon(Icons.search),
suffixIcon: IconButton(icon: const Icon(Icons.send), onPressed: () => _submit(_controller.text)),
prefixIcon: const AppIcon(Symbols.search_rounded),
suffixIcon: IconButton(
icon: const AppIcon(Symbols.send_rounded),
onPressed: () => _submit(_controller.text),
),
),
onSubmitted: _submit,
),
@@ -723,7 +729,7 @@ class _RemoteCard extends StatelessWidget {
child: Column(
mainAxisAlignment: .center,
children: [
Icon(icon, size: 32),
AppIcon(icon, size: 32),
const SizedBox(height: 8),
Text(label, style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center),
],
+26 -47
View File
@@ -9,7 +9,6 @@ import '../widgets/server_activities_button.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import '../focus/focusable_action_bar.dart';
import '../focus/focusable_button.dart';
import '../focus/hub_vertical_navigation.dart';
import '../focus/input_mode_tracker.dart';
import '../focus/key_event_utils.dart';
@@ -57,6 +56,7 @@ import '../utils/layout_constants.dart';
import '../utils/platform_detector.dart';
import '../theme/mono_tokens.dart';
import 'libraries/content_state_builder.dart';
import 'libraries/state_messages.dart';
import 'main_screen.dart';
import 'settings/settings_screen.dart';
import '../watch_together/watch_together.dart';
@@ -87,10 +87,7 @@ class _DiscoverScreenState extends State<DiscoverScreen>
bool get _hasMoreContinueWatching => _discover.hasMoreContinueWatching;
bool get _isLoading => _discover.isLoading;
bool get _areHubsLoading => _discover.areHubsLoading;
String? get _errorMessage {
final raw = _discover.errorMessage;
return raw == null ? null : t.errors.failedToLoad(context: t.discover.title, error: raw);
}
String? get _errorMessage => _discover.errorMessage == null ? null : t.errors.unableToLoad(context: t.discover.title);
bool _switchingProfile = false;
final PageController _heroController = PageController();
@@ -1115,19 +1112,10 @@ class _DiscoverScreenState extends State<DiscoverScreen>
),
if (_onDeck.isEmpty && _hubs.isEmpty && !_areHubsLoading)
SliverFillRemaining(
child: Center(
child: Column(
mainAxisAlignment: .center,
children: [
const AppIcon(Symbols.movie_rounded, fill: 1, size: 64, color: Colors.grey),
const SizedBox(height: 16),
Text(t.discover.noContentAvailable),
const SizedBox(height: 8),
Text(t.discover.addMediaToLibraries, style: const TextStyle(color: Colors.grey)),
],
),
),
SliverEmptyState(
message: t.discover.noContentAvailable,
subtitle: t.discover.addMediaToLibraries,
icon: Symbols.movie_rounded,
),
SliverToBoxAdapter(child: SizedBox(height: 24 + bottomPadding)),
@@ -1195,35 +1183,18 @@ class _DiscoverScreenState extends State<DiscoverScreen>
children: [
if (_isLoading || (_areHubsLoading && browseHubs.isEmpty)) const Center(child: CircularProgressIndicator()),
if (_errorMessage != null)
Center(
child: Column(
mainAxisSize: .min,
children: [
const AppIcon(Symbols.error_outline_rounded, fill: 1, size: 64, color: Colors.grey),
const SizedBox(height: 16),
Text(_errorMessage!),
const SizedBox(height: 16),
FocusableButton(
autofocus: true,
onPressed: _discover.load,
useBackgroundFocus: true,
child: FilledButton(onPressed: _discover.load, child: Text(t.common.retry)),
),
],
),
ErrorStateWidget(
message: _errorMessage!,
icon: Symbols.error_outline_rounded,
onRetry: _discover.load,
actionAutofocus: true,
actionUseBackgroundFocus: true,
),
if (!_isLoading && _errorMessage == null && browseHubs.isEmpty && !_areHubsLoading)
Center(
child: Column(
mainAxisAlignment: .center,
children: [
const AppIcon(Symbols.movie_rounded, fill: 1, size: 64, color: Colors.grey),
const SizedBox(height: 16),
Text(t.discover.noContentAvailable),
const SizedBox(height: 8),
Text(t.discover.addMediaToLibraries, style: const TextStyle(color: Colors.grey)),
],
),
EmptyStateWidget(
message: t.discover.noContentAvailable,
subtitle: t.discover.addMediaToLibraries,
icon: Symbols.movie_rounded,
),
if (browseHubs.isNotEmpty)
Positioned(
@@ -1550,13 +1521,20 @@ class _DiscoverScreenState extends State<DiscoverScreen>
child: Builder(
builder: (context) {
final dpr = MediaImageHelper.effectiveDevicePixelRatio(context);
final targetWidth = (heroLogoWidth * dpr).round();
final targetHeight = (heroLogoHeight * dpr).round();
final (memCacheWidth, memCacheHeight) = MediaImageHelper.getMemCacheDimensions(
displayWidth: targetWidth,
displayHeight: targetHeight,
imageType: ImageType.heroLogo,
);
final logoUrl = MediaImageHelper.getOptimizedImageUrl(
client: heroClient,
thumbPath: heroItem.clearLogoPath,
maxWidth: heroLogoWidth,
maxHeight: heroLogoHeight,
devicePixelRatio: dpr,
imageType: ImageType.logo,
imageType: ImageType.heroLogo,
);
return blurArtwork(
@@ -1565,7 +1543,8 @@ class _DiscoverScreenState extends State<DiscoverScreen>
cacheManager: PlexImageCacheManager.instance,
filterQuality: FilterQuality.medium,
fit: BoxFit.contain,
memCacheWidth: (heroLogoWidth * dpr).clamp(200, isTv ? 1000 : 800).round(),
memCacheWidth: memCacheWidth,
memCacheHeight: memCacheHeight,
alignment: alignLeft ? Alignment.bottomLeft : Alignment.bottomCenter,
placeholder: (context, url) => const SizedBox.shrink(),
errorBuilder: (context, error, stackTrace) {
+41 -39
View File
@@ -20,11 +20,11 @@ import '../../widgets/settings_builder.dart';
import '../../utils/global_key_utils.dart';
import '../../mixins/tab_navigation_mixin.dart';
import '../../mixins/refreshable.dart';
import '../../utils/grid_size_calculator.dart';
import '../../utils/media_image_helper.dart';
import '../../utils/platform_detector.dart';
import '../../widgets/desktop_app_bar.dart';
import '../../widgets/focusable_media_card.dart';
import '../../widgets/media_grid_delegate.dart';
import '../../widgets/media_card_sliver_layout.dart';
import '../../widgets/download_tree_view.dart';
import '../main_screen.dart';
import '../libraries/state_messages.dart';
@@ -153,7 +153,7 @@ class DownloadsScreenState extends State<DownloadsScreen>
onNavigateDown: _focusCurrentTab,
actions: [
FocusableAction(
icon: Symbols.rule_settings,
icon: Symbols.rule_settings_rounded,
tooltip: t.downloads.activeSyncRules,
onPressed: () =>
Navigator.push(context, MaterialPageRoute(builder: (_) => const SyncRulesScreen())),
@@ -306,50 +306,37 @@ class _DownloadsGridContentState extends State<_DownloadsGridContent> {
const effectivePadding = EdgeInsets.only(left: 8, right: 8, top: 8);
return SettingsBuilder(
prefs: const [SettingsService.libraryDensity, SettingsService.tvFullCardLayout],
prefs: const [SettingsService.viewMode, SettingsService.libraryDensity, SettingsService.tvFullCardLayout],
builder: (context) {
final settings = SettingsService.instance;
final viewMode = settings.read(SettingsService.viewMode);
final density = settings.read(SettingsService.libraryDensity);
final fullCardLayout = PlatformDetector.isTV() && settings.read(SettingsService.tvFullCardLayout);
final maxCrossAxisExtent = GridSizeCalculator.getMaxCrossAxisExtent(context, density);
// Use LayoutBuilder to get actual available width (accounting for sidebar)
return LayoutBuilder(
builder: (context, constraints) {
final availableWidth = constraints.maxWidth - effectivePadding.left - effectivePadding.right;
final gridSpacing = MediaGridDelegate.spacingFor(context: context, fullBleedImage: fullCardLayout);
final columnCount = GridSizeCalculator.getColumnCount(
availableWidth,
maxCrossAxisExtent,
crossAxisSpacing: gridSpacing,
);
return GridView.builder(
addAutomaticKeepAlives: false,
addSemanticIndexes: false,
padding: effectivePadding,
// Allow focus decoration to render outside scroll bounds
clipBehavior: Clip.none,
gridDelegate: MediaGridDelegate.createDelegate(
context: context,
density: density,
fullBleedImage: fullCardLayout,
),
return CustomScrollView(
// Allow focus decoration to render outside scroll bounds.
clipBehavior: Clip.none,
slivers: [
MediaCardSliverLayout(
viewMode: viewMode,
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
final isFirstColumn = GridSizeCalculator.isFirstColumn(index, columnCount);
final isFirst = index == 0;
density: density,
padding: effectivePadding,
fullBleedImage: fullCardLayout,
itemBuilder: (context, position) {
final item = items[position.index];
return FocusableMediaCard(
item: item,
focusNode: isFirst ? _firstItemFocusNode : null,
focusNode: position.index == 0 ? _firstItemFocusNode : null,
disableScale: position.disableScale,
onBack: widget.onBack,
isOffline: true, // Downloaded content works without server
fullBleedImage: fullCardLayout,
onNavigateLeft: isFirstColumn ? _navigateToSidebar : null,
fullBleedImage: fullCardLayout && position.isGrid,
onNavigateLeft: position.isFirstColumn ? _navigateToSidebar : null,
);
},
);
},
),
],
);
},
);
@@ -437,7 +424,22 @@ class _DownloadedMusicContentState extends State<_DownloadedMusicContent> {
final artist = album.albumArtistTitle;
final serverId = album.serverId;
final localArt = serverId == null ? null : provider.getArtworkLocalPath(ServerId(serverId), album.thumbPath);
final ImageProvider? localCoverImage;
if (localArt == null) {
localCoverImage = null;
} else {
final dpr = MediaImageHelper.effectiveDevicePixelRatio(context);
final (memWidth, memHeight) = MediaImageHelper.getMemCacheDimensions(
displayWidth: (48 * dpr).round(),
displayHeight: (48 * dpr).round(),
imageType: ImageType.square,
);
localCoverImage = MediaImageHelper.boundedDecode(
FileImage(File(localArt)),
memWidth: memWidth,
memHeight: memHeight,
);
}
Widget fallbackCover() => Container(
width: 48,
height: 48,
@@ -451,9 +453,9 @@ class _DownloadedMusicContentState extends State<_DownloadedMusicContent> {
children: [
ClipRRect(
borderRadius: BorderRadius.circular(tk.radiusSm),
child: localArt != null
? Image.file(
File(localArt),
child: localCoverImage != null
? Image(
image: localCoverImage,
width: 48,
height: 48,
fit: BoxFit.cover,
+3 -2
View File
@@ -14,6 +14,7 @@ import '../../services/sync_rule_executor.dart';
import '../../utils/content_utils.dart';
import '../../utils/download_utils.dart';
import '../../widgets/focused_scroll_scaffold.dart';
import '../../widgets/app_icon.dart';
import '../libraries/state_messages.dart';
import '../../i18n/strings.g.dart';
@@ -247,7 +248,7 @@ class _SyncRuleTileState extends State<_SyncRuleTile> {
dense: true,
visualDensity: const VisualDensity(vertical: -3),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
leading: Icon(_leadingIcon(), color: rule.enabled ? Colors.teal : null, size: 20),
leading: AppIcon(_leadingIcon(), color: rule.enabled ? Colors.teal : null, size: 20),
title: Text(title, maxLines: 1, overflow: .ellipsis),
subtitle: Column(
crossAxisAlignment: .start,
@@ -334,7 +335,7 @@ class _SwipeRevealDeleteActionState extends State<_SwipeRevealDeleteAction> {
child: Column(
mainAxisAlignment: .center,
children: [
Icon(Symbols.delete_rounded, color: colorScheme.onError, size: 20),
AppIcon(Symbols.delete_rounded, color: colorScheme.onError, size: 20),
const SizedBox(height: 2),
Text(
t.common.delete,
+19 -19
View File
@@ -11,6 +11,7 @@ import '../widgets/ios_status_bar_tap_scroll_to_top.dart';
import '../widgets/settings_builder.dart';
import '../widgets/focusable_media_card.dart';
import '../widgets/media_card_sliver_layout.dart';
import '../widgets/overlay_sheet.dart';
import '../widgets/skeleton_media_card.dart';
/// Extract the stable id from a [MediaItem]/[MediaPlaylist] for use as a
@@ -89,25 +90,24 @@ mixin FocusableDetailScreenMixin<T extends StatefulWidget> on State<T>, GridFocu
FocusNode _focusNodeForIndex(int index) => focusNodeForIndex(index, firstItemFocusNode, prefix: 'detail_grid_item');
/// Wrap [slivers] in the standard detail-screen scaffold — PopScope that
/// defers to [handleBackNavigation], plus a Scaffold with a CustomScrollView
/// bound as the primary scroll view. Callers build the slivers themselves
/// (typically `[appBar, ...header, ...buildStateSlivers(), grid]`).
/// Wrap [slivers] in the standard detail-screen scaffold — an overlay-sheet
/// host that defers route back to [handleBackNavigation], plus a Scaffold
/// with a CustomScrollView bound as the primary scroll view. Callers build
/// the slivers themselves (typically
/// `[appBar, ...header, ...buildStateSlivers(), grid]`).
Widget buildDetailScaffold({required List<Widget> slivers}) {
return PopScope(
canPop: PlatformDetector.isHandheldIOS(context),
onPopInvokedWithResult: (didPop, result) {
if (BackKeyCoordinator.consumeIfHandled()) return;
if (didPop) return;
final shouldPop = handleBackNavigation();
if (shouldPop && mounted) {
Navigator.pop(context);
}
},
child: PrimaryScrollController(
return PrimaryScrollController(
controller: scrollController,
child: IosStatusBarTapScrollToTop(
controller: scrollController,
child: IosStatusBarTapScrollToTop(
controller: scrollController,
child: OverlaySheetHost(
canPop: PlatformDetector.isHandheldIOS(context),
onSystemBack: () {
if (BackKeyCoordinator.consumeIfHandled()) return;
if (handleBackNavigation() && mounted) {
Navigator.pop(context);
}
},
child: Scaffold(body: CustomScrollView(primary: true, slivers: slivers)),
),
),
@@ -165,7 +165,7 @@ mixin FocusableDetailScreenMixin<T extends StatefulWidget> on State<T>, GridFocu
/// for album grids); null keeps the stock poster geometry.
Widget buildFocusableGrid({
required List<dynamic> items,
required void Function(String itemId) onRefresh,
required void Function(MediaItem source) onRefresh,
String? collectionId,
VoidCallback? onListRefresh,
CardShape? shape,
@@ -216,7 +216,7 @@ mixin FocusableDetailScreenMixin<T extends StatefulWidget> on State<T>, GridFocu
Widget buildSparseFocusableGrid({
required int totalItems,
required MediaItem? Function(int index) itemAt,
required void Function(String itemId) onRefresh,
required void Function(MediaItem source) onRefresh,
void Function(int index)? onSkeletonVisible,
String? collectionId,
VoidCallback? onListRefresh,
+10 -17
View File
@@ -14,6 +14,7 @@ import '../services/settings_service.dart';
import '../widgets/settings_builder.dart';
import '../utils/app_logger.dart';
import '../utils/continuation_pagination_coordinator.dart';
import '../utils/error_message_utils.dart';
import '../utils/platform_detector.dart';
import '../utils/plex_library_section_utils.dart';
import '../utils/provider_extensions.dart';
@@ -325,11 +326,11 @@ class _HubDetailScreenState extends State<HubDetailScreen>
}
appLogger.d('Loaded ${items.length} items for hub: ${widget.hub.title}');
} catch (e) {
appLogger.e('Failed to load hub content', error: e);
} catch (e, stackTrace) {
final message = localizedLoadErrorMessage(e, stackTrace, context: widget.hub.title);
if (!mounted) return;
setState(() {
_errorMessage = t.messages.errorLoading(error: e.toString());
_errorMessage = message;
_isLoading = false;
});
}
@@ -383,30 +384,22 @@ class _HubDetailScreenState extends State<HubDetailScreen>
return items.where((item) => int.tryParse(item.libraryId ?? '') == sectionFilter).toList();
}
Future<void> _handleItemRefresh(String ratingKey) async {
final itemIndex = _items.indexWhere((item) => item.id == ratingKey);
final filteredIndex = _filteredItems.indexWhere((item) => item.id == ratingKey);
final existing = itemIndex != -1
? _items[itemIndex]
: filteredIndex != -1
? _filteredItems[filteredIndex]
: null;
if (existing == null) return;
final serverId = existing.serverId ?? widget.hub.serverId;
Future<void> _handleItemRefresh(MediaItem source) async {
final serverId = source.serverId;
if (serverId == null) return;
try {
final updated = await context.tryGetMediaClientForServer(ServerId(serverId))?.fetchItem(ratingKey);
final updated = await context.tryGetMediaClientForServer(ServerId(serverId))?.fetchItem(source.id);
if (updated == null || !mounted) return;
setState(() {
final currentItemIndex = _items.indexWhere((item) => item.id == ratingKey);
final currentItemIndex = _items.indexWhere((item) => item.globalKey == source.globalKey);
if (currentItemIndex != -1) _items[currentItemIndex] = updated;
final currentFilteredIndex = _filteredItems.indexWhere((item) => item.id == ratingKey);
final currentFilteredIndex = _filteredItems.indexWhere((item) => item.globalKey == source.globalKey);
if (currentFilteredIndex != -1) _filteredItems[currentFilteredIndex] = updated;
});
if (_selectedSort != null) _applySort();
} catch (e) {
appLogger.d('Item refresh skipped for: $ratingKey', error: e);
appLogger.d('Item refresh skipped for: ${source.globalKey}', error: e);
}
}
+4 -6
View File
@@ -159,6 +159,10 @@ class _AlphaJumpBarState extends State<AlphaJumpBar> {
}
});
if (selectResult != KeyEventResult.ignored) return selectResult;
if (widget.onBack != null) {
final backResult = handleBackKeyAction(event, widget.onBack!);
if (backResult != KeyEventResult.ignored) return backResult;
}
if (event is! KeyDownEvent && event is! KeyRepeatEvent) {
return KeyEventResult.ignored;
@@ -182,12 +186,6 @@ class _AlphaJumpBarState extends State<AlphaJumpBar> {
widget.onNavigateLeft?.call();
return KeyEventResult.handled;
}
if (event.logicalKey == LogicalKeyboardKey.escape ||
event.logicalKey == LogicalKeyboardKey.goBack ||
event.logicalKey == LogicalKeyboardKey.gameButtonB) {
widget.onBack?.call();
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
}
@@ -14,6 +14,8 @@ class SliverErrorState extends StatelessWidget {
final VoidCallback? onActionNavigateLeft;
final VoidCallback? onActionNavigateRight;
final VoidCallback? onActionBack;
final bool actionAutofocus;
final bool actionUseBackgroundFocus;
const SliverErrorState({
super.key,
@@ -26,6 +28,8 @@ class SliverErrorState extends StatelessWidget {
this.onActionNavigateLeft,
this.onActionNavigateRight,
this.onActionBack,
this.actionAutofocus = false,
this.actionUseBackgroundFocus = false,
});
@override
@@ -34,13 +38,15 @@ class SliverErrorState extends StatelessWidget {
message: message,
icon: Symbols.error_outline_rounded,
onRetry: onRetry,
retryLabel: retryLabel ?? t.common.retry,
retryLabel: retryLabel,
actionFocusNode: actionFocusNode,
onActionNavigateUp: onActionNavigateUp,
onActionNavigateDown: onActionNavigateDown,
onActionNavigateLeft: onActionNavigateLeft,
onActionNavigateRight: onActionNavigateRight,
onActionBack: onActionBack,
actionAutofocus: actionAutofocus,
actionUseBackgroundFocus: actionUseBackgroundFocus,
),
);
}
+8 -20
View File
@@ -1,4 +1,3 @@
import 'dart:io';
import 'dart:ui';
import '../../media/ids.dart';
@@ -19,7 +18,6 @@ import '../../utils/provider_extensions.dart';
import '../../widgets/app_menu.dart';
import '../../widgets/media_context_menu.dart';
import '../../widgets/optimized_media_image.dart';
import '../../widgets/overlay_sheet.dart';
import '../../widgets/watched_indicator.dart';
import '../../theme/mono_tokens.dart';
import '../../i18n/strings.g.dart';
@@ -42,7 +40,7 @@ class FolderTreeItem extends StatefulWidget {
final FocusNode? focusNode;
final VoidCallback? onNavigateUp;
final VoidCallback? onNavigateLeft;
final void Function(String itemId)? onRefresh;
final void Function(MediaItem source)? onRefresh;
final VoidCallback? onListRefresh;
final String? serverId;
@@ -150,23 +148,13 @@ class _FolderTreeItemState extends State<FolderTreeItem> with ContextMenuTapMixi
final previousFocus = FocusManager.instance.primaryFocus;
final position = lastTapPosition;
final fromKeyboard = position == null;
final useBottomSheet = Platform.isIOS || Platform.isAndroid;
String? selected;
if (useBottomSheet) {
selected = await OverlaySheetController.showAdaptive<String>(
context,
showDragHandle: true,
builder: (context) => AppMenuSheet<String>(title: _rowTitle(), entries: entries, focusFirstItem: fromKeyboard),
);
} else {
selected = await showAppMenu<String>(
context,
entries: entries,
position: position ?? _rowCenter(),
focusFirstItem: fromKeyboard,
);
}
final selected = await showAdaptiveAppMenu<String>(
context,
title: _rowTitle(),
entries: entries,
position: position ?? _rowCenter(),
focusFirstItem: fromKeyboard,
);
if (!mounted) return;
+9 -8
View File
@@ -22,7 +22,7 @@ class FolderTreeView extends StatefulWidget {
final String libraryKey;
final String? serverId; // Server this library belongs to
final MediaKind? libraryKind;
final void Function(String)? onRefresh;
final void Function(MediaItem source)? onRefresh;
final FocusNode? firstItemFocusNode;
final VoidCallback? onNavigateUp;
final VoidCallback? onNavigateLeft;
@@ -117,11 +117,12 @@ class FolderTreeViewState extends State<FolderTreeView> {
});
appLogger.d('Loaded ${folders.length} root folders');
} catch (e) {
} catch (e, stackTrace) {
if (!mounted || epoch != _loadEpoch) return;
final message = localizedLoadErrorMessage(e, stackTrace, context: t.libraries.folders);
setState(() {
_errorMessage = mapUnexpectedErrorToMessage(e, context: t.libraries.folders);
_errorMessage = message;
_isLoadingRoot = false;
});
}
@@ -181,10 +182,10 @@ class FolderTreeViewState extends State<FolderTreeView> {
});
appLogger.d('Loaded ${children.length} children for folder: ${folder.title}');
} catch (e) {
} catch (e, stackTrace) {
if (!mounted || epoch != _loadEpoch) return;
final message = mapUnexpectedErrorToMessage(e, context: t.libraries.folders);
final message = localizedLoadErrorMessage(e, stackTrace, context: t.libraries.folders);
setState(() {
_loadingFolders.remove(folderIdentity);
// Drop partial pages so a retry refetches instead of leaving a
@@ -228,14 +229,14 @@ class FolderTreeViewState extends State<FolderTreeView> {
_loadFolderChildren(parent);
}
Future<void> _handleItemTap(MediaItem item) async {
Future<void> _handleItemTap(MediaItem item, MediaItem? parent) async {
final result = await navigateToMediaItem(context, item, onRefresh: widget.onRefresh);
if (!mounted) return;
switch (result) {
case MediaNavigationResult.unsupported:
showAppSnackBar(context, t.messages.musicNotSupported);
case MediaNavigationResult.listRefreshNeeded:
widget.onRefresh?.call(item.id);
_refreshAfterDeletion(parent);
case MediaNavigationResult.navigated:
case MediaNavigationResult.librarySelected:
break;
@@ -398,7 +399,7 @@ class FolderTreeViewState extends State<FolderTreeView> {
isLoading: isLoading,
serverId: widget.serverId,
onExpand: isExpandable ? () => _toggleFolder(item) : null,
onTap: !isExpandable ? () => _handleItemTap(item) : null,
onTap: !isExpandable ? () => _handleItemTap(item, entry.parent) : null,
onPlayAll: canPlayFolder ? () => _handleFolderPlay(item) : null,
onShuffle: canPlayFolder ? () => _handleFolderShuffle(item) : null,
focusNode: isFirstRootItem ? widget.firstItemFocusNode : null,
+1 -1
View File
@@ -512,7 +512,7 @@ class _LibrariesScreenState extends State<LibrariesScreen>
}
@override
void updateItemInLists(String itemId, MediaItem updatedItem) {
void updateItemInLists(String sourceGlobalKey, MediaItem updatedItem) {
// Delegate to the active tab — parent doesn't maintain its own item list
}
+22 -1
View File
@@ -3,6 +3,7 @@ import 'package:plezy/widgets/app_icon.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../../focus/focusable_button.dart';
import '../../i18n/strings.g.dart';
/// Base widget for displaying state messages (empty, error, etc.)
/// Provides a consistent UI pattern for showing icons, messages, and actions
@@ -43,6 +44,12 @@ class StateMessageWidget extends StatelessWidget {
final VoidCallback? onActionNavigateRight;
final VoidCallback? onActionBack;
/// Whether the action button should request focus when it appears.
final bool actionAutofocus;
/// Whether the action button uses a background focus indicator.
final bool actionUseBackgroundFocus;
const StateMessageWidget({
super.key,
required this.message,
@@ -61,6 +68,8 @@ class StateMessageWidget extends StatelessWidget {
this.onActionNavigateRight,
this.onActionBack,
this.actionIcon,
this.actionAutofocus = false,
this.actionUseBackgroundFocus = false,
});
@override
@@ -106,6 +115,8 @@ class StateMessageWidget extends StatelessWidget {
onNavigateRight: onActionNavigateRight,
onBack: onActionBack,
onPressed: onAction,
autofocus: actionAutofocus,
useBackgroundFocus: actionUseBackgroundFocus,
child: FilledButton.icon(
onPressed: onAction,
icon: AppIcon(actionIcon ?? Symbols.refresh_rounded, fill: 1),
@@ -197,6 +208,12 @@ class ErrorStateWidget extends StatelessWidget {
/// Optional callback for retry action
final VoidCallback? onRetry;
/// Whether the retry action should request focus when it appears.
final bool actionAutofocus;
/// Whether the retry action uses a background focus indicator.
final bool actionUseBackgroundFocus;
/// Optional label for the retry button
final String? retryLabel;
final FocusNode? actionFocusNode;
@@ -218,6 +235,8 @@ class ErrorStateWidget extends StatelessWidget {
this.onActionNavigateLeft,
this.onActionNavigateRight,
this.onActionBack,
this.actionAutofocus = false,
this.actionUseBackgroundFocus = false,
});
@override
@@ -228,7 +247,7 @@ class ErrorStateWidget extends StatelessWidget {
iconColor: Theme.of(context).colorScheme.error,
textColor: Theme.of(context).colorScheme.error,
onAction: onRetry,
actionLabel: retryLabel ?? 'Retry',
actionLabel: retryLabel ?? t.common.retry,
actionIcon: Symbols.refresh_rounded,
actionFocusNode: actionFocusNode,
onActionNavigateUp: onActionNavigateUp,
@@ -236,6 +255,8 @@ class ErrorStateWidget extends StatelessWidget {
onActionNavigateLeft: onActionNavigateLeft,
onActionNavigateRight: onActionNavigateRight,
onActionBack: onActionBack,
actionAutofocus: actionAutofocus,
actionUseBackgroundFocus: actionUseBackgroundFocus,
);
}
}
@@ -2,7 +2,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import '../../../focus/input_mode_tracker.dart';
import '../../../media/media_library.dart';
import '../../../utils/app_logger.dart';
import '../../../utils/error_message_utils.dart';
import '../../../mixins/library_tab_state.dart';
import '../../../mixins/refreshable.dart';
import '../content_state_builder.dart';
@@ -246,12 +246,12 @@ abstract class BaseLibraryTabState<T, W extends BaseLibraryTab<T>> extends State
widget.onDataLoaded!();
});
}
} catch (e) {
} catch (e, stackTrace) {
final message = localizedLoadErrorMessage(e, stackTrace, context: errorContext);
if (!mounted) return;
appLogger.e('Error loading $errorContext', error: e);
setState(() {
_errorMessage = 'Failed to load $errorContext: ${e.toString()}';
_errorMessage = message;
_isLoading = false;
});
}
@@ -4,7 +4,6 @@ import '../../../media/ids.dart';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import 'package:cached_network_image_ce/cached_network_image.dart';
import '../../../media/library_first_character.dart';
import '../../../media/library_query.dart';
import '../../../media/media_backend.dart';
@@ -12,13 +11,11 @@ import '../../../media/media_item.dart';
import '../../../media/media_kind.dart';
import '../../../providers/multi_server_provider.dart';
import '../../../utils/media_server_http_client.dart';
import '../../../exceptions/media_server_exceptions.dart';
import '../../../focus/dpad_navigator.dart';
import '../../../focus/input_mode_tracker.dart';
import '../../../media/media_filter.dart';
import '../../../media/media_sort.dart';
import '../../../widgets/settings_builder.dart';
import '../../../services/image_cache_service.dart';
import '../../../services/library_query_translator.dart';
import '../../../services/plex_constants.dart';
import '../../../utils/error_message_utils.dart';
@@ -108,9 +105,6 @@ class _LibraryBrowseTabState extends BaseLibraryTabState<MediaItem, LibraryBrows
DeletionAware,
PaginatedItemLoader<MediaItem, LibraryBrowseTab>,
SkeletonUpgradeScheduler {
@override
String? get itemServerId => widget.library.serverId;
String _toGlobalKey(String ratingKey, {required ServerId serverId}) => buildGlobalKey(serverId, ratingKey);
@override
@@ -161,7 +155,7 @@ class _LibraryBrowseTabState extends BaseLibraryTabState<MediaItem, LibraryBrows
final affectedIds = {event.itemId, ...event.parentChain};
for (final item in loadedItems.values) {
if (affectedIds.contains(item.id)) {
unawaited(updateItem(item.id));
unawaited(updateItem(item));
}
}
}
@@ -219,9 +213,9 @@ class _LibraryBrowseTabState extends BaseLibraryTabState<MediaItem, LibraryBrows
int get itemCount => totalSize;
@override
void updateItemInLists(String itemId, MediaItem updatedMetadata) {
void updateItemInLists(String sourceGlobalKey, MediaItem updatedMetadata) {
for (final entry in loadedItems.entries) {
if (entry.value.id == itemId) {
if (entry.value.globalKey == sourceGlobalKey) {
loadedItems[entry.key] = updatedMetadata;
break;
}
@@ -600,10 +594,11 @@ class _LibraryBrowseTabState extends BaseLibraryTabState<MediaItem, LibraryBrows
// Load items and first characters in parallel
// _loadItems manages its own requestId internally
await Future.wait([_loadItems(), _loadFirstCharacters(requestId: firstCharactersGeneration)]);
} catch (e) {
} catch (e, stackTrace) {
if (!mounted) return;
final message = localizedLoadErrorMessage(e, stackTrace, context: t.libraries.content);
setState(() {
errorMessage = _getErrorMessage(e);
errorMessage = message;
isLoading = false;
});
}
@@ -727,10 +722,11 @@ class _LibraryBrowseTabState extends BaseLibraryTabState<MediaItem, LibraryBrows
widget.onDataLoaded!();
});
}
} catch (e) {
} catch (e, stackTrace) {
if (generation != _contentRequestId || !mounted) return;
final message = localizedLoadErrorMessage(e, stackTrace, context: t.libraries.content);
setState(() {
errorMessage = _getErrorMessage(e);
errorMessage = message;
isLoading = false;
});
}
@@ -827,13 +823,6 @@ class _LibraryBrowseTabState extends BaseLibraryTabState<MediaItem, LibraryBrows
}
}
String _getErrorMessage(dynamic error) {
if (error is MediaServerHttpException) {
return mapHttpErrorToMessage(error, context: t.libraries.content);
}
return mapUnexpectedErrorToMessage(error, context: t.libraries.content);
}
Widget _buildBrowseOptionsSheet(BuildContext sheetContext) {
final controller = OverlaySheetController.of(sheetContext);
return BottomSheetPageScaffold(
@@ -1634,7 +1623,7 @@ class _LibraryBrowseTabState extends BaseLibraryTabState<MediaItem, LibraryBrows
final item = items[i];
final thumb = item.posterThumb(mode: episodePosterMode);
if (thumb == null || thumb.isEmpty) continue;
final imageType = item.usesWideAspectRatio(episodePosterMode) ? ImageType.thumb : ImageType.poster;
final imageType = MediaImageHelper.cardImageType(item, episodePosterMode);
final imageUrl = MediaImageHelper.getOptimizedImageUrl(
client: client,
@@ -1648,19 +1637,14 @@ class _LibraryBrowseTabState extends BaseLibraryTabState<MediaItem, LibraryBrows
final scaledWidth = itemWidth * devicePixelRatio;
final scaledHeight = itemHeight * devicePixelRatio;
final (_, memHeight) = MediaImageHelper.getMemCacheDimensions(
final (memWidth, memHeight) = MediaImageHelper.getMemCacheDimensions(
displayWidth: scaledWidth.isFinite && scaledWidth > 0 ? scaledWidth.round() : 0,
displayHeight: scaledHeight.isFinite && scaledHeight > 0 ? scaledHeight.round() : 0,
imageType: imageType,
);
precacheImage(
CachedNetworkImageProvider(
imageUrl,
cacheManager: PlexImageCacheManager.instance,
headers: const {'User-Agent': 'Plezy'},
maxHeight: memHeight,
),
MediaImageHelper.serverArtworkProvider(imageUrl: imageUrl, memWidth: memWidth, memHeight: memHeight),
context,
).ignore();
}
@@ -6,7 +6,7 @@ import '../../../media/media_item.dart';
import '../../../mixins/library_tab_focus_mixin.dart';
import '../../../mixins/paginated_item_loader.dart';
import '../../../services/settings_service.dart';
import '../../../utils/app_logger.dart';
import '../../../utils/error_message_utils.dart';
import '../../../utils/layout_constants.dart';
import '../../../utils/library_refresh_notifier.dart';
import '../../../utils/media_server_http_client.dart';
@@ -79,6 +79,7 @@ class _LibraryCollectionsTabState extends BaseLibraryTabState<MediaItem, Library
@override
Future<void> loadItems() async {
String? loadErrorMessage;
await loadInitialPaginatedItems(
pageSize: _pageSize,
resetViewState: () {
@@ -90,8 +91,8 @@ class _LibraryCollectionsTabState extends BaseLibraryTabState<MediaItem, Library
items = loaded;
isLoading = false;
},
applyError: (error, _) {
errorMessage = 'Failed to load $errorContext: ${error.toString()}';
applyError: (error, stackTrace) {
errorMessage = loadErrorMessage ?? t.errors.unableToLoad(context: errorContext);
isLoading = false;
},
onLoaded: (_, _) {
@@ -104,7 +105,7 @@ class _LibraryCollectionsTabState extends BaseLibraryTabState<MediaItem, Library
}
},
onError: (error, stackTrace) {
appLogger.e('Error loading $errorContext', error: error, stackTrace: stackTrace);
loadErrorMessage = localizedLoadErrorMessage(error, stackTrace, context: errorContext);
},
);
}
@@ -7,7 +7,7 @@ import '../../../media/media_playlist.dart';
import '../../../mixins/library_tab_focus_mixin.dart';
import '../../../mixins/paginated_item_loader.dart';
import '../../../services/settings_service.dart';
import '../../../utils/app_logger.dart';
import '../../../utils/error_message_utils.dart';
import '../../../utils/layout_constants.dart';
import '../../../utils/library_refresh_notifier.dart';
import '../../../utils/media_server_http_client.dart';
@@ -84,6 +84,7 @@ class _LibraryPlaylistsTabState extends BaseLibraryTabState<MediaPlaylist, Libra
@override
Future<void> loadItems() async {
String? loadErrorMessage;
await loadInitialPaginatedItems(
pageSize: _pageSize,
resetViewState: () {
@@ -95,8 +96,8 @@ class _LibraryPlaylistsTabState extends BaseLibraryTabState<MediaPlaylist, Libra
items = loaded;
isLoading = false;
},
applyError: (error, _) {
errorMessage = 'Failed to load $errorContext: ${error.toString()}';
applyError: (error, stackTrace) {
errorMessage = loadErrorMessage ?? t.errors.unableToLoad(context: errorContext);
isLoading = false;
},
onLoaded: (_, _) {
@@ -109,7 +110,7 @@ class _LibraryPlaylistsTabState extends BaseLibraryTabState<MediaPlaylist, Libra
}
},
onError: (error, stackTrace) {
appLogger.e('Error loading $errorContext', error: error, stackTrace: stackTrace);
loadErrorMessage = localizedLoadErrorMessage(error, stackTrace, context: errorContext);
},
);
}
@@ -59,9 +59,6 @@ class _LibraryRecommendedTabState extends BaseLibraryTabState<MediaHub, LibraryR
super.dispose();
}
@override
String? get itemServerId => widget.library.serverId;
@override
String? get watchStateServerId => widget.library.serverId;
@@ -106,12 +103,12 @@ class _LibraryRecommendedTabState extends BaseLibraryTabState<MediaHub, LibraryR
}
@override
void updateItemInLists(String itemId, MediaItem updatedItem) {
void updateItemInLists(String sourceGlobalKey, MediaItem updatedItem) {
// Update the item in any hub that contains it. MediaHub items are
// immutable lists; rebuild the affected hub in-place.
for (var i = 0; i < items.length; i++) {
final hub = items[i];
final itemIndex = hub.items.indexWhere((item) => item.id == itemId);
final itemIndex = hub.items.indexWhere((item) => item.globalKey == sourceGlobalKey);
if (itemIndex != -1) {
final newItems = List<MediaItem>.from(hub.items);
newItems[itemIndex] = updatedItem;
@@ -131,16 +128,16 @@ class _LibraryRecommendedTabState extends BaseLibraryTabState<MediaHub, LibraryR
}
final affectedIds = {event.itemId, ...event.parentChain};
final refreshIds = <String>{};
final refreshItems = <String, MediaItem>{};
for (final hub in items) {
for (final item in hub.items) {
if (affectedIds.contains(item.id)) {
refreshIds.add(item.id);
refreshItems[item.globalKey] = item;
}
}
}
for (final itemId in refreshIds) {
unawaited(updateItem(itemId));
for (final item in refreshItems.values) {
unawaited(updateItem(item));
}
}
+13 -28
View File
@@ -7,7 +7,6 @@ import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import '../../focus/focusable_action_bar.dart';
import '../../focus/focusable_button.dart';
import '../../i18n/strings.g.dart';
import '../../media/live_tv_support.dart';
import '../../media/media_server_client.dart';
@@ -19,13 +18,14 @@ import '../../providers/multi_server_provider.dart';
import '../../services/settings_service.dart';
import '../../widgets/settings_builder.dart';
import '../../utils/app_logger.dart';
import '../../utils/error_message_utils.dart';
import '../../utils/desktop_window_padding.dart';
import '../../utils/platform_detector.dart';
import '../../utils/serial_future_queue.dart';
import '../../utils/snackbar_helper.dart';
import '../../widgets/app_icon.dart';
import '../../widgets/focusable_tab_chip.dart';
import '../../widgets/overlay_sheet.dart';
import '../libraries/state_messages.dart';
import 'reorder_favorites_sheet.dart';
import 'tabs/guide_tab.dart';
import 'tabs/recordings_tab.dart';
@@ -420,12 +420,12 @@ class _LiveTvScreenState extends State<LiveTvScreen>
if (mounted) _focusCurrentTab();
});
}
} catch (e) {
appLogger.e('Failed to load Live TV channels', error: e);
} catch (e, stackTrace) {
final message = localizedLoadErrorMessage(e, stackTrace, context: t.liveTv.title);
if (mounted) {
setState(() {
_isLoading = false;
_error = e.toString();
_error = message;
});
}
}
@@ -633,7 +633,6 @@ class _LiveTvScreenState extends State<LiveTvScreen>
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final useSideNav = PlatformDetector.shouldUseSideNavigation(context);
final isRecordings = _currentTab == LiveTvTab.recordings;
@@ -674,7 +673,7 @@ class _LiveTvScreenState extends State<LiveTvScreen>
),
]),
),
body: _buildLiveTvBody(theme, useSideNav),
body: _buildLiveTvBody(useSideNav),
);
}
@@ -698,31 +697,17 @@ class _LiveTvScreenState extends State<LiveTvScreen>
};
}
Widget _buildLiveTvBody(ThemeData theme, bool useSideNav) {
Widget _buildLiveTvBody(bool useSideNav) {
if (_isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (_error != null) {
return Center(
child: Column(
mainAxisSize: .min,
children: [
AppIcon(Symbols.error_rounded, size: 48, color: theme.colorScheme.error),
const SizedBox(height: 16),
Text(_error!, style: theme.textTheme.bodyLarge),
const SizedBox(height: 16),
FocusableButton(
autofocus: true,
onPressed: _loadChannels,
useBackgroundFocus: true,
child: FilledButton.icon(
onPressed: _loadChannels,
icon: const AppIcon(Symbols.refresh_rounded),
label: Text(t.common.retry),
),
),
],
),
return ErrorStateWidget(
message: _error!,
icon: Symbols.error_rounded,
onRetry: _loadChannels,
actionAutofocus: true,
actionUseBackgroundFocus: true,
);
}
if (_channels.isEmpty && !_visibleTabs.contains(LiveTvTab.recordings)) {
@@ -122,6 +122,7 @@ class _LiveTvShowScheduleScreenState extends State<LiveTvShowScheduleScreen>
child: Builder(
builder: (hostContext) => FocusedScrollScaffold(
title: Text(widget.showTitle),
focusableAppBarActions: true,
actions: showRecord
? [
FocusableActionBar(
+2 -1
View File
@@ -18,6 +18,7 @@ import '../services/tvos_system_navigation_service.dart';
import '../services/update_service.dart';
import '../utils/app_logger.dart';
import '../widgets/auth_error_banner.dart';
import '../widgets/app_icon.dart';
import '../utils/provider_extensions.dart';
import '../utils/platform_detector.dart';
import '../utils/snackbar_helper.dart';
@@ -1880,7 +1881,7 @@ class _MainScreenState extends State<MainScreen>
),
)
else
Icon(Symbols.wifi_rounded, size: 18, color: Theme.of(context).colorScheme.primary),
AppIcon(Symbols.wifi_rounded, size: 18, color: Theme.of(context).colorScheme.primary),
const SizedBox(width: 8),
Text(
t.common.reconnect,
+1 -1
View File
@@ -424,7 +424,7 @@ extension _MediaDetailActionButtons on _MediaDetailScreenState {
return MediaContextMenu(
key: _contextMenuKey,
item: metadata,
onRefresh: (itemId) => unawaited(_refreshItemInPlace(itemId)),
onRefresh: (source) => unawaited(_refreshItemInPlace(source)),
onPlayTrailer: onPlayTrailer,
child: Builder(
builder: (buttonContext) => IconButton.filledTonal(
+39 -25
View File
@@ -39,6 +39,7 @@ import '../media/paged_media_list_state.dart';
import '../widgets/media_card.dart';
import '../widgets/media_rating_badge.dart';
import '../i18n/strings.g.dart';
import '../theme/mono_tokens.dart';
import '../widgets/optimized_media_image.dart';
import '../utils/media_image_helper.dart';
import '../utils/media_quality_labels.dart';
@@ -453,45 +454,48 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
);
}
void _patchItemEverywhere(MediaItem item) {
void _patchItemEverywhere(String sourceGlobalKey, MediaItem item) {
final base = _fullMetadata ?? widget.metadata;
if (base.id == item.id) {
if (base.globalKey == sourceGlobalKey) {
_fullMetadata = _normalizeRefreshedItem(item, base);
}
final onDeckEpisode = _onDeckEpisode;
if (onDeckEpisode != null && onDeckEpisode.id == item.id) {
if (onDeckEpisode != null && onDeckEpisode.globalKey == sourceGlobalKey) {
_onDeckEpisode = _normalizeRefreshedItem(item, onDeckEpisode);
}
_patchItemInList(_seasons, item);
_patchItemInList(_episodes, item);
_patchItemInList(_seasons, sourceGlobalKey, item);
_patchItemInList(_episodes, sourceGlobalKey, item);
_syncFlattenEpisodeState();
_seasonEpisodePager.patchEpisode(item.id, (existing) => _normalizeRefreshedItem(item, existing));
_seasonEpisodePager.patchEpisode(
item.id,
(existing) => existing.globalKey == sourceGlobalKey ? _normalizeRefreshedItem(item, existing) : existing,
);
final extras = _extras;
if (extras != null) {
_patchItemInList(extras, item);
_patchItemInList(extras, sourceGlobalKey, item);
}
_relatedHubs = _patchItemInHubs(_relatedHubs, item);
_relatedHubs = _patchItemInHubs(_relatedHubs, sourceGlobalKey, item);
}
void _patchItemInList(List<MediaItem> items, MediaItem item) {
void _patchItemInList(List<MediaItem> items, String sourceGlobalKey, MediaItem item) {
for (var i = 0; i < items.length; i++) {
if (items[i].id == item.id) {
if (items[i].globalKey == sourceGlobalKey) {
items[i] = _normalizeRefreshedItem(item, items[i]);
}
}
}
List<MediaHub> _patchItemInHubs(List<MediaHub> hubs, MediaItem item) {
List<MediaHub> _patchItemInHubs(List<MediaHub> hubs, String sourceGlobalKey, MediaItem item) {
var changed = false;
final updatedHubs = <MediaHub>[];
for (final hub in hubs) {
var hubChanged = false;
final items = List<MediaItem>.of(hub.items);
for (var i = 0; i < items.length; i++) {
if (items[i].id == item.id) {
if (items[i].globalKey == sourceGlobalKey) {
items[i] = _normalizeRefreshedItem(item, items[i]);
hubChanged = true;
}
@@ -502,18 +506,20 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
return changed ? updatedHubs : hubs;
}
Future<void> _refreshItemInPlace(String itemId) async {
final client = _getMediaClientForMetadata(context);
Future<void> _refreshItemInPlace(MediaItem source) async {
final serverId = source.serverId;
if (serverId == null) return;
final client = context.tryGetMediaClientForServer(ServerId(serverId));
if (client == null) return;
try {
final refreshed = await client.fetchItem(itemId);
final refreshed = await client.fetchItem(source.id);
if (refreshed == null || !mounted) return;
setStateIfMounted(() {
_patchItemEverywhere(refreshed);
_patchItemEverywhere(source.globalKey, refreshed);
});
} catch (e) {
appLogger.d('Item refresh failed for $itemId', error: e);
appLogger.d('Item refresh failed for ${source.globalKey}', error: e);
}
}
@@ -2305,9 +2311,9 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
child: MediaContextMenu(
key: contextMenuKey,
item: season,
onRefresh: (itemId) {
onRefresh: (source) {
_watchStateChanged = true;
unawaited(_refreshItemInPlace(itemId));
unawaited(_refreshItemInPlace(source));
},
onListRefresh: () {
if (widget.isOffline) {
@@ -2542,7 +2548,7 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
return Padding(
padding: const EdgeInsets.all(32),
child: Center(
child: Text(message, style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: Colors.grey)),
child: Text(message, style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: tokens(context).textMuted)),
),
);
}
@@ -2608,7 +2614,7 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
context,
metadata: episode,
isOffline: widget.isOffline,
onRefresh: () => unawaited(_refreshItemInPlace(episode.id)),
onRefresh: () => unawaited(_refreshItemInPlace(episode)),
);
},
onRefresh: widget.isOffline ? null : _refreshItemInPlace,
@@ -3388,7 +3394,7 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
hubs: detailHubs,
iconForHub: _getTvDetailHubIcon,
onFocusedHubItemChanged: _handleTvDetailFocusedRailItemChanged,
onRefresh: (itemId) => unawaited(_refreshItemInPlace(itemId)),
onRefresh: (source) => unawaited(_refreshItemInPlace(source)),
onActiveHubChanged: _handleTvDetailHubChanged,
onActivateItem: _handleTvDetailRailItemActivated,
trailingForHub: _tvDetailTrailingState,
@@ -3661,20 +3667,27 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
artworkPaths: [metadata.clearLogoPath],
fit: BoxFit.contain,
alignment: .centerLeft,
imageType: ImageType.logo,
imageType: ImageType.heroLogo,
errorWidget: (context, url, error) => titleFallback(context),
);
if (localArtwork != null) return localArtwork;
final client = _getArtworkMediaClient(context);
final dpr = MediaImageHelper.effectiveDevicePixelRatio(context);
final targetWidth = (width * dpr).round();
final targetHeight = (height * dpr).round();
final (memCacheWidth, memCacheHeight) = MediaImageHelper.getMemCacheDimensions(
displayWidth: targetWidth,
displayHeight: targetHeight,
imageType: ImageType.heroLogo,
);
final logoUrl = MediaImageHelper.getOptimizedImageUrl(
client: client,
thumbPath: metadata.clearLogoPath,
maxWidth: width,
maxHeight: height,
devicePixelRatio: dpr,
imageType: ImageType.logo,
imageType: ImageType.heroLogo,
);
if (logoUrl.isEmpty) return titleFallback(context);
@@ -3686,7 +3699,8 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
filterQuality: FilterQuality.medium,
fit: BoxFit.contain,
alignment: .centerLeft,
memCacheWidth: (width * dpr).clamp(200, 1000).round(),
memCacheWidth: memCacheWidth,
memCacheHeight: memCacheHeight,
placeholder: (context, url) => const SizedBox.shrink(),
errorBuilder: (context, error, stackTrace) => titleFallback(context),
),
+1 -1
View File
@@ -562,7 +562,7 @@ class _ArtworkPickerDialogState extends State<ArtworkPickerDialog> {
child: Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(color: Theme.of(context).colorScheme.primary, shape: BoxShape.circle),
child: Icon(Symbols.check_rounded, size: 16, color: Theme.of(context).colorScheme.onPrimary),
child: AppIcon(Symbols.check_rounded, size: 16, color: Theme.of(context).colorScheme.onPrimary),
),
),
],
@@ -86,9 +86,6 @@ class _AlbumDetailScreenState extends BaseMediaListDetailScreen<AlbumDetailScree
@override
Future<List<MediaItem>> fetchItems() => mediaClient.fetchAlbumTracks(widget.album.id);
@override
String getLoadErrorMessage(Object error) => t.messages.errorLoading(error: error.toString());
@override
Future<void> loadItems() async {
await super.loadItems();
+4 -5
View File
@@ -13,6 +13,7 @@ import '../../mixins/grid_focus_node_mixin.dart';
import '../../services/music/music_playback_service.dart';
import '../../theme/mono_tokens.dart';
import '../../utils/formatters.dart';
import '../../utils/error_message_utils.dart';
import '../../utils/media_image_helper.dart';
import '../../utils/music_navigation.dart';
import '../../utils/platform_detector.dart';
@@ -63,9 +64,6 @@ class _ArtistDetailScreenState extends BaseMediaListDetailScreen<ArtistDetailScr
@override
Future<List<MediaItem>> fetchItems() => mediaClient.fetchArtistAlbums(widget.artist.id);
@override
String getLoadErrorMessage(Object error) => t.messages.errorLoading(error: error.toString());
@override
Future<void> loadItems() async {
await super.loadItems();
@@ -87,8 +85,9 @@ class _ArtistDetailScreenState extends BaseMediaListDetailScreen<ArtistDetailScr
List<MediaItem> tracks;
try {
tracks = await mediaClient.fetchPlayableDescendants(widget.artist.id);
} catch (e) {
if (mounted) showErrorSnackBar(context, t.messages.errorLoading(error: e.toString()));
} catch (e, stackTrace) {
final message = localizedLoadErrorMessage(e, stackTrace, context: widget.artist.displayTitle);
if (mounted) showErrorSnackBar(context, message);
return;
}
if (!mounted) return;
@@ -14,6 +14,7 @@ import '../../services/media_list_playback_launcher.dart';
import '../../services/music/music_playback_service.dart';
import '../../services/playlist_items_loader.dart';
import '../../utils/app_logger.dart';
import '../../utils/error_message_utils.dart';
import '../../utils/continuation_pagination_coordinator.dart';
import '../../utils/music_navigation.dart';
import '../../widgets/app_icon.dart';
@@ -34,6 +35,7 @@ import '../../widgets/listenable_selector.dart';
import '../base_media_list_detail_screen.dart';
import '../focusable_detail_screen_mixin.dart';
import '../../mixins/grid_focus_node_mixin.dart';
import '../../widgets/overlay_sheet.dart';
/// Screen to display the contents of a playlist
class PlaylistDetailScreen extends StatefulWidget {
@@ -55,9 +57,6 @@ class _PlaylistDetailScreenState extends BaseMediaListDetailScreen<PlaylistDetai
@override
Object get mediaItem => widget.playlist;
@override
String? get itemServerId => widget.playlist.serverId;
@override
String get title => widget.playlist.title;
@@ -103,9 +102,9 @@ class _PlaylistDetailScreenState extends BaseMediaListDetailScreen<PlaylistDetai
} else {
try {
tracks = await fetchAllPlaylistItems(mediaClient, widget.playlist.id);
} catch (e) {
appLogger.w('Failed to fetch full audio playlist ${widget.playlist.id}', error: e);
if (mounted) showErrorSnackBar(context, t.messages.errorLoading(error: e.toString()));
} catch (e, stackTrace) {
final message = localizedLoadErrorMessage(e, stackTrace, context: widget.playlist.title);
if (mounted) showErrorSnackBar(context, message);
return;
}
if (!mounted) return;
@@ -266,11 +265,11 @@ class _PlaylistDetailScreenState extends BaseMediaListDetailScreen<PlaylistDetai
_autoFocusAfterLoad();
if (_continuation.hasMore) unawaited(_continuation.loadRemaining());
} catch (e) {
appLogger.e('Failed to load playlist items', error: e);
} catch (e, stackTrace) {
final message = localizedLoadErrorMessage(e, stackTrace, context: widget.playlist.title);
if (!mounted) return;
setState(() {
errorMessage = getLoadErrorMessage(e);
errorMessage = message;
isLoading = false;
});
}
@@ -385,7 +384,7 @@ class _PlaylistDetailScreenState extends BaseMediaListDetailScreen<PlaylistDetai
if (!mounted) return;
if (success) {
showSuccessSnackBar(context, t.playlists.deleted);
Navigator.pop(context); // Return to playlists screen
Navigator.pop(context, true); // Signal the parent list to refresh.
} else {
showErrorSnackBar(context, t.playlists.errorDeleting);
}
@@ -574,11 +573,11 @@ class _PlaylistDetailScreenState extends BaseMediaListDetailScreen<PlaylistDetai
final backResult = handleBackKeyAction(event, () {
if (_movingIndex != null) {
// Cancel move mode, set flag to prevent PopScope exit
// Cancel move mode and suppress route-level back handling.
backHandledByKeyEvent = true;
_cancelMoveMode();
} else {
// Navigate to app bar on BACK, set flag to prevent PopScope exit
// Navigate to the app bar and suppress route-level back handling.
handleBackFromContent();
}
});
@@ -720,7 +719,7 @@ class _PlaylistDetailScreenState extends BaseMediaListDetailScreen<PlaylistDetai
return false;
}
/// Handle back navigation for PopScope - extends mixin with move mode support
/// Handle route back from [OverlaySheetHost], extending the mixin with move mode support.
bool _handleBackNavigation() {
// If BACK was already handled by a key event, don't pop
if (backHandledByKeyEvent) {
@@ -805,11 +804,10 @@ class _PlaylistDetailScreenState extends BaseMediaListDetailScreen<PlaylistDetai
);
}
return PopScope(
return OverlaySheetHost(
canPop: allowsNativeBackGesture && _movingIndex == null,
onPopInvokedWithResult: (didPop, result) {
onSystemBack: () {
if (BackKeyCoordinator.consumeIfHandled()) return;
if (didPop) return;
final shouldPop = _handleBackNavigation();
if (shouldPop && mounted) {
Navigator.pop(context);
+29 -32
View File
@@ -6,11 +6,12 @@ import '../../media/media_item.dart';
import '../../media/media_kind.dart';
import '../../mixins/context_menu_tap_mixin.dart';
import '../../providers/watch_state_store.dart';
import '../../theme/mono_tokens.dart';
import '../../utils/formatters.dart';
import '../../utils/provider_extensions.dart';
import '../../i18n/strings.g.dart';
import '../../widgets/media_context_menu.dart';
import '../../widgets/media_progress_bar.dart';
import '../../widgets/watched_indicator.dart';
import '../../widgets/optimized_media_image.dart';
/// Custom list item widget for playlist items
@@ -20,7 +21,7 @@ class PlaylistItemCard extends StatefulWidget {
final int index;
final VoidCallback onRemove;
final VoidCallback? onTap;
final void Function(String itemId)? onRefresh;
final void Function(MediaItem source)? onRefresh;
final bool canReorder; // Whether drag handle should be shown
// Focus state for keyboard/D-pad navigation
@@ -52,6 +53,7 @@ class _PlaylistItemCardState extends State<PlaylistItemCard> with ContextMenuTap
Widget build(BuildContext context) {
final item = _effectiveItem(context);
final colorScheme = Theme.of(context).colorScheme;
final textMuted = tokens(context).textMuted;
// Determine if row is focused (main content area)
final isRowFocused = widget.isFocused && widget.focusedColumn == 0;
@@ -116,7 +118,7 @@ class _PlaylistItemCardState extends State<PlaylistItemCard> with ContextMenuTap
child: AppIcon(
widget.isMoving ? Symbols.swap_vert_rounded : Symbols.drag_indicator_rounded,
fill: 1,
color: (widget.isMoving || isDragHandleFocused) ? colorScheme.primary : Colors.grey,
color: (widget.isMoving || isDragHandleFocused) ? colorScheme.primary : textMuted,
),
),
),
@@ -147,21 +149,10 @@ class _PlaylistItemCardState extends State<PlaylistItemCard> with ContextMenuTap
// Subtitle (episode info or type)
Text(
_buildSubtitle(item),
style: TextStyle(fontSize: 13, color: Colors.grey[400]),
style: TextStyle(fontSize: 13, color: textMuted),
maxLines: 1,
overflow: .ellipsis,
),
// Progress indicator if partially watched
if (item.viewOffsetMs != null && item.durationMs != null)
Padding(
padding: const EdgeInsets.only(top: 6),
child: MediaProgressBar(
viewOffset: item.viewOffsetMs!,
duration: item.durationMs!,
minHeight: 3,
),
),
],
),
),
@@ -170,10 +161,7 @@ class _PlaylistItemCardState extends State<PlaylistItemCard> with ContextMenuTap
// Duration
if (item.durationMs != null)
Text(
formatDurationTextual(item.durationMs!),
style: TextStyle(fontSize: 13, color: Colors.grey[400]),
),
Text(formatDurationTextual(item.durationMs!), style: TextStyle(fontSize: 13, color: textMuted)),
const SizedBox(width: 8),
@@ -189,7 +177,7 @@ class _PlaylistItemCardState extends State<PlaylistItemCard> with ContextMenuTap
icon: const AppIcon(Symbols.close_rounded, fill: 1, size: 20),
onPressed: widget.onRemove,
tooltip: t.playlists.removeItem,
color: isRemoveButtonFocused ? colorScheme.primary : Colors.grey[400],
color: isRemoveButtonFocused ? colorScheme.primary : textMuted,
),
),
],
@@ -202,18 +190,27 @@ class _PlaylistItemCardState extends State<PlaylistItemCard> with ContextMenuTap
Widget _buildPosterImage(BuildContext context, MediaItem item) {
final posterUrl = item.posterThumb();
return ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(6)),
child: OptimizedMediaImage.poster(
// Backend-neutral lookup so Jellyfin items render via their own
// image transcoder; null falls through to the placeholder below.
client: context.tryGetMediaClientWithFallback(serverIdOrNull(item.serverId)),
imagePath: posterUrl,
width: 60,
height: 90,
fit: BoxFit.cover,
placeholder: (context, url) => _buildPlaceholder(),
errorWidget: (context, url, error) => _buildPlaceholder(),
return SizedBox(
width: 60,
height: 90,
child: Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(6)),
child: OptimizedMediaImage.poster(
// Backend-neutral lookup so Jellyfin items render via their own
// image transcoder; null falls through to the placeholder below.
client: context.tryGetMediaClientWithFallback(serverIdOrNull(item.serverId)),
imagePath: posterUrl,
width: 60,
height: 90,
fit: BoxFit.cover,
placeholder: (context, url) => _buildPlaceholder(),
errorWidget: (context, url, error) => _buildPlaceholder(),
),
),
WatchedIndicator(item: item, size: WatchedIndicatorSize.compact),
],
),
);
}
+3 -3
View File
@@ -539,7 +539,7 @@ class _TvPinInputState extends State<_TvPinInput> with ControllerDisposerMixin {
Widget _buildKeyContent(BuildContext context, _PinKey key, Color foreground) {
final icon = key.icon;
if (icon != null) return Icon(icon, color: foreground, size: 30);
if (icon != null) return AppIcon(icon, color: foreground, size: 30);
return FittedBox(
fit: BoxFit.scaleDown,
@@ -678,9 +678,9 @@ class _PinKey {
case _PinKeyType.digit:
return null;
case _PinKeyType.backspace:
return Icons.backspace_outlined;
return Symbols.backspace_rounded;
case _PinKeyType.close:
return Icons.close_rounded;
return Symbols.close_rounded;
}
}
}
+18 -4
View File
@@ -6,6 +6,7 @@ import 'package:provider/provider.dart';
import '../focus/focusable_text_field.dart';
import '../focus/focusable_button.dart';
import '../i18n/strings.g.dart';
import '../media/ids.dart';
import '../media/media_item.dart';
import '../mixins/debounced_media_search.dart';
import '../mixins/mounted_set_state_mixin.dart';
@@ -153,10 +154,23 @@ class _SearchScreenState extends State<SearchScreen>
searchController.clear();
}
void updateItem(String _) {
Future<void> updateItem(MediaItem source) async {
if (!mounted) return;
// Trigger a refresh of the search to get updated metadata
runSearch(searchController.text.trim());
final serverId = source.serverId;
if (serverId == null) return;
try {
final multiServer = context.read<MultiServerProvider>();
final updated = await multiServer.getClientForServer(ServerId(serverId))?.fetchItem(source.id);
if (!mounted || updated == null) return;
final index = searchResults.indexWhere((item) => item.globalKey == source.globalKey);
if (index == -1) return;
setState(() {
searchResults[index] = updated;
});
} catch (e) {
appLogger.d('Search item refresh skipped for ${source.globalKey}', error: e);
}
}
/// Navigate focus to the sidebar
@@ -180,7 +194,7 @@ class _SearchScreenState extends State<SearchScreen>
disableScale: true,
focusNode: index == 0 ? firstResultFocusNode : null,
onRefresh: updateItem,
onListRefresh: () => updateItem(item.id),
onListRefresh: refresh,
onNavigateLeft: _navigateToSidebar,
onNavigateUp: index == 0 ? focusSearchInput : null,
showServerName: showServerName,
+2 -1
View File
@@ -6,6 +6,7 @@ import '../../widgets/focused_scroll_scaffold.dart';
import '../../widgets/focusable_list_tile.dart';
import '../../widgets/settings_section.dart';
import '../../i18n/strings.g.dart';
import '../../theme/mono_tokens.dart';
import 'licenses_screen.dart';
class AboutScreen extends StatelessWidget {
@@ -39,7 +40,7 @@ class AboutScreen extends StatelessWidget {
const SizedBox(height: 8),
Text(
t.about.versionLabel(version: appVersion),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: Colors.grey),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: tokens(context).textMuted),
),
const SizedBox(height: 24),
Text(
@@ -199,13 +199,6 @@ class AppearanceSettingsScreen extends StatelessWidget {
);
}
String _themeModeLabel(settings.ThemeMode mode) => switch (mode) {
settings.ThemeMode.system => t.settings.systemTheme,
settings.ThemeMode.light => t.settings.lightTheme,
settings.ThemeMode.dark => t.settings.darkTheme,
settings.ThemeMode.oled => t.settings.oledTheme,
};
// Writes the pref directly; ThemeProvider listens to the pref's listenable
// and applies the change live. The Consumer only feeds the dynamic icon.
Widget _themeSelector() {
@@ -215,8 +208,8 @@ class AppearanceSettingsScreen extends StatelessWidget {
pref: SettingsService.themeMode,
icon: themeProvider.themeModeIcon,
title: t.settings.theme,
subtitleBuilder: _themeModeLabel,
options: settings.ThemeMode.values.map((m) => DialogOption(value: m, title: _themeModeLabel(m))).toList(),
subtitleBuilder: themeModeLabel,
options: settings.ThemeMode.values.map((m) => DialogOption(value: m, title: themeModeLabel(m))).toList(),
decode: (v) => v,
encode: (v) => v,
);
+2 -1
View File
@@ -11,6 +11,7 @@ import 'package:logger/logger.dart';
import 'package:package_info_plus/package_info_plus.dart';
import '../../focus/focusable_action_bar.dart';
import '../../widgets/dialog_action_button.dart';
import '../../widgets/app_icon.dart';
import '../../focus/key_event_utils.dart';
import '../../i18n/strings.g.dart';
import '../../mixins/mounted_set_state_mixin.dart';
@@ -207,7 +208,7 @@ class _LogsScreenState extends State<LogsScreen> with MountedSetStateMixin {
),
const SizedBox(width: 8),
IconButton(
icon: const Icon(Icons.copy, size: 20),
icon: const AppIcon(Symbols.content_copy_rounded, size: 20),
onPressed: () {
Clipboard.setData(ClipboardData(text: id));
showSuccessSnackBar(context, t.messages.logsCopied);
+42 -45
View File
@@ -51,6 +51,7 @@ import 'logs_screen.dart';
import 'playback_settings_screen.dart';
import '../profile/profile_switch_screen.dart';
import 'services_settings_screen.dart';
import 'settings_utils.dart';
import '../../widgets/loading_indicator_box.dart';
class SettingsScreen extends StatefulWidget {
@@ -191,12 +192,12 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab, Moun
}
Widget _buildDonateTile() {
return FocusableListTile(
return SettingNavigationTile(
focusNode: _focusTracker.get(_kDonate),
leading: const AppIcon(Symbols.favorite_rounded, fill: 1),
title: Text(t.settings.supportDeveloper),
subtitle: Text(t.settings.supportDeveloperDescription),
trailing: const AppIcon(Symbols.open_in_new_rounded, fill: 1),
icon: Symbols.favorite_rounded,
title: t.settings.supportDeveloper,
subtitle: t.settings.supportDeveloperDescription,
trailingIcon: Symbols.open_in_new_rounded,
onTap: () async {
final url = Uri.parse(DonationService.donationUrl);
if (await canLaunchUrl(url)) {
@@ -211,7 +212,7 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab, Moun
builder: (context, themeProvider, _) => SettingValueBuilder<int>(
pref: settings.SettingsService.libraryDensity,
builder: (context, libraryDensity, _) {
final summary = '${themeProvider.themeModeDisplayName} · ${t.settings.libraryDensity} $libraryDensity';
final summary = '${themeModeLabel(themeProvider.themeMode)} · ${t.settings.libraryDensity} $libraryDensity';
return SettingNavigationTile(
focusNode: _focusTracker.get(_kAppearance),
icon: Symbols.palette_rounded,
@@ -336,6 +337,8 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab, Moun
subtitle: Text(currentPath, maxLines: 2, overflow: .ellipsis),
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
onTap: () => _showDownloadLocationDialog(),
dense: false,
visualDensity: VisualDensity.standard,
);
},
),
@@ -390,12 +393,11 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab, Moun
return SettingsGroup(
title: t.settings.advanced,
children: [
FocusableListTile(
SettingNavigationTile(
focusNode: _focusTracker.get(_kWatchTogetherRelay),
leading: const AppIcon(Symbols.dns_rounded, fill: 1),
title: Text(t.settings.watchTogetherRelay),
subtitle: Text(t.settings.watchTogetherRelayDescription),
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
icon: Symbols.dns_rounded,
title: t.settings.watchTogetherRelay,
subtitle: t.settings.watchTogetherRelayDescription,
onTap: () => _showRelayUrlDialog(),
),
SettingSwitchTile(
@@ -419,38 +421,34 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab, Moun
subtitle: t.settings.viewLogsDescription,
destinationBuilder: (context) => const LogsScreen(),
),
FocusableListTile(
SettingNavigationTile(
focusNode: _focusTracker.get(_kClearCache),
leading: const AppIcon(Symbols.cleaning_services_rounded, fill: 1),
title: Text(t.settings.clearCache),
subtitle: Text(t.settings.clearCacheDescription),
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
icon: Symbols.cleaning_services_rounded,
title: t.settings.clearCache,
subtitle: t.settings.clearCacheDescription,
onTap: () => _showClearCacheDialog(),
),
FocusableListTile(
SettingNavigationTile(
focusNode: _focusTracker.get(_kResetSettings),
leading: const AppIcon(Symbols.restore_rounded, fill: 1),
title: Text(t.settings.resetSettings),
subtitle: Text(t.settings.resetSettingsDescription),
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
icon: Symbols.restore_rounded,
title: t.settings.resetSettings,
subtitle: t.settings.resetSettingsDescription,
onTap: () => _showResetSettingsDialog(),
),
if (kDebugMode)
FocusableListTile(
leading: const AppIcon(Symbols.error_rounded, fill: 1),
title: const Text('Test Sentry'),
subtitle: const Text('Send a test error'),
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
SettingNavigationTile(
icon: Symbols.error_rounded,
title: 'Test Sentry',
subtitle: 'Send a test error',
onTap: () {
throw Exception("Example exception");
},
),
if (kDebugMode)
FocusableListTile(
leading: const AppIcon(Symbols.timer_rounded, fill: 1),
title: const Text('Test ANR'),
subtitle: const Text('Block the main thread for 10 seconds'),
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
SettingNavigationTile(
icon: Symbols.timer_rounded,
title: 'Test ANR',
subtitle: 'Block the main thread for 10 seconds',
onTap: () {
showSnackBar(context, 'Blocking main thread...');
final end = DateTime.now().add(const Duration(seconds: 10));
@@ -465,20 +463,18 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab, Moun
return SettingsGroup(
title: t.settings.backup,
children: [
FocusableListTile(
SettingNavigationTile(
focusNode: _focusTracker.get(_kExportSettings),
leading: const AppIcon(Symbols.upload_rounded, fill: 1),
title: Text(t.settings.exportSettings),
subtitle: Text(t.settings.exportSettingsDescription),
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
icon: Symbols.upload_rounded,
title: t.settings.exportSettings,
subtitle: t.settings.exportSettingsDescription,
onTap: _handleExportSettings,
),
FocusableListTile(
SettingNavigationTile(
focusNode: _focusTracker.get(_kImportSettings),
leading: const AppIcon(Symbols.download_rounded, fill: 1),
title: Text(t.settings.importSettings),
subtitle: Text(t.settings.importSettingsDescription),
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
icon: Symbols.download_rounded,
title: t.settings.importSettings,
subtitle: t.settings.importSettingsDescription,
onTap: _showImportSettingsDialog,
),
],
@@ -498,11 +494,10 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab, Moun
return SettingsGroup(
title: t.settings.updates,
children: [
FocusableListTile(
SettingNavigationTile(
focusNode: _focusTracker.get(_kCheckForUpdates),
leading: const AppIcon(Symbols.system_update_rounded, fill: 1),
title: Text(t.settings.checkForUpdates),
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
icon: Symbols.system_update_rounded,
title: t.settings.checkForUpdates,
onTap: () => UpdateService.checkForUpdatesNative(inBackground: false),
),
_buildAutoCheckUpdatesOnStartupTile(),
@@ -536,6 +531,8 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab, Moun
_checkForUpdates();
}
},
dense: false,
visualDensity: VisualDensity.standard,
),
_buildAutoCheckUpdatesOnStartupTile(),
],
+13 -3
View File
@@ -1,15 +1,25 @@
import 'package:flex_color_picker/flex_color_picker.dart';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../../focus/focusable_text_field.dart';
import '../../focus/input_mode_tracker.dart';
import '../../i18n/strings.g.dart';
import '../../services/settings_service.dart' as settings;
import '../../utils/dialogs.dart';
import '../../widgets/app_icon.dart';
import '../../widgets/dialog_action_button.dart';
import '../../widgets/focusable_list_tile.dart';
import '../../widgets/tv_color_picker.dart';
import '../../widgets/tv_number_spinner.dart';
String themeModeLabel(settings.ThemeMode mode) => switch (mode) {
settings.ThemeMode.system => t.settings.systemTheme,
settings.ThemeMode.light => t.settings.lightTheme,
settings.ThemeMode.dark => t.settings.darkTheme,
settings.ThemeMode.oled => t.settings.oledTheme,
};
/// Model for option selection dialogs.
class DialogOption<T> {
final T value;
@@ -118,8 +128,8 @@ Future<T?> showSelectionDialog<T>({
final selected = option.value == currentValue;
return FocusableListTile(
key: ValueKey(option.value),
leading: Icon(
selected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
leading: AppIcon(
selected ? Symbols.radio_button_checked_rounded : Symbols.radio_button_unchecked_rounded,
color: selected ? Theme.of(dialogContext).colorScheme.primary : null,
),
title: Text(option.title),
@@ -380,7 +390,7 @@ void showRegexInputDialog({
contentBuilder: (_, _, setDialogState, saveFocusNode) {
return FocusableTextField(
controller: controller,
decoration: InputDecoration(labelText: 'Regex', errorText: errorText),
decoration: InputDecoration(labelText: t.settings.regex, errorText: errorText),
autofocus: true,
textInputAction: TextInputAction.done,
onEditingComplete: () => saveFocusNode.requestFocus(),
@@ -15,17 +15,17 @@ class SubtitleStylingScreen extends StatelessWidget {
String _assOverrideLabel(SubAssOverride value) {
return switch (value) {
SubAssOverride.no => 'No',
SubAssOverride.yes => 'Yes',
SubAssOverride.scale => 'Scale',
SubAssOverride.force => 'Force',
SubAssOverride.strip => 'Strip',
SubAssOverride.no => t.common.no,
SubAssOverride.yes => t.common.yes,
SubAssOverride.scale => t.subtitlingStyling.overrideScale,
SubAssOverride.force => t.subtitlingStyling.overrideForce,
SubAssOverride.strip => t.subtitlingStyling.overrideStrip,
};
}
String _formatPosition(int value) {
if (value == 0) return 'Top';
if (value == 100) return 'Bottom';
if (value == 0) return t.subtitlingStyling.positionTop;
if (value == 100) return t.subtitlingStyling.positionBottom;
return '$value%';
}
@@ -155,7 +155,7 @@ class TrackerSettingsScreen extends StatelessWidget {
toggles: [
TrackerSettingsToggle(
pref: config.scrobblePref,
icon: Symbols.auto_timer,
icon: Symbols.auto_timer_rounded,
title: t.services.scrobble,
subtitle: t.services.scrobbleDescription,
onAfterWrite: config.onScrobbleChanged,
@@ -72,7 +72,7 @@ class TraktSettingsScreen extends StatelessWidget {
toggles: [
TrackerSettingsToggle(
pref: SettingsService.enableTraktScrobble,
icon: Symbols.auto_timer,
icon: Symbols.auto_timer_rounded,
title: t.trakt.scrobble,
subtitle: t.trakt.scrobbleDescription,
onAfterWrite: TraktScrobbleService.instance.setEnabled,
@@ -30,7 +30,7 @@ class VideoPlayerMacPipPlaceholder extends StatelessWidget {
child: Column(
mainAxisSize: .min,
children: [
Icon(Symbols.picture_in_picture_alt_rounded, size: 48, color: Colors.white.withValues(alpha: 0.5)),
AppIcon(Symbols.picture_in_picture_alt_rounded, size: 48, color: Colors.white.withValues(alpha: 0.5)),
const SizedBox(height: 12),
Text(
t.videoControls.pipActive,
@@ -119,7 +119,7 @@ class VideoPlayerWatchTogetherOverlays extends StatelessWidget {
mainAxisSize: .min,
children: [
if (PlatformDetector.isTV())
const Icon(Symbols.sync_rounded, size: 14, color: Colors.white)
const AppIcon(Symbols.sync_rounded, size: 14, color: Colors.white)
else
const SizedBox(
width: 14,
+2 -1
View File
@@ -47,6 +47,7 @@ import '../utils/resolution_label.dart';
import '../utils/track_label_builder.dart';
import '../exceptions/media_server_exceptions.dart';
import '../i18n/strings.g.dart';
import '../utils/json_utils.dart';
import '../utils/jellyfin_time.dart';
import 'jellyfin_auth_header.dart';
import '../media/download_resolution.dart';
@@ -285,7 +286,7 @@ class JellyfinClient
@override
Future<HealthStatus> checkHealth() async {
try {
final response = await _http.get('/Users/Me', timeout: const Duration(seconds: 8));
final response = await _http.get('/Users/Me', timeout: MediaServerTimeouts.jellyfinProbe);
final ok = response.statusCode >= 200 && response.statusCode < 300;
if (ok) {
final data = response.data;
@@ -27,23 +27,22 @@ mixin _JellyfinFileInfoMethods on MediaServerCacheMixin {
final audioTracks = parsed.audioTracks;
final subtitleTracks = parsed.subtitleTracks;
final width = videoStream?['Width'] as int?;
final height = videoStream?['Height'] as int?;
final width = flexibleInt(videoStream?['Width']);
final height = flexibleInt(videoStream?['Height']);
final aspectRatioString = videoStream?['AspectRatio'] as String?;
double? aspectRatio;
if (aspectRatioString != null && aspectRatioString.contains(':')) {
final parts = aspectRatioString.split(':');
final num = double.tryParse(parts.first);
final den = double.tryParse(parts[1]);
final num = flexibleDouble(parts.first);
final den = flexibleDouble(parts[1]);
if (num != null && den != null && den != 0) aspectRatio = num / den;
}
aspectRatio ??= (width != null && height != null && height != 0) ? width / height : null;
final runtimeTicks = source['RunTimeTicks'] as int?;
final durationMs = runtimeTicks != null ? (runtimeTicks ~/ 10_000) : null;
final durationMs = jellyfinTicksToMs(flexibleInt(source['RunTimeTicks']));
final bitrateBps = source['Bitrate'] as int?;
final videoBitrateBps = videoStream?['BitRate'] as int?;
final bitrateBps = flexibleInt(source['Bitrate']);
final videoBitrateBps = flexibleInt(videoStream?['BitRate']);
return MediaFileInfo(
container: source['Container'] as String?,
@@ -55,21 +54,20 @@ mixin _JellyfinFileInfoMethods on MediaServerCacheMixin {
height: height,
aspectRatio: aspectRatio,
// Plex stores bitrate as kbps; Jellyfin returns bps. Normalise to kbps.
bitrate: bitrateBps != null ? bitrateBps ~/ 1000 : null,
bitrate: bitrateKbpsFromBps(bitrateBps),
duration: durationMs,
audioCodec: audioStream?['Codec'] as String?,
audioProfile: audioStream?['Profile'] as String?,
audioChannels: audioStream?['Channels'] as int?,
audioChannels: flexibleInt(audioStream?['Channels']),
filePath: source['Path'] as String?,
fileSize: source['Size'] as int?,
fileSize: flexibleInt(source['Size']),
colorSpace: videoStream?['ColorSpace'] as String?,
colorRange: videoStream?['ColorRange'] as String?,
colorPrimaries: videoStream?['ColorPrimaries'] as String?,
chromaSubsampling: null,
frameRate:
(videoStream?['RealFrameRate'] as num?)?.toDouble() ?? (videoStream?['AverageFrameRate'] as num?)?.toDouble(),
bitDepth: videoStream?['BitDepth'] as int?,
videoBitrate: videoBitrateBps != null ? videoBitrateBps ~/ 1000 : null,
frameRate: flexibleDouble(videoStream?['RealFrameRate']) ?? flexibleDouble(videoStream?['AverageFrameRate']),
bitDepth: flexibleInt(videoStream?['BitDepth']),
videoBitrate: bitrateKbpsFromBps(videoBitrateBps),
audioChannelLayout: audioStream?['ChannelLayout'] as String?,
audioTracks: audioTracks,
subtitleTracks: subtitleTracks,
+15 -13
View File
@@ -2,7 +2,7 @@ import 'dart:io';
import 'package:auto_updater/auto_updater.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:logger/logger.dart';
import 'package:plezy/utils/app_logger.dart';
import 'package:plezy/utils/media_server_http_client.dart';
import 'base_shared_preferences_service.dart';
@@ -13,7 +13,6 @@ import 'base_shared_preferences_service.dart';
/// via auto_updater for native update dialogs and in-app installs.
/// On all other platforms: falls back to GitHub API check + browser link dialog.
class UpdateService {
static final Logger _logger = Logger();
static const String _githubRepo = 'edde746/plezy';
static const String _feedUrl = 'https://cdn.jsdelivr.net/gh/edde746/plezy@appcast/appcast.xml';
@@ -47,8 +46,8 @@ class UpdateService {
try {
await autoUpdater.setFeedURL(_feedUrl);
_nativeUpdaterInitialized = true;
} catch (e) {
_logger.e('Failed to initialize native auto updater: $e');
} catch (error, stackTrace) {
appLogger.e('Failed to initialize native auto updater', error: error, stackTrace: stackTrace);
}
}
@@ -61,8 +60,8 @@ class UpdateService {
}
try {
await autoUpdater.checkForUpdates(inBackground: inBackground);
} catch (e) {
_logger.e('Native update check failed: $e');
} catch (error, stackTrace) {
appLogger.e('Native update check failed', error: error, stackTrace: stackTrace);
}
}
@@ -72,7 +71,8 @@ class UpdateService {
try {
final execPath = Platform.resolvedExecutable;
return execPath.contains('/Caskroom/') || execPath.contains('/homebrew/');
} catch (_) {
} catch (error, stackTrace) {
appLogger.e('Failed to determine Homebrew install status', error: error, stackTrace: stackTrace);
return false;
}
}
@@ -83,7 +83,8 @@ class UpdateService {
try {
final exeDir = File(Platform.resolvedExecutable).parent.path;
return File('$exeDir\\.winget').existsSync();
} catch (_) {
} catch (error, stackTrace) {
appLogger.e('Failed to determine winget install status', error: error, stackTrace: stackTrace);
return false;
}
}
@@ -94,7 +95,8 @@ class UpdateService {
try {
final exeDir = File(Platform.resolvedExecutable).parent.path;
return File('$exeDir\\unins000.exe').existsSync();
} catch (_) {
} catch (error, stackTrace) {
appLogger.e('Failed to determine Windows installation status', error: error, stackTrace: stackTrace);
return false;
}
}
@@ -190,8 +192,8 @@ class UpdateService {
if (respectCooldown) {
await _updateLastCheckTime();
}
} catch (e) {
_logger.e('Failed to check for updates: $e');
} catch (error, stackTrace) {
appLogger.e('Failed to check for updates', error: error, stackTrace: stackTrace);
}
return null;
@@ -237,8 +239,8 @@ class UpdateService {
}
return false;
} catch (e) {
_logger.e('Error comparing versions: $e');
} catch (error, stackTrace) {
appLogger.e('Error comparing versions', error: error, stackTrace: stackTrace);
return false;
}
}
+19 -18
View File
@@ -1,24 +1,25 @@
import '../exceptions/media_server_exceptions.dart';
import '../i18n/strings.g.dart';
import 'app_logger.dart';
import '../exceptions/media_server_exceptions.dart';
/// Shared helpers for translating network errors into user-friendly messages.
String mapHttpErrorToMessage(MediaServerHttpException error, {required String context}) {
switch (error.type) {
case MediaServerHttpErrorType.connectionTimeout:
case MediaServerHttpErrorType.receiveTimeout:
return t.errors.connectionTimeout(context: context);
case MediaServerHttpErrorType.connectionError:
return t.errors.connectionFailed;
default:
appLogger.e('Error loading $context', error: error);
final msg = error.message.isNotEmpty ? error.message : t.common.unknown;
return t.errors.failedToLoad(context: context, error: msg);
/// Logs a load failure once and returns a localized, user-safe message.
///
/// The returned text never includes exception or server response content.
String localizedLoadErrorMessage(Object error, StackTrace stackTrace, {required String context}) {
appLogger.e('Error loading $context', error: error, stackTrace: stackTrace);
if (error is MediaServerHttpException) {
switch (error.type) {
case MediaServerHttpErrorType.connectionTimeout:
case MediaServerHttpErrorType.receiveTimeout:
return t.errors.connectionTimeout(context: context);
case MediaServerHttpErrorType.connectionError:
return t.errors.connectionFailed;
case MediaServerHttpErrorType.cancelled:
case MediaServerHttpErrorType.unknown:
break;
}
}
}
/// Generic fallback for unexpected errors.
String mapUnexpectedErrorToMessage(dynamic error, {required String context}) {
appLogger.e('Unexpected error in $context', error: error);
return t.errors.failedToLoad(context: context, error: error.toString());
return t.errors.unableToLoad(context: context);
}
+3 -3
View File
@@ -184,10 +184,10 @@ String formatRating(double value) =>
final RegExp _trailingZeroPattern = RegExp(r'\.?0+$');
/// Format a playback rate for display (e.g. 1.25 → "1.25x", 2.0 → "2x").
/// When [normalAtOne] is true, 1.0 renders as "Normal" for menu labels;
/// the in-player pill passes false to keep a numeric indicator.
/// When [normalAtOne] is true, 1.0 renders with the localized normal-speed
/// label for menus; the in-player pill passes false to keep a numeric indicator.
String formatPlaybackRate(double rate, {bool normalAtOne = false}) {
if (normalAtOne && (rate - 1.0).abs() < 0.005) return 'Normal';
if (normalAtOne && (rate - 1.0).abs() < 0.005) return t.videoSettings.normalSpeed;
return '${rate.toStringAsFixed(2).replaceFirst(_trailingZeroPattern, '')}x';
}
+60
View File
@@ -1,7 +1,15 @@
import 'dart:convert';
import 'dart:math';
import 'package:cached_network_image_ce/cached_network_image.dart';
import 'package:crypto/crypto.dart';
import 'package:flutter/widgets.dart';
import '../media/media_item.dart';
import '../media/media_server_client.dart';
import '../services/device_performance.dart';
import '../services/image_cache_service.dart';
import '../services/settings_service.dart' show EpisodePosterMode;
import 'platform_detector.dart';
/// Image types for different transcoding strategies
@@ -10,6 +18,7 @@ enum ImageType {
art, // Wide background art
thumb, // 16:9 episode thumbnails
logo, // Variable ratio clear logos
heroLogo, // Large hero clear logos
avatar, // Square-ish user avatars
square, // 1:1 music artwork (albums, artists, tracks)
}
@@ -107,6 +116,7 @@ class MediaImageHelper {
return roundDimensions(coverWidth, coverHeight);
case ImageType.logo:
case ImageType.heroLogo:
final logoWidth = targetWidth;
final logoHeight = targetHeight;
return roundDimensions(logoWidth, logoHeight);
@@ -243,6 +253,7 @@ class MediaImageHelper {
ImageType.art when DevicePerformance.isReduced => (_reducedMaxArtWidth, _reducedMaxArtHeight),
ImageType.art => (1920, 1080),
ImageType.logo => (600, 300),
ImageType.heroLogo => (1000, 500),
ImageType.avatar => (300, 300),
};
@@ -262,6 +273,55 @@ class MediaImageHelper {
return ResizeImage(provider, width: width, height: height, policy: ResizeImagePolicy.fit);
}
/// Selects the decode/transcode shape used by media cards and their
/// prefetchers. Keeping this derived from [MediaItem.cardShape] prevents the
/// renderer and prefetch pipeline from assigning different cache budgets.
static ImageType cardImageType(MediaItem item, EpisodePosterMode episodePosterMode, {bool mixedHubContext = false}) {
return switch (item.cardShape(episodePosterMode, mixedHubContext: mixedHubContext)) {
CardShape.square => ImageType.square,
CardShape.wide => ImageType.thumb,
CardShape.poster => ImageType.poster,
};
}
/// Creates the final provider for server-hosted artwork.
///
/// The disk key deliberately depends only on the fully bucketed URL. Decode
/// dimensions belong to Flutter's memory-cache key and must not fragment the
/// shared disk cache during small layout changes.
static ImageProvider serverArtworkProvider({
required String imageUrl,
required int memWidth,
required int memHeight,
String? cacheKey,
}) {
final provider = CachedNetworkImageProvider(
imageUrl,
cacheKey: cacheKey ?? _serverArtworkCacheKey(imageUrl),
cacheManager: PlexImageCacheManager.instance,
headers: const {'User-Agent': 'Plezy'},
);
return boundedDecode(provider, memWidth: memWidth, memHeight: memHeight);
}
static final _serverArtworkCacheKeys = <String, String>{};
static const _serverArtworkCacheKeyLimit = 512;
static String _serverArtworkCacheKey(String imageUrl) {
final cached = _serverArtworkCacheKeys.remove(imageUrl);
if (cached != null) {
_serverArtworkCacheKeys[imageUrl] = cached;
return cached;
}
final key = 'plex_optimized_${sha1.convert(utf8.encode(imageUrl))}';
if (_serverArtworkCacheKeys.length >= _serverArtworkCacheKeyLimit) {
_serverArtworkCacheKeys.remove(_serverArtworkCacheKeys.keys.first);
}
_serverArtworkCacheKeys[imageUrl] = key;
return key;
}
/// Determines if an image path is suitable for transcoding
static bool shouldTranscode(String? imagePath) {
if (imagePath == null || imagePath.isEmpty) return false;
+12 -9
View File
@@ -140,8 +140,8 @@ bool shouldOpenEpisodeDetailsForActivation({
/// For artists/albums, navigates to the music detail screens; tracks start
/// playback in their album queue.
///
/// The [onRefresh] callback is invoked with the item's id after returning from
/// the detail screen, allowing the caller to refresh state.
/// The [onRefresh] callback is invoked with the source item after returning
/// from the detail screen, preserving its server-qualified identity.
///
/// Set [isOffline] to true for downloaded content without server access.
///
@@ -156,13 +156,16 @@ bool shouldOpenEpisodeDetailsForActivation({
Future<MediaNavigationResult> navigateToMediaItem(
BuildContext context,
Object item, {
void Function(String)? onRefresh,
void Function(MediaItem source)? onRefresh,
bool isOffline = false,
bool playDirectly = false,
}) async {
if (item is MediaPlaylist) {
await Navigator.push(context, MaterialPageRoute(builder: (context) => PlaylistDetailScreen(playlist: item)));
return MediaNavigationResult.navigated;
final result = await Navigator.push<bool>(
context,
MaterialPageRoute(builder: (context) => PlaylistDetailScreen(playlist: item)),
);
return result == true ? MediaNavigationResult.listRefreshNeeded : MediaNavigationResult.navigated;
}
if (item is! MediaItem) {
@@ -233,7 +236,7 @@ Future<MediaNavigationResult> navigateToMediaItem(
}
final result = await navigateToVideoPlayer(context, metadata: mi, isOffline: isOffline);
if (result == true && context.mounted) {
onRefresh?.call(mi.id);
onRefresh?.call(mi);
}
return MediaNavigationResult.navigated;
@@ -241,7 +244,7 @@ Future<MediaNavigationResult> navigateToMediaItem(
if (playDirectly && !shouldOpenContinueWatchingDetails) {
final result = await navigateToVideoPlayer(context, metadata: mi, isOffline: isOffline);
if (result == true && context.mounted) {
onRefresh?.call(mi.id);
onRefresh?.call(mi);
}
return MediaNavigationResult.navigated;
}
@@ -259,7 +262,7 @@ Future<MediaNavigationResult> navigateToMediaItemDetails(
BuildContext context,
MediaItem mi, {
bool isOffline = false,
void Function(String)? onRefresh,
void Function(MediaItem source)? onRefresh,
MediaItem? metadataOverride,
}) async {
// Catalog stand-ins (Explore tab) must never reach MediaDetailScreen — it
@@ -284,7 +287,7 @@ Future<MediaNavigationResult> navigateToMediaItemDetails(
),
);
if (result == true && context.mounted) {
onRefresh?.call(mi.id);
onRefresh?.call(mi);
}
return MediaNavigationResult.navigated;
}
@@ -18,6 +18,7 @@ import '../../utils/app_logger.dart';
import '../../utils/dialogs.dart';
import '../../utils/snackbar_helper.dart';
import '../../widgets/dialog_action_button.dart';
import '../../widgets/app_icon.dart';
import '../../utils/video_player_navigation.dart';
import '../../widgets/focused_scroll_scaffold.dart';
import '../../widgets/app_menu.dart';
@@ -135,7 +136,7 @@ class _NotInSessionViewState extends State<_NotInSessionView> with MountedSetSta
child: Column(
mainAxisSize: .min,
children: [
Icon(Symbols.group_rounded, size: 80, color: theme.colorScheme.primary),
AppIcon(Symbols.group_rounded, size: 80, color: theme.colorScheme.primary),
const SizedBox(height: 24),
Text(t.watchTogether.title, style: theme.textTheme.headlineMedium, textAlign: TextAlign.center),
const SizedBox(height: 8),
@@ -152,7 +153,7 @@ class _NotInSessionViewState extends State<_NotInSessionView> with MountedSetSta
padding: const EdgeInsets.all(12),
child: Row(
children: [
Icon(Symbols.warning_rounded, color: theme.colorScheme.onErrorContainer),
AppIcon(Symbols.warning_rounded, color: theme.colorScheme.onErrorContainer),
const SizedBox(width: 12),
Expanded(
child: Text(
@@ -174,7 +175,7 @@ class _NotInSessionViewState extends State<_NotInSessionView> with MountedSetSta
useBackgroundFocus: true,
child: FilledButton.icon(
onPressed: _isBusy ? null : _createSession,
icon: _isCreating ? const LoadingIndicatorBox(size: 20) : const Icon(Symbols.add_rounded),
icon: _isCreating ? const LoadingIndicatorBox(size: 20) : const AppIcon(Symbols.add_rounded),
label: Text(_isCreating ? t.watchTogether.creating : t.watchTogether.createSession),
),
),
@@ -186,7 +187,7 @@ class _NotInSessionViewState extends State<_NotInSessionView> with MountedSetSta
onPressed: _isBusy ? null : _joinSession,
child: OutlinedButton.icon(
onPressed: _isBusy ? null : _joinSession,
icon: _isJoining ? const LoadingIndicatorBox(size: 20) : const Icon(Symbols.group_add_rounded),
icon: _isJoining ? const LoadingIndicatorBox(size: 20) : const AppIcon(Symbols.group_add_rounded),
label: Text(_isJoining ? t.watchTogether.joining : t.watchTogether.joinSession),
),
),
@@ -422,7 +423,7 @@ class _RecentRoomTile extends StatelessWidget {
type: MaterialType.transparency,
child: ListTile(
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
leading: isEntering ? const LoadingIndicatorBox(size: 24) : const Icon(Symbols.meeting_room_rounded),
leading: isEntering ? const LoadingIndicatorBox(size: 24) : const AppIcon(Symbols.meeting_room_rounded),
title: Text(title, maxLines: 1, overflow: .ellipsis),
subtitle: room.name != null
? Text(
@@ -430,7 +431,10 @@ class _RecentRoomTile extends StatelessWidget {
style: TextStyle(fontFamily: 'monospace', color: theme.colorScheme.onSurfaceVariant),
)
: null,
trailing: IconButton(icon: const Icon(Symbols.more_vert_rounded), onPressed: () => _showActions(context)),
trailing: IconButton(
icon: const AppIcon(Symbols.more_vert_rounded),
onPressed: () => _showActions(context),
),
onTap: isBusy ? null : onTap,
onLongPress: () => _showActions(context),
),
@@ -485,7 +489,7 @@ class _ActiveSessionContent extends StatelessWidget {
children: [
Row(
children: [
Icon(
AppIcon(
watchTogether.isHost ? Symbols.star_rounded : Symbols.group_rounded,
color: theme.colorScheme.primary,
),
@@ -509,7 +513,7 @@ class _ActiveSessionContent extends StatelessWidget {
const SizedBox(height: 8),
Row(
children: [
Icon(
AppIcon(
session.controlMode == ControlMode.anyone
? Symbols.groups_rounded
: Symbols.admin_panel_settings_rounded,
@@ -540,7 +544,7 @@ class _ActiveSessionContent extends StatelessWidget {
children: [
Row(
children: [
Icon(Symbols.people_rounded, color: theme.colorScheme.primary),
AppIcon(Symbols.people_rounded, color: theme.colorScheme.primary),
const SizedBox(width: 12),
Text(
'${t.watchTogether.participants} (${watchTogether.participantCount})',
@@ -554,7 +558,7 @@ class _ActiveSessionContent extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
Icon(
AppIcon(
participant.isHost ? Symbols.star_rounded : Symbols.person_rounded,
size: 20,
color: participant.isHost ? Colors.amber : theme.colorScheme.onSurfaceVariant,
@@ -611,7 +615,7 @@ class _ActiveSessionContent extends StatelessWidget {
foregroundColor: theme.colorScheme.error,
side: BorderSide(color: theme.colorScheme.error),
),
icon: Icon(watchTogether.isHost ? Symbols.close_rounded : Symbols.logout_rounded),
icon: AppIcon(watchTogether.isHost ? Symbols.close_rounded : Symbols.logout_rounded),
label: Text(watchTogether.isHost ? t.watchTogether.endSession : t.watchTogether.leaveSession),
),
),
@@ -691,7 +695,7 @@ class _JoinCurrentPlaybackCardState extends State<_JoinCurrentPlaybackCard> {
children: [
Row(
children: [
Icon(Symbols.play_circle_rounded, color: theme.colorScheme.primary),
AppIcon(Symbols.play_circle_rounded, color: theme.colorScheme.primary),
const SizedBox(width: 12),
Expanded(
child: Column(
@@ -719,7 +723,7 @@ class _JoinCurrentPlaybackCardState extends State<_JoinCurrentPlaybackCard> {
useBackgroundFocus: true,
child: FilledButton.icon(
onPressed: _isJoining ? null : _joinCurrentPlayback,
icon: _isJoining ? const LoadingIndicatorBox() : const Icon(Symbols.play_arrow_rounded),
icon: _isJoining ? const LoadingIndicatorBox() : const AppIcon(Symbols.play_arrow_rounded),
label: Text(_isJoining ? t.watchTogether.joining : t.watchTogether.joinCurrentPlayback),
),
),
@@ -762,7 +766,7 @@ class _SessionCodeRow extends StatelessWidget {
),
),
const SizedBox(width: 4),
Icon(Symbols.content_copy_rounded, size: 14, color: theme.colorScheme.onSurfaceVariant),
AppIcon(Symbols.content_copy_rounded, size: 14, color: theme.colorScheme.onSurfaceVariant),
],
),
),
@@ -8,6 +8,7 @@ import '../../focus/focusable_wrapper.dart';
import '../../i18n/strings.g.dart';
import '../../mixins/controller_disposer_mixin.dart';
import '../../utils/dialogs.dart';
import '../../widgets/app_icon.dart';
class JoinSessionDialog extends StatefulWidget {
const JoinSessionDialog({super.key});
@@ -48,7 +49,7 @@ class _JoinSessionDialogState extends State<JoinSessionDialog> with ControllerDi
children: [
Row(
children: [
Icon(Symbols.group_add, color: theme.colorScheme.primary),
AppIcon(Symbols.group_add_rounded, color: theme.colorScheme.primary),
const SizedBox(width: 12),
Expanded(child: Text(t.watchTogether.joinWatchSession, style: theme.textTheme.titleLarge)),
FocusableWrapper(
@@ -59,7 +60,10 @@ class _JoinSessionDialogState extends State<JoinSessionDialog> with ControllerDi
descendantsAreFocusable: false,
onSelect: () => Navigator.of(context).pop(),
onNavigateDown: _sessionIdFocusNode.requestFocus,
child: IconButton(onPressed: () => Navigator.of(context).pop(), icon: const Icon(Symbols.close)),
child: IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: const AppIcon(Symbols.close_rounded),
),
),
],
),
@@ -72,10 +76,10 @@ class _JoinSessionDialogState extends State<JoinSessionDialog> with ControllerDi
decoration: InputDecoration(
labelText: t.watchTogether.sessionCode,
hintText: t.watchTogether.enterCodeHint,
prefixIcon: const Icon(Symbols.tag),
prefixIcon: const AppIcon(Symbols.tag_rounded),
suffixIcon: IconButton(
onPressed: _pasteFromClipboard,
icon: const Icon(Symbols.content_paste),
icon: const AppIcon(Symbols.content_paste_rounded),
tooltip: t.watchTogether.pasteFromClipboard,
),
border: const OutlineInputBorder(),
@@ -115,7 +119,7 @@ class _JoinSessionDialogState extends State<JoinSessionDialog> with ControllerDi
useBackgroundFocus: true,
child: FilledButton.icon(
onPressed: _join,
icon: const Icon(Symbols.group_add),
icon: const AppIcon(Symbols.group_add_rounded),
label: Text(t.watchTogether.joinSession),
),
),
@@ -12,6 +12,7 @@ import '../../utils/dialogs.dart';
import '../../utils/platform_detector.dart';
import '../../utils/snackbar_helper.dart';
import '../../widgets/bottom_sheet_header.dart';
import '../../widgets/app_icon.dart';
import '../../widgets/focusable_list_tile.dart';
import '../../widgets/overlay_sheet.dart';
import '../models/watch_session.dart';
@@ -95,11 +96,11 @@ class _SessionIndicator extends StatelessWidget {
width: 16,
height: 16,
child: PlatformDetector.isTV()
? const Icon(Symbols.sync_rounded, size: 16, color: Colors.white)
? const AppIcon(Symbols.sync_rounded, size: 16, color: Colors.white)
: const CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
)
else
Icon(Symbols.group, size: 18, color: isHost ? Colors.amber : Colors.white),
AppIcon(Symbols.group_rounded, size: 18, color: isHost ? Colors.amber : Colors.white),
const SizedBox(width: 6),
Text(
'$participantCount',
@@ -143,7 +144,7 @@ class _SessionMenuSheet extends StatelessWidget {
children: [
BottomSheetHeader(
title: t.watchTogether.title,
icon: Symbols.group,
icon: Symbols.group_rounded,
iconColor: theme.colorScheme.primary,
action: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
@@ -197,7 +198,7 @@ class _SessionMenuSheet extends StatelessWidget {
style: theme.textTheme.bodySmall?.copyWith(fontFamily: 'monospace', fontWeight: .bold),
),
const SizedBox(width: 8),
Icon(Symbols.content_copy_rounded, size: 16, color: theme.colorScheme.onSurfaceVariant),
AppIcon(Symbols.content_copy_rounded, size: 16, color: theme.colorScheme.onSurfaceVariant),
],
),
),
@@ -215,8 +216,8 @@ class _SessionMenuSheet extends StatelessWidget {
backgroundColor: participant.isHost
? theme.colorScheme.primary
: theme.colorScheme.surfaceContainerHighest,
child: Icon(
participant.isHost ? Symbols.star : Symbols.person,
child: AppIcon(
participant.isHost ? Symbols.star_rounded : Symbols.person_rounded,
color: participant.isHost ? Colors.white : theme.colorScheme.onSurfaceVariant,
size: 20,
),
@@ -228,7 +229,7 @@ class _SessionMenuSheet extends StatelessWidget {
width: 16,
height: 16,
child: PlatformDetector.isTV()
? const Icon(Symbols.hourglass_empty_rounded, size: 16)
? const AppIcon(Symbols.hourglass_empty_rounded, size: 16)
: const CircularProgressIndicator(strokeWidth: 2),
)
: null,
@@ -239,7 +240,7 @@ class _SessionMenuSheet extends StatelessWidget {
const Divider(),
const SizedBox(height: 8),
FocusableListTile(
leading: Icon(Symbols.logout, color: theme.colorScheme.error),
leading: AppIcon(Symbols.logout_rounded, color: theme.colorScheme.error),
title: Text(
provider.isHost ? t.watchTogether.endSession : t.watchTogether.leaveSession,
style: TextStyle(color: theme.colorScheme.error),
@@ -430,7 +431,7 @@ class _StatusPill extends StatelessWidget {
width: 14,
height: 14,
child: PlatformDetector.isTV()
? Icon(tvIcon, size: 14, color: Colors.white)
? AppIcon(tvIcon, size: 14, color: Colors.white)
: const CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
),
const SizedBox(width: 8),
+36
View File
@@ -119,6 +119,42 @@ Future<T?> showAppMenu<T>(
);
}
Future<T?> showAdaptiveAppMenu<T>(
BuildContext context, {
required List<AppMenuEntry<T>> entries,
String? title,
Offset? position,
Rect? anchorRect,
AppMenuAnchorAlignment anchorAlignment = AppMenuAnchorAlignment.start,
bool focusFirstItem = false,
double minWidth = 220,
double? maxWidth,
bool isScrollControlled = false,
}) {
// ThemeData.platform follows the real target platform by default, including
// Android TV and tvOS, while remaining overrideable in widget tests.
final platform = Theme.of(context).platform;
if (platform == TargetPlatform.iOS || platform == TargetPlatform.android) {
return OverlaySheetController.showAdaptive<T>(
context,
showDragHandle: true,
isScrollControlled: isScrollControlled,
builder: (context) => AppMenuSheet<T>(title: title, entries: entries, focusFirstItem: focusFirstItem),
);
}
return showAppMenu<T>(
context,
entries: entries,
position: position,
anchorRect: anchorRect,
anchorAlignment: anchorAlignment,
focusFirstItem: focusFirstItem,
minWidth: minWidth,
maxWidth: maxWidth,
);
}
Alignment _transitionAlignment(BuildContext context, {Offset? position, Rect? anchorRect}) {
final size = MediaQuery.sizeOf(context);
final origin = position ?? anchorRect?.center ?? Offset(size.width / 2, size.height / 2);
+2 -1
View File
@@ -38,8 +38,9 @@ Future<void> showCatalogItemMenu(BuildContext context, CatalogItem item, {Offset
anchorRect = renderBox.localToGlobal(Offset.zero) & renderBox.size;
}
final action = await showAppMenu<_CatalogMenuAction>(
final action = await showAdaptiveAppMenu<_CatalogMenuAction>(
context,
title: item.title,
anchorRect: anchorRect,
focusFirstItem: position == null,
entries: [
@@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import '../../connection/connection_registry.dart';
@@ -18,9 +19,11 @@ import '../../profiles/profile_connection_registry.dart';
import '../../providers/companion_remote_provider.dart';
import '../../services/base_peer_service.dart';
import '../../services/settings_service.dart';
import '../../theme/mono_tokens.dart';
import '../../utils/app_logger.dart';
import '../loading_indicator_box.dart';
import '../app_icon.dart';
@visibleForTesting
String companionRemotePairingErrorMessage(Object error) {
@@ -211,13 +214,13 @@ class _DiscoveryViewState extends State<DiscoveryView> with ControllerDisposerMi
IconData _platformIcon(String platform) {
switch (platform.toLowerCase()) {
case 'macos':
return Icons.desktop_mac;
return Symbols.desktop_mac_rounded;
case 'windows':
return Icons.desktop_windows;
return Symbols.desktop_windows_rounded;
case 'linux':
return Icons.computer;
return Symbols.computer_rounded;
default:
return Icons.devices;
return Symbols.devices_rounded;
}
}
@@ -242,7 +245,7 @@ class _DiscoveryViewState extends State<DiscoveryView> with ControllerDisposerMi
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Icon(Icons.error_outline, color: Theme.of(context).colorScheme.onErrorContainer),
AppIcon(Symbols.error_outline_rounded, color: Theme.of(context).colorScheme.onErrorContainer),
const SizedBox(width: 12),
Expanded(
child: Text(
@@ -291,7 +294,7 @@ class _DiscoveryViewState extends State<DiscoveryView> with ControllerDisposerMi
padding: const EdgeInsets.all(24.0),
child: Column(
children: [
const Icon(Icons.warning_amber, size: 48, color: Colors.orange),
const AppIcon(Symbols.warning_amber_rounded, size: 48, color: Colors.orange),
const SizedBox(height: 12),
Text(t.companionRemote.pairing.cryptoInitFailed, textAlign: TextAlign.center),
],
@@ -321,7 +324,7 @@ class _DiscoveryViewState extends State<DiscoveryView> with ControllerDisposerMi
padding: const EdgeInsets.all(24.0),
child: Column(
children: [
const Icon(Icons.devices_other, size: 48, color: Colors.grey),
AppIcon(Symbols.devices_other_rounded, size: 48, color: tokens(context).textMuted),
const SizedBox(height: 12),
Text(
t.companionRemote.pairing.noDevicesFound,
@@ -331,7 +334,7 @@ class _DiscoveryViewState extends State<DiscoveryView> with ControllerDisposerMi
const SizedBox(height: 8),
Text(
t.companionRemote.pairing.noDevicesHint,
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.grey),
style: Theme.of(context).textTheme.bodySmall,
textAlign: TextAlign.center,
),
],
@@ -348,10 +351,12 @@ class _DiscoveryViewState extends State<DiscoveryView> with ControllerDisposerMi
..._hosts.map(
(host) => Card(
child: ListTile(
leading: Icon(_platformIcon(host.platform), size: 32),
leading: AppIcon(_platformIcon(host.platform), size: 32),
title: Text(host.name),
subtitle: Text(host.platform),
trailing: _isConnecting ? const LoadingIndicatorBox(size: 24) : const Icon(Icons.arrow_forward),
trailing: _isConnecting
? const LoadingIndicatorBox(size: 24)
: const AppIcon(Symbols.arrow_forward_rounded),
onTap: _isConnecting ? null : () => _connect(() => _provider.connectToDiscoveredHost(host)),
),
),
@@ -378,8 +383,8 @@ class _DiscoveryViewState extends State<DiscoveryView> with ControllerDisposerMi
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
Icon(
_showManualEntry ? Icons.expand_less : Icons.expand_more,
AppIcon(
_showManualEntry ? Symbols.expand_less_rounded : Symbols.expand_more_rounded,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(width: 8),
@@ -408,7 +413,7 @@ class _DiscoveryViewState extends State<DiscoveryView> with ControllerDisposerMi
labelText: t.companionRemote.session.hostAddress,
hintText: t.companionRemote.pairing.hostAddressHint,
border: const OutlineInputBorder(),
prefixIcon: const Icon(Icons.computer),
prefixIcon: const AppIcon(Symbols.computer_rounded),
),
validator: (value) {
final hostAddress = value?.trim() ?? '';
@@ -428,7 +433,7 @@ class _DiscoveryViewState extends State<DiscoveryView> with ControllerDisposerMi
onPressed: _isConnecting ? null : _submitManualHost,
child: FilledButton.icon(
onPressed: _isConnecting ? null : _submitManualHost,
icon: _isConnecting ? const LoadingIndicatorBox(size: 16) : const Icon(Icons.link),
icon: _isConnecting ? const LoadingIndicatorBox(size: 16) : const AppIcon(Symbols.link_rounded),
label: Text(_isConnecting ? t.companionRemote.pairing.connecting : t.common.connect),
),
),
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import '../../i18n/strings.g.dart';
@@ -6,10 +7,12 @@ import '../../mixins/mounted_set_state_mixin.dart';
import '../../providers/companion_remote_provider.dart';
import '../../services/companion_remote/companion_remote_host_controller.dart';
import '../../services/settings_service.dart';
import '../../theme/mono_tokens.dart';
import '../../utils/dialogs.dart';
import '../../focus/focusable_button.dart';
import '../../focus/key_event_utils.dart';
import '../dialog_action_button.dart';
import '../app_icon.dart';
class RemoteSessionDialog extends StatefulWidget {
const RemoteSessionDialog({super.key});
@@ -148,7 +151,7 @@ class _RemoteSessionDialogState extends State<RemoteSessionDialog> with MountedS
children: [
Row(
children: [
const Icon(Icons.phone_android, size: 32),
const AppIcon(Symbols.phone_android_rounded, size: 32),
const SizedBox(width: 16),
Expanded(
child: Column(
@@ -166,7 +169,7 @@ class _RemoteSessionDialogState extends State<RemoteSessionDialog> with MountedS
onBack: _close,
onNavigateDown: () => _toggleFocusNode.requestFocus(),
useBackgroundFocus: true,
child: IconButton(icon: const Icon(Icons.close), onPressed: _close),
child: IconButton(icon: const AppIcon(Symbols.close_rounded), onPressed: _close),
),
],
),
@@ -193,7 +196,9 @@ class _RemoteSessionDialogState extends State<RemoteSessionDialog> with MountedS
useBackgroundFocus: true,
child: TextButton.icon(
onPressed: _toggleServer,
icon: Icon(provider.isHostServerRunning ? Icons.stop : Icons.play_arrow),
icon: AppIcon(
provider.isHostServerRunning ? Symbols.stop_rounded : Symbols.play_arrow_rounded,
),
label: Text(
provider.isHostServerRunning
? t.companionRemote.session.stopServer
@@ -233,12 +238,12 @@ class _RemoteSessionDialogState extends State<RemoteSessionDialog> with MountedS
if (provider.isHostServerRunning) {
return Text(
t.companionRemote.session.serverRunning,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: Theme.of(context).textTheme.bodySmall?.color),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: tokens(context).textMuted),
);
}
return Text(
t.companionRemote.session.serverStopped,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: Colors.grey),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: tokens(context).textMuted),
);
}
@@ -253,7 +258,10 @@ class _RemoteSessionDialogState extends State<RemoteSessionDialog> with MountedS
Container(
width: 12,
height: 12,
decoration: BoxDecoration(shape: BoxShape.circle, color: isRunning ? Colors.green : Colors.grey),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isRunning ? Colors.green : tokens(context).textMuted,
),
),
const SizedBox(width: 16),
Expanded(
@@ -287,7 +295,7 @@ class _RemoteSessionDialogState extends State<RemoteSessionDialog> with MountedS
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const Icon(Icons.check_circle, color: Colors.green, size: 48),
const AppIcon(Symbols.check_circle_rounded, color: Colors.green, size: 48),
const SizedBox(height: 8),
Text(t.companionRemote.session.connected, style: Theme.of(context).textTheme.titleLarge),
const SizedBox(height: 8),
+3 -1
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:url_launcher/url_launcher.dart';
import '../focus/focusable_button.dart';
@@ -7,6 +8,7 @@ import '../focus/focusable_wrapper.dart';
import '../i18n/strings.g.dart';
import '../models/trackers/device_code.dart';
import '../utils/snackbar_helper.dart';
import 'app_icon.dart';
import 'dialog_action_button.dart';
import 'loading_indicator_box.dart';
@@ -77,7 +79,7 @@ class DeviceCodeDialog extends StatelessWidget {
onPressed: _open,
useBackgroundFocus: true,
child: FilledButton.icon(
icon: const Icon(Icons.open_in_new),
icon: const AppIcon(Symbols.open_in_new_rounded),
label: Text(t.services.deviceCode.openToActivate(service: serviceName)),
onPressed: _open,
),
+2 -2
View File
@@ -32,7 +32,7 @@ class EpisodeCard extends StatefulWidget {
final MediaItem episode;
final MediaServerClient? client;
final VoidCallback onTap;
final Future<void> Function(String)? onRefresh;
final Future<void> Function(MediaItem source)? onRefresh;
final Future<void> Function()? onListRefresh;
final bool autofocus;
final bool isOffline;
@@ -92,7 +92,7 @@ class _EpisodeCardState extends State<EpisodeCard> with ContextMenuTapMixin<Epis
children: [
const Padding(
padding: .only(top: 2),
child: Icon(Symbols.star_rounded, size: 12, fill: 1, color: Colors.amber),
child: AppIcon(Symbols.star_rounded, size: 12, fill: 1, color: Colors.amber),
),
const SizedBox(width: 2),
Text(
+2 -1
View File
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import '../focus/focus_theme.dart';
import '../focus/focusable_wrapper.dart';
import '../media/media_item.dart';
import '../utils/platform_detector.dart';
import 'media_card.dart';
@@ -17,7 +18,7 @@ class FocusableMediaCard extends StatefulWidget {
final Object item;
final double? width;
final double? height;
final void Function(String itemId)? onRefresh;
final void Function(MediaItem source)? onRefresh;
final VoidCallback? onRemoveFromContinueWatching;
final VoidCallback? onListRefresh;
final bool forceGridMode;
+3 -6
View File
@@ -47,7 +47,7 @@ enum HubCardSizing {
class HubSection extends StatefulWidget {
final MediaHub hub;
final IconData icon;
final void Function(String)? onRefresh;
final void Function(MediaItem source)? onRefresh;
final VoidCallback? onRemoveFromContinueWatching;
final bool isInContinueWatching;
final bool usesContinueWatchingAction;
@@ -585,7 +585,7 @@ class HubSectionState extends State<HubSection> with MountedSetStateMixin, Skele
child: Column(
mainAxisSize: .min,
children: [
Icon(
AppIcon(
Symbols.arrow_forward_rounded,
size: isTv ? 42 : 32,
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7),
@@ -695,10 +695,7 @@ class HubSectionState extends State<HubSection> with MountedSetStateMixin, Skele
padding: widget.inset
? const EdgeInsets.symmetric(vertical: 8)
: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
t.messages.noItemsAvailable,
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.grey),
),
child: Text(t.messages.noItemsAvailable, style: Theme.of(context).textTheme.bodySmall),
),
],
),
+4 -3
View File
@@ -121,7 +121,7 @@ class MediaCard extends StatefulWidget {
final Object item;
final double? width;
final double? height;
final void Function(String itemId)? onRefresh;
final void Function(MediaItem source)? onRefresh;
final VoidCallback? onRemoveFromContinueWatching;
final VoidCallback? onListRefresh; // Callback to refresh the entire parent list
/// Overrides the card's default media navigation for specialized surfaces.
@@ -881,11 +881,12 @@ Widget _buildPosterImage(
posterUrl = useRememberedFallback ? posterFallbackUrl : primaryPosterUrl;
final mediaClient = isOffline ? null : context.tryGetMediaClientWithFallback(serverIdOrNull(item.serverId));
final fallbackIcon = _mediaPosterFallbackIcon(item);
final imageType = MediaImageHelper.cardImageType(item, episodePosterMode, mixedHubContext: mixedHubContext);
Widget image;
// Square 1:1 artwork for music (artists/albums/tracks)
if (item.kind.isMusic) {
if (imageType == ImageType.square) {
image = OptimizedMediaImage(
client: mediaClient,
imagePath: posterUrl,
@@ -912,7 +913,7 @@ Widget _buildPosterImage(
imageType: ImageType.square,
localFilePath: localPosterPath,
);
} else if (item.usesWideAspectRatio(episodePosterMode, mixedHubContext: mixedHubContext)) {
} else if (imageType == ImageType.thumb) {
// Use thumb image type for 16:9 content (episodes, or movies in mixed hubs)
image = OptimizedMediaImage.thumb(
client: mediaClient,
+49 -68
View File
@@ -1,6 +1,5 @@
import 'dart:async';
import '../media/ids.dart';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:plezy/widgets/app_icon.dart';
import 'package:material_symbols_icons/symbols.dart';
@@ -98,7 +97,7 @@ class MediaContextMenu extends StatefulWidget {
/// Dart has no nominal union type — guarded at runtime via the
/// [_itemAsMediaItem] / [_itemAsPlaylist] helpers.
final Object item;
final void Function(String itemId)? onRefresh;
final void Function(MediaItem source)? onRefresh;
final VoidCallback? onRemoveFromContinueWatching;
final VoidCallback? onListRefresh; // For refreshing list after deletion
final VoidCallback? onTap;
@@ -142,9 +141,9 @@ class MediaContextMenuState extends State<MediaContextMenu> {
bool get isContextMenuOpen => _isContextMenuOpen;
void _notifyRefresh(String itemId) {
void _notifyRefresh(MediaItem source) {
if (!mounted) return;
widget.onRefresh?.call(itemId);
widget.onRefresh?.call(source);
}
void _notifyListRefresh() {
@@ -189,7 +188,7 @@ class MediaContextMenuState extends State<MediaContextMenu> {
_ => null,
};
/// Item identifier for refresh callbacks.
/// Item identifier used for playlist and download sync operations.
String _itemId() => switch (widget.item) {
MediaItem(:final id) => id,
MediaPlaylist(:final id) => id,
@@ -234,8 +233,6 @@ class MediaContextMenuState extends State<MediaContextMenu> {
(mediaKind == MediaKind.movie || mediaKind == MediaKind.episode) &&
mediaItem?.hasActiveProgress == true;
final useBottomSheet = Platform.isIOS || Platform.isAndroid;
// Check if user has admin privileges. Backend-neutral: Plex uses the
// server-owned flag (folded with the active Plex Home profile's admin
// bit, when applicable); Jellyfin uses `JellyfinConnection.isAdministrator`
@@ -575,44 +572,29 @@ class MediaContextMenuState extends State<MediaContextMenu> {
menuActions.add(_MenuAction(value: 'extra_$i', icon: entry.icon, label: entry.label));
}
String? selected;
final openedFromKeyboard = _openedFromKeyboard;
_openedFromKeyboard = false;
if (useBottomSheet) {
// Present from the menu's own context: it sits at the trigger widget,
// below any screen-level OverlaySheetHost, while callers often pass a
// screen context from ABOVE its host (which would skip the host and
// fall back to a hostless modal sheet).
selected = await OverlaySheetController.showAdaptive<String>(
this.context,
showDragHandle: true,
isScrollControlled: true,
builder: (context) => AppMenuSheet<String>(
title: _itemDisplayTitle(),
entries: _menuEntries(menuActions),
focusFirstItem: openedFromKeyboard,
),
);
} else {
Offset position;
if (_tapPosition != null) {
position = _tapPosition!;
} else {
final RenderBox? overlay = Overlay.of(context).context.findRenderObject() as RenderBox?;
final RenderBox renderBox = context.findRenderObject() as RenderBox;
position = renderBox.localToGlobal(Offset.zero, ancestor: overlay);
}
selected = await showAppMenu<String>(
context,
entries: _menuEntries(menuActions),
position: position,
focusFirstItem: openedFromKeyboard,
);
var position = _tapPosition;
if (position == null) {
final RenderBox? overlay = Overlay.of(context).context.findRenderObject() as RenderBox?;
final RenderBox renderBox = context.findRenderObject() as RenderBox;
position = renderBox.localToGlobal(Offset.zero, ancestor: overlay);
}
// Present from the menu's own context: it sits at the trigger widget,
// below any screen-level OverlaySheetHost, while callers often pass a
// screen context from ABOVE its host (which would skip the host and
// fall back to a hostless modal sheet).
final selected = await showAdaptiveAppMenu<String>(
this.context,
title: _itemDisplayTitle(),
entries: _menuEntries(menuActions),
position: position,
focusFirstItem: openedFromKeyboard,
isScrollControlled: true,
);
try {
if (!context.mounted) return;
@@ -656,7 +638,7 @@ class MediaContextMenuState extends State<MediaContextMenu> {
context,
watched ? t.messages.markedAsWatchedOffline : t.messages.markedAsUnwatchedOffline,
);
_notifyRefresh(item.id);
_notifyRefresh(item);
}
} else {
await _executeAction(context, () async {
@@ -676,7 +658,7 @@ class MediaContextMenuState extends State<MediaContextMenu> {
if (widget.onRemoveFromContinueWatching != null) {
widget.onRemoveFromContinueWatching!();
} else {
_notifyRefresh(mediaItem.id);
_notifyRefresh(mediaItem);
}
}
} catch (e) {
@@ -711,7 +693,7 @@ class MediaContextMenuState extends State<MediaContextMenu> {
if (context.mounted) {
final item = mediaItem!;
await Navigator.push(context, MaterialPageRoute(builder: (context) => MetadataEditScreen(metadata: item)));
_notifyRefresh(item.id);
_notifyRefresh(item);
}
break;
@@ -720,7 +702,7 @@ class MediaContextMenuState extends State<MediaContextMenu> {
if (context.mounted) {
final item = mediaItem!;
await Navigator.push(context, MaterialPageRoute(builder: (context) => PlexMatchScreen(metadata: item)));
_notifyRefresh(item.id);
_notifyRefresh(item);
}
break;
@@ -737,13 +719,16 @@ class MediaContextMenuState extends State<MediaContextMenu> {
await _navigateToRelated(
context,
mediaItem!.kind == MediaKind.season ? mediaItem.parentId : mediaItem.grandparentId,
(item) {
(context, item) async {
final target = mediaDetailNavigationTargetFor(mediaItem, metadataOverride: item);
return mediaDetailRoute(
metadata: target.metadata,
initialSeasonIndex: target.initialSeasonIndex,
initialSeasonId: target.initialSeasonId,
initialEpisodeId: target.initialEpisodeId,
await Navigator.push(
context,
mediaDetailRoute(
metadata: target.metadata,
initialSeasonIndex: target.initialSeasonIndex,
initialSeasonId: target.initialSeasonId,
initialEpisodeId: target.initialEpisodeId,
),
);
},
t.messages.errorLoadingSeries,
@@ -828,12 +813,7 @@ class MediaContextMenuState extends State<MediaContextMenu> {
case 'music_album':
didNavigate = true;
await _navigateToRelated(
context,
mediaItem!.parentId,
(item) => MaterialPageRoute(builder: (_) => AlbumDetailScreen(album: item)),
t.common.error,
);
await _navigateToRelated(context, mediaItem!.parentId, navigateToAlbum, t.common.error);
break;
case 'music_artist':
@@ -841,7 +821,7 @@ class MediaContextMenuState extends State<MediaContextMenu> {
await _navigateToRelated(
context,
mediaItem!.kind == MediaKind.album ? mediaItem.parentId : mediaItem.grandparentId,
(item) => MaterialPageRoute(builder: (_) => ArtistDetailScreen(artist: item)),
navigateToArtist,
t.common.error,
);
break;
@@ -884,7 +864,7 @@ class MediaContextMenuState extends State<MediaContextMenu> {
await action();
if (context.mounted) {
showSuccessSnackBar(context, successMessage);
_notifyRefresh(_itemId());
if (_mediaItem case final source?) _notifyRefresh(source);
}
} catch (e) {
if (context.mounted) {
@@ -916,7 +896,7 @@ class MediaContextMenuState extends State<MediaContextMenu> {
if (!context.mounted) return;
if (success) {
showSuccessSnackBar(context, t.matchScreen.unmatchSuccess);
_notifyRefresh(item.id);
_notifyRefresh(item);
} else {
showErrorSnackBar(context, t.matchScreen.unmatchFailed);
}
@@ -927,23 +907,24 @@ class MediaContextMenuState extends State<MediaContextMenu> {
}
}
/// Navigate to a related item (series or season)
/// Fetch and navigate to a related item (series, album, or artist).
Future<void> _navigateToRelated(
BuildContext context,
String? id,
Route<Object?> Function(MediaItem) routeBuilder,
Future<void> Function(BuildContext context, MediaItem item) navigate,
String errorPrefix,
) async {
if (id == null) return;
final client = _getMediaClientForItem();
final refreshItemId = _itemId();
final source = _mediaItem;
if (source == null) return;
try {
final metadata = await client.fetchItem(id);
if (metadata != null && context.mounted) {
await Navigator.push(context, routeBuilder(metadata));
_notifyRefresh(refreshItemId);
await navigate(context, metadata);
_notifyRefresh(source);
}
} catch (e) {
if (context.mounted) {
@@ -1325,8 +1306,8 @@ class MediaContextMenuState extends State<MediaContextMenu> {
builder: (context) => RatingBottomSheet(
item: item,
serverClient: client,
onServerRatingChanged: (_) => _notifyRefresh(item.id),
onServerFavoriteChanged: (_) => _notifyRefresh(item.id),
onServerRatingChanged: (_) => _notifyRefresh(item),
onServerFavoriteChanged: (_) => _notifyRefresh(item),
),
);
}
@@ -1644,7 +1625,7 @@ class MediaContextMenuState extends State<MediaContextMenu> {
// DownloadProvider.deleteDownload now broadcasts the DeletionEvent,
// so DeletionAware screens (e.g. offline season detail) update without
// a duplicate notification here.
_notifyRefresh(item.id);
_notifyRefresh(item);
}
} catch (e) {
appLogger.e('Failed to delete download', error: e);
@@ -1908,7 +1889,7 @@ class _PickerDialogScaffoldState<T> extends State<_PickerDialogScaffold<T>> {
decoration: pillInputDecoration(
context,
hintText: widget.searchHint,
prefixIcon: const Icon(Symbols.search_rounded, size: 20),
prefixIcon: const AppIcon(Symbols.search_rounded, size: 20),
),
onChanged: _onFilterChanged,
),

Some files were not shown because too many files have changed in this diff Show More