feat: bottom sheet polish

This commit is contained in:
edde746
2026-03-02 21:24:53 +01:00
parent 3b2e278868
commit 47f2afb665
22 changed files with 295 additions and 271 deletions
+1 -2
View File
@@ -32,6 +32,7 @@
"delete": "Löschen",
"shuffle": "Zufall",
"addTo": "Hinzufügen zu...",
"createNew": "Neu erstellen",
"remove": "Entfernen",
"paste": "Einfügen",
"connect": "Verbinden",
@@ -634,7 +635,6 @@
"itemAdded": "Zur Wiedergabeliste hinzugefügt",
"itemRemoved": "Aus Wiedergabeliste entfernt",
"selectPlaylist": "Wiedergabeliste auswählen",
"createNewPlaylist": "Neue Wiedergabeliste erstellen",
"errorCreating": "Wiedergabeliste konnte nicht erstellt werden",
"errorDeleting": "Wiedergabeliste konnte nicht gelöscht werden",
"errorLoading": "Wiedergabelisten konnten nicht geladen werden",
@@ -655,7 +655,6 @@
"deleteFailedWithError": "Sammlung konnte nicht gelöscht werden: ${error}",
"failedToLoadItems": "Sammlungselemente konnten nicht geladen werden: ${error}",
"selectCollection": "Sammlung auswählen",
"createNewCollection": "Neue Sammlung erstellen",
"collectionName": "Sammlungsname",
"enterCollectionName": "Sammlungsnamen eingeben",
"addedToCollection": "Zur Sammlung hinzugefügt",
+1 -2
View File
@@ -32,6 +32,7 @@
"delete": "Delete",
"shuffle": "Shuffle",
"addTo": "Add to...",
"createNew": "Create new",
"remove": "Remove",
"paste": "Paste",
"connect": "Connect",
@@ -607,7 +608,6 @@
"deleteFailedWithError": "Failed to delete collection: ${error}",
"failedToLoadItems": "Failed to load collection items: ${error}",
"selectCollection": "Select Collection",
"createNewCollection": "Create New Collection",
"collectionName": "Collection Name",
"enterCollectionName": "Enter collection name",
"addedToCollection": "Added to collection",
@@ -639,7 +639,6 @@
"itemAdded": "Added to playlist",
"itemRemoved": "Removed from playlist",
"selectPlaylist": "Select Playlist",
"createNewPlaylist": "Create New Playlist",
"errorCreating": "Failed to create playlist",
"errorDeleting": "Failed to delete playlist",
"errorLoading": "Failed to load playlists",
+1 -2
View File
@@ -32,6 +32,7 @@
"delete": "Eliminar",
"shuffle": "Aleatorio",
"addTo": "Añadir a...",
"createNew": "Crear",
"remove": "Eliminar",
"paste": "Pegar",
"connect": "Conectar",
@@ -607,7 +608,6 @@
"deleteFailedWithError": "Error al eliminar la colección: ${error}",
"failedToLoadItems": "Error al cargar los elementos de la colección: ${error}",
"selectCollection": "Seleccionar Colección",
"createNewCollection": "Crear Nueva Colección",
"collectionName": "Nombre de la Colección",
"enterCollectionName": "Introduce el nombre de la colección",
"addedToCollection": "Añadido a la colección",
@@ -639,7 +639,6 @@
"itemAdded": "Añadido a la lista",
"itemRemoved": "Eliminado de la lista",
"selectPlaylist": "Seleccionar Lista",
"createNewPlaylist": "Crear Nueva Lista",
"errorCreating": "Error al crear la lista",
"errorDeleting": "Error al eliminar la lista",
"errorLoading": "Error al cargar las listas",
+1 -2
View File
@@ -32,6 +32,7 @@
"delete": "Supprimer",
"shuffle": "Mélanger",
"addTo": "Ajouter à...",
"createNew": "Créer",
"remove": "Supprimer",
"paste": "Coller",
"connect": "Connecter",
@@ -607,7 +608,6 @@
"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",
"createNewCollection": "Créer une nouvelle collection",
"collectionName": "Nom de la collection",
"enterCollectionName": "Entrez le nom de la collection",
"addedToCollection": "Ajouté à la collection",
@@ -639,7 +639,6 @@
"itemAdded": "Ajouté à la playlist",
"itemRemoved": "Retiré de la playlist",
"selectPlaylist": "Select Playlist",
"createNewPlaylist": "Créer une nouvelle playlist",
"errorCreating": "Échec de la création de playlist",
"errorDeleting": "Échec de suppression de playlist",
"errorLoading": "Échec de chargement de playlists",
+1 -2
View File
@@ -32,6 +32,7 @@
"delete": "Elimina",
"shuffle": "Casuale",
"addTo": "Aggiungi a...",
"createNew": "Crea",
"remove": "Rimuovi",
"paste": "Incolla",
"connect": "Connetti",
@@ -634,7 +635,6 @@
"itemAdded": "Aggiunto alla playlist",
"itemRemoved": "Rimosso dalla playlist",
"selectPlaylist": "Seleziona playlist",
"createNewPlaylist": "Crea nuova playlist",
"errorCreating": "Errore durante la creazione della playlist",
"errorDeleting": "Errore durante l'eliminazione della playlist",
"errorLoading": "Errore durante il caricamento delle playlist",
@@ -655,7 +655,6 @@
"deleteFailedWithError": "Impossibile eliminare la raccolta: ${error}",
"failedToLoadItems": "Impossibile caricare gli elementi della raccolta: ${error}",
"selectCollection": "Seleziona raccolta",
"createNewCollection": "Crea nuova raccolta",
"collectionName": "Nome raccolta",
"enterCollectionName": "Inserisci nome raccolta",
"addedToCollection": "Aggiunto alla raccolta",
+1 -2
View File
@@ -32,6 +32,7 @@
"delete": "삭제",
"shuffle": "무작위 재생",
"addTo": "추가하기...",
"createNew": "새로 만들기",
"remove": "삭제",
"paste": "붙여넣기",
"connect": "연결",
@@ -607,7 +608,6 @@
"deleteFailedWithError": "컬렉션 삭제 실패: ${error}",
"failedToLoadItems": "컬렉션 항목 로드 실패: ${error}",
"selectCollection": "컬렉션 선택",
"createNewCollection": "새 컬렉션 생성",
"collectionName": "컬렉션 이름",
"enterCollectionName": "컬렉션 이름 입력",
"addedToCollection": "컬렉션에 추가됨",
@@ -639,7 +639,6 @@
"itemAdded": "재생 목록에 추가 되었습니다",
"itemRemoved": "재생 목록에서 제거됨",
"selectPlaylist": "재생 목록 선택",
"createNewPlaylist": "새 재생 목록 생성",
"errorCreating": "재생 목록 생성 실패",
"errorDeleting": "재생 목록 삭제 실패",
"errorLoading": "재생 목록 로드 실패",
+1 -2
View File
@@ -32,6 +32,7 @@
"delete": "Verwijderen",
"shuffle": "Willekeurig",
"addTo": "Toevoegen aan...",
"createNew": "Nieuw aanmaken",
"remove": "Verwijderen",
"paste": "Plakken",
"connect": "Verbinden",
@@ -634,7 +635,6 @@
"itemAdded": "Toegevoegd aan afspeellijst",
"itemRemoved": "Verwijderd uit afspeellijst",
"selectPlaylist": "Selecteer afspeellijst",
"createNewPlaylist": "Nieuwe afspeellijst maken",
"errorCreating": "Fout bij maken afspeellijst",
"errorDeleting": "Fout bij verwijderen afspeellijst",
"errorLoading": "Fout bij laden afspeellijsten",
@@ -655,7 +655,6 @@
"deleteFailedWithError": "Collectie verwijderen mislukt: ${error}",
"failedToLoadItems": "Collectie-items laden mislukt: ${error}",
"selectCollection": "Selecteer collectie",
"createNewCollection": "Nieuwe collectie maken",
"collectionName": "Collectienaam",
"enterCollectionName": "Voer collectienaam in",
"addedToCollection": "Toegevoegd aan collectie",
+2 -2
View File
@@ -4,9 +4,9 @@
/// To regenerate, run: `dart run slang`
///
/// Locales: 9
/// Strings: 7272 (808 per locale)
/// Strings: 7263 (807 per locale)
///
/// Built on 2026-03-02 at 16:42 UTC
/// Built on 2026-03-02 at 20:21 UTC
// coverage:ignore-file
// ignore_for_file: type=lint, unused_import
+5 -7
View File
@@ -130,6 +130,7 @@ class _TranslationsCommonDe implements TranslationsCommonEn {
@override String get delete => 'Löschen';
@override String get shuffle => 'Zufall';
@override String get addTo => 'Hinzufügen zu...';
@override String get createNew => 'Neu erstellen';
@override String get remove => 'Entfernen';
@override String get paste => 'Einfügen';
@override String get connect => 'Verbinden';
@@ -506,7 +507,7 @@ class _TranslationsVideoControlsDe implements TranslationsVideoControlsEn {
@override String get timelineSlider => 'Video-Zeitleiste';
@override String get volumeSlider => 'Lautstärkepegel';
@override String endsAt({required Object time}) => 'Endet um ${time}';
@override String get pipActive => 'Playing in Picture-in-Picture';
@override String get pipActive => 'Wiedergabe im Bild-in-Bild-Modus';
@override String get pipFailed => 'Bild-in-Bild konnte nicht gestartet werden';
@override late final _TranslationsVideoControlsPipErrorsDe pipErrors = _TranslationsVideoControlsPipErrorsDe._(_root);
@override String get chapters => 'Kapitel';
@@ -888,7 +889,6 @@ class _TranslationsPlaylistsDe implements TranslationsPlaylistsEn {
@override String get itemAdded => 'Zur Wiedergabeliste hinzugefügt';
@override String get itemRemoved => 'Aus Wiedergabeliste entfernt';
@override String get selectPlaylist => 'Wiedergabeliste auswählen';
@override String get createNewPlaylist => 'Neue Wiedergabeliste erstellen';
@override String get errorCreating => 'Wiedergabeliste konnte nicht erstellt werden';
@override String get errorDeleting => 'Wiedergabeliste konnte nicht gelöscht werden';
@override String get errorLoading => 'Wiedergabelisten konnten nicht geladen werden';
@@ -916,7 +916,6 @@ class _TranslationsCollectionsDe implements TranslationsCollectionsEn {
@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 createNewCollection => 'Neue Sammlung erstellen';
@override String get collectionName => 'Sammlungsname';
@override String get enterCollectionName => 'Sammlungsnamen eingeben';
@override String get addedToCollection => 'Zur Sammlung hinzugefügt';
@@ -1333,6 +1332,7 @@ extension on TranslationsDe {
'common.delete' => 'Löschen',
'common.shuffle' => 'Zufall',
'common.addTo' => 'Hinzufügen zu...',
'common.createNew' => 'Neu erstellen',
'common.remove' => 'Entfernen',
'common.paste' => 'Einfügen',
'common.connect' => 'Verbinden',
@@ -1628,7 +1628,7 @@ extension on TranslationsDe {
'videoControls.timelineSlider' => 'Video-Zeitleiste',
'videoControls.volumeSlider' => 'Lautstärkepegel',
'videoControls.endsAt' => ({required Object time}) => 'Endet um ${time}',
'videoControls.pipActive' => 'Playing in Picture-in-Picture',
'videoControls.pipActive' => 'Wiedergabe im Bild-in-Bild-Modus',
'videoControls.pipFailed' => 'Bild-in-Bild konnte nicht gestartet werden',
'videoControls.pipErrors.androidVersion' => 'Erfordert Android 8.0 oder neuer',
'videoControls.pipErrors.iosVersion' => 'Erfordert iOS 15.0 oder neuer',
@@ -1816,9 +1816,9 @@ extension on TranslationsDe {
'liveTv.noChannels' => 'Keine Kanäle verfügbar',
'liveTv.noDvr' => 'Kein DVR auf einem Server konfiguriert',
'liveTv.tuneFailed' => 'Kanal konnte nicht eingestellt werden',
'liveTv.loading' => 'Kanäle werden geladen...',
_ => null,
} ?? switch (path) {
'liveTv.loading' => 'Kanäle werden geladen...',
'liveTv.nowPlaying' => 'Läuft gerade',
'liveTv.noPrograms' => 'Keine Programmdaten verfügbar',
'liveTv.channelNumber' => ({required Object number}) => 'Kanal ${number}',
@@ -1873,7 +1873,6 @@ extension on TranslationsDe {
'playlists.itemAdded' => 'Zur Wiedergabeliste hinzugefügt',
'playlists.itemRemoved' => 'Aus Wiedergabeliste entfernt',
'playlists.selectPlaylist' => 'Wiedergabeliste auswählen',
'playlists.createNewPlaylist' => 'Neue Wiedergabeliste erstellen',
'playlists.errorCreating' => 'Wiedergabeliste konnte nicht erstellt werden',
'playlists.errorDeleting' => 'Wiedergabeliste konnte nicht gelöscht werden',
'playlists.errorLoading' => 'Wiedergabelisten konnten nicht geladen werden',
@@ -1892,7 +1891,6 @@ extension on TranslationsDe {
'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.createNewCollection' => 'Neue Sammlung erstellen',
'collections.collectionName' => 'Sammlungsname',
'collections.enterCollectionName' => 'Sammlungsnamen eingeben',
'collections.addedToCollection' => 'Zur Sammlung hinzugefügt',
+5 -9
View File
@@ -190,6 +190,9 @@ class TranslationsCommonEn {
/// en: 'Add to...'
String get addTo => 'Add to...';
/// en: 'Create new'
String get createNew => 'Create new';
/// en: 'Remove'
String get remove => 'Remove';
@@ -1871,9 +1874,6 @@ class TranslationsCollectionsEn {
/// en: 'Select Collection'
String get selectCollection => 'Select Collection';
/// en: 'Create New Collection'
String get createNewCollection => 'Create New Collection';
/// en: 'Collection Name'
String get collectionName => 'Collection Name';
@@ -1970,9 +1970,6 @@ class TranslationsPlaylistsEn {
/// en: 'Select Playlist'
String get selectPlaylist => 'Select Playlist';
/// en: 'Create New Playlist'
String get createNewPlaylist => 'Create New Playlist';
/// en: 'Failed to create playlist'
String get errorCreating => 'Failed to create playlist';
@@ -2956,6 +2953,7 @@ extension on Translations {
'common.delete' => 'Delete',
'common.shuffle' => 'Shuffle',
'common.addTo' => 'Add to...',
'common.createNew' => 'Create new',
'common.remove' => 'Remove',
'common.paste' => 'Paste',
'common.connect' => 'Connect',
@@ -3439,9 +3437,9 @@ extension on Translations {
'liveTv.noChannels' => 'No channels available',
'liveTv.noDvr' => 'No DVR configured on any server',
'liveTv.tuneFailed' => 'Failed to tune channel',
'liveTv.loading' => 'Loading channels...',
_ => null,
} ?? switch (path) {
'liveTv.loading' => 'Loading channels...',
'liveTv.nowPlaying' => 'Now Playing',
'liveTv.noPrograms' => 'No program data available',
'liveTv.channelNumber' => ({required Object number}) => 'Ch. ${number}',
@@ -3471,7 +3469,6 @@ extension on Translations {
'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.createNewCollection' => 'Create New Collection',
'collections.collectionName' => 'Collection Name',
'collections.enterCollectionName' => 'Enter collection name',
'collections.addedToCollection' => 'Added to collection',
@@ -3501,7 +3498,6 @@ extension on Translations {
'playlists.itemAdded' => 'Added to playlist',
'playlists.itemRemoved' => 'Removed from playlist',
'playlists.selectPlaylist' => 'Select Playlist',
'playlists.createNewPlaylist' => 'Create New Playlist',
'playlists.errorCreating' => 'Failed to create playlist',
'playlists.errorDeleting' => 'Failed to delete playlist',
'playlists.errorLoading' => 'Failed to load playlists',
+5 -7
View File
@@ -130,6 +130,7 @@ class _TranslationsCommonEs implements TranslationsCommonEn {
@override String get delete => 'Eliminar';
@override String get shuffle => 'Aleatorio';
@override String get addTo => 'Añadir a...';
@override String get createNew => 'Crear';
@override String get remove => 'Eliminar';
@override String get paste => 'Pegar';
@override String get connect => 'Conectar';
@@ -506,7 +507,7 @@ class _TranslationsVideoControlsEs implements TranslationsVideoControlsEn {
@override String get timelineSlider => 'Línea de tiempo del video';
@override String get volumeSlider => 'Nivel de volumen';
@override String endsAt({required Object time}) => 'Termina a las ${time}';
@override String get pipActive => 'Playing in Picture-in-Picture';
@override String get pipActive => 'Reproduciendo en Imagen en Imagen';
@override String get pipFailed => 'Error al iniciar Imagen en Imagen';
@override late final _TranslationsVideoControlsPipErrorsEs pipErrors = _TranslationsVideoControlsPipErrorsEs._(_root);
@override String get chapters => 'Capítulos';
@@ -854,7 +855,6 @@ class _TranslationsCollectionsEs implements TranslationsCollectionsEn {
@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 createNewCollection => 'Crear Nueva Colección';
@override String get collectionName => 'Nombre de la Colección';
@override String get enterCollectionName => 'Introduce el nombre de la colección';
@override String get addedToCollection => 'Añadido a la colección';
@@ -893,7 +893,6 @@ class _TranslationsPlaylistsEs implements TranslationsPlaylistsEn {
@override String get itemAdded => 'Añadido a la lista';
@override String get itemRemoved => 'Eliminado de la lista';
@override String get selectPlaylist => 'Seleccionar Lista';
@override String get createNewPlaylist => 'Crear Nueva Lista';
@override String get errorCreating => 'Error al crear la lista';
@override String get errorDeleting => 'Error al eliminar la lista';
@override String get errorLoading => 'Error al cargar las listas';
@@ -1333,6 +1332,7 @@ extension on TranslationsEs {
'common.delete' => 'Eliminar',
'common.shuffle' => 'Aleatorio',
'common.addTo' => 'Añadir a...',
'common.createNew' => 'Crear',
'common.remove' => 'Eliminar',
'common.paste' => 'Pegar',
'common.connect' => 'Conectar',
@@ -1628,7 +1628,7 @@ extension on TranslationsEs {
'videoControls.timelineSlider' => 'Línea de tiempo del video',
'videoControls.volumeSlider' => 'Nivel de volumen',
'videoControls.endsAt' => ({required Object time}) => 'Termina a las ${time}',
'videoControls.pipActive' => 'Playing in Picture-in-Picture',
'videoControls.pipActive' => 'Reproduciendo en Imagen en Imagen',
'videoControls.pipFailed' => 'Error al iniciar Imagen en Imagen',
'videoControls.pipErrors.androidVersion' => 'Requiere Android 8.0 o más reciente',
'videoControls.pipErrors.iosVersion' => 'Requiere iOS 15.0 o más reciente',
@@ -1816,9 +1816,9 @@ extension on TranslationsEs {
'liveTv.noChannels' => 'No hay canales disponibles',
'liveTv.noDvr' => 'No hay DVR configurado en ningún servidor',
'liveTv.tuneFailed' => 'Error al sintonizar el canal',
'liveTv.loading' => 'Cargando canales...',
_ => null,
} ?? switch (path) {
'liveTv.loading' => 'Cargando canales...',
'liveTv.nowPlaying' => 'Reproduciendo ahora',
'liveTv.noPrograms' => 'No hay datos de programación disponibles',
'liveTv.channelNumber' => ({required Object number}) => 'Canal ${number}',
@@ -1848,7 +1848,6 @@ extension on TranslationsEs {
'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.createNewCollection' => 'Crear Nueva Colección',
'collections.collectionName' => 'Nombre de la Colección',
'collections.enterCollectionName' => 'Introduce el nombre de la colección',
'collections.addedToCollection' => 'Añadido a la colección',
@@ -1878,7 +1877,6 @@ extension on TranslationsEs {
'playlists.itemAdded' => 'Añadido a la lista',
'playlists.itemRemoved' => 'Eliminado de la lista',
'playlists.selectPlaylist' => 'Seleccionar Lista',
'playlists.createNewPlaylist' => 'Crear Nueva Lista',
'playlists.errorCreating' => 'Error al crear la lista',
'playlists.errorDeleting' => 'Error al eliminar la lista',
'playlists.errorLoading' => 'Error al cargar las listas',
+5 -7
View File
@@ -130,6 +130,7 @@ class _TranslationsCommonFr implements TranslationsCommonEn {
@override String get delete => 'Supprimer';
@override String get shuffle => 'Mélanger';
@override String get addTo => 'Ajouter à...';
@override String get createNew => 'Créer';
@override String get remove => 'Supprimer';
@override String get paste => 'Coller';
@override String get connect => 'Connecter';
@@ -506,7 +507,7 @@ class _TranslationsVideoControlsFr implements TranslationsVideoControlsEn {
@override String get timelineSlider => 'Timeline vidéo';
@override String get volumeSlider => 'Niveau sonore';
@override String endsAt({required Object time}) => 'Fin à ${time}';
@override String get pipActive => 'Playing in Picture-in-Picture';
@override String get pipActive => 'Lecture en mode image dans l\'image';
@override String get pipFailed => 'Échec du démarrage du mode image dans l\'image';
@override late final _TranslationsVideoControlsPipErrorsFr pipErrors = _TranslationsVideoControlsPipErrorsFr._(_root);
@override String get chapters => 'Chapitres';
@@ -854,7 +855,6 @@ class _TranslationsCollectionsFr implements TranslationsCollectionsEn {
@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 createNewCollection => 'Créer une nouvelle collection';
@override String get collectionName => 'Nom de la collection';
@override String get enterCollectionName => 'Entrez le nom de la collection';
@override String get addedToCollection => 'Ajouté à la collection';
@@ -893,7 +893,6 @@ class _TranslationsPlaylistsFr implements TranslationsPlaylistsEn {
@override String get itemAdded => 'Ajouté à la playlist';
@override String get itemRemoved => 'Retiré de la playlist';
@override String get selectPlaylist => 'Select Playlist';
@override String get createNewPlaylist => 'Créer une nouvelle playlist';
@override String get errorCreating => 'Échec de la création de playlist';
@override String get errorDeleting => 'Échec de suppression de playlist';
@override String get errorLoading => 'Échec de chargement de playlists';
@@ -1333,6 +1332,7 @@ extension on TranslationsFr {
'common.delete' => 'Supprimer',
'common.shuffle' => 'Mélanger',
'common.addTo' => 'Ajouter à...',
'common.createNew' => 'Créer',
'common.remove' => 'Supprimer',
'common.paste' => 'Coller',
'common.connect' => 'Connecter',
@@ -1628,7 +1628,7 @@ extension on TranslationsFr {
'videoControls.timelineSlider' => 'Timeline vidéo',
'videoControls.volumeSlider' => 'Niveau sonore',
'videoControls.endsAt' => ({required Object time}) => 'Fin à ${time}',
'videoControls.pipActive' => 'Playing in Picture-in-Picture',
'videoControls.pipActive' => 'Lecture en mode image dans l\'image',
'videoControls.pipFailed' => 'Échec du démarrage du mode image dans l\'image',
'videoControls.pipErrors.androidVersion' => 'Nécessite Android 8.0 ou plus récent',
'videoControls.pipErrors.iosVersion' => 'Nécessite iOS 15.0 ou plus récent',
@@ -1816,9 +1816,9 @@ extension on TranslationsFr {
'liveTv.noChannels' => 'Aucune chaîne disponible',
'liveTv.noDvr' => 'Aucun DVR configuré sur les serveurs',
'liveTv.tuneFailed' => 'Impossible de syntoniser la chaîne',
'liveTv.loading' => 'Chargement des chaînes...',
_ => null,
} ?? switch (path) {
'liveTv.loading' => 'Chargement des chaînes...',
'liveTv.nowPlaying' => 'En cours de lecture',
'liveTv.noPrograms' => 'Aucune donnée de programme disponible',
'liveTv.channelNumber' => ({required Object number}) => 'Ch. ${number}',
@@ -1848,7 +1848,6 @@ extension on TranslationsFr {
'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.createNewCollection' => 'Créer une nouvelle collection',
'collections.collectionName' => 'Nom de la collection',
'collections.enterCollectionName' => 'Entrez le nom de la collection',
'collections.addedToCollection' => 'Ajouté à la collection',
@@ -1878,7 +1877,6 @@ extension on TranslationsFr {
'playlists.itemAdded' => 'Ajouté à la playlist',
'playlists.itemRemoved' => 'Retiré de la playlist',
'playlists.selectPlaylist' => 'Select Playlist',
'playlists.createNewPlaylist' => 'Créer une nouvelle playlist',
'playlists.errorCreating' => 'Échec de la création de playlist',
'playlists.errorDeleting' => 'Échec de suppression de playlist',
'playlists.errorLoading' => 'Échec de chargement de playlists',
+5 -7
View File
@@ -130,6 +130,7 @@ class _TranslationsCommonIt implements TranslationsCommonEn {
@override String get delete => 'Elimina';
@override String get shuffle => 'Casuale';
@override String get addTo => 'Aggiungi a...';
@override String get createNew => 'Crea';
@override String get remove => 'Rimuovi';
@override String get paste => 'Incolla';
@override String get connect => 'Connetti';
@@ -506,7 +507,7 @@ class _TranslationsVideoControlsIt implements TranslationsVideoControlsEn {
@override String get timelineSlider => 'Timeline video';
@override String get volumeSlider => 'Livello volume';
@override String endsAt({required Object time}) => 'Finisce alle ${time}';
@override String get pipActive => 'Playing in Picture-in-Picture';
@override String get pipActive => 'Riproduzione in Picture-in-Picture';
@override String get pipFailed => 'Impossibile avviare la modalità Picture-in-Picture';
@override late final _TranslationsVideoControlsPipErrorsIt pipErrors = _TranslationsVideoControlsPipErrorsIt._(_root);
@override String get chapters => 'Capitoli';
@@ -888,7 +889,6 @@ class _TranslationsPlaylistsIt implements TranslationsPlaylistsEn {
@override String get itemAdded => 'Aggiunto alla playlist';
@override String get itemRemoved => 'Rimosso dalla playlist';
@override String get selectPlaylist => 'Seleziona playlist';
@override String get createNewPlaylist => 'Crea nuova playlist';
@override String get errorCreating => 'Errore durante la creazione della playlist';
@override String get errorDeleting => 'Errore durante l\'eliminazione della playlist';
@override String get errorLoading => 'Errore durante il caricamento delle playlist';
@@ -916,7 +916,6 @@ class _TranslationsCollectionsIt implements TranslationsCollectionsEn {
@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 createNewCollection => 'Crea nuova raccolta';
@override String get collectionName => 'Nome raccolta';
@override String get enterCollectionName => 'Inserisci nome raccolta';
@override String get addedToCollection => 'Aggiunto alla raccolta';
@@ -1333,6 +1332,7 @@ extension on TranslationsIt {
'common.delete' => 'Elimina',
'common.shuffle' => 'Casuale',
'common.addTo' => 'Aggiungi a...',
'common.createNew' => 'Crea',
'common.remove' => 'Rimuovi',
'common.paste' => 'Incolla',
'common.connect' => 'Connetti',
@@ -1628,7 +1628,7 @@ extension on TranslationsIt {
'videoControls.timelineSlider' => 'Timeline video',
'videoControls.volumeSlider' => 'Livello volume',
'videoControls.endsAt' => ({required Object time}) => 'Finisce alle ${time}',
'videoControls.pipActive' => 'Playing in Picture-in-Picture',
'videoControls.pipActive' => 'Riproduzione in Picture-in-Picture',
'videoControls.pipFailed' => 'Impossibile avviare la modalità Picture-in-Picture',
'videoControls.pipErrors.androidVersion' => 'Richiede Android 8.0 o versioni successive',
'videoControls.pipErrors.iosVersion' => 'Richiede iOS 15.0 o versioni successive',
@@ -1816,9 +1816,9 @@ extension on TranslationsIt {
'liveTv.noChannels' => 'Nessun canale disponibile',
'liveTv.noDvr' => 'Nessun DVR configurato su nessun server',
'liveTv.tuneFailed' => 'Impossibile sintonizzare il canale',
'liveTv.loading' => 'Caricamento canali...',
_ => null,
} ?? switch (path) {
'liveTv.loading' => 'Caricamento canali...',
'liveTv.nowPlaying' => 'In riproduzione',
'liveTv.noPrograms' => 'Nessun dato di programma disponibile',
'liveTv.channelNumber' => ({required Object number}) => 'Canale ${number}',
@@ -1873,7 +1873,6 @@ extension on TranslationsIt {
'playlists.itemAdded' => 'Aggiunto alla playlist',
'playlists.itemRemoved' => 'Rimosso dalla playlist',
'playlists.selectPlaylist' => 'Seleziona playlist',
'playlists.createNewPlaylist' => 'Crea nuova playlist',
'playlists.errorCreating' => 'Errore durante la creazione della playlist',
'playlists.errorDeleting' => 'Errore durante l\'eliminazione della playlist',
'playlists.errorLoading' => 'Errore durante il caricamento delle playlist',
@@ -1892,7 +1891,6 @@ extension on TranslationsIt {
'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.createNewCollection' => 'Crea nuova raccolta',
'collections.collectionName' => 'Nome raccolta',
'collections.enterCollectionName' => 'Inserisci nome raccolta',
'collections.addedToCollection' => 'Aggiunto alla raccolta',
+5 -7
View File
@@ -130,6 +130,7 @@ class _TranslationsCommonKo implements TranslationsCommonEn {
@override String get delete => '삭제';
@override String get shuffle => '무작위 재생';
@override String get addTo => '추가하기...';
@override String get createNew => '새로 만들기';
@override String get remove => '삭제';
@override String get paste => '붙여넣기';
@override String get connect => '연결';
@@ -506,7 +507,7 @@ class _TranslationsVideoControlsKo implements TranslationsVideoControlsEn {
@override String get timelineSlider => '타임라인';
@override String get volumeSlider => '볼륨 조절';
@override String endsAt({required Object time}) => '${time}에 종료';
@override String get pipActive => 'Playing in Picture-in-Picture';
@override String get pipActive => '화면 속 화면으로 재생 중';
@override String get pipFailed => '화면 속 화면 모드를 시작할 수 없습니다';
@override late final _TranslationsVideoControlsPipErrorsKo pipErrors = _TranslationsVideoControlsPipErrorsKo._(_root);
@override String get chapters => '챕터';
@@ -854,7 +855,6 @@ class _TranslationsCollectionsKo implements TranslationsCollectionsEn {
@override String deleteFailedWithError({required Object error}) => '컬렉션 삭제 실패: ${error}';
@override String failedToLoadItems({required Object error}) => '컬렉션 항목 로드 실패: ${error}';
@override String get selectCollection => '컬렉션 선택';
@override String get createNewCollection => '새 컬렉션 생성';
@override String get collectionName => '컬렉션 이름';
@override String get enterCollectionName => '컬렉션 이름 입력';
@override String get addedToCollection => '컬렉션에 추가됨';
@@ -893,7 +893,6 @@ class _TranslationsPlaylistsKo implements TranslationsPlaylistsEn {
@override String get itemAdded => '재생 목록에 추가 되었습니다';
@override String get itemRemoved => '재생 목록에서 제거됨';
@override String get selectPlaylist => '재생 목록 선택';
@override String get createNewPlaylist => '새 재생 목록 생성';
@override String get errorCreating => '재생 목록 생성 실패';
@override String get errorDeleting => '재생 목록 삭제 실패';
@override String get errorLoading => '재생 목록 로드 실패';
@@ -1333,6 +1332,7 @@ extension on TranslationsKo {
'common.delete' => '삭제',
'common.shuffle' => '무작위 재생',
'common.addTo' => '추가하기...',
'common.createNew' => '새로 만들기',
'common.remove' => '삭제',
'common.paste' => '붙여넣기',
'common.connect' => '연결',
@@ -1628,7 +1628,7 @@ extension on TranslationsKo {
'videoControls.timelineSlider' => '타임라인',
'videoControls.volumeSlider' => '볼륨 조절',
'videoControls.endsAt' => ({required Object time}) => '${time}에 종료',
'videoControls.pipActive' => 'Playing in Picture-in-Picture',
'videoControls.pipActive' => '화면 속 화면으로 재생 중',
'videoControls.pipFailed' => '화면 속 화면 모드를 시작할 수 없습니다',
'videoControls.pipErrors.androidVersion' => 'Android 8.0 이상이 필요합니다',
'videoControls.pipErrors.iosVersion' => 'iOS 15.0 이상이 필요합니다',
@@ -1816,9 +1816,9 @@ extension on TranslationsKo {
'liveTv.noChannels' => '사용 가능한 채널이 없습니다',
'liveTv.noDvr' => '서버에 DVR이 구성되어 있지 않습니다',
'liveTv.tuneFailed' => '채널 튜닝에 실패했습니다',
'liveTv.loading' => '채널 로딩 중...',
_ => null,
} ?? switch (path) {
'liveTv.loading' => '채널 로딩 중...',
'liveTv.nowPlaying' => '현재 재생 중',
'liveTv.noPrograms' => '프로그램 데이터가 없습니다',
'liveTv.channelNumber' => ({required Object number}) => '채널 ${number}',
@@ -1848,7 +1848,6 @@ extension on TranslationsKo {
'collections.deleteFailedWithError' => ({required Object error}) => '컬렉션 삭제 실패: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => '컬렉션 항목 로드 실패: ${error}',
'collections.selectCollection' => '컬렉션 선택',
'collections.createNewCollection' => '새 컬렉션 생성',
'collections.collectionName' => '컬렉션 이름',
'collections.enterCollectionName' => '컬렉션 이름 입력',
'collections.addedToCollection' => '컬렉션에 추가됨',
@@ -1878,7 +1877,6 @@ extension on TranslationsKo {
'playlists.itemAdded' => '재생 목록에 추가 되었습니다',
'playlists.itemRemoved' => '재생 목록에서 제거됨',
'playlists.selectPlaylist' => '재생 목록 선택',
'playlists.createNewPlaylist' => '새 재생 목록 생성',
'playlists.errorCreating' => '재생 목록 생성 실패',
'playlists.errorDeleting' => '재생 목록 삭제 실패',
'playlists.errorLoading' => '재생 목록 로드 실패',
+5 -7
View File
@@ -130,6 +130,7 @@ class _TranslationsCommonNl implements TranslationsCommonEn {
@override String get delete => 'Verwijderen';
@override String get shuffle => 'Willekeurig';
@override String get addTo => 'Toevoegen aan...';
@override String get createNew => 'Nieuw aanmaken';
@override String get remove => 'Verwijderen';
@override String get paste => 'Plakken';
@override String get connect => 'Verbinden';
@@ -506,7 +507,7 @@ class _TranslationsVideoControlsNl implements TranslationsVideoControlsEn {
@override String get timelineSlider => 'Videotijdlijn';
@override String get volumeSlider => 'Volumeniveau';
@override String endsAt({required Object time}) => 'Eindigt om ${time}';
@override String get pipActive => 'Playing in Picture-in-Picture';
@override String get pipActive => 'Afspelen in beeld-in-beeld';
@override String get pipFailed => 'Beeld-in-beeld kon niet worden gestart';
@override late final _TranslationsVideoControlsPipErrorsNl pipErrors = _TranslationsVideoControlsPipErrorsNl._(_root);
@override String get chapters => 'Hoofdstukken';
@@ -888,7 +889,6 @@ class _TranslationsPlaylistsNl implements TranslationsPlaylistsEn {
@override String get itemAdded => 'Toegevoegd aan afspeellijst';
@override String get itemRemoved => 'Verwijderd uit afspeellijst';
@override String get selectPlaylist => 'Selecteer afspeellijst';
@override String get createNewPlaylist => 'Nieuwe afspeellijst maken';
@override String get errorCreating => 'Fout bij maken afspeellijst';
@override String get errorDeleting => 'Fout bij verwijderen afspeellijst';
@override String get errorLoading => 'Fout bij laden afspeellijsten';
@@ -916,7 +916,6 @@ class _TranslationsCollectionsNl implements TranslationsCollectionsEn {
@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 createNewCollection => 'Nieuwe collectie maken';
@override String get collectionName => 'Collectienaam';
@override String get enterCollectionName => 'Voer collectienaam in';
@override String get addedToCollection => 'Toegevoegd aan collectie';
@@ -1333,6 +1332,7 @@ extension on TranslationsNl {
'common.delete' => 'Verwijderen',
'common.shuffle' => 'Willekeurig',
'common.addTo' => 'Toevoegen aan...',
'common.createNew' => 'Nieuw aanmaken',
'common.remove' => 'Verwijderen',
'common.paste' => 'Plakken',
'common.connect' => 'Verbinden',
@@ -1628,7 +1628,7 @@ extension on TranslationsNl {
'videoControls.timelineSlider' => 'Videotijdlijn',
'videoControls.volumeSlider' => 'Volumeniveau',
'videoControls.endsAt' => ({required Object time}) => 'Eindigt om ${time}',
'videoControls.pipActive' => 'Playing in Picture-in-Picture',
'videoControls.pipActive' => 'Afspelen in beeld-in-beeld',
'videoControls.pipFailed' => 'Beeld-in-beeld kon niet worden gestart',
'videoControls.pipErrors.androidVersion' => 'Vereist Android 8.0 of nieuwer',
'videoControls.pipErrors.iosVersion' => 'Vereist iOS 15.0 of nieuwer',
@@ -1816,9 +1816,9 @@ extension on TranslationsNl {
'liveTv.noChannels' => 'Geen zenders beschikbaar',
'liveTv.noDvr' => 'Geen DVR geconfigureerd op een server',
'liveTv.tuneFailed' => 'Kan zender niet afstemmen',
'liveTv.loading' => 'Zenders laden...',
_ => null,
} ?? switch (path) {
'liveTv.loading' => 'Zenders laden...',
'liveTv.nowPlaying' => 'Nu aan het afspelen',
'liveTv.noPrograms' => 'Geen programmagegevens beschikbaar',
'liveTv.channelNumber' => ({required Object number}) => 'Kanaal ${number}',
@@ -1873,7 +1873,6 @@ extension on TranslationsNl {
'playlists.itemAdded' => 'Toegevoegd aan afspeellijst',
'playlists.itemRemoved' => 'Verwijderd uit afspeellijst',
'playlists.selectPlaylist' => 'Selecteer afspeellijst',
'playlists.createNewPlaylist' => 'Nieuwe afspeellijst maken',
'playlists.errorCreating' => 'Fout bij maken afspeellijst',
'playlists.errorDeleting' => 'Fout bij verwijderen afspeellijst',
'playlists.errorLoading' => 'Fout bij laden afspeellijsten',
@@ -1892,7 +1891,6 @@ extension on TranslationsNl {
'collections.deleteFailedWithError' => ({required Object error}) => 'Collectie verwijderen mislukt: ${error}',
'collections.failedToLoadItems' => ({required Object error}) => 'Collectie-items laden mislukt: ${error}',
'collections.selectCollection' => 'Selecteer collectie',
'collections.createNewCollection' => 'Nieuwe collectie maken',
'collections.collectionName' => 'Collectienaam',
'collections.enterCollectionName' => 'Voer collectienaam in',
'collections.addedToCollection' => 'Toegevoegd aan collectie',
+5 -7
View File
@@ -130,6 +130,7 @@ class _TranslationsCommonSv implements TranslationsCommonEn {
@override String get delete => 'Ta bort';
@override String get shuffle => 'Blanda';
@override String get addTo => 'Lägg till i...';
@override String get createNew => 'Skapa ny';
@override String get remove => 'Ta bort';
@override String get paste => 'Klistra in';
@override String get connect => 'Anslut';
@@ -506,7 +507,7 @@ class _TranslationsVideoControlsSv implements TranslationsVideoControlsEn {
@override String get timelineSlider => 'Videotidslinje';
@override String get volumeSlider => 'Volymnivå';
@override String endsAt({required Object time}) => 'Slutar ${time}';
@override String get pipActive => 'Playing in Picture-in-Picture';
@override String get pipActive => 'Spelar i bild-i-bild';
@override String get pipFailed => 'Bild-i-bild kunde inte starta';
@override late final _TranslationsVideoControlsPipErrorsSv pipErrors = _TranslationsVideoControlsPipErrorsSv._(_root);
@override String get chapters => 'Kapitel';
@@ -888,7 +889,6 @@ class _TranslationsPlaylistsSv implements TranslationsPlaylistsEn {
@override String get itemAdded => 'Tillagd i spellista';
@override String get itemRemoved => 'Borttagen från spellista';
@override String get selectPlaylist => 'Välj spellista';
@override String get createNewPlaylist => 'Skapa ny spellista';
@override String get errorCreating => 'Det gick inte att skapa spellista';
@override String get errorDeleting => 'Det gick inte att ta bort spellista';
@override String get errorLoading => 'Det gick inte att ladda spellistor';
@@ -916,7 +916,6 @@ class _TranslationsCollectionsSv implements TranslationsCollectionsEn {
@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 createNewCollection => 'Skapa ny samling';
@override String get collectionName => 'Samlingsnamn';
@override String get enterCollectionName => 'Ange samlingsnamn';
@override String get addedToCollection => 'Tillagd i samling';
@@ -1333,6 +1332,7 @@ extension on TranslationsSv {
'common.delete' => 'Ta bort',
'common.shuffle' => 'Blanda',
'common.addTo' => 'Lägg till i...',
'common.createNew' => 'Skapa ny',
'common.remove' => 'Ta bort',
'common.paste' => 'Klistra in',
'common.connect' => 'Anslut',
@@ -1628,7 +1628,7 @@ extension on TranslationsSv {
'videoControls.timelineSlider' => 'Videotidslinje',
'videoControls.volumeSlider' => 'Volymnivå',
'videoControls.endsAt' => ({required Object time}) => 'Slutar ${time}',
'videoControls.pipActive' => 'Playing in Picture-in-Picture',
'videoControls.pipActive' => 'Spelar i bild-i-bild',
'videoControls.pipFailed' => 'Bild-i-bild kunde inte starta',
'videoControls.pipErrors.androidVersion' => 'Kräver Android 8.0 eller nyare',
'videoControls.pipErrors.iosVersion' => 'Kräver iOS 15.0 eller nyare',
@@ -1816,9 +1816,9 @@ extension on TranslationsSv {
'liveTv.noChannels' => 'Inga kanaler tillgängliga',
'liveTv.noDvr' => 'Ingen DVR konfigurerad på någon server',
'liveTv.tuneFailed' => 'Kunde inte ställa in kanalen',
'liveTv.loading' => 'Laddar kanaler...',
_ => null,
} ?? switch (path) {
'liveTv.loading' => 'Laddar kanaler...',
'liveTv.nowPlaying' => 'Spelas nu',
'liveTv.noPrograms' => 'Ingen programdata tillgänglig',
'liveTv.channelNumber' => ({required Object number}) => 'Kanal ${number}',
@@ -1873,7 +1873,6 @@ extension on TranslationsSv {
'playlists.itemAdded' => 'Tillagd i spellista',
'playlists.itemRemoved' => 'Borttagen från spellista',
'playlists.selectPlaylist' => 'Välj spellista',
'playlists.createNewPlaylist' => 'Skapa ny spellista',
'playlists.errorCreating' => 'Det gick inte att skapa spellista',
'playlists.errorDeleting' => 'Det gick inte att ta bort spellista',
'playlists.errorLoading' => 'Det gick inte att ladda spellistor',
@@ -1892,7 +1891,6 @@ extension on TranslationsSv {
'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.createNewCollection' => 'Skapa ny samling',
'collections.collectionName' => 'Samlingsnamn',
'collections.enterCollectionName' => 'Ange samlingsnamn',
'collections.addedToCollection' => 'Tillagd i samling',
+5 -7
View File
@@ -130,6 +130,7 @@ class _TranslationsCommonZh implements TranslationsCommonEn {
@override String get delete => '删除';
@override String get shuffle => '随机播放';
@override String get addTo => '添加到...';
@override String get createNew => '新建';
@override String get remove => '删除';
@override String get paste => '粘贴';
@override String get connect => '连接';
@@ -506,7 +507,7 @@ class _TranslationsVideoControlsZh implements TranslationsVideoControlsEn {
@override String get timelineSlider => '视频时间轴';
@override String get volumeSlider => '音量调节';
@override String endsAt({required Object time}) => '${time} 结束';
@override String get pipActive => 'Playing in Picture-in-Picture';
@override String get pipActive => '正在画中画模式中播放';
@override String get pipFailed => '画中画启动失败';
@override late final _TranslationsVideoControlsPipErrorsZh pipErrors = _TranslationsVideoControlsPipErrorsZh._(_root);
@override String get chapters => '章节';
@@ -888,7 +889,6 @@ class _TranslationsPlaylistsZh implements TranslationsPlaylistsEn {
@override String get itemAdded => '已添加到播放列表';
@override String get itemRemoved => '已从播放列表中移除';
@override String get selectPlaylist => '选择播放列表';
@override String get createNewPlaylist => '创建新播放列表';
@override String get errorCreating => '创建播放列表失败';
@override String get errorDeleting => '删除播放列表失败';
@override String get errorLoading => '加载播放列表失败';
@@ -916,7 +916,6 @@ class _TranslationsCollectionsZh implements TranslationsCollectionsEn {
@override String deleteFailedWithError({required Object error}) => '删除合集失败:${error}';
@override String failedToLoadItems({required Object error}) => '加载合集项目失败:${error}';
@override String get selectCollection => '选择合集';
@override String get createNewCollection => '创建新合集';
@override String get collectionName => '合集名称';
@override String get enterCollectionName => '输入合集名称';
@override String get addedToCollection => '已添加到合集';
@@ -1333,6 +1332,7 @@ extension on TranslationsZh {
'common.delete' => '删除',
'common.shuffle' => '随机播放',
'common.addTo' => '添加到...',
'common.createNew' => '新建',
'common.remove' => '删除',
'common.paste' => '粘贴',
'common.connect' => '连接',
@@ -1628,7 +1628,7 @@ extension on TranslationsZh {
'videoControls.timelineSlider' => '视频时间轴',
'videoControls.volumeSlider' => '音量调节',
'videoControls.endsAt' => ({required Object time}) => '${time} 结束',
'videoControls.pipActive' => 'Playing in Picture-in-Picture',
'videoControls.pipActive' => '正在画中画模式中播放',
'videoControls.pipFailed' => '画中画启动失败',
'videoControls.pipErrors.androidVersion' => '需要 Android 8.0 或更高版本',
'videoControls.pipErrors.iosVersion' => '需要 iOS 15.0 或更高版本',
@@ -1816,9 +1816,9 @@ extension on TranslationsZh {
'liveTv.noChannels' => '没有可用的频道',
'liveTv.noDvr' => '没有服务器配置了DVR',
'liveTv.tuneFailed' => '无法调谐频道',
'liveTv.loading' => '正在加载频道...',
_ => null,
} ?? switch (path) {
'liveTv.loading' => '正在加载频道...',
'liveTv.nowPlaying' => '正在播放',
'liveTv.noPrograms' => '没有可用的节目数据',
'liveTv.channelNumber' => ({required Object number}) => '频道 ${number}',
@@ -1873,7 +1873,6 @@ extension on TranslationsZh {
'playlists.itemAdded' => '已添加到播放列表',
'playlists.itemRemoved' => '已从播放列表中移除',
'playlists.selectPlaylist' => '选择播放列表',
'playlists.createNewPlaylist' => '创建新播放列表',
'playlists.errorCreating' => '创建播放列表失败',
'playlists.errorDeleting' => '删除播放列表失败',
'playlists.errorLoading' => '加载播放列表失败',
@@ -1892,7 +1891,6 @@ extension on TranslationsZh {
'collections.deleteFailedWithError' => ({required Object error}) => '删除合集失败:${error}',
'collections.failedToLoadItems' => ({required Object error}) => '加载合集项目失败:${error}',
'collections.selectCollection' => '选择合集',
'collections.createNewCollection' => '创建新合集',
'collections.collectionName' => '合集名称',
'collections.enterCollectionName' => '输入合集名称',
'collections.addedToCollection' => '已添加到合集',
+1 -2
View File
@@ -32,6 +32,7 @@
"delete": "Ta bort",
"shuffle": "Blanda",
"addTo": "Lägg till i...",
"createNew": "Skapa ny",
"remove": "Ta bort",
"paste": "Klistra in",
"connect": "Anslut",
@@ -634,7 +635,6 @@
"itemAdded": "Tillagd i spellista",
"itemRemoved": "Borttagen från spellista",
"selectPlaylist": "Välj spellista",
"createNewPlaylist": "Skapa ny spellista",
"errorCreating": "Det gick inte att skapa spellista",
"errorDeleting": "Det gick inte att ta bort spellista",
"errorLoading": "Det gick inte att ladda spellistor",
@@ -655,7 +655,6 @@
"deleteFailedWithError": "Det gick inte att ta bort samlingen: ${error}",
"failedToLoadItems": "Det gick inte att läsa in samlingsobjekt: ${error}",
"selectCollection": "Välj samling",
"createNewCollection": "Skapa ny samling",
"collectionName": "Samlingsnamn",
"enterCollectionName": "Ange samlingsnamn",
"addedToCollection": "Tillagd i samling",
+1 -2
View File
@@ -32,6 +32,7 @@
"delete": "删除",
"shuffle": "随机播放",
"addTo": "添加到...",
"createNew": "新建",
"remove": "删除",
"paste": "粘贴",
"connect": "连接",
@@ -634,7 +635,6 @@
"itemAdded": "已添加到播放列表",
"itemRemoved": "已从播放列表中移除",
"selectPlaylist": "选择播放列表",
"createNewPlaylist": "创建新播放列表",
"errorCreating": "创建播放列表失败",
"errorDeleting": "删除播放列表失败",
"errorLoading": "加载播放列表失败",
@@ -655,7 +655,6 @@
"deleteFailedWithError": "删除合集失败:${error}",
"failedToLoadItems": "加载合集项目失败:${error}",
"selectCollection": "选择合集",
"createNewCollection": "创建新合集",
"collectionName": "合集名称",
"enterCollectionName": "输入合集名称",
"addedToCollection": "已添加到合集",
+20 -50
View File
@@ -314,6 +314,7 @@ class MediaContextMenuState extends State<MediaContextMenu> {
if (useBottomSheet) {
selected = await OverlaySheetController.showAdaptive<String>(
context,
showDragHandle: true,
builder: (context) => _FocusableContextMenuSheet(
title: widget.item.title,
actions: menuActions,
@@ -576,7 +577,6 @@ class MediaContextMenuState extends State<MediaContextMenu> {
await OverlaySheetController.showAdaptive(
context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => FileInfoBottomSheet(fileInfo: fileInfo, title: metadata.title),
);
} else if (context.mounted) {
@@ -611,58 +611,27 @@ class MediaContextMenuState extends State<MediaContextMenu> {
/// Show submenu for Add to... (Playlist or Collection)
Future<void> _showAddToSubmenu(BuildContext context) async {
final useBottomSheet = Platform.isIOS || Platform.isAndroid;
final submenuActions = [
_MenuAction(value: 'playlist', icon: Symbols.playlist_play_rounded, label: t.playlists.playlist),
_MenuAction(value: 'collection', icon: Symbols.collections_rounded, label: t.collections.collection),
];
String? selected;
if (useBottomSheet) {
selected = await OverlaySheetController.pushAdaptive<String>(
context,
builder: (context) => SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Text(t.common.addTo, style: Theme.of(context).textTheme.titleMedium),
),
...submenuActions.map((action) {
return ListTile(
leading: AppIcon(action.icon, fill: 1),
title: Text(action.label),
onTap: () => OverlaySheetController.popAdaptive(context, action.value),
);
}),
const SizedBox(height: 8),
],
),
),
);
} else {
selected = await showMenu<String>(
context: context,
position: RelativeRect.fromLTRB(
_tapPosition?.dx ?? 0,
_tapPosition?.dy ?? 0,
_tapPosition?.dx ?? 0,
_tapPosition?.dy ?? 0,
),
items: submenuActions.map((action) {
return PopupMenuItem<String>(
value: action.value,
final selected = await showDialog<String>(
context: context,
builder: (dialogContext) => SimpleDialog(
title: Text(t.common.addTo),
contentPadding: const EdgeInsets.symmetric(vertical: 8),
children: submenuActions.map((action) {
return SimpleDialogOption(
onPressed: () => Navigator.pop(dialogContext, action.value),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [AppIcon(action.icon, fill: 1, size: 20), const SizedBox(width: 12), Text(action.label)],
children: [AppIcon(action.icon, fill: 1, size: 24), const SizedBox(width: 16), Text(action.label, style: Theme.of(dialogContext).textTheme.bodyLarge)],
),
);
}).toList(),
);
}
),
);
// Handle the submenu selection
if (selected == 'playlist' && context.mounted) {
@@ -839,7 +808,7 @@ class MediaContextMenuState extends State<MediaContextMenu> {
// Create new collection flow
final collectionName = await showTextInputDialog(
context,
title: t.collections.createNewCollection,
title: t.common.createNew,
labelText: t.collections.collectionName,
hintText: t.collections.enterCollectionName,
);
@@ -932,8 +901,9 @@ class MediaContextMenuState extends State<MediaContextMenu> {
final currentStarValue = (metadata.userRating != null && metadata.userRating! > 0)
? metadata.userRating! / 2.0
: 0.0;
await showModalBottomSheet(
context: context,
await OverlaySheetController.showAdaptive(
context,
showDragHandle: true,
builder: (context) => RatingBottomSheet(
currentRating: currentStarValue,
onRate: (stars) async {
@@ -1212,7 +1182,7 @@ class _PlaylistSelectionDialog extends StatelessWidget {
// Create new playlist option (always shown first)
return ListTile(
leading: const AppIcon(Symbols.add_rounded, fill: 1),
title: Text(t.playlists.createNewPlaylist),
title: Text(t.common.createNew),
onTap: () => Navigator.pop(context, '_create_new'),
);
}
@@ -1266,7 +1236,7 @@ class _CollectionSelectionDialog extends StatelessWidget {
// Create new collection option (always shown first)
return ListTile(
leading: const AppIcon(Symbols.add_rounded, fill: 1),
title: Text(t.collections.createNewCollection),
title: Text(t.common.createNew),
onTap: () => Navigator.pop(context, '_create_new'),
);
}
@@ -1325,7 +1295,7 @@ class _FocusableContextMenuSheetState extends State<_FocusableContextMenuSheet>
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.all(16.0),
padding: const EdgeInsets.fromLTRB(16, 4, 16, 8),
child: Text(
widget.title,
style: Theme.of(context).textTheme.titleMedium,
+124 -31
View File
@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -60,6 +61,7 @@ class OverlaySheetController {
bool barrierDismissible = true,
FocusNode? initialFocusNode,
Alignment alignment = Alignment.bottomCenter,
bool showDragHandle = false,
}) {
return _state._show<T>(
builder: builder,
@@ -68,6 +70,7 @@ class OverlaySheetController {
barrierDismissible: barrierDismissible,
initialFocusNode: initialFocusNode,
alignment: alignment,
showDragHandle: showDragHandle,
);
}
@@ -104,6 +107,7 @@ class OverlaySheetController {
bool isScrollControlled = false,
FocusNode? initialFocusNode,
Alignment alignment = Alignment.bottomCenter,
bool showDragHandle = false,
}) {
final controller = maybeOf(context);
if (controller != null) {
@@ -114,6 +118,7 @@ class OverlaySheetController {
barrierDismissible: barrierDismissible,
initialFocusNode: initialFocusNode,
alignment: alignment,
showDragHandle: showDragHandle,
);
}
return showModalBottomSheet<T>(
@@ -193,6 +198,7 @@ class _OverlaySheetHostState extends State<OverlaySheetHost> with SingleTickerPr
bool _isOpen = false;
bool _isClosing = false;
bool _barrierDismissible = true;
bool _showDragHandle = false;
BoxConstraints? _constraints;
Color? _explicitBackgroundColor;
Alignment _alignment = Alignment.bottomCenter;
@@ -200,6 +206,7 @@ class _OverlaySheetHostState extends State<OverlaySheetHost> with SingleTickerPr
// Drag-to-dismiss state
double _dragOffset = 0;
bool _isDragging = false;
final _sheetKey = GlobalKey();
@override
void initState() {
@@ -240,6 +247,7 @@ class _OverlaySheetHostState extends State<OverlaySheetHost> with SingleTickerPr
bool barrierDismissible = true,
FocusNode? initialFocusNode,
Alignment alignment = Alignment.bottomCenter,
bool showDragHandle = false,
}) {
// If already open, close first (instant)
if (_isOpen) {
@@ -260,6 +268,7 @@ class _OverlaySheetHostState extends State<OverlaySheetHost> with SingleTickerPr
_isOpen = true;
_isClosing = false;
_barrierDismissible = barrierDismissible;
_showDragHandle = showDragHandle;
_constraints = constraints;
_explicitBackgroundColor = backgroundColor;
_alignment = alignment;
@@ -458,10 +467,28 @@ class _OverlaySheetHostState extends State<OverlaySheetHost> with SingleTickerPr
);
}
double _getSheetHeight() {
final renderBox = _sheetKey.currentContext?.findRenderObject() as RenderBox?;
return renderBox?.size.height ?? 300;
}
void _checkDismiss(double velocity) {
final sheetHeight = _getSheetHeight();
if (_dragOffset > sheetHeight * 0.25 || velocity > 500) {
_close();
} else {
setState(() {
_dragOffset = 0;
});
}
}
Widget _buildSheet(BuildContext context) {
final size = MediaQuery.of(context).size;
final isDesktop = size.width > 600;
final isTop = _alignment.y < 0;
final isTV = PlatformDetector.isTV();
final showHandle = _showDragHandle && !isTV && !isTop;
final effectiveConstraints =
_constraints ??
@@ -473,6 +500,73 @@ class _OverlaySheetHostState extends State<OverlaySheetHost> with SingleTickerPr
? const BorderRadius.vertical(bottom: Radius.circular(16))
: const BorderRadius.vertical(top: Radius.circular(16));
final colorScheme = Theme.of(context).colorScheme;
Widget content = _pageStack.isNotEmpty ? _pageStack.last.builder(context) : const SizedBox.shrink();
// Wrap content in NotificationListener for scroll-aware drag-to-dismiss
if (showHandle) {
content = NotificationListener<ScrollNotification>(
onNotification: (notification) {
if (notification is OverscrollNotification) {
// Android (ClampingScrollPhysics): overscroll fires reliably
if (notification.overscroll < 0) {
setState(() {
_dragOffset += -notification.overscroll;
});
return true;
}
} else if (notification is ScrollUpdateNotification) {
// iOS (BouncingScrollPhysics): pixels go negative when bouncing past top
if (notification.metrics.pixels < 0) {
setState(() {
_dragOffset = -notification.metrics.pixels;
});
return true;
}
// If user scrolled back down from overscroll, reset drag offset
if (_dragOffset > 0 && notification.metrics.pixels >= 0) {
setState(() {
_dragOffset = 0;
});
}
} else if (notification is ScrollEndNotification) {
if (_dragOffset > 0) {
_checkDismiss(0);
return true;
}
}
return false;
},
child: content,
);
}
// Build the sheet content column (handle + content)
Widget sheetContent;
if (showHandle) {
sheetContent = Column(
mainAxisSize: MainAxisSize.min,
children: [
// M3 drag handle: 32x4, rounded, with 12dp top / 4dp bottom margin
Padding(
padding: const EdgeInsets.only(top: 12, bottom: 4),
child: Container(
width: 32,
height: 4,
decoration: BoxDecoration(
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.4),
borderRadius: const BorderRadius.all(Radius.circular(2)),
),
),
),
Flexible(child: content),
],
);
} else {
sheetContent = content;
}
Widget sheet = FocusScope(
node: _sheetFocusScopeNode,
child: Focus(
@@ -493,15 +587,16 @@ class _OverlaySheetHostState extends State<OverlaySheetHost> with SingleTickerPr
child: Transform.translate(
offset: Offset(0, _dragOffset.clamp(0, double.infinity)),
child: Material(
color: _explicitBackgroundColor ?? Theme.of(context).colorScheme.surface,
key: _sheetKey,
color: _explicitBackgroundColor ?? colorScheme.surface,
borderRadius: borderRadius,
clipBehavior: Clip.antiAlias,
child: SafeArea(
top: !isTop,
bottom: isTop,
top: isTop,
bottom: !isTop,
child: ConstrainedBox(
constraints: effectiveConstraints,
child: _pageStack.isNotEmpty ? _pageStack.last.builder(context) : const SizedBox.shrink(),
child: sheetContent,
),
),
),
@@ -511,33 +606,31 @@ class _OverlaySheetHostState extends State<OverlaySheetHost> with SingleTickerPr
),
);
// Swipe-down-to-dismiss (skip on TV and for top-aligned sheets)
if (!PlatformDetector.isTV() && !isTop) {
sheet = GestureDetector(
onVerticalDragStart: (_) {
_isDragging = true;
_dragOffset = 0;
},
onVerticalDragUpdate: (details) {
if (!_isDragging) return;
setState(() {
_dragOffset += details.delta.dy;
});
},
onVerticalDragEnd: (details) {
if (!_isDragging) return;
_isDragging = false;
final sheetHeight = effectiveConstraints.minHeight;
final velocity = details.primaryVelocity ?? 0;
if (_dragOffset > sheetHeight * 0.25 || velocity > 500) {
_close();
} else {
setState(() {
_dragOffset = 0;
});
}
// Swipe-down-to-dismiss on non-scrollable areas (skip on TV and top-aligned)
if (showHandle) {
sheet = RawGestureDetector(
gestures: <Type, GestureRecognizerFactory>{
VerticalDragGestureRecognizer: GestureRecognizerFactoryWithHandlers<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer()..onlyAcceptDragOnThreshold = true,
(instance) {
instance
..onStart = (_) {
_isDragging = true;
_dragOffset = 0;
}
..onUpdate = (details) {
if (!_isDragging) return;
setState(() {
_dragOffset += details.delta.dy;
});
}
..onEnd = (details) {
if (!_isDragging) return;
_isDragging = false;
_checkDismiss(details.primaryVelocity ?? 0);
};
},
),
},
child: sheet,
);
+95 -105
View File
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../widgets/app_icon.dart';
import '../widgets/overlay_sheet.dart';
import '../focus/dpad_navigator.dart';
import '../i18n/strings.g.dart';
@@ -44,116 +45,105 @@ class _RatingBottomSheetState extends State<RatingBottomSheet> {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return SafeArea(
child: Padding(
padding: const EdgeInsets.fromLTRB(24, 16, 24, 24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 32,
height: 4,
decoration: BoxDecoration(
color: theme.colorScheme.onSurfaceVariant.withValues(alpha: 0.4),
borderRadius: const BorderRadius.all(Radius.circular(2)),
),
),
const SizedBox(height: 20),
Text(
_selectedRating > 0
? '${formatRating(_selectedRating)} / 5'
: t.mediaMenu.rate,
style: theme.textTheme.titleMedium,
),
const SizedBox(height: 16),
Focus(
focusNode: _starsFocusNode,
autofocus: true,
onKeyEvent: _handleStarsKeyEvent,
child: LayoutBuilder(
builder: (context, constraints) {
final starSize = (constraints.maxWidth / 5).clamp(0.0, 48.0);
return GestureDetector(
onTapDown: (details) => _handleStarTap(details.localPosition.dx, starSize),
onPanUpdate: (details) => _handleStarTap(details.localPosition.dx, starSize),
child: AnimatedBuilder(
animation: _starsFocusNode,
builder: (context, child) {
final hasFocus = _starsFocusNode.hasFocus;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(12)),
color: hasFocus
? theme.colorScheme.primary.withValues(alpha: 0.12)
: null,
),
child: child,
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: List.generate(5, (i) {
final starIndex = i + 1;
final IconData icon;
if (_selectedRating >= starIndex) {
icon = Symbols.star_rounded;
} else if (_selectedRating >= starIndex - 0.5) {
icon = Symbols.star_half_rounded;
} else {
icon = Symbols.star_rounded;
}
return SizedBox(
width: starSize,
height: starSize,
child: Center(
child: AppIcon(
icon,
fill: _selectedRating >= starIndex - 0.5 ? 1 : 0,
color: _selectedRating >= starIndex - 0.5
? Colors.amber
: theme.colorScheme.onSurfaceVariant.withValues(alpha: 0.3),
size: starSize * 0.8,
),
return Padding(
padding: const EdgeInsets.fromLTRB(24, 4, 24, 24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
_selectedRating > 0
? '${formatRating(_selectedRating)} / 5'
: t.mediaMenu.rate,
style: theme.textTheme.titleMedium,
),
const SizedBox(height: 16),
Focus(
focusNode: _starsFocusNode,
autofocus: true,
onKeyEvent: _handleStarsKeyEvent,
child: LayoutBuilder(
builder: (context, constraints) {
final starSize = (constraints.maxWidth / 5).clamp(0.0, 48.0);
return GestureDetector(
onTapDown: (details) => _handleStarTap(details.localPosition.dx, starSize),
onPanUpdate: (details) => _handleStarTap(details.localPosition.dx, starSize),
child: AnimatedBuilder(
animation: _starsFocusNode,
builder: (context, child) {
final hasFocus = _starsFocusNode.hasFocus;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(12)),
color: hasFocus
? theme.colorScheme.primary.withValues(alpha: 0.12)
: null,
),
child: child,
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: List.generate(5, (i) {
final starIndex = i + 1;
final IconData icon;
if (_selectedRating >= starIndex) {
icon = Symbols.star_rounded;
} else if (_selectedRating >= starIndex - 0.5) {
icon = Symbols.star_half_rounded;
} else {
icon = Symbols.star_rounded;
}
return SizedBox(
width: starSize,
height: starSize,
child: Center(
child: AppIcon(
icon,
fill: _selectedRating >= starIndex - 0.5 ? 1 : 0,
color: _selectedRating >= starIndex - 0.5
? Colors.amber
: theme.colorScheme.onSurfaceVariant.withValues(alpha: 0.3),
size: starSize * 0.8,
),
);
}),
),
),
);
},
),
),
const SizedBox(height: 20),
Row(
children: [
if (widget.currentRating > 0)
Expanded(
child: OutlinedButton(
onPressed: () {
widget.onClear();
Navigator.pop(context);
},
child: Text(t.common.clear),
),
);
}),
),
),
if (widget.currentRating > 0) const SizedBox(width: 12),
);
},
),
),
const SizedBox(height: 20),
Row(
children: [
if (widget.currentRating > 0)
Expanded(
child: FilledButton(
onPressed: _selectedRating > 0
? () {
widget.onRate(_selectedRating);
Navigator.pop(context);
}
: null,
child: Text(t.mediaMenu.rate),
child: OutlinedButton(
onPressed: () {
widget.onClear();
OverlaySheetController.closeAdaptive(context);
},
child: Text(t.common.clear),
),
),
],
),
],
),
if (widget.currentRating > 0) const SizedBox(width: 12),
Expanded(
child: FilledButton(
onPressed: _selectedRating > 0
? () {
widget.onRate(_selectedRating);
OverlaySheetController.closeAdaptive(context);
}
: null,
child: Text(t.mediaMenu.rate),
),
),
],
),
],
),
);
}
@@ -177,7 +167,7 @@ class _RatingBottomSheetState extends State<RatingBottomSheet> {
if (key == LogicalKeyboardKey.select || key == LogicalKeyboardKey.enter) {
if (_selectedRating > 0) {
widget.onRate(_selectedRating);
Navigator.pop(context);
OverlaySheetController.closeAdaptive(context);
}
return KeyEventResult.handled;
}