diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index 4d9f68fc..a94dcf81 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -4,9 +4,9 @@ /// To regenerate, run: `dart run slang` /// /// Locales: 6 -/// Strings: 2448 (408 per locale) +/// Strings: 2634 (439 per locale) /// -/// Built on 2025-11-16 at 22:35 UTC +/// Built on 2025-11-18 at 05:02 UTC // coverage:ignore-file // ignore_for_file: type=lint @@ -163,6 +163,7 @@ class Translations implements BaseTranslations { late final _StringsPinEntryEn pinEntry = _StringsPinEntryEn._(_root); late final _StringsFileInfoEn fileInfo = _StringsFileInfoEn._(_root); late final _StringsMediaMenuEn mediaMenu = _StringsMediaMenuEn._(_root); + late final _StringsAccessibilityEn accessibility = _StringsAccessibilityEn._(_root); late final _StringsTooltipsEn tooltips = _StringsTooltipsEn._(_root); late final _StringsVideoControlsEn videoControls = _StringsVideoControlsEn._(_root); late final _StringsUserStatusEn userStatus = _StringsUserStatusEn._(_root); @@ -445,6 +446,23 @@ class _StringsMediaMenuEn { String get fileInfo => 'File Info'; } +// Path: accessibility +class _StringsAccessibilityEn { + _StringsAccessibilityEn._(this._root); + + final Translations _root; // ignore: unused_field + + // Translations + String mediaCardMovie({required Object title}) => '${title}, movie'; + String mediaCardShow({required Object title}) => '${title}, TV show'; + String mediaCardEpisode({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}'; + String mediaCardSeason({required Object title, required Object seasonInfo}) => '${title}, ${seasonInfo}'; + String get mediaCardWatched => 'watched'; + String mediaCardPartiallyWatched({required Object percent}) => '${percent} percent watched'; + String get mediaCardUnwatched => 'unwatched'; + String get tapToPlay => 'Tap to play'; +} + // Path: tooltips class _StringsTooltipsEn { _StringsTooltipsEn._(this._root); @@ -481,6 +499,29 @@ class _StringsVideoControlsEn { String get timerActive => 'Timer Active'; String playbackWillPauseIn({required Object duration}) => 'Playback will pause in ${duration}'; String get sleepTimerCompleted => 'Sleep timer completed - playback paused'; + String get playButton => 'Play'; + String get pauseButton => 'Pause'; + String seekBackwardButton({required Object seconds}) => 'Seek backward ${seconds} seconds'; + String seekForwardButton({required Object seconds}) => 'Seek forward ${seconds} seconds'; + String get previousButton => 'Previous episode'; + String get nextButton => 'Next episode'; + String get previousChapterButton => 'Previous chapter'; + String get nextChapterButton => 'Next chapter'; + String get volumeButton => 'Volume'; + String get muteButton => 'Mute'; + String get unmuteButton => 'Unmute'; + String get settingsButton => 'Video settings'; + String get audioTrackButton => 'Audio tracks'; + String get subtitlesButton => 'Subtitles'; + String get chaptersButton => 'Chapters'; + String get versionsButton => 'Video versions'; + String get aspectRatioButton => 'Aspect ratio'; + String get fullscreenButton => 'Enter fullscreen'; + String get exitFullscreenButton => 'Exit fullscreen'; + String get rotationLockButton => 'Rotation lock'; + String get timelineSlider => 'Video timeline'; + String get volumeSlider => 'Volume level'; + String get backButton => 'Back'; } // Path: userStatus @@ -907,6 +948,7 @@ class _StringsDe implements Translations { @override late final _StringsPinEntryDe pinEntry = _StringsPinEntryDe._(_root); @override late final _StringsFileInfoDe fileInfo = _StringsFileInfoDe._(_root); @override late final _StringsMediaMenuDe mediaMenu = _StringsMediaMenuDe._(_root); + @override late final _StringsAccessibilityDe accessibility = _StringsAccessibilityDe._(_root); @override late final _StringsTooltipsDe tooltips = _StringsTooltipsDe._(_root); @override late final _StringsVideoControlsDe videoControls = _StringsVideoControlsDe._(_root); @override late final _StringsUserStatusDe userStatus = _StringsUserStatusDe._(_root); @@ -1189,6 +1231,23 @@ class _StringsMediaMenuDe implements _StringsMediaMenuEn { @override String get fileInfo => 'Dateiinfo'; } +// Path: accessibility +class _StringsAccessibilityDe implements _StringsAccessibilityEn { + _StringsAccessibilityDe._(this._root); + + @override final _StringsDe _root; // ignore: unused_field + + // Translations + @override String mediaCardMovie({required Object title}) => '${title}, movie'; + @override String mediaCardShow({required Object title}) => '${title}, TV show'; + @override String mediaCardEpisode({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}'; + @override String mediaCardSeason({required Object title, required Object seasonInfo}) => '${title}, ${seasonInfo}'; + @override String get mediaCardWatched => 'watched'; + @override String mediaCardPartiallyWatched({required Object percent}) => '${percent} percent watched'; + @override String get mediaCardUnwatched => 'unwatched'; + @override String get tapToPlay => 'Tap to play'; +} + // Path: tooltips class _StringsTooltipsDe implements _StringsTooltipsEn { _StringsTooltipsDe._(this._root); @@ -1225,6 +1284,29 @@ class _StringsVideoControlsDe implements _StringsVideoControlsEn { @override String get timerActive => 'Timer aktiv'; @override String playbackWillPauseIn({required Object duration}) => 'Wiedergabe wird pausiert in ${duration}'; @override String get sleepTimerCompleted => 'Schlaf-Timer abgelaufen - Wiedergabe pausiert'; + @override String get playButton => 'Play'; + @override String get pauseButton => 'Pause'; + @override String seekBackwardButton({required Object seconds}) => 'Seek backward ${seconds} seconds'; + @override String seekForwardButton({required Object seconds}) => 'Seek forward ${seconds} seconds'; + @override String get previousButton => 'Previous episode'; + @override String get nextButton => 'Next episode'; + @override String get previousChapterButton => 'Previous chapter'; + @override String get nextChapterButton => 'Next chapter'; + @override String get volumeButton => 'Volume'; + @override String get muteButton => 'Mute'; + @override String get unmuteButton => 'Unmute'; + @override String get settingsButton => 'Video settings'; + @override String get audioTrackButton => 'Audio tracks'; + @override String get subtitlesButton => 'Subtitles'; + @override String get chaptersButton => 'Chapters'; + @override String get versionsButton => 'Video versions'; + @override String get aspectRatioButton => 'Aspect ratio'; + @override String get fullscreenButton => 'Enter fullscreen'; + @override String get exitFullscreenButton => 'Exit fullscreen'; + @override String get rotationLockButton => 'Rotation lock'; + @override String get timelineSlider => 'Video timeline'; + @override String get volumeSlider => 'Volume level'; + @override String get backButton => 'Back'; } // Path: userStatus @@ -1651,6 +1733,7 @@ class _StringsIt implements Translations { @override late final _StringsPinEntryIt pinEntry = _StringsPinEntryIt._(_root); @override late final _StringsFileInfoIt fileInfo = _StringsFileInfoIt._(_root); @override late final _StringsMediaMenuIt mediaMenu = _StringsMediaMenuIt._(_root); + @override late final _StringsAccessibilityIt accessibility = _StringsAccessibilityIt._(_root); @override late final _StringsTooltipsIt tooltips = _StringsTooltipsIt._(_root); @override late final _StringsVideoControlsIt videoControls = _StringsVideoControlsIt._(_root); @override late final _StringsUserStatusIt userStatus = _StringsUserStatusIt._(_root); @@ -1933,6 +2016,23 @@ class _StringsMediaMenuIt implements _StringsMediaMenuEn { @override String get fileInfo => 'Info sul file'; } +// Path: accessibility +class _StringsAccessibilityIt implements _StringsAccessibilityEn { + _StringsAccessibilityIt._(this._root); + + @override final _StringsIt _root; // ignore: unused_field + + // Translations + @override String mediaCardMovie({required Object title}) => '${title}, movie'; + @override String mediaCardShow({required Object title}) => '${title}, TV show'; + @override String mediaCardEpisode({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}'; + @override String mediaCardSeason({required Object title, required Object seasonInfo}) => '${title}, ${seasonInfo}'; + @override String get mediaCardWatched => 'watched'; + @override String mediaCardPartiallyWatched({required Object percent}) => '${percent} percent watched'; + @override String get mediaCardUnwatched => 'unwatched'; + @override String get tapToPlay => 'Tap to play'; +} + // Path: tooltips class _StringsTooltipsIt implements _StringsTooltipsEn { _StringsTooltipsIt._(this._root); @@ -1969,6 +2069,29 @@ class _StringsVideoControlsIt implements _StringsVideoControlsEn { @override String get timerActive => 'Timer attivo'; @override String playbackWillPauseIn({required Object duration}) => 'La riproduzione si interromperà tra ${duration}'; @override String get sleepTimerCompleted => 'Timer di spegnimento completato - riproduzione in pausa'; + @override String get playButton => 'Play'; + @override String get pauseButton => 'Pause'; + @override String seekBackwardButton({required Object seconds}) => 'Seek backward ${seconds} seconds'; + @override String seekForwardButton({required Object seconds}) => 'Seek forward ${seconds} seconds'; + @override String get previousButton => 'Previous episode'; + @override String get nextButton => 'Next episode'; + @override String get previousChapterButton => 'Previous chapter'; + @override String get nextChapterButton => 'Next chapter'; + @override String get volumeButton => 'Volume'; + @override String get muteButton => 'Mute'; + @override String get unmuteButton => 'Unmute'; + @override String get settingsButton => 'Video settings'; + @override String get audioTrackButton => 'Audio tracks'; + @override String get subtitlesButton => 'Subtitles'; + @override String get chaptersButton => 'Chapters'; + @override String get versionsButton => 'Video versions'; + @override String get aspectRatioButton => 'Aspect ratio'; + @override String get fullscreenButton => 'Enter fullscreen'; + @override String get exitFullscreenButton => 'Exit fullscreen'; + @override String get rotationLockButton => 'Rotation lock'; + @override String get timelineSlider => 'Video timeline'; + @override String get volumeSlider => 'Volume level'; + @override String get backButton => 'Back'; } // Path: userStatus @@ -2395,6 +2518,7 @@ class _StringsNl implements Translations { @override late final _StringsPinEntryNl pinEntry = _StringsPinEntryNl._(_root); @override late final _StringsFileInfoNl fileInfo = _StringsFileInfoNl._(_root); @override late final _StringsMediaMenuNl mediaMenu = _StringsMediaMenuNl._(_root); + @override late final _StringsAccessibilityNl accessibility = _StringsAccessibilityNl._(_root); @override late final _StringsTooltipsNl tooltips = _StringsTooltipsNl._(_root); @override late final _StringsVideoControlsNl videoControls = _StringsVideoControlsNl._(_root); @override late final _StringsUserStatusNl userStatus = _StringsUserStatusNl._(_root); @@ -2677,6 +2801,23 @@ class _StringsMediaMenuNl implements _StringsMediaMenuEn { @override String get fileInfo => 'Bestand info'; } +// Path: accessibility +class _StringsAccessibilityNl implements _StringsAccessibilityEn { + _StringsAccessibilityNl._(this._root); + + @override final _StringsNl _root; // ignore: unused_field + + // Translations + @override String mediaCardMovie({required Object title}) => '${title}, movie'; + @override String mediaCardShow({required Object title}) => '${title}, TV show'; + @override String mediaCardEpisode({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}'; + @override String mediaCardSeason({required Object title, required Object seasonInfo}) => '${title}, ${seasonInfo}'; + @override String get mediaCardWatched => 'watched'; + @override String mediaCardPartiallyWatched({required Object percent}) => '${percent} percent watched'; + @override String get mediaCardUnwatched => 'unwatched'; + @override String get tapToPlay => 'Tap to play'; +} + // Path: tooltips class _StringsTooltipsNl implements _StringsTooltipsEn { _StringsTooltipsNl._(this._root); @@ -2713,6 +2854,29 @@ class _StringsVideoControlsNl implements _StringsVideoControlsEn { @override String get timerActive => 'Timer actief'; @override String playbackWillPauseIn({required Object duration}) => 'Afspelen wordt gepauzeerd over ${duration}'; @override String get sleepTimerCompleted => 'Slaaptimer voltooid - afspelen gepauzeerd'; + @override String get playButton => 'Play'; + @override String get pauseButton => 'Pause'; + @override String seekBackwardButton({required Object seconds}) => 'Seek backward ${seconds} seconds'; + @override String seekForwardButton({required Object seconds}) => 'Seek forward ${seconds} seconds'; + @override String get previousButton => 'Previous episode'; + @override String get nextButton => 'Next episode'; + @override String get previousChapterButton => 'Previous chapter'; + @override String get nextChapterButton => 'Next chapter'; + @override String get volumeButton => 'Volume'; + @override String get muteButton => 'Mute'; + @override String get unmuteButton => 'Unmute'; + @override String get settingsButton => 'Video settings'; + @override String get audioTrackButton => 'Audio tracks'; + @override String get subtitlesButton => 'Subtitles'; + @override String get chaptersButton => 'Chapters'; + @override String get versionsButton => 'Video versions'; + @override String get aspectRatioButton => 'Aspect ratio'; + @override String get fullscreenButton => 'Enter fullscreen'; + @override String get exitFullscreenButton => 'Exit fullscreen'; + @override String get rotationLockButton => 'Rotation lock'; + @override String get timelineSlider => 'Video timeline'; + @override String get volumeSlider => 'Volume level'; + @override String get backButton => 'Back'; } // Path: userStatus @@ -3139,6 +3303,7 @@ class _StringsSv implements Translations { @override late final _StringsPinEntrySv pinEntry = _StringsPinEntrySv._(_root); @override late final _StringsFileInfoSv fileInfo = _StringsFileInfoSv._(_root); @override late final _StringsMediaMenuSv mediaMenu = _StringsMediaMenuSv._(_root); + @override late final _StringsAccessibilitySv accessibility = _StringsAccessibilitySv._(_root); @override late final _StringsTooltipsSv tooltips = _StringsTooltipsSv._(_root); @override late final _StringsVideoControlsSv videoControls = _StringsVideoControlsSv._(_root); @override late final _StringsUserStatusSv userStatus = _StringsUserStatusSv._(_root); @@ -3421,6 +3586,23 @@ class _StringsMediaMenuSv implements _StringsMediaMenuEn { @override String get fileInfo => 'Filinformation'; } +// Path: accessibility +class _StringsAccessibilitySv implements _StringsAccessibilityEn { + _StringsAccessibilitySv._(this._root); + + @override final _StringsSv _root; // ignore: unused_field + + // Translations + @override String mediaCardMovie({required Object title}) => '${title}, movie'; + @override String mediaCardShow({required Object title}) => '${title}, TV show'; + @override String mediaCardEpisode({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}'; + @override String mediaCardSeason({required Object title, required Object seasonInfo}) => '${title}, ${seasonInfo}'; + @override String get mediaCardWatched => 'watched'; + @override String mediaCardPartiallyWatched({required Object percent}) => '${percent} percent watched'; + @override String get mediaCardUnwatched => 'unwatched'; + @override String get tapToPlay => 'Tap to play'; +} + // Path: tooltips class _StringsTooltipsSv implements _StringsTooltipsEn { _StringsTooltipsSv._(this._root); @@ -3457,6 +3639,29 @@ class _StringsVideoControlsSv implements _StringsVideoControlsEn { @override String get timerActive => 'Timer aktiv'; @override String playbackWillPauseIn({required Object duration}) => 'Uppspelningen pausas om ${duration}'; @override String get sleepTimerCompleted => 'Sovtimer slutförd - uppspelning pausad'; + @override String get playButton => 'Play'; + @override String get pauseButton => 'Pause'; + @override String seekBackwardButton({required Object seconds}) => 'Seek backward ${seconds} seconds'; + @override String seekForwardButton({required Object seconds}) => 'Seek forward ${seconds} seconds'; + @override String get previousButton => 'Previous episode'; + @override String get nextButton => 'Next episode'; + @override String get previousChapterButton => 'Previous chapter'; + @override String get nextChapterButton => 'Next chapter'; + @override String get volumeButton => 'Volume'; + @override String get muteButton => 'Mute'; + @override String get unmuteButton => 'Unmute'; + @override String get settingsButton => 'Video settings'; + @override String get audioTrackButton => 'Audio tracks'; + @override String get subtitlesButton => 'Subtitles'; + @override String get chaptersButton => 'Chapters'; + @override String get versionsButton => 'Video versions'; + @override String get aspectRatioButton => 'Aspect ratio'; + @override String get fullscreenButton => 'Enter fullscreen'; + @override String get exitFullscreenButton => 'Exit fullscreen'; + @override String get rotationLockButton => 'Rotation lock'; + @override String get timelineSlider => 'Video timeline'; + @override String get volumeSlider => 'Volume level'; + @override String get backButton => 'Back'; } // Path: userStatus @@ -3883,6 +4088,7 @@ class _StringsZh implements Translations { @override late final _StringsPinEntryZh pinEntry = _StringsPinEntryZh._(_root); @override late final _StringsFileInfoZh fileInfo = _StringsFileInfoZh._(_root); @override late final _StringsMediaMenuZh mediaMenu = _StringsMediaMenuZh._(_root); + @override late final _StringsAccessibilityZh accessibility = _StringsAccessibilityZh._(_root); @override late final _StringsTooltipsZh tooltips = _StringsTooltipsZh._(_root); @override late final _StringsVideoControlsZh videoControls = _StringsVideoControlsZh._(_root); @override late final _StringsUserStatusZh userStatus = _StringsUserStatusZh._(_root); @@ -4165,6 +4371,23 @@ class _StringsMediaMenuZh implements _StringsMediaMenuEn { @override String get fileInfo => '文件信息'; } +// Path: accessibility +class _StringsAccessibilityZh implements _StringsAccessibilityEn { + _StringsAccessibilityZh._(this._root); + + @override final _StringsZh _root; // ignore: unused_field + + // Translations + @override String mediaCardMovie({required Object title}) => '${title}, movie'; + @override String mediaCardShow({required Object title}) => '${title}, TV show'; + @override String mediaCardEpisode({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}'; + @override String mediaCardSeason({required Object title, required Object seasonInfo}) => '${title}, ${seasonInfo}'; + @override String get mediaCardWatched => 'watched'; + @override String mediaCardPartiallyWatched({required Object percent}) => '${percent} percent watched'; + @override String get mediaCardUnwatched => 'unwatched'; + @override String get tapToPlay => 'Tap to play'; +} + // Path: tooltips class _StringsTooltipsZh implements _StringsTooltipsEn { _StringsTooltipsZh._(this._root); @@ -4201,6 +4424,29 @@ class _StringsVideoControlsZh implements _StringsVideoControlsEn { @override String get timerActive => '定时器已激活'; @override String playbackWillPauseIn({required Object duration}) => '播放将在 ${duration} 后暂停'; @override String get sleepTimerCompleted => '睡眠定时器已完成 - 播放已暂停'; + @override String get playButton => 'Play'; + @override String get pauseButton => 'Pause'; + @override String seekBackwardButton({required Object seconds}) => 'Seek backward ${seconds} seconds'; + @override String seekForwardButton({required Object seconds}) => 'Seek forward ${seconds} seconds'; + @override String get previousButton => 'Previous episode'; + @override String get nextButton => 'Next episode'; + @override String get previousChapterButton => 'Previous chapter'; + @override String get nextChapterButton => 'Next chapter'; + @override String get volumeButton => 'Volume'; + @override String get muteButton => 'Mute'; + @override String get unmuteButton => 'Unmute'; + @override String get settingsButton => 'Video settings'; + @override String get audioTrackButton => 'Audio tracks'; + @override String get subtitlesButton => 'Subtitles'; + @override String get chaptersButton => 'Chapters'; + @override String get versionsButton => 'Video versions'; + @override String get aspectRatioButton => 'Aspect ratio'; + @override String get fullscreenButton => 'Enter fullscreen'; + @override String get exitFullscreenButton => 'Exit fullscreen'; + @override String get rotationLockButton => 'Rotation lock'; + @override String get timelineSlider => 'Video timeline'; + @override String get volumeSlider => 'Volume level'; + @override String get backButton => 'Back'; } // Path: userStatus @@ -4761,6 +5007,14 @@ extension on Translations { case 'mediaMenu.goToSeason': return 'Go to season'; case 'mediaMenu.shufflePlay': return 'Shuffle Play'; case 'mediaMenu.fileInfo': return 'File Info'; + case 'accessibility.mediaCardMovie': return ({required Object title}) => '${title}, movie'; + case 'accessibility.mediaCardShow': return ({required Object title}) => '${title}, TV show'; + case 'accessibility.mediaCardEpisode': return ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}'; + case 'accessibility.mediaCardSeason': return ({required Object title, required Object seasonInfo}) => '${title}, ${seasonInfo}'; + case 'accessibility.mediaCardWatched': return 'watched'; + case 'accessibility.mediaCardPartiallyWatched': return ({required Object percent}) => '${percent} percent watched'; + case 'accessibility.mediaCardUnwatched': return 'unwatched'; + case 'accessibility.tapToPlay': return 'Tap to play'; case 'tooltips.shufflePlay': return 'Shuffle play'; case 'tooltips.markAsWatched': return 'Mark as watched'; case 'tooltips.markAsUnwatched': return 'Mark as unwatched'; @@ -4781,6 +5035,29 @@ extension on Translations { case 'videoControls.timerActive': return 'Timer Active'; case 'videoControls.playbackWillPauseIn': return ({required Object duration}) => 'Playback will pause in ${duration}'; case 'videoControls.sleepTimerCompleted': return 'Sleep timer completed - playback paused'; + case 'videoControls.playButton': return 'Play'; + case 'videoControls.pauseButton': return 'Pause'; + case 'videoControls.seekBackwardButton': return ({required Object seconds}) => 'Seek backward ${seconds} seconds'; + case 'videoControls.seekForwardButton': return ({required Object seconds}) => 'Seek forward ${seconds} seconds'; + case 'videoControls.previousButton': return 'Previous episode'; + case 'videoControls.nextButton': return 'Next episode'; + case 'videoControls.previousChapterButton': return 'Previous chapter'; + case 'videoControls.nextChapterButton': return 'Next chapter'; + case 'videoControls.volumeButton': return 'Volume'; + case 'videoControls.muteButton': return 'Mute'; + case 'videoControls.unmuteButton': return 'Unmute'; + case 'videoControls.settingsButton': return 'Video settings'; + case 'videoControls.audioTrackButton': return 'Audio tracks'; + case 'videoControls.subtitlesButton': return 'Subtitles'; + case 'videoControls.chaptersButton': return 'Chapters'; + case 'videoControls.versionsButton': return 'Video versions'; + case 'videoControls.aspectRatioButton': return 'Aspect ratio'; + case 'videoControls.fullscreenButton': return 'Enter fullscreen'; + case 'videoControls.exitFullscreenButton': return 'Exit fullscreen'; + case 'videoControls.rotationLockButton': return 'Rotation lock'; + case 'videoControls.timelineSlider': return 'Video timeline'; + case 'videoControls.volumeSlider': return 'Volume level'; + case 'videoControls.backButton': return 'Back'; case 'userStatus.admin': return 'Admin'; case 'userStatus.restricted': return 'Restricted'; case 'userStatus.protected': return 'Protected'; @@ -5177,6 +5454,14 @@ extension on _StringsDe { case 'mediaMenu.goToSeason': return 'Zur Staffel'; case 'mediaMenu.shufflePlay': return 'Zufallswiedergabe'; case 'mediaMenu.fileInfo': return 'Dateiinfo'; + case 'accessibility.mediaCardMovie': return ({required Object title}) => '${title}, movie'; + case 'accessibility.mediaCardShow': return ({required Object title}) => '${title}, TV show'; + case 'accessibility.mediaCardEpisode': return ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}'; + case 'accessibility.mediaCardSeason': return ({required Object title, required Object seasonInfo}) => '${title}, ${seasonInfo}'; + case 'accessibility.mediaCardWatched': return 'watched'; + case 'accessibility.mediaCardPartiallyWatched': return ({required Object percent}) => '${percent} percent watched'; + case 'accessibility.mediaCardUnwatched': return 'unwatched'; + case 'accessibility.tapToPlay': return 'Tap to play'; case 'tooltips.shufflePlay': return 'Zufallswiedergabe'; case 'tooltips.markAsWatched': return 'Als gesehen markieren'; case 'tooltips.markAsUnwatched': return 'Als ungesehen markieren'; @@ -5197,6 +5482,29 @@ extension on _StringsDe { case 'videoControls.timerActive': return 'Timer aktiv'; case 'videoControls.playbackWillPauseIn': return ({required Object duration}) => 'Wiedergabe wird pausiert in ${duration}'; case 'videoControls.sleepTimerCompleted': return 'Schlaf-Timer abgelaufen - Wiedergabe pausiert'; + case 'videoControls.playButton': return 'Play'; + case 'videoControls.pauseButton': return 'Pause'; + case 'videoControls.seekBackwardButton': return ({required Object seconds}) => 'Seek backward ${seconds} seconds'; + case 'videoControls.seekForwardButton': return ({required Object seconds}) => 'Seek forward ${seconds} seconds'; + case 'videoControls.previousButton': return 'Previous episode'; + case 'videoControls.nextButton': return 'Next episode'; + case 'videoControls.previousChapterButton': return 'Previous chapter'; + case 'videoControls.nextChapterButton': return 'Next chapter'; + case 'videoControls.volumeButton': return 'Volume'; + case 'videoControls.muteButton': return 'Mute'; + case 'videoControls.unmuteButton': return 'Unmute'; + case 'videoControls.settingsButton': return 'Video settings'; + case 'videoControls.audioTrackButton': return 'Audio tracks'; + case 'videoControls.subtitlesButton': return 'Subtitles'; + case 'videoControls.chaptersButton': return 'Chapters'; + case 'videoControls.versionsButton': return 'Video versions'; + case 'videoControls.aspectRatioButton': return 'Aspect ratio'; + case 'videoControls.fullscreenButton': return 'Enter fullscreen'; + case 'videoControls.exitFullscreenButton': return 'Exit fullscreen'; + case 'videoControls.rotationLockButton': return 'Rotation lock'; + case 'videoControls.timelineSlider': return 'Video timeline'; + case 'videoControls.volumeSlider': return 'Volume level'; + case 'videoControls.backButton': return 'Back'; case 'userStatus.admin': return 'Eigentümer'; case 'userStatus.restricted': return 'Eingeschränkt'; case 'userStatus.protected': return 'Geschützt'; @@ -5593,6 +5901,14 @@ extension on _StringsIt { case 'mediaMenu.goToSeason': return 'Vai alla stagione'; case 'mediaMenu.shufflePlay': return 'Riproduzione casuale'; case 'mediaMenu.fileInfo': return 'Info sul file'; + case 'accessibility.mediaCardMovie': return ({required Object title}) => '${title}, movie'; + case 'accessibility.mediaCardShow': return ({required Object title}) => '${title}, TV show'; + case 'accessibility.mediaCardEpisode': return ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}'; + case 'accessibility.mediaCardSeason': return ({required Object title, required Object seasonInfo}) => '${title}, ${seasonInfo}'; + case 'accessibility.mediaCardWatched': return 'watched'; + case 'accessibility.mediaCardPartiallyWatched': return ({required Object percent}) => '${percent} percent watched'; + case 'accessibility.mediaCardUnwatched': return 'unwatched'; + case 'accessibility.tapToPlay': return 'Tap to play'; case 'tooltips.shufflePlay': return 'Riproduzione casuale'; case 'tooltips.markAsWatched': return 'Segna come visto'; case 'tooltips.markAsUnwatched': return 'Segna come non visto'; @@ -5613,6 +5929,29 @@ extension on _StringsIt { case 'videoControls.timerActive': return 'Timer attivo'; case 'videoControls.playbackWillPauseIn': return ({required Object duration}) => 'La riproduzione si interromperà tra ${duration}'; case 'videoControls.sleepTimerCompleted': return 'Timer di spegnimento completato - riproduzione in pausa'; + case 'videoControls.playButton': return 'Play'; + case 'videoControls.pauseButton': return 'Pause'; + case 'videoControls.seekBackwardButton': return ({required Object seconds}) => 'Seek backward ${seconds} seconds'; + case 'videoControls.seekForwardButton': return ({required Object seconds}) => 'Seek forward ${seconds} seconds'; + case 'videoControls.previousButton': return 'Previous episode'; + case 'videoControls.nextButton': return 'Next episode'; + case 'videoControls.previousChapterButton': return 'Previous chapter'; + case 'videoControls.nextChapterButton': return 'Next chapter'; + case 'videoControls.volumeButton': return 'Volume'; + case 'videoControls.muteButton': return 'Mute'; + case 'videoControls.unmuteButton': return 'Unmute'; + case 'videoControls.settingsButton': return 'Video settings'; + case 'videoControls.audioTrackButton': return 'Audio tracks'; + case 'videoControls.subtitlesButton': return 'Subtitles'; + case 'videoControls.chaptersButton': return 'Chapters'; + case 'videoControls.versionsButton': return 'Video versions'; + case 'videoControls.aspectRatioButton': return 'Aspect ratio'; + case 'videoControls.fullscreenButton': return 'Enter fullscreen'; + case 'videoControls.exitFullscreenButton': return 'Exit fullscreen'; + case 'videoControls.rotationLockButton': return 'Rotation lock'; + case 'videoControls.timelineSlider': return 'Video timeline'; + case 'videoControls.volumeSlider': return 'Volume level'; + case 'videoControls.backButton': return 'Back'; case 'userStatus.admin': return 'Admin'; case 'userStatus.restricted': return 'Limitato'; case 'userStatus.protected': return 'Protetto'; @@ -6009,6 +6348,14 @@ extension on _StringsNl { case 'mediaMenu.goToSeason': return 'Ga naar seizoen'; case 'mediaMenu.shufflePlay': return 'Willekeurig afspelen'; case 'mediaMenu.fileInfo': return 'Bestand info'; + case 'accessibility.mediaCardMovie': return ({required Object title}) => '${title}, movie'; + case 'accessibility.mediaCardShow': return ({required Object title}) => '${title}, TV show'; + case 'accessibility.mediaCardEpisode': return ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}'; + case 'accessibility.mediaCardSeason': return ({required Object title, required Object seasonInfo}) => '${title}, ${seasonInfo}'; + case 'accessibility.mediaCardWatched': return 'watched'; + case 'accessibility.mediaCardPartiallyWatched': return ({required Object percent}) => '${percent} percent watched'; + case 'accessibility.mediaCardUnwatched': return 'unwatched'; + case 'accessibility.tapToPlay': return 'Tap to play'; case 'tooltips.shufflePlay': return 'Willekeurig afspelen'; case 'tooltips.markAsWatched': return 'Markeer als gekeken'; case 'tooltips.markAsUnwatched': return 'Markeer als ongekeken'; @@ -6029,6 +6376,29 @@ extension on _StringsNl { case 'videoControls.timerActive': return 'Timer actief'; case 'videoControls.playbackWillPauseIn': return ({required Object duration}) => 'Afspelen wordt gepauzeerd over ${duration}'; case 'videoControls.sleepTimerCompleted': return 'Slaaptimer voltooid - afspelen gepauzeerd'; + case 'videoControls.playButton': return 'Play'; + case 'videoControls.pauseButton': return 'Pause'; + case 'videoControls.seekBackwardButton': return ({required Object seconds}) => 'Seek backward ${seconds} seconds'; + case 'videoControls.seekForwardButton': return ({required Object seconds}) => 'Seek forward ${seconds} seconds'; + case 'videoControls.previousButton': return 'Previous episode'; + case 'videoControls.nextButton': return 'Next episode'; + case 'videoControls.previousChapterButton': return 'Previous chapter'; + case 'videoControls.nextChapterButton': return 'Next chapter'; + case 'videoControls.volumeButton': return 'Volume'; + case 'videoControls.muteButton': return 'Mute'; + case 'videoControls.unmuteButton': return 'Unmute'; + case 'videoControls.settingsButton': return 'Video settings'; + case 'videoControls.audioTrackButton': return 'Audio tracks'; + case 'videoControls.subtitlesButton': return 'Subtitles'; + case 'videoControls.chaptersButton': return 'Chapters'; + case 'videoControls.versionsButton': return 'Video versions'; + case 'videoControls.aspectRatioButton': return 'Aspect ratio'; + case 'videoControls.fullscreenButton': return 'Enter fullscreen'; + case 'videoControls.exitFullscreenButton': return 'Exit fullscreen'; + case 'videoControls.rotationLockButton': return 'Rotation lock'; + case 'videoControls.timelineSlider': return 'Video timeline'; + case 'videoControls.volumeSlider': return 'Volume level'; + case 'videoControls.backButton': return 'Back'; case 'userStatus.admin': return 'Beheerder'; case 'userStatus.restricted': return 'Beperkt'; case 'userStatus.protected': return 'Beschermd'; @@ -6425,6 +6795,14 @@ extension on _StringsSv { case 'mediaMenu.goToSeason': return 'Gå till säsong'; case 'mediaMenu.shufflePlay': return 'Blanda uppspelning'; case 'mediaMenu.fileInfo': return 'Filinformation'; + case 'accessibility.mediaCardMovie': return ({required Object title}) => '${title}, movie'; + case 'accessibility.mediaCardShow': return ({required Object title}) => '${title}, TV show'; + case 'accessibility.mediaCardEpisode': return ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}'; + case 'accessibility.mediaCardSeason': return ({required Object title, required Object seasonInfo}) => '${title}, ${seasonInfo}'; + case 'accessibility.mediaCardWatched': return 'watched'; + case 'accessibility.mediaCardPartiallyWatched': return ({required Object percent}) => '${percent} percent watched'; + case 'accessibility.mediaCardUnwatched': return 'unwatched'; + case 'accessibility.tapToPlay': return 'Tap to play'; case 'tooltips.shufflePlay': return 'Blanda uppspelning'; case 'tooltips.markAsWatched': return 'Markera som sedd'; case 'tooltips.markAsUnwatched': return 'Markera som osedd'; @@ -6445,6 +6823,29 @@ extension on _StringsSv { case 'videoControls.timerActive': return 'Timer aktiv'; case 'videoControls.playbackWillPauseIn': return ({required Object duration}) => 'Uppspelningen pausas om ${duration}'; case 'videoControls.sleepTimerCompleted': return 'Sovtimer slutförd - uppspelning pausad'; + case 'videoControls.playButton': return 'Play'; + case 'videoControls.pauseButton': return 'Pause'; + case 'videoControls.seekBackwardButton': return ({required Object seconds}) => 'Seek backward ${seconds} seconds'; + case 'videoControls.seekForwardButton': return ({required Object seconds}) => 'Seek forward ${seconds} seconds'; + case 'videoControls.previousButton': return 'Previous episode'; + case 'videoControls.nextButton': return 'Next episode'; + case 'videoControls.previousChapterButton': return 'Previous chapter'; + case 'videoControls.nextChapterButton': return 'Next chapter'; + case 'videoControls.volumeButton': return 'Volume'; + case 'videoControls.muteButton': return 'Mute'; + case 'videoControls.unmuteButton': return 'Unmute'; + case 'videoControls.settingsButton': return 'Video settings'; + case 'videoControls.audioTrackButton': return 'Audio tracks'; + case 'videoControls.subtitlesButton': return 'Subtitles'; + case 'videoControls.chaptersButton': return 'Chapters'; + case 'videoControls.versionsButton': return 'Video versions'; + case 'videoControls.aspectRatioButton': return 'Aspect ratio'; + case 'videoControls.fullscreenButton': return 'Enter fullscreen'; + case 'videoControls.exitFullscreenButton': return 'Exit fullscreen'; + case 'videoControls.rotationLockButton': return 'Rotation lock'; + case 'videoControls.timelineSlider': return 'Video timeline'; + case 'videoControls.volumeSlider': return 'Volume level'; + case 'videoControls.backButton': return 'Back'; case 'userStatus.admin': return 'Admin'; case 'userStatus.restricted': return 'Begränsad'; case 'userStatus.protected': return 'Skyddad'; @@ -6841,6 +7242,14 @@ extension on _StringsZh { case 'mediaMenu.goToSeason': return '转到季'; case 'mediaMenu.shufflePlay': return '随机播放'; case 'mediaMenu.fileInfo': return '文件信息'; + case 'accessibility.mediaCardMovie': return ({required Object title}) => '${title}, movie'; + case 'accessibility.mediaCardShow': return ({required Object title}) => '${title}, TV show'; + case 'accessibility.mediaCardEpisode': return ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}'; + case 'accessibility.mediaCardSeason': return ({required Object title, required Object seasonInfo}) => '${title}, ${seasonInfo}'; + case 'accessibility.mediaCardWatched': return 'watched'; + case 'accessibility.mediaCardPartiallyWatched': return ({required Object percent}) => '${percent} percent watched'; + case 'accessibility.mediaCardUnwatched': return 'unwatched'; + case 'accessibility.tapToPlay': return 'Tap to play'; case 'tooltips.shufflePlay': return '随机播放'; case 'tooltips.markAsWatched': return '标记为已观看'; case 'tooltips.markAsUnwatched': return '标记为未观看'; @@ -6861,6 +7270,29 @@ extension on _StringsZh { case 'videoControls.timerActive': return '定时器已激活'; case 'videoControls.playbackWillPauseIn': return ({required Object duration}) => '播放将在 ${duration} 后暂停'; case 'videoControls.sleepTimerCompleted': return '睡眠定时器已完成 - 播放已暂停'; + case 'videoControls.playButton': return 'Play'; + case 'videoControls.pauseButton': return 'Pause'; + case 'videoControls.seekBackwardButton': return ({required Object seconds}) => 'Seek backward ${seconds} seconds'; + case 'videoControls.seekForwardButton': return ({required Object seconds}) => 'Seek forward ${seconds} seconds'; + case 'videoControls.previousButton': return 'Previous episode'; + case 'videoControls.nextButton': return 'Next episode'; + case 'videoControls.previousChapterButton': return 'Previous chapter'; + case 'videoControls.nextChapterButton': return 'Next chapter'; + case 'videoControls.volumeButton': return 'Volume'; + case 'videoControls.muteButton': return 'Mute'; + case 'videoControls.unmuteButton': return 'Unmute'; + case 'videoControls.settingsButton': return 'Video settings'; + case 'videoControls.audioTrackButton': return 'Audio tracks'; + case 'videoControls.subtitlesButton': return 'Subtitles'; + case 'videoControls.chaptersButton': return 'Chapters'; + case 'videoControls.versionsButton': return 'Video versions'; + case 'videoControls.aspectRatioButton': return 'Aspect ratio'; + case 'videoControls.fullscreenButton': return 'Enter fullscreen'; + case 'videoControls.exitFullscreenButton': return 'Exit fullscreen'; + case 'videoControls.rotationLockButton': return 'Rotation lock'; + case 'videoControls.timelineSlider': return 'Video timeline'; + case 'videoControls.volumeSlider': return 'Volume level'; + case 'videoControls.backButton': return 'Back'; case 'userStatus.admin': return '管理员'; case 'userStatus.restricted': return '受限'; case 'userStatus.protected': return '受保护'; diff --git a/lib/i18n/strings.i18n.json b/lib/i18n/strings.i18n.json index e749ab23..6f5cd309 100644 --- a/lib/i18n/strings.i18n.json +++ b/lib/i18n/strings.i18n.json @@ -184,6 +184,16 @@ "shufflePlay": "Shuffle Play", "fileInfo": "File Info" }, + "accessibility": { + "mediaCardMovie": "${title}, movie", + "mediaCardShow": "${title}, TV show", + "mediaCardEpisode": "${title}, ${episodeInfo}", + "mediaCardSeason": "${title}, ${seasonInfo}", + "mediaCardWatched": "watched", + "mediaCardPartiallyWatched": "${percent} percent watched", + "mediaCardUnwatched": "unwatched", + "tapToPlay": "Tap to play" + }, "tooltips": { "shufflePlay": "Shuffle play", "markAsWatched": "Mark as watched", @@ -206,7 +216,30 @@ "sleepTimer": "Sleep Timer", "timerActive": "Timer Active", "playbackWillPauseIn": "Playback will pause in ${duration}", - "sleepTimerCompleted": "Sleep timer completed - playback paused" + "sleepTimerCompleted": "Sleep timer completed - playback paused", + "playButton": "Play", + "pauseButton": "Pause", + "seekBackwardButton": "Seek backward ${seconds} seconds", + "seekForwardButton": "Seek forward ${seconds} seconds", + "previousButton": "Previous episode", + "nextButton": "Next episode", + "previousChapterButton": "Previous chapter", + "nextChapterButton": "Next chapter", + "volumeButton": "Volume", + "muteButton": "Mute", + "unmuteButton": "Unmute", + "settingsButton": "Video settings", + "audioTrackButton": "Audio tracks", + "subtitlesButton": "Subtitles", + "chaptersButton": "Chapters", + "versionsButton": "Video versions", + "aspectRatioButton": "Aspect ratio", + "fullscreenButton": "Enter fullscreen", + "exitFullscreenButton": "Exit fullscreen", + "rotationLockButton": "Rotation lock", + "timelineSlider": "Video timeline", + "volumeSlider": "Volume level", + "backButton": "Back" }, "userStatus": { "admin": "Admin", diff --git a/lib/i18n/strings_de.i18n.json b/lib/i18n/strings_de.i18n.json index 6a3078d8..b9a01c52 100644 --- a/lib/i18n/strings_de.i18n.json +++ b/lib/i18n/strings_de.i18n.json @@ -184,6 +184,16 @@ "shufflePlay": "Zufallswiedergabe", "fileInfo": "Dateiinfo" }, + "accessibility": { + "mediaCardMovie": "${title}, movie", + "mediaCardShow": "${title}, TV show", + "mediaCardEpisode": "${title}, ${episodeInfo}", + "mediaCardSeason": "${title}, ${seasonInfo}", + "mediaCardWatched": "watched", + "mediaCardPartiallyWatched": "${percent} percent watched", + "mediaCardUnwatched": "unwatched", + "tapToPlay": "Tap to play" + }, "tooltips": { "shufflePlay": "Zufallswiedergabe", "markAsWatched": "Als gesehen markieren", @@ -206,7 +216,30 @@ "sleepTimer": "Schlaf-Timer", "timerActive": "Timer aktiv", "playbackWillPauseIn": "Wiedergabe wird pausiert in ${duration}", - "sleepTimerCompleted": "Schlaf-Timer abgelaufen - Wiedergabe pausiert" + "sleepTimerCompleted": "Schlaf-Timer abgelaufen - Wiedergabe pausiert", + "playButton": "Play", + "pauseButton": "Pause", + "seekBackwardButton": "Seek backward ${seconds} seconds", + "seekForwardButton": "Seek forward ${seconds} seconds", + "previousButton": "Previous episode", + "nextButton": "Next episode", + "previousChapterButton": "Previous chapter", + "nextChapterButton": "Next chapter", + "volumeButton": "Volume", + "muteButton": "Mute", + "unmuteButton": "Unmute", + "settingsButton": "Video settings", + "audioTrackButton": "Audio tracks", + "subtitlesButton": "Subtitles", + "chaptersButton": "Chapters", + "versionsButton": "Video versions", + "aspectRatioButton": "Aspect ratio", + "fullscreenButton": "Enter fullscreen", + "exitFullscreenButton": "Exit fullscreen", + "rotationLockButton": "Rotation lock", + "timelineSlider": "Video timeline", + "volumeSlider": "Volume level", + "backButton": "Back" }, "userStatus": { "admin": "Eigentümer", diff --git a/lib/i18n/strings_it.i18n.json b/lib/i18n/strings_it.i18n.json index 76ca3fef..ca3bc4b8 100644 --- a/lib/i18n/strings_it.i18n.json +++ b/lib/i18n/strings_it.i18n.json @@ -184,6 +184,16 @@ "shufflePlay": "Riproduzione casuale", "fileInfo": "Info sul file" }, + "accessibility": { + "mediaCardMovie": "${title}, movie", + "mediaCardShow": "${title}, TV show", + "mediaCardEpisode": "${title}, ${episodeInfo}", + "mediaCardSeason": "${title}, ${seasonInfo}", + "mediaCardWatched": "watched", + "mediaCardPartiallyWatched": "${percent} percent watched", + "mediaCardUnwatched": "unwatched", + "tapToPlay": "Tap to play" + }, "tooltips": { "shufflePlay": "Riproduzione casuale", "markAsWatched": "Segna come visto", @@ -206,7 +216,30 @@ "sleepTimer": "Timer di spegnimento", "timerActive": "Timer attivo", "playbackWillPauseIn": "La riproduzione si interromperà tra ${duration}", - "sleepTimerCompleted": "Timer di spegnimento completato - riproduzione in pausa" + "sleepTimerCompleted": "Timer di spegnimento completato - riproduzione in pausa", + "playButton": "Play", + "pauseButton": "Pause", + "seekBackwardButton": "Seek backward ${seconds} seconds", + "seekForwardButton": "Seek forward ${seconds} seconds", + "previousButton": "Previous episode", + "nextButton": "Next episode", + "previousChapterButton": "Previous chapter", + "nextChapterButton": "Next chapter", + "volumeButton": "Volume", + "muteButton": "Mute", + "unmuteButton": "Unmute", + "settingsButton": "Video settings", + "audioTrackButton": "Audio tracks", + "subtitlesButton": "Subtitles", + "chaptersButton": "Chapters", + "versionsButton": "Video versions", + "aspectRatioButton": "Aspect ratio", + "fullscreenButton": "Enter fullscreen", + "exitFullscreenButton": "Exit fullscreen", + "rotationLockButton": "Rotation lock", + "timelineSlider": "Video timeline", + "volumeSlider": "Volume level", + "backButton": "Back" }, "userStatus": { "admin": "Admin", diff --git a/lib/i18n/strings_nl.i18n.json b/lib/i18n/strings_nl.i18n.json index 63551766..72b64cbe 100644 --- a/lib/i18n/strings_nl.i18n.json +++ b/lib/i18n/strings_nl.i18n.json @@ -184,6 +184,16 @@ "shufflePlay": "Willekeurig afspelen", "fileInfo": "Bestand info" }, + "accessibility": { + "mediaCardMovie": "${title}, movie", + "mediaCardShow": "${title}, TV show", + "mediaCardEpisode": "${title}, ${episodeInfo}", + "mediaCardSeason": "${title}, ${seasonInfo}", + "mediaCardWatched": "watched", + "mediaCardPartiallyWatched": "${percent} percent watched", + "mediaCardUnwatched": "unwatched", + "tapToPlay": "Tap to play" + }, "tooltips": { "shufflePlay": "Willekeurig afspelen", "markAsWatched": "Markeer als gekeken", @@ -206,7 +216,30 @@ "sleepTimer": "Slaaptimer", "timerActive": "Timer actief", "playbackWillPauseIn": "Afspelen wordt gepauzeerd over ${duration}", - "sleepTimerCompleted": "Slaaptimer voltooid - afspelen gepauzeerd" + "sleepTimerCompleted": "Slaaptimer voltooid - afspelen gepauzeerd", + "playButton": "Play", + "pauseButton": "Pause", + "seekBackwardButton": "Seek backward ${seconds} seconds", + "seekForwardButton": "Seek forward ${seconds} seconds", + "previousButton": "Previous episode", + "nextButton": "Next episode", + "previousChapterButton": "Previous chapter", + "nextChapterButton": "Next chapter", + "volumeButton": "Volume", + "muteButton": "Mute", + "unmuteButton": "Unmute", + "settingsButton": "Video settings", + "audioTrackButton": "Audio tracks", + "subtitlesButton": "Subtitles", + "chaptersButton": "Chapters", + "versionsButton": "Video versions", + "aspectRatioButton": "Aspect ratio", + "fullscreenButton": "Enter fullscreen", + "exitFullscreenButton": "Exit fullscreen", + "rotationLockButton": "Rotation lock", + "timelineSlider": "Video timeline", + "volumeSlider": "Volume level", + "backButton": "Back" }, "userStatus": { "admin": "Beheerder", diff --git a/lib/i18n/strings_sv.i18n.json b/lib/i18n/strings_sv.i18n.json index 00d7d5e8..e5571cfe 100644 --- a/lib/i18n/strings_sv.i18n.json +++ b/lib/i18n/strings_sv.i18n.json @@ -184,6 +184,16 @@ "shufflePlay": "Blanda uppspelning", "fileInfo": "Filinformation" }, + "accessibility": { + "mediaCardMovie": "${title}, movie", + "mediaCardShow": "${title}, TV show", + "mediaCardEpisode": "${title}, ${episodeInfo}", + "mediaCardSeason": "${title}, ${seasonInfo}", + "mediaCardWatched": "watched", + "mediaCardPartiallyWatched": "${percent} percent watched", + "mediaCardUnwatched": "unwatched", + "tapToPlay": "Tap to play" + }, "tooltips": { "shufflePlay": "Blanda uppspelning", "markAsWatched": "Markera som sedd", @@ -206,7 +216,30 @@ "sleepTimer": "Sovtimer", "timerActive": "Timer aktiv", "playbackWillPauseIn": "Uppspelningen pausas om ${duration}", - "sleepTimerCompleted": "Sovtimer slutförd - uppspelning pausad" + "sleepTimerCompleted": "Sovtimer slutförd - uppspelning pausad", + "playButton": "Play", + "pauseButton": "Pause", + "seekBackwardButton": "Seek backward ${seconds} seconds", + "seekForwardButton": "Seek forward ${seconds} seconds", + "previousButton": "Previous episode", + "nextButton": "Next episode", + "previousChapterButton": "Previous chapter", + "nextChapterButton": "Next chapter", + "volumeButton": "Volume", + "muteButton": "Mute", + "unmuteButton": "Unmute", + "settingsButton": "Video settings", + "audioTrackButton": "Audio tracks", + "subtitlesButton": "Subtitles", + "chaptersButton": "Chapters", + "versionsButton": "Video versions", + "aspectRatioButton": "Aspect ratio", + "fullscreenButton": "Enter fullscreen", + "exitFullscreenButton": "Exit fullscreen", + "rotationLockButton": "Rotation lock", + "timelineSlider": "Video timeline", + "volumeSlider": "Volume level", + "backButton": "Back" }, "userStatus": { "admin": "Admin", diff --git a/lib/i18n/strings_zh.i18n.json b/lib/i18n/strings_zh.i18n.json index 4db098e4..d2414996 100644 --- a/lib/i18n/strings_zh.i18n.json +++ b/lib/i18n/strings_zh.i18n.json @@ -184,6 +184,16 @@ "shufflePlay": "随机播放", "fileInfo": "文件信息" }, + "accessibility": { + "mediaCardMovie": "${title}, movie", + "mediaCardShow": "${title}, TV show", + "mediaCardEpisode": "${title}, ${episodeInfo}", + "mediaCardSeason": "${title}, ${seasonInfo}", + "mediaCardWatched": "watched", + "mediaCardPartiallyWatched": "${percent} percent watched", + "mediaCardUnwatched": "unwatched", + "tapToPlay": "Tap to play" + }, "tooltips": { "shufflePlay": "随机播放", "markAsWatched": "标记为已观看", @@ -206,7 +216,30 @@ "sleepTimer": "睡眠定时器", "timerActive": "定时器已激活", "playbackWillPauseIn": "播放将在 ${duration} 后暂停", - "sleepTimerCompleted": "睡眠定时器已完成 - 播放已暂停" + "sleepTimerCompleted": "睡眠定时器已完成 - 播放已暂停", + "playButton": "Play", + "pauseButton": "Pause", + "seekBackwardButton": "Seek backward ${seconds} seconds", + "seekForwardButton": "Seek forward ${seconds} seconds", + "previousButton": "Previous episode", + "nextButton": "Next episode", + "previousChapterButton": "Previous chapter", + "nextChapterButton": "Next chapter", + "volumeButton": "Volume", + "muteButton": "Mute", + "unmuteButton": "Unmute", + "settingsButton": "Video settings", + "audioTrackButton": "Audio tracks", + "subtitlesButton": "Subtitles", + "chaptersButton": "Chapters", + "versionsButton": "Video versions", + "aspectRatioButton": "Aspect ratio", + "fullscreenButton": "Enter fullscreen", + "exitFullscreenButton": "Exit fullscreen", + "rotationLockButton": "Rotation lock", + "timelineSlider": "Video timeline", + "volumeSlider": "Volume level", + "backButton": "Back" }, "userStatus": { "admin": "管理员", diff --git a/lib/screens/discover_screen.dart b/lib/screens/discover_screen.dart index f794d695..4e0789f9 100644 --- a/lib/screens/discover_screen.dart +++ b/lib/screens/discover_screen.dart @@ -807,11 +807,15 @@ class _DiscoverScreenState extends State ? t.discover.movie : t.discover.tvShow; + // Build semantic label for hero item + final heroLabel = isEpisode + ? "${heroItem.grandparentTitle}, ${heroItem.title}" + : heroItem.title; + return Semantics( - label: "media-hero-${heroItem.ratingKey}", - identifier: "media-hero-${heroItem.ratingKey}", + label: heroLabel, button: true, - hint: "Tap to play ${heroItem.title}", + hint: t.accessibility.tapToPlay, child: GestureDetector( onTap: () { final clientProvider = context.plexClient; diff --git a/lib/widgets/app_bar_back_button.dart b/lib/widgets/app_bar_back_button.dart index a23b924f..979c350e 100644 --- a/lib/widgets/app_bar_back_button.dart +++ b/lib/widgets/app_bar_back_button.dart @@ -29,11 +29,13 @@ class AppBarBackButton extends StatefulWidget { /// [style] determines the visual appearance of the back button. /// [onPressed] is called when the button is tapped. If null, defaults to Navigator.pop. /// [color] overrides the default icon color. If null, uses white for circular/video, theme default for plain. + /// [semanticLabel] provides accessibility label for screen readers. const AppBarBackButton({ super.key, this.style = BackButtonStyle.circular, this.onPressed, this.color, + this.semanticLabel, }); /// The visual style of the back button @@ -45,6 +47,9 @@ class AppBarBackButton extends StatefulWidget { /// The color of the back arrow icon. If null, uses style-appropriate default. final Color? color; + /// Semantic label for screen readers + final String? semanticLabel; + @override State createState() => _AppBarBackButtonState(); } @@ -124,7 +129,7 @@ class _AppBarBackButtonState extends State break; } - final button = MouseRegion( + final buttonWidget = MouseRegion( cursor: SystemMouseCursors.click, onEnter: (_) => _onHoverChange(true), onExit: (_) => _onHoverChange(false), @@ -154,6 +159,15 @@ class _AppBarBackButtonState extends State ), ); + final button = widget.semanticLabel != null + ? Semantics( + label: widget.semanticLabel, + button: true, + excludeSemantics: true, + child: buttonWidget, + ) + : buttonWidget; + return widget.style == BackButtonStyle.circular ? SafeArea(child: button) : button; diff --git a/lib/widgets/media_card.dart b/lib/widgets/media_card.dart index e420a0ce..629a33b9 100644 --- a/lib/widgets/media_card.dart +++ b/lib/widgets/media_card.dart @@ -49,6 +49,50 @@ class MediaCard extends StatefulWidget { } class _MediaCardState extends State { + String _buildSemanticLabel() { + final item = widget.item; + final itemType = item.type.toLowerCase(); + + // Build base label based on type + String baseLabel; + if (itemType == 'episode') { + final episodeInfo = item.parentIndex != null && item.index != null + ? 'S${item.parentIndex} E${item.index}' + : ''; + baseLabel = t.accessibility.mediaCardEpisode( + title: item.displayTitle, + episodeInfo: episodeInfo, + ); + } else if (itemType == 'season') { + final seasonInfo = item.parentIndex != null + ? 'Season ${item.parentIndex}' + : ''; + baseLabel = t.accessibility.mediaCardSeason( + title: item.displayTitle, + seasonInfo: seasonInfo, + ); + } else if (itemType == 'movie') { + baseLabel = t.accessibility.mediaCardMovie(title: item.displayTitle); + } else { + baseLabel = t.accessibility.mediaCardShow(title: item.displayTitle); + } + + // Add watched status + if (item.isWatched) { + baseLabel = '$baseLabel, ${t.accessibility.mediaCardWatched}'; + } else if (item.viewOffset != null && + item.duration != null && + item.viewOffset! > 0) { + final percent = ((item.viewOffset! / item.duration!) * 100).round(); + baseLabel = + '$baseLabel, ${t.accessibility.mediaCardPartiallyWatched(percent: percent)}'; + } else { + baseLabel = '$baseLabel, ${t.accessibility.mediaCardUnwatched}'; + } + + return baseLabel; + } + void _handleTap(BuildContext context) async { final client = context.client; if (client == null) return; @@ -138,15 +182,19 @@ class _MediaCardState extends State { ? ViewMode.grid : settingsProvider.viewMode; + final semanticLabel = _buildSemanticLabel(); + final cardWidget = viewMode == ViewMode.grid ? _MediaCardGrid( item: widget.item, width: widget.width, height: widget.height, + semanticLabel: semanticLabel, onTap: () => _handleTap(context), ) : _MediaCardList( item: widget.item, + semanticLabel: semanticLabel, onTap: () => _handleTap(context), density: settingsProvider.libraryDensity, ); @@ -170,12 +218,14 @@ class _MediaCardGrid extends StatelessWidget { final dynamic item; // Can be PlexMetadata or PlexPlaylist final double? width; final double? height; + final String semanticLabel; final VoidCallback onTap; const _MediaCardGrid({ required this.item, this.width, this.height, + required this.semanticLabel, required this.onTap, }); @@ -184,8 +234,7 @@ class _MediaCardGrid extends StatelessWidget { return SizedBox( width: width, child: Semantics( - label: "media-card-${item.ratingKey}", - identifier: "media-card-${item.ratingKey}", + label: semanticLabel, button: true, child: InkWell( onTap: onTap, @@ -334,11 +383,13 @@ class _MediaCardGrid extends StatelessWidget { /// List layout for media cards class _MediaCardList extends StatelessWidget { final dynamic item; // Can be PlexMetadata or PlexPlaylist + final String semanticLabel; final VoidCallback onTap; final LibraryDensity density; const _MediaCardList({ required this.item, + required this.semanticLabel, required this.onTap, required this.density, }); @@ -501,8 +552,7 @@ class _MediaCardList extends StatelessWidget { final subtitle = _buildSubtitleText(); return Semantics( - label: "media-card-${item.ratingKey}", - identifier: "media-card-${item.ratingKey}", + label: semanticLabel, button: true, child: InkWell( onTap: onTap, diff --git a/lib/widgets/video_controls/video_control_button.dart b/lib/widgets/video_controls/video_control_button.dart index 0b0f6b3a..e48fd441 100644 --- a/lib/widgets/video_controls/video_control_button.dart +++ b/lib/widgets/video_controls/video_control_button.dart @@ -18,6 +18,10 @@ class VideoControlButton extends StatelessWidget { /// Optional tooltip text shown on hover or long press. final String? tooltip; + /// Optional semantic label for screen readers. + /// If not provided, falls back to tooltip. + final String? semanticLabel; + /// Whether this button represents an active state (e.g., a feature is enabled). /// When true, the icon color defaults to amber instead of white. final bool isActive; @@ -28,6 +32,7 @@ class VideoControlButton extends StatelessWidget { required this.onPressed, this.color, this.tooltip, + this.semanticLabel, this.isActive = false, }); @@ -36,11 +41,20 @@ class VideoControlButton extends StatelessWidget { // Determine the effective color: explicit color > active amber > default white final effectiveColor = color ?? (isActive ? Colors.amber : Colors.white); - return IconButton( + final button = IconButton( icon: Icon(icon, color: effectiveColor), onPressed: onPressed, tooltip: tooltip, constraints: const BoxConstraints(minWidth: 40, minHeight: 40), ); + + return semanticLabel != null + ? Semantics( + label: semanticLabel, + button: true, + excludeSemantics: true, + child: button, + ) + : button; } } diff --git a/lib/widgets/video_controls/video_controls.dart b/lib/widgets/video_controls/video_controls.dart index 9b8ed853..be939e5f 100644 --- a/lib/widgets/video_controls/video_controls.dart +++ b/lib/widgets/video_controls/video_controls.dart @@ -455,6 +455,7 @@ class _PlexVideoControlsState extends State return VideoControlButton( icon: Icons.tune, isActive: isActive, + semanticLabel: t.videoControls.settingsButton, onPressed: () async { await VideoSettingsSheet.show( context, @@ -473,6 +474,7 @@ class _PlexVideoControlsState extends State if (_hasMultipleAudioTracks(tracks)) VideoControlButton( icon: Icons.audiotrack, + semanticLabel: t.videoControls.audioTrackButton, onPressed: () => AudioTrackSheet.show( context, widget.player, @@ -482,6 +484,7 @@ class _PlexVideoControlsState extends State if (_hasSubtitles(tracks)) VideoControlButton( icon: Icons.subtitles, + semanticLabel: t.videoControls.subtitlesButton, onPressed: () => SubtitleTrackSheet.show( context, widget.player, @@ -491,6 +494,7 @@ class _PlexVideoControlsState extends State if (_chapters.isNotEmpty) VideoControlButton( icon: Icons.video_library, + semanticLabel: t.videoControls.chaptersButton, onPressed: () => ChapterSheet.show( context, widget.player, @@ -501,6 +505,7 @@ class _PlexVideoControlsState extends State if (widget.availableVersions.length > 1) VideoControlButton( icon: Icons.video_file, + semanticLabel: t.videoControls.versionsButton, onPressed: () => VersionSheet.show( context, widget.availableVersions, @@ -513,6 +518,7 @@ class _PlexVideoControlsState extends State VideoControlButton( icon: _getBoxFitIcon(widget.boxFitMode), tooltip: _getBoxFitTooltip(widget.boxFitMode), + semanticLabel: t.videoControls.aspectRatioButton, onPressed: widget.onCycleBoxFitMode, ), // Rotation lock toggle (mobile only) @@ -524,6 +530,7 @@ class _PlexVideoControlsState extends State tooltip: _isRotationLocked ? t.videoControls.unlockRotation : t.videoControls.lockRotation, + semanticLabel: t.videoControls.rotationLockButton, onPressed: _toggleRotationLock, ), // Fullscreen toggle (desktop only) @@ -532,6 +539,9 @@ class _PlexVideoControlsState extends State icon: _isFullscreen ? Icons.fullscreen_exit : Icons.fullscreen, + semanticLabel: _isFullscreen + ? t.videoControls.exitFullscreenButton + : t.videoControls.fullscreenButton, onPressed: _toggleFullscreen, ), ], @@ -1030,6 +1040,7 @@ class _PlexVideoControlsState extends State children: [ AppBarBackButton( style: BackButtonStyle.video, + semanticLabel: t.videoControls.backButton, onPressed: () => Navigator.of(context).pop(true), ), const SizedBox(width: 16), @@ -1094,16 +1105,23 @@ class _PlexVideoControlsState extends State color: Colors.black.withValues(alpha: 0.5), shape: BoxShape.circle, ), - child: IconButton( - icon: Icon( - _getReplayIcon(_seekTimeSmall), - color: Colors.white, - size: 48, + child: Semantics( + label: t.videoControls.seekBackwardButton( + seconds: _seekTimeSmall, + ), + button: true, + excludeSemantics: true, + child: IconButton( + icon: Icon( + _getReplayIcon(_seekTimeSmall), + color: Colors.white, + size: 48, + ), + iconSize: 48, + onPressed: () { + _seekWithClamping(Duration(seconds: -_seekTimeSmall)); + }, ), - iconSize: 48, - onPressed: () { - _seekWithClamping(Duration(seconds: -_seekTimeSmall)); - }, ), ), const SizedBox(width: 48), @@ -1112,22 +1130,29 @@ class _PlexVideoControlsState extends State color: Colors.black.withValues(alpha: 0.5), shape: BoxShape.circle, ), - child: IconButton( - icon: Icon( - isPlaying ? Icons.pause : Icons.play_arrow, - color: Colors.white, - size: 72, + child: Semantics( + label: isPlaying + ? t.videoControls.pauseButton + : t.videoControls.playButton, + button: true, + excludeSemantics: true, + child: IconButton( + icon: Icon( + isPlaying ? Icons.pause : Icons.play_arrow, + color: Colors.white, + size: 72, + ), + iconSize: 72, + onPressed: () { + if (isPlaying) { + widget.player.pause(); + _hideTimer?.cancel(); // Cancel auto-hide when paused + } else { + widget.player.play(); + _startHideTimer(); // Start auto-hide when playing + } + }, ), - iconSize: 72, - onPressed: () { - if (isPlaying) { - widget.player.pause(); - _hideTimer?.cancel(); // Cancel auto-hide when paused - } else { - widget.player.play(); - _startHideTimer(); // Start auto-hide when playing - } - }, ), ), const SizedBox(width: 48), @@ -1136,16 +1161,23 @@ class _PlexVideoControlsState extends State color: Colors.black.withValues(alpha: 0.5), shape: BoxShape.circle, ), - child: IconButton( - icon: Icon( - _getForwardIcon(_seekTimeSmall), - color: Colors.white, - size: 48, + child: Semantics( + label: t.videoControls.seekForwardButton( + seconds: _seekTimeSmall, + ), + button: true, + excludeSemantics: true, + child: IconButton( + icon: Icon( + _getForwardIcon(_seekTimeSmall), + color: Colors.white, + size: 48, + ), + iconSize: 48, + onPressed: () { + _seekWithClamping(Duration(seconds: _seekTimeSmall)); + }, ), - iconSize: 48, - onPressed: () { - _seekWithClamping(Duration(seconds: _seekTimeSmall)); - }, ), ), ], @@ -1235,6 +1267,7 @@ class _PlexVideoControlsState extends State children: [ AppBarBackButton( style: BackButtonStyle.video, + semanticLabel: t.videoControls.backButton, onPressed: () => Navigator.of(context).pop(true), ), const SizedBox(width: 16), @@ -1360,24 +1393,38 @@ class _PlexVideoControlsState extends State Row( children: [ // Previous item - IconButton( - icon: Icon( - Icons.skip_previous, - color: widget.onPrevious != null - ? Colors.white - : Colors.white54, + Semantics( + label: t.videoControls.previousButton, + button: true, + excludeSemantics: true, + child: IconButton( + icon: Icon( + Icons.skip_previous, + color: widget.onPrevious != null + ? Colors.white + : Colors.white54, + ), + onPressed: widget.onPrevious, ), - onPressed: widget.onPrevious, ), // Previous chapter (or skip backward if no chapters) - IconButton( - icon: Icon( - _chapters.isEmpty - ? _getReplayIcon(_seekTimeSmall) - : Icons.fast_rewind, - color: Colors.white, + Semantics( + label: _chapters.isEmpty + ? t.videoControls.seekBackwardButton( + seconds: _seekTimeSmall, + ) + : t.videoControls.previousChapterButton, + button: true, + excludeSemantics: true, + child: IconButton( + icon: Icon( + _chapters.isEmpty + ? _getReplayIcon(_seekTimeSmall) + : Icons.fast_rewind, + color: Colors.white, + ), + onPressed: _seekToPreviousChapter, ), - onPressed: _seekToPreviousChapter, ), // Play/Pause StreamBuilder( @@ -1385,42 +1432,63 @@ class _PlexVideoControlsState extends State initialData: widget.player.state.playing, builder: (context, snapshot) { final isPlaying = snapshot.data ?? false; - return IconButton( - icon: Icon( - isPlaying ? Icons.pause : Icons.play_arrow, - color: Colors.white, - size: 32, + return Semantics( + label: isPlaying + ? t.videoControls.pauseButton + : t.videoControls.playButton, + button: true, + excludeSemantics: true, + child: IconButton( + icon: Icon( + isPlaying ? Icons.pause : Icons.play_arrow, + color: Colors.white, + size: 32, + ), + iconSize: 32, + onPressed: () { + if (isPlaying) { + widget.player.pause(); + _hideTimer?.cancel(); // Cancel auto-hide when paused + } else { + widget.player.play(); + _startHideTimer(); // Start auto-hide when playing + } + }, ), - iconSize: 32, - onPressed: () { - if (isPlaying) { - widget.player.pause(); - _hideTimer?.cancel(); // Cancel auto-hide when paused - } else { - widget.player.play(); - _startHideTimer(); // Start auto-hide when playing - } - }, ); }, ), // Next chapter (or skip forward if no chapters) - IconButton( - icon: Icon( - _chapters.isEmpty - ? _getForwardIcon(_seekTimeSmall) - : Icons.fast_forward, - color: Colors.white, + Semantics( + label: _chapters.isEmpty + ? t.videoControls.seekForwardButton(seconds: _seekTimeSmall) + : t.videoControls.nextChapterButton, + button: true, + excludeSemantics: true, + child: IconButton( + icon: Icon( + _chapters.isEmpty + ? _getForwardIcon(_seekTimeSmall) + : Icons.fast_forward, + color: Colors.white, + ), + onPressed: _seekToNextChapter, ), - onPressed: _seekToNextChapter, ), // Next item - IconButton( - icon: Icon( - Icons.skip_next, - color: widget.onNext != null ? Colors.white : Colors.white54, + Semantics( + label: t.videoControls.nextButton, + button: true, + excludeSemantics: true, + child: IconButton( + icon: Icon( + Icons.skip_next, + color: widget.onNext != null + ? Colors.white + : Colors.white54, + ), + onPressed: widget.onNext, ), - onPressed: widget.onNext, ), const Spacer(), // Volume control @@ -1479,20 +1547,24 @@ class _PlexVideoControlsState extends State ), ), // Slider - Slider( - value: duration.inMilliseconds > 0 - ? position.inMilliseconds.toDouble() - : 0.0, - min: 0.0, - max: duration.inMilliseconds.toDouble(), - onChanged: (value) { - _throttledSeek(Duration(milliseconds: value.toInt())); - }, - onChangeEnd: (value) { - _finalizeSeek(Duration(milliseconds: value.toInt())); - }, - activeColor: Colors.white, - inactiveColor: Colors.white.withValues(alpha: 0.3), + Semantics( + label: t.videoControls.timelineSlider, + slider: true, + child: Slider( + value: duration.inMilliseconds > 0 + ? position.inMilliseconds.toDouble() + : 0.0, + min: 0.0, + max: duration.inMilliseconds.toDouble(), + onChanged: (value) { + _throttledSeek(Duration(milliseconds: value.toInt())); + }, + onChangeEnd: (value) { + _finalizeSeek(Duration(milliseconds: value.toInt())); + }, + activeColor: Colors.white, + inactiveColor: Colors.white.withValues(alpha: 0.3), + ), ), // Chapter marker indicators if (_chaptersLoaded && @@ -1526,19 +1598,26 @@ class _PlexVideoControlsState extends State return Row( mainAxisSize: MainAxisSize.min, children: [ - IconButton( - icon: Icon( - isMuted ? Icons.volume_off : Icons.volume_up, - color: Colors.white, + Semantics( + label: isMuted + ? t.videoControls.unmuteButton + : t.videoControls.muteButton, + button: true, + excludeSemantics: true, + child: IconButton( + icon: Icon( + isMuted ? Icons.volume_off : Icons.volume_up, + color: Colors.white, + ), + onPressed: () async { + final newVolume = isMuted ? 100.0 : 0.0; + widget.player.setVolume(newVolume); + final settings = await SettingsService.getInstance(); + await settings.setVolume(newVolume); + }, + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), ), - onPressed: () async { - final newVolume = isMuted ? 100.0 : 0.0; - widget.player.setVolume(newVolume); - final settings = await SettingsService.getInstance(); - await settings.setVolume(newVolume); - }, - padding: EdgeInsets.zero, - constraints: const BoxConstraints(), ), const SizedBox(width: 8), SizedBox( @@ -1553,19 +1632,23 @@ class _PlexVideoControlsState extends State overlayRadius: 12, ), ), - child: Slider( - value: volume, - min: 0.0, - max: 100.0, - onChanged: (value) { - widget.player.setVolume(value); - }, - onChangeEnd: (value) async { - final settings = await SettingsService.getInstance(); - await settings.setVolume(value); - }, - activeColor: Colors.white, - inactiveColor: Colors.white.withValues(alpha: 0.3), + child: Semantics( + label: t.videoControls.volumeSlider, + slider: true, + child: Slider( + value: volume, + min: 0.0, + max: 100.0, + onChanged: (value) { + widget.player.setVolume(value); + }, + onChangeEnd: (value) async { + final settings = await SettingsService.getInstance(); + await settings.setVolume(value); + }, + activeColor: Colors.white, + inactiveColor: Colors.white.withValues(alpha: 0.3), + ), ), ), ),