From 48eef0853da4488bd536b095eb4fcf381cbe7f73 Mon Sep 17 00:00:00 2001 From: zarzet Date: Sun, 29 Mar 2026 17:02:12 +0700 Subject: [PATCH] i18n: extract hardcoded strings into l10n keys Move hardcoded UI strings across multiple screens and the notification service into ARB-backed l10n keys so they can be translated via Crowdin. Adds 62 new keys covering sort labels, dialog copy, metadata error snackbars, folder-picker errors, home-tab error states, extensions home feed selector, and all notification titles/bodies. NotificationService now caches an AppLocalizations instance (injected from MainShell via didChangeDependencies) and falls back to English literals when no locale is available. --- lib/l10n/app_localizations.dart | 292 ++++++++++++++++ lib/l10n/app_localizations_de.dart | 188 +++++++++++ lib/l10n/app_localizations_en.dart | 188 +++++++++++ lib/l10n/app_localizations_es.dart | 188 +++++++++++ lib/l10n/app_localizations_fr.dart | 188 +++++++++++ lib/l10n/app_localizations_hi.dart | 188 +++++++++++ lib/l10n/app_localizations_id.dart | 188 +++++++++++ lib/l10n/app_localizations_ja.dart | 188 +++++++++++ lib/l10n/app_localizations_ko.dart | 188 +++++++++++ lib/l10n/app_localizations_nl.dart | 188 +++++++++++ lib/l10n/app_localizations_pt.dart | 188 +++++++++++ lib/l10n/app_localizations_ru.dart | 188 +++++++++++ lib/l10n/app_localizations_tr.dart | 188 +++++++++++ lib/l10n/app_localizations_zh.dart | 188 +++++++++++ lib/l10n/arb/app_en.arb | 312 ++++++++++++++++++ lib/screens/home_tab.dart | 18 +- lib/screens/main_shell.dart | 7 + lib/screens/queue_tab.dart | 12 +- .../settings/download_settings_page.dart | 4 +- lib/screens/settings/extensions_page.dart | 20 +- .../settings/options_settings_page.dart | 6 +- lib/screens/setup_screen.dart | 10 +- lib/screens/track_metadata_screen.dart | 15 +- lib/services/notification_service.dart | 87 +++-- 24 files changed, 3161 insertions(+), 66 deletions(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 87228ea8..4adf7a37 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -5300,6 +5300,298 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Samples'** String get audioAnalysisSamples; + + /// Extensions page - subtitle for built-in search provider option + /// + /// In en, this message translates to: + /// **'Search with {providerName}'** + String extensionsSearchWith(String providerName); + + /// Extensions page - label for home feed provider selector + /// + /// In en, this message translates to: + /// **'Home Feed Provider'** + String get extensionsHomeFeedProvider; + + /// Extensions page - description for home feed provider picker + /// + /// In en, this message translates to: + /// **'Choose which extension provides the home feed on the main screen'** + String get extensionsHomeFeedDescription; + + /// Extensions page - home feed provider option: auto + /// + /// In en, this message translates to: + /// **'Auto'** + String get extensionsHomeFeedAuto; + + /// Extensions page - subtitle for auto home feed option + /// + /// In en, this message translates to: + /// **'Automatically select the best available'** + String get extensionsHomeFeedAutoSubtitle; + + /// Extensions page - subtitle for a specific extension home feed option + /// + /// In en, this message translates to: + /// **'Use {extensionName} home feed'** + String extensionsHomeFeedUse(String extensionName); + + /// Extensions page - shown when no installed extension has home feed + /// + /// In en, this message translates to: + /// **'No extensions with home feed'** + String get extensionsNoHomeFeedExtensions; + + /// Sort option - alphabetical ascending + /// + /// In en, this message translates to: + /// **'A-Z'** + String get sortAlphaAsc; + + /// Sort option - alphabetical descending + /// + /// In en, this message translates to: + /// **'Z-A'** + String get sortAlphaDesc; + + /// Dialog title when confirming cancellation of an active download + /// + /// In en, this message translates to: + /// **'Cancel download?'** + String get cancelDownloadTitle; + + /// Dialog body when confirming cancellation of an active download + /// + /// In en, this message translates to: + /// **'This will cancel the active download for \"{trackName}\".'** + String cancelDownloadContent(String trackName); + + /// Dialog button - keep the active download (do not cancel) + /// + /// In en, this message translates to: + /// **'Keep'** + String get cancelDownloadKeep; + + /// Snackbar error when FFmpeg fails to write metadata + /// + /// In en, this message translates to: + /// **'Failed to save metadata via FFmpeg'** + String get metadataSaveFailedFfmpeg; + + /// Snackbar error when writing metadata file back to storage fails + /// + /// In en, this message translates to: + /// **'Failed to write metadata back to storage'** + String get metadataSaveFailedStorage; + + /// Snackbar shown when folder picker fails to open + /// + /// In en, this message translates to: + /// **'Failed to open folder picker: {error}'** + String snackbarFolderPickerFailed(String error); + + /// Error state shown when album fails to load + /// + /// In en, this message translates to: + /// **'Failed to load album'** + String get errorLoadAlbum; + + /// Error state shown when playlist fails to load + /// + /// In en, this message translates to: + /// **'Failed to load playlist'** + String get errorLoadPlaylist; + + /// Error state shown when artist fails to load + /// + /// In en, this message translates to: + /// **'Failed to load artist'** + String get errorLoadArtist; + + /// Android notification channel name for download progress + /// + /// In en, this message translates to: + /// **'Download Progress'** + String get notifChannelDownloadName; + + /// Android notification channel description for download progress + /// + /// In en, this message translates to: + /// **'Shows download progress for tracks'** + String get notifChannelDownloadDesc; + + /// Android notification channel name for library scan + /// + /// In en, this message translates to: + /// **'Library Scan'** + String get notifChannelLibraryScanName; + + /// Android notification channel description for library scan + /// + /// In en, this message translates to: + /// **'Shows local library scan progress'** + String get notifChannelLibraryScanDesc; + + /// Notification title while downloading a track + /// + /// In en, this message translates to: + /// **'Downloading {trackName}'** + String notifDownloadingTrack(String trackName); + + /// Notification title while finalizing (embedding metadata) a track + /// + /// In en, this message translates to: + /// **'Finalizing {trackName}'** + String notifFinalizingTrack(String trackName); + + /// Notification body while embedding metadata into a downloaded track + /// + /// In en, this message translates to: + /// **'Embedding metadata...'** + String get notifEmbeddingMetadata; + + /// Notification title when track is already in library, with count + /// + /// In en, this message translates to: + /// **'Already in Library ({completed}/{total})'** + String notifAlreadyInLibraryCount(int completed, int total); + + /// Notification title when track is already in library + /// + /// In en, this message translates to: + /// **'Already in Library'** + String get notifAlreadyInLibrary; + + /// Notification title when download is complete, with count + /// + /// In en, this message translates to: + /// **'Download Complete ({completed}/{total})'** + String notifDownloadCompleteCount(int completed, int total); + + /// Notification title when a single download is complete + /// + /// In en, this message translates to: + /// **'Download Complete'** + String get notifDownloadComplete; + + /// Notification title when queue finishes with some failures + /// + /// In en, this message translates to: + /// **'Downloads Finished ({completed} done, {failed} failed)'** + String notifDownloadsFinished(int completed, int failed); + + /// Notification title when all downloads finish successfully + /// + /// In en, this message translates to: + /// **'All Downloads Complete'** + String get notifAllDownloadsComplete; + + /// Notification body for queue complete - how many tracks were downloaded + /// + /// In en, this message translates to: + /// **'{count} tracks downloaded successfully'** + String notifTracksDownloadedSuccess(int count); + + /// Notification title while scanning local library + /// + /// In en, this message translates to: + /// **'Scanning local library'** + String get notifScanningLibrary; + + /// Notification body for library scan progress when total is known + /// + /// In en, this message translates to: + /// **'{scanned}/{total} files • {percentage}%'** + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ); + + /// Notification body for library scan progress when total is unknown + /// + /// In en, this message translates to: + /// **'{scanned} files scanned • {percentage}%'** + String notifLibraryScanProgressNoTotal(int scanned, int percentage); + + /// Notification title when library scan finishes + /// + /// In en, this message translates to: + /// **'Library scan complete'** + String get notifLibraryScanComplete; + + /// Notification body for library scan complete - number of indexed tracks + /// + /// In en, this message translates to: + /// **'{count} tracks indexed'** + String notifLibraryScanCompleteBody(int count); + + /// Library scan complete suffix - excluded track count + /// + /// In en, this message translates to: + /// **'{count} excluded'** + String notifLibraryScanExcluded(int count); + + /// Library scan complete suffix - error count + /// + /// In en, this message translates to: + /// **'{count} errors'** + String notifLibraryScanErrors(int count); + + /// Notification title when library scan fails + /// + /// In en, this message translates to: + /// **'Library scan failed'** + String get notifLibraryScanFailed; + + /// Notification title when library scan is cancelled by the user + /// + /// In en, this message translates to: + /// **'Library scan cancelled'** + String get notifLibraryScanCancelled; + + /// Notification body when library scan is cancelled + /// + /// In en, this message translates to: + /// **'Scan stopped before completion.'** + String get notifLibraryScanStopped; + + /// Notification title while downloading an app update + /// + /// In en, this message translates to: + /// **'Downloading SpotiFLAC v{version}'** + String notifDownloadingUpdate(String version); + + /// Notification body showing update download progress + /// + /// In en, this message translates to: + /// **'{received} / {total} MB • {percentage}%'** + String notifUpdateProgress(String received, String total, int percentage); + + /// Notification title when app update download is complete + /// + /// In en, this message translates to: + /// **'Update Ready'** + String get notifUpdateReady; + + /// Notification body when app update is ready to install + /// + /// In en, this message translates to: + /// **'SpotiFLAC v{version} downloaded. Tap to install.'** + String notifUpdateReadyBody(String version); + + /// Notification title when app update download fails + /// + /// In en, this message translates to: + /// **'Update Failed'** + String get notifUpdateFailed; + + /// Notification body when app update download fails + /// + /// In en, this message translates to: + /// **'Could not download update. Try again later.'** + String get notifUpdateFailedBody; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 916db930..5c9eedd0 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -3124,4 +3124,192 @@ class AppLocalizationsDe extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + + @override + String extensionsSearchWith(String providerName) { + return 'Search with $providerName'; + } + + @override + String get extensionsHomeFeedProvider => 'Home Feed Provider'; + + @override + String get extensionsHomeFeedDescription => + 'Choose which extension provides the home feed on the main screen'; + + @override + String get extensionsHomeFeedAuto => 'Auto'; + + @override + String get extensionsHomeFeedAutoSubtitle => + 'Automatically select the best available'; + + @override + String extensionsHomeFeedUse(String extensionName) { + return 'Use $extensionName home feed'; + } + + @override + String get extensionsNoHomeFeedExtensions => 'No extensions with home feed'; + + @override + String get sortAlphaAsc => 'A-Z'; + + @override + String get sortAlphaDesc => 'Z-A'; + + @override + String get cancelDownloadTitle => 'Cancel download?'; + + @override + String cancelDownloadContent(String trackName) { + return 'This will cancel the active download for \"$trackName\".'; + } + + @override + String get cancelDownloadKeep => 'Keep'; + + @override + String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg'; + + @override + String get metadataSaveFailedStorage => + 'Failed to write metadata back to storage'; + + @override + String snackbarFolderPickerFailed(String error) { + return 'Failed to open folder picker: $error'; + } + + @override + String get errorLoadAlbum => 'Failed to load album'; + + @override + String get errorLoadPlaylist => 'Failed to load playlist'; + + @override + String get errorLoadArtist => 'Failed to load artist'; + + @override + String get notifChannelDownloadName => 'Download Progress'; + + @override + String get notifChannelDownloadDesc => 'Shows download progress for tracks'; + + @override + String get notifChannelLibraryScanName => 'Library Scan'; + + @override + String get notifChannelLibraryScanDesc => 'Shows local library scan progress'; + + @override + String notifDownloadingTrack(String trackName) { + return 'Downloading $trackName'; + } + + @override + String notifFinalizingTrack(String trackName) { + return 'Finalizing $trackName'; + } + + @override + String get notifEmbeddingMetadata => 'Embedding metadata...'; + + @override + String notifAlreadyInLibraryCount(int completed, int total) { + return 'Already in Library ($completed/$total)'; + } + + @override + String get notifAlreadyInLibrary => 'Already in Library'; + + @override + String notifDownloadCompleteCount(int completed, int total) { + return 'Download Complete ($completed/$total)'; + } + + @override + String get notifDownloadComplete => 'Download Complete'; + + @override + String notifDownloadsFinished(int completed, int failed) { + return 'Downloads Finished ($completed done, $failed failed)'; + } + + @override + String get notifAllDownloadsComplete => 'All Downloads Complete'; + + @override + String notifTracksDownloadedSuccess(int count) { + return '$count tracks downloaded successfully'; + } + + @override + String get notifScanningLibrary => 'Scanning local library'; + + @override + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ) { + return '$scanned/$total files • $percentage%'; + } + + @override + String notifLibraryScanProgressNoTotal(int scanned, int percentage) { + return '$scanned files scanned • $percentage%'; + } + + @override + String get notifLibraryScanComplete => 'Library scan complete'; + + @override + String notifLibraryScanCompleteBody(int count) { + return '$count tracks indexed'; + } + + @override + String notifLibraryScanExcluded(int count) { + return '$count excluded'; + } + + @override + String notifLibraryScanErrors(int count) { + return '$count errors'; + } + + @override + String get notifLibraryScanFailed => 'Library scan failed'; + + @override + String get notifLibraryScanCancelled => 'Library scan cancelled'; + + @override + String get notifLibraryScanStopped => 'Scan stopped before completion.'; + + @override + String notifDownloadingUpdate(String version) { + return 'Downloading SpotiFLAC v$version'; + } + + @override + String notifUpdateProgress(String received, String total, int percentage) { + return '$received / $total MB • $percentage%'; + } + + @override + String get notifUpdateReady => 'Update Ready'; + + @override + String notifUpdateReadyBody(String version) { + return 'SpotiFLAC v$version downloaded. Tap to install.'; + } + + @override + String get notifUpdateFailed => 'Update Failed'; + + @override + String get notifUpdateFailedBody => + 'Could not download update. Try again later.'; } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index ea2cc6e6..e5cb5cbb 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -3092,4 +3092,192 @@ class AppLocalizationsEn extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + + @override + String extensionsSearchWith(String providerName) { + return 'Search with $providerName'; + } + + @override + String get extensionsHomeFeedProvider => 'Home Feed Provider'; + + @override + String get extensionsHomeFeedDescription => + 'Choose which extension provides the home feed on the main screen'; + + @override + String get extensionsHomeFeedAuto => 'Auto'; + + @override + String get extensionsHomeFeedAutoSubtitle => + 'Automatically select the best available'; + + @override + String extensionsHomeFeedUse(String extensionName) { + return 'Use $extensionName home feed'; + } + + @override + String get extensionsNoHomeFeedExtensions => 'No extensions with home feed'; + + @override + String get sortAlphaAsc => 'A-Z'; + + @override + String get sortAlphaDesc => 'Z-A'; + + @override + String get cancelDownloadTitle => 'Cancel download?'; + + @override + String cancelDownloadContent(String trackName) { + return 'This will cancel the active download for \"$trackName\".'; + } + + @override + String get cancelDownloadKeep => 'Keep'; + + @override + String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg'; + + @override + String get metadataSaveFailedStorage => + 'Failed to write metadata back to storage'; + + @override + String snackbarFolderPickerFailed(String error) { + return 'Failed to open folder picker: $error'; + } + + @override + String get errorLoadAlbum => 'Failed to load album'; + + @override + String get errorLoadPlaylist => 'Failed to load playlist'; + + @override + String get errorLoadArtist => 'Failed to load artist'; + + @override + String get notifChannelDownloadName => 'Download Progress'; + + @override + String get notifChannelDownloadDesc => 'Shows download progress for tracks'; + + @override + String get notifChannelLibraryScanName => 'Library Scan'; + + @override + String get notifChannelLibraryScanDesc => 'Shows local library scan progress'; + + @override + String notifDownloadingTrack(String trackName) { + return 'Downloading $trackName'; + } + + @override + String notifFinalizingTrack(String trackName) { + return 'Finalizing $trackName'; + } + + @override + String get notifEmbeddingMetadata => 'Embedding metadata...'; + + @override + String notifAlreadyInLibraryCount(int completed, int total) { + return 'Already in Library ($completed/$total)'; + } + + @override + String get notifAlreadyInLibrary => 'Already in Library'; + + @override + String notifDownloadCompleteCount(int completed, int total) { + return 'Download Complete ($completed/$total)'; + } + + @override + String get notifDownloadComplete => 'Download Complete'; + + @override + String notifDownloadsFinished(int completed, int failed) { + return 'Downloads Finished ($completed done, $failed failed)'; + } + + @override + String get notifAllDownloadsComplete => 'All Downloads Complete'; + + @override + String notifTracksDownloadedSuccess(int count) { + return '$count tracks downloaded successfully'; + } + + @override + String get notifScanningLibrary => 'Scanning local library'; + + @override + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ) { + return '$scanned/$total files • $percentage%'; + } + + @override + String notifLibraryScanProgressNoTotal(int scanned, int percentage) { + return '$scanned files scanned • $percentage%'; + } + + @override + String get notifLibraryScanComplete => 'Library scan complete'; + + @override + String notifLibraryScanCompleteBody(int count) { + return '$count tracks indexed'; + } + + @override + String notifLibraryScanExcluded(int count) { + return '$count excluded'; + } + + @override + String notifLibraryScanErrors(int count) { + return '$count errors'; + } + + @override + String get notifLibraryScanFailed => 'Library scan failed'; + + @override + String get notifLibraryScanCancelled => 'Library scan cancelled'; + + @override + String get notifLibraryScanStopped => 'Scan stopped before completion.'; + + @override + String notifDownloadingUpdate(String version) { + return 'Downloading SpotiFLAC v$version'; + } + + @override + String notifUpdateProgress(String received, String total, int percentage) { + return '$received / $total MB • $percentage%'; + } + + @override + String get notifUpdateReady => 'Update Ready'; + + @override + String notifUpdateReadyBody(String version) { + return 'SpotiFLAC v$version downloaded. Tap to install.'; + } + + @override + String get notifUpdateFailed => 'Update Failed'; + + @override + String get notifUpdateFailedBody => + 'Could not download update. Try again later.'; } diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 5ca7ef2b..fa0eed76 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -3092,6 +3092,194 @@ class AppLocalizationsEs extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + + @override + String extensionsSearchWith(String providerName) { + return 'Search with $providerName'; + } + + @override + String get extensionsHomeFeedProvider => 'Home Feed Provider'; + + @override + String get extensionsHomeFeedDescription => + 'Choose which extension provides the home feed on the main screen'; + + @override + String get extensionsHomeFeedAuto => 'Auto'; + + @override + String get extensionsHomeFeedAutoSubtitle => + 'Automatically select the best available'; + + @override + String extensionsHomeFeedUse(String extensionName) { + return 'Use $extensionName home feed'; + } + + @override + String get extensionsNoHomeFeedExtensions => 'No extensions with home feed'; + + @override + String get sortAlphaAsc => 'A-Z'; + + @override + String get sortAlphaDesc => 'Z-A'; + + @override + String get cancelDownloadTitle => 'Cancel download?'; + + @override + String cancelDownloadContent(String trackName) { + return 'This will cancel the active download for \"$trackName\".'; + } + + @override + String get cancelDownloadKeep => 'Keep'; + + @override + String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg'; + + @override + String get metadataSaveFailedStorage => + 'Failed to write metadata back to storage'; + + @override + String snackbarFolderPickerFailed(String error) { + return 'Failed to open folder picker: $error'; + } + + @override + String get errorLoadAlbum => 'Failed to load album'; + + @override + String get errorLoadPlaylist => 'Failed to load playlist'; + + @override + String get errorLoadArtist => 'Failed to load artist'; + + @override + String get notifChannelDownloadName => 'Download Progress'; + + @override + String get notifChannelDownloadDesc => 'Shows download progress for tracks'; + + @override + String get notifChannelLibraryScanName => 'Library Scan'; + + @override + String get notifChannelLibraryScanDesc => 'Shows local library scan progress'; + + @override + String notifDownloadingTrack(String trackName) { + return 'Downloading $trackName'; + } + + @override + String notifFinalizingTrack(String trackName) { + return 'Finalizing $trackName'; + } + + @override + String get notifEmbeddingMetadata => 'Embedding metadata...'; + + @override + String notifAlreadyInLibraryCount(int completed, int total) { + return 'Already in Library ($completed/$total)'; + } + + @override + String get notifAlreadyInLibrary => 'Already in Library'; + + @override + String notifDownloadCompleteCount(int completed, int total) { + return 'Download Complete ($completed/$total)'; + } + + @override + String get notifDownloadComplete => 'Download Complete'; + + @override + String notifDownloadsFinished(int completed, int failed) { + return 'Downloads Finished ($completed done, $failed failed)'; + } + + @override + String get notifAllDownloadsComplete => 'All Downloads Complete'; + + @override + String notifTracksDownloadedSuccess(int count) { + return '$count tracks downloaded successfully'; + } + + @override + String get notifScanningLibrary => 'Scanning local library'; + + @override + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ) { + return '$scanned/$total files • $percentage%'; + } + + @override + String notifLibraryScanProgressNoTotal(int scanned, int percentage) { + return '$scanned files scanned • $percentage%'; + } + + @override + String get notifLibraryScanComplete => 'Library scan complete'; + + @override + String notifLibraryScanCompleteBody(int count) { + return '$count tracks indexed'; + } + + @override + String notifLibraryScanExcluded(int count) { + return '$count excluded'; + } + + @override + String notifLibraryScanErrors(int count) { + return '$count errors'; + } + + @override + String get notifLibraryScanFailed => 'Library scan failed'; + + @override + String get notifLibraryScanCancelled => 'Library scan cancelled'; + + @override + String get notifLibraryScanStopped => 'Scan stopped before completion.'; + + @override + String notifDownloadingUpdate(String version) { + return 'Downloading SpotiFLAC v$version'; + } + + @override + String notifUpdateProgress(String received, String total, int percentage) { + return '$received / $total MB • $percentage%'; + } + + @override + String get notifUpdateReady => 'Update Ready'; + + @override + String notifUpdateReadyBody(String version) { + return 'SpotiFLAC v$version downloaded. Tap to install.'; + } + + @override + String get notifUpdateFailed => 'Update Failed'; + + @override + String get notifUpdateFailedBody => + 'Could not download update. Try again later.'; } /// The translations for Spanish Castilian, as used in Spain (`es_ES`). diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 288273a2..32c085a6 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -3093,4 +3093,192 @@ class AppLocalizationsFr extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + + @override + String extensionsSearchWith(String providerName) { + return 'Search with $providerName'; + } + + @override + String get extensionsHomeFeedProvider => 'Home Feed Provider'; + + @override + String get extensionsHomeFeedDescription => + 'Choose which extension provides the home feed on the main screen'; + + @override + String get extensionsHomeFeedAuto => 'Auto'; + + @override + String get extensionsHomeFeedAutoSubtitle => + 'Automatically select the best available'; + + @override + String extensionsHomeFeedUse(String extensionName) { + return 'Use $extensionName home feed'; + } + + @override + String get extensionsNoHomeFeedExtensions => 'No extensions with home feed'; + + @override + String get sortAlphaAsc => 'A-Z'; + + @override + String get sortAlphaDesc => 'Z-A'; + + @override + String get cancelDownloadTitle => 'Cancel download?'; + + @override + String cancelDownloadContent(String trackName) { + return 'This will cancel the active download for \"$trackName\".'; + } + + @override + String get cancelDownloadKeep => 'Keep'; + + @override + String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg'; + + @override + String get metadataSaveFailedStorage => + 'Failed to write metadata back to storage'; + + @override + String snackbarFolderPickerFailed(String error) { + return 'Failed to open folder picker: $error'; + } + + @override + String get errorLoadAlbum => 'Failed to load album'; + + @override + String get errorLoadPlaylist => 'Failed to load playlist'; + + @override + String get errorLoadArtist => 'Failed to load artist'; + + @override + String get notifChannelDownloadName => 'Download Progress'; + + @override + String get notifChannelDownloadDesc => 'Shows download progress for tracks'; + + @override + String get notifChannelLibraryScanName => 'Library Scan'; + + @override + String get notifChannelLibraryScanDesc => 'Shows local library scan progress'; + + @override + String notifDownloadingTrack(String trackName) { + return 'Downloading $trackName'; + } + + @override + String notifFinalizingTrack(String trackName) { + return 'Finalizing $trackName'; + } + + @override + String get notifEmbeddingMetadata => 'Embedding metadata...'; + + @override + String notifAlreadyInLibraryCount(int completed, int total) { + return 'Already in Library ($completed/$total)'; + } + + @override + String get notifAlreadyInLibrary => 'Already in Library'; + + @override + String notifDownloadCompleteCount(int completed, int total) { + return 'Download Complete ($completed/$total)'; + } + + @override + String get notifDownloadComplete => 'Download Complete'; + + @override + String notifDownloadsFinished(int completed, int failed) { + return 'Downloads Finished ($completed done, $failed failed)'; + } + + @override + String get notifAllDownloadsComplete => 'All Downloads Complete'; + + @override + String notifTracksDownloadedSuccess(int count) { + return '$count tracks downloaded successfully'; + } + + @override + String get notifScanningLibrary => 'Scanning local library'; + + @override + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ) { + return '$scanned/$total files • $percentage%'; + } + + @override + String notifLibraryScanProgressNoTotal(int scanned, int percentage) { + return '$scanned files scanned • $percentage%'; + } + + @override + String get notifLibraryScanComplete => 'Library scan complete'; + + @override + String notifLibraryScanCompleteBody(int count) { + return '$count tracks indexed'; + } + + @override + String notifLibraryScanExcluded(int count) { + return '$count excluded'; + } + + @override + String notifLibraryScanErrors(int count) { + return '$count errors'; + } + + @override + String get notifLibraryScanFailed => 'Library scan failed'; + + @override + String get notifLibraryScanCancelled => 'Library scan cancelled'; + + @override + String get notifLibraryScanStopped => 'Scan stopped before completion.'; + + @override + String notifDownloadingUpdate(String version) { + return 'Downloading SpotiFLAC v$version'; + } + + @override + String notifUpdateProgress(String received, String total, int percentage) { + return '$received / $total MB • $percentage%'; + } + + @override + String get notifUpdateReady => 'Update Ready'; + + @override + String notifUpdateReadyBody(String version) { + return 'SpotiFLAC v$version downloaded. Tap to install.'; + } + + @override + String get notifUpdateFailed => 'Update Failed'; + + @override + String get notifUpdateFailedBody => + 'Could not download update. Try again later.'; } diff --git a/lib/l10n/app_localizations_hi.dart b/lib/l10n/app_localizations_hi.dart index 6b20fe2e..ff3ca387 100644 --- a/lib/l10n/app_localizations_hi.dart +++ b/lib/l10n/app_localizations_hi.dart @@ -3091,4 +3091,192 @@ class AppLocalizationsHi extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + + @override + String extensionsSearchWith(String providerName) { + return 'Search with $providerName'; + } + + @override + String get extensionsHomeFeedProvider => 'Home Feed Provider'; + + @override + String get extensionsHomeFeedDescription => + 'Choose which extension provides the home feed on the main screen'; + + @override + String get extensionsHomeFeedAuto => 'Auto'; + + @override + String get extensionsHomeFeedAutoSubtitle => + 'Automatically select the best available'; + + @override + String extensionsHomeFeedUse(String extensionName) { + return 'Use $extensionName home feed'; + } + + @override + String get extensionsNoHomeFeedExtensions => 'No extensions with home feed'; + + @override + String get sortAlphaAsc => 'A-Z'; + + @override + String get sortAlphaDesc => 'Z-A'; + + @override + String get cancelDownloadTitle => 'Cancel download?'; + + @override + String cancelDownloadContent(String trackName) { + return 'This will cancel the active download for \"$trackName\".'; + } + + @override + String get cancelDownloadKeep => 'Keep'; + + @override + String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg'; + + @override + String get metadataSaveFailedStorage => + 'Failed to write metadata back to storage'; + + @override + String snackbarFolderPickerFailed(String error) { + return 'Failed to open folder picker: $error'; + } + + @override + String get errorLoadAlbum => 'Failed to load album'; + + @override + String get errorLoadPlaylist => 'Failed to load playlist'; + + @override + String get errorLoadArtist => 'Failed to load artist'; + + @override + String get notifChannelDownloadName => 'Download Progress'; + + @override + String get notifChannelDownloadDesc => 'Shows download progress for tracks'; + + @override + String get notifChannelLibraryScanName => 'Library Scan'; + + @override + String get notifChannelLibraryScanDesc => 'Shows local library scan progress'; + + @override + String notifDownloadingTrack(String trackName) { + return 'Downloading $trackName'; + } + + @override + String notifFinalizingTrack(String trackName) { + return 'Finalizing $trackName'; + } + + @override + String get notifEmbeddingMetadata => 'Embedding metadata...'; + + @override + String notifAlreadyInLibraryCount(int completed, int total) { + return 'Already in Library ($completed/$total)'; + } + + @override + String get notifAlreadyInLibrary => 'Already in Library'; + + @override + String notifDownloadCompleteCount(int completed, int total) { + return 'Download Complete ($completed/$total)'; + } + + @override + String get notifDownloadComplete => 'Download Complete'; + + @override + String notifDownloadsFinished(int completed, int failed) { + return 'Downloads Finished ($completed done, $failed failed)'; + } + + @override + String get notifAllDownloadsComplete => 'All Downloads Complete'; + + @override + String notifTracksDownloadedSuccess(int count) { + return '$count tracks downloaded successfully'; + } + + @override + String get notifScanningLibrary => 'Scanning local library'; + + @override + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ) { + return '$scanned/$total files • $percentage%'; + } + + @override + String notifLibraryScanProgressNoTotal(int scanned, int percentage) { + return '$scanned files scanned • $percentage%'; + } + + @override + String get notifLibraryScanComplete => 'Library scan complete'; + + @override + String notifLibraryScanCompleteBody(int count) { + return '$count tracks indexed'; + } + + @override + String notifLibraryScanExcluded(int count) { + return '$count excluded'; + } + + @override + String notifLibraryScanErrors(int count) { + return '$count errors'; + } + + @override + String get notifLibraryScanFailed => 'Library scan failed'; + + @override + String get notifLibraryScanCancelled => 'Library scan cancelled'; + + @override + String get notifLibraryScanStopped => 'Scan stopped before completion.'; + + @override + String notifDownloadingUpdate(String version) { + return 'Downloading SpotiFLAC v$version'; + } + + @override + String notifUpdateProgress(String received, String total, int percentage) { + return '$received / $total MB • $percentage%'; + } + + @override + String get notifUpdateReady => 'Update Ready'; + + @override + String notifUpdateReadyBody(String version) { + return 'SpotiFLAC v$version downloaded. Tap to install.'; + } + + @override + String get notifUpdateFailed => 'Update Failed'; + + @override + String get notifUpdateFailedBody => + 'Could not download update. Try again later.'; } diff --git a/lib/l10n/app_localizations_id.dart b/lib/l10n/app_localizations_id.dart index b4b09433..89e25ab4 100644 --- a/lib/l10n/app_localizations_id.dart +++ b/lib/l10n/app_localizations_id.dart @@ -3102,4 +3102,192 @@ class AppLocalizationsId extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + + @override + String extensionsSearchWith(String providerName) { + return 'Search with $providerName'; + } + + @override + String get extensionsHomeFeedProvider => 'Home Feed Provider'; + + @override + String get extensionsHomeFeedDescription => + 'Choose which extension provides the home feed on the main screen'; + + @override + String get extensionsHomeFeedAuto => 'Auto'; + + @override + String get extensionsHomeFeedAutoSubtitle => + 'Automatically select the best available'; + + @override + String extensionsHomeFeedUse(String extensionName) { + return 'Use $extensionName home feed'; + } + + @override + String get extensionsNoHomeFeedExtensions => 'No extensions with home feed'; + + @override + String get sortAlphaAsc => 'A-Z'; + + @override + String get sortAlphaDesc => 'Z-A'; + + @override + String get cancelDownloadTitle => 'Cancel download?'; + + @override + String cancelDownloadContent(String trackName) { + return 'This will cancel the active download for \"$trackName\".'; + } + + @override + String get cancelDownloadKeep => 'Keep'; + + @override + String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg'; + + @override + String get metadataSaveFailedStorage => + 'Failed to write metadata back to storage'; + + @override + String snackbarFolderPickerFailed(String error) { + return 'Failed to open folder picker: $error'; + } + + @override + String get errorLoadAlbum => 'Failed to load album'; + + @override + String get errorLoadPlaylist => 'Failed to load playlist'; + + @override + String get errorLoadArtist => 'Failed to load artist'; + + @override + String get notifChannelDownloadName => 'Download Progress'; + + @override + String get notifChannelDownloadDesc => 'Shows download progress for tracks'; + + @override + String get notifChannelLibraryScanName => 'Library Scan'; + + @override + String get notifChannelLibraryScanDesc => 'Shows local library scan progress'; + + @override + String notifDownloadingTrack(String trackName) { + return 'Downloading $trackName'; + } + + @override + String notifFinalizingTrack(String trackName) { + return 'Finalizing $trackName'; + } + + @override + String get notifEmbeddingMetadata => 'Embedding metadata...'; + + @override + String notifAlreadyInLibraryCount(int completed, int total) { + return 'Already in Library ($completed/$total)'; + } + + @override + String get notifAlreadyInLibrary => 'Already in Library'; + + @override + String notifDownloadCompleteCount(int completed, int total) { + return 'Download Complete ($completed/$total)'; + } + + @override + String get notifDownloadComplete => 'Download Complete'; + + @override + String notifDownloadsFinished(int completed, int failed) { + return 'Downloads Finished ($completed done, $failed failed)'; + } + + @override + String get notifAllDownloadsComplete => 'All Downloads Complete'; + + @override + String notifTracksDownloadedSuccess(int count) { + return '$count tracks downloaded successfully'; + } + + @override + String get notifScanningLibrary => 'Scanning local library'; + + @override + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ) { + return '$scanned/$total files • $percentage%'; + } + + @override + String notifLibraryScanProgressNoTotal(int scanned, int percentage) { + return '$scanned files scanned • $percentage%'; + } + + @override + String get notifLibraryScanComplete => 'Library scan complete'; + + @override + String notifLibraryScanCompleteBody(int count) { + return '$count tracks indexed'; + } + + @override + String notifLibraryScanExcluded(int count) { + return '$count excluded'; + } + + @override + String notifLibraryScanErrors(int count) { + return '$count errors'; + } + + @override + String get notifLibraryScanFailed => 'Library scan failed'; + + @override + String get notifLibraryScanCancelled => 'Library scan cancelled'; + + @override + String get notifLibraryScanStopped => 'Scan stopped before completion.'; + + @override + String notifDownloadingUpdate(String version) { + return 'Downloading SpotiFLAC v$version'; + } + + @override + String notifUpdateProgress(String received, String total, int percentage) { + return '$received / $total MB • $percentage%'; + } + + @override + String get notifUpdateReady => 'Update Ready'; + + @override + String notifUpdateReadyBody(String version) { + return 'SpotiFLAC v$version downloaded. Tap to install.'; + } + + @override + String get notifUpdateFailed => 'Update Failed'; + + @override + String get notifUpdateFailedBody => + 'Could not download update. Try again later.'; } diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index 48973518..4a44ecde 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -3078,4 +3078,192 @@ class AppLocalizationsJa extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + + @override + String extensionsSearchWith(String providerName) { + return 'Search with $providerName'; + } + + @override + String get extensionsHomeFeedProvider => 'Home Feed Provider'; + + @override + String get extensionsHomeFeedDescription => + 'Choose which extension provides the home feed on the main screen'; + + @override + String get extensionsHomeFeedAuto => 'Auto'; + + @override + String get extensionsHomeFeedAutoSubtitle => + 'Automatically select the best available'; + + @override + String extensionsHomeFeedUse(String extensionName) { + return 'Use $extensionName home feed'; + } + + @override + String get extensionsNoHomeFeedExtensions => 'No extensions with home feed'; + + @override + String get sortAlphaAsc => 'A-Z'; + + @override + String get sortAlphaDesc => 'Z-A'; + + @override + String get cancelDownloadTitle => 'Cancel download?'; + + @override + String cancelDownloadContent(String trackName) { + return 'This will cancel the active download for \"$trackName\".'; + } + + @override + String get cancelDownloadKeep => 'Keep'; + + @override + String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg'; + + @override + String get metadataSaveFailedStorage => + 'Failed to write metadata back to storage'; + + @override + String snackbarFolderPickerFailed(String error) { + return 'Failed to open folder picker: $error'; + } + + @override + String get errorLoadAlbum => 'Failed to load album'; + + @override + String get errorLoadPlaylist => 'Failed to load playlist'; + + @override + String get errorLoadArtist => 'Failed to load artist'; + + @override + String get notifChannelDownloadName => 'Download Progress'; + + @override + String get notifChannelDownloadDesc => 'Shows download progress for tracks'; + + @override + String get notifChannelLibraryScanName => 'Library Scan'; + + @override + String get notifChannelLibraryScanDesc => 'Shows local library scan progress'; + + @override + String notifDownloadingTrack(String trackName) { + return 'Downloading $trackName'; + } + + @override + String notifFinalizingTrack(String trackName) { + return 'Finalizing $trackName'; + } + + @override + String get notifEmbeddingMetadata => 'Embedding metadata...'; + + @override + String notifAlreadyInLibraryCount(int completed, int total) { + return 'Already in Library ($completed/$total)'; + } + + @override + String get notifAlreadyInLibrary => 'Already in Library'; + + @override + String notifDownloadCompleteCount(int completed, int total) { + return 'Download Complete ($completed/$total)'; + } + + @override + String get notifDownloadComplete => 'Download Complete'; + + @override + String notifDownloadsFinished(int completed, int failed) { + return 'Downloads Finished ($completed done, $failed failed)'; + } + + @override + String get notifAllDownloadsComplete => 'All Downloads Complete'; + + @override + String notifTracksDownloadedSuccess(int count) { + return '$count tracks downloaded successfully'; + } + + @override + String get notifScanningLibrary => 'Scanning local library'; + + @override + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ) { + return '$scanned/$total files • $percentage%'; + } + + @override + String notifLibraryScanProgressNoTotal(int scanned, int percentage) { + return '$scanned files scanned • $percentage%'; + } + + @override + String get notifLibraryScanComplete => 'Library scan complete'; + + @override + String notifLibraryScanCompleteBody(int count) { + return '$count tracks indexed'; + } + + @override + String notifLibraryScanExcluded(int count) { + return '$count excluded'; + } + + @override + String notifLibraryScanErrors(int count) { + return '$count errors'; + } + + @override + String get notifLibraryScanFailed => 'Library scan failed'; + + @override + String get notifLibraryScanCancelled => 'Library scan cancelled'; + + @override + String get notifLibraryScanStopped => 'Scan stopped before completion.'; + + @override + String notifDownloadingUpdate(String version) { + return 'Downloading SpotiFLAC v$version'; + } + + @override + String notifUpdateProgress(String received, String total, int percentage) { + return '$received / $total MB • $percentage%'; + } + + @override + String get notifUpdateReady => 'Update Ready'; + + @override + String notifUpdateReadyBody(String version) { + return 'SpotiFLAC v$version downloaded. Tap to install.'; + } + + @override + String get notifUpdateFailed => 'Update Failed'; + + @override + String get notifUpdateFailedBody => + 'Could not download update. Try again later.'; } diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index 4bc727cf..23416c5d 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -3071,4 +3071,192 @@ class AppLocalizationsKo extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + + @override + String extensionsSearchWith(String providerName) { + return 'Search with $providerName'; + } + + @override + String get extensionsHomeFeedProvider => 'Home Feed Provider'; + + @override + String get extensionsHomeFeedDescription => + 'Choose which extension provides the home feed on the main screen'; + + @override + String get extensionsHomeFeedAuto => 'Auto'; + + @override + String get extensionsHomeFeedAutoSubtitle => + 'Automatically select the best available'; + + @override + String extensionsHomeFeedUse(String extensionName) { + return 'Use $extensionName home feed'; + } + + @override + String get extensionsNoHomeFeedExtensions => 'No extensions with home feed'; + + @override + String get sortAlphaAsc => 'A-Z'; + + @override + String get sortAlphaDesc => 'Z-A'; + + @override + String get cancelDownloadTitle => 'Cancel download?'; + + @override + String cancelDownloadContent(String trackName) { + return 'This will cancel the active download for \"$trackName\".'; + } + + @override + String get cancelDownloadKeep => 'Keep'; + + @override + String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg'; + + @override + String get metadataSaveFailedStorage => + 'Failed to write metadata back to storage'; + + @override + String snackbarFolderPickerFailed(String error) { + return 'Failed to open folder picker: $error'; + } + + @override + String get errorLoadAlbum => 'Failed to load album'; + + @override + String get errorLoadPlaylist => 'Failed to load playlist'; + + @override + String get errorLoadArtist => 'Failed to load artist'; + + @override + String get notifChannelDownloadName => 'Download Progress'; + + @override + String get notifChannelDownloadDesc => 'Shows download progress for tracks'; + + @override + String get notifChannelLibraryScanName => 'Library Scan'; + + @override + String get notifChannelLibraryScanDesc => 'Shows local library scan progress'; + + @override + String notifDownloadingTrack(String trackName) { + return 'Downloading $trackName'; + } + + @override + String notifFinalizingTrack(String trackName) { + return 'Finalizing $trackName'; + } + + @override + String get notifEmbeddingMetadata => 'Embedding metadata...'; + + @override + String notifAlreadyInLibraryCount(int completed, int total) { + return 'Already in Library ($completed/$total)'; + } + + @override + String get notifAlreadyInLibrary => 'Already in Library'; + + @override + String notifDownloadCompleteCount(int completed, int total) { + return 'Download Complete ($completed/$total)'; + } + + @override + String get notifDownloadComplete => 'Download Complete'; + + @override + String notifDownloadsFinished(int completed, int failed) { + return 'Downloads Finished ($completed done, $failed failed)'; + } + + @override + String get notifAllDownloadsComplete => 'All Downloads Complete'; + + @override + String notifTracksDownloadedSuccess(int count) { + return '$count tracks downloaded successfully'; + } + + @override + String get notifScanningLibrary => 'Scanning local library'; + + @override + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ) { + return '$scanned/$total files • $percentage%'; + } + + @override + String notifLibraryScanProgressNoTotal(int scanned, int percentage) { + return '$scanned files scanned • $percentage%'; + } + + @override + String get notifLibraryScanComplete => 'Library scan complete'; + + @override + String notifLibraryScanCompleteBody(int count) { + return '$count tracks indexed'; + } + + @override + String notifLibraryScanExcluded(int count) { + return '$count excluded'; + } + + @override + String notifLibraryScanErrors(int count) { + return '$count errors'; + } + + @override + String get notifLibraryScanFailed => 'Library scan failed'; + + @override + String get notifLibraryScanCancelled => 'Library scan cancelled'; + + @override + String get notifLibraryScanStopped => 'Scan stopped before completion.'; + + @override + String notifDownloadingUpdate(String version) { + return 'Downloading SpotiFLAC v$version'; + } + + @override + String notifUpdateProgress(String received, String total, int percentage) { + return '$received / $total MB • $percentage%'; + } + + @override + String get notifUpdateReady => 'Update Ready'; + + @override + String notifUpdateReadyBody(String version) { + return 'SpotiFLAC v$version downloaded. Tap to install.'; + } + + @override + String get notifUpdateFailed => 'Update Failed'; + + @override + String get notifUpdateFailedBody => + 'Could not download update. Try again later.'; } diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index e91bec0b..60bda8f9 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -3091,4 +3091,192 @@ class AppLocalizationsNl extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + + @override + String extensionsSearchWith(String providerName) { + return 'Search with $providerName'; + } + + @override + String get extensionsHomeFeedProvider => 'Home Feed Provider'; + + @override + String get extensionsHomeFeedDescription => + 'Choose which extension provides the home feed on the main screen'; + + @override + String get extensionsHomeFeedAuto => 'Auto'; + + @override + String get extensionsHomeFeedAutoSubtitle => + 'Automatically select the best available'; + + @override + String extensionsHomeFeedUse(String extensionName) { + return 'Use $extensionName home feed'; + } + + @override + String get extensionsNoHomeFeedExtensions => 'No extensions with home feed'; + + @override + String get sortAlphaAsc => 'A-Z'; + + @override + String get sortAlphaDesc => 'Z-A'; + + @override + String get cancelDownloadTitle => 'Cancel download?'; + + @override + String cancelDownloadContent(String trackName) { + return 'This will cancel the active download for \"$trackName\".'; + } + + @override + String get cancelDownloadKeep => 'Keep'; + + @override + String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg'; + + @override + String get metadataSaveFailedStorage => + 'Failed to write metadata back to storage'; + + @override + String snackbarFolderPickerFailed(String error) { + return 'Failed to open folder picker: $error'; + } + + @override + String get errorLoadAlbum => 'Failed to load album'; + + @override + String get errorLoadPlaylist => 'Failed to load playlist'; + + @override + String get errorLoadArtist => 'Failed to load artist'; + + @override + String get notifChannelDownloadName => 'Download Progress'; + + @override + String get notifChannelDownloadDesc => 'Shows download progress for tracks'; + + @override + String get notifChannelLibraryScanName => 'Library Scan'; + + @override + String get notifChannelLibraryScanDesc => 'Shows local library scan progress'; + + @override + String notifDownloadingTrack(String trackName) { + return 'Downloading $trackName'; + } + + @override + String notifFinalizingTrack(String trackName) { + return 'Finalizing $trackName'; + } + + @override + String get notifEmbeddingMetadata => 'Embedding metadata...'; + + @override + String notifAlreadyInLibraryCount(int completed, int total) { + return 'Already in Library ($completed/$total)'; + } + + @override + String get notifAlreadyInLibrary => 'Already in Library'; + + @override + String notifDownloadCompleteCount(int completed, int total) { + return 'Download Complete ($completed/$total)'; + } + + @override + String get notifDownloadComplete => 'Download Complete'; + + @override + String notifDownloadsFinished(int completed, int failed) { + return 'Downloads Finished ($completed done, $failed failed)'; + } + + @override + String get notifAllDownloadsComplete => 'All Downloads Complete'; + + @override + String notifTracksDownloadedSuccess(int count) { + return '$count tracks downloaded successfully'; + } + + @override + String get notifScanningLibrary => 'Scanning local library'; + + @override + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ) { + return '$scanned/$total files • $percentage%'; + } + + @override + String notifLibraryScanProgressNoTotal(int scanned, int percentage) { + return '$scanned files scanned • $percentage%'; + } + + @override + String get notifLibraryScanComplete => 'Library scan complete'; + + @override + String notifLibraryScanCompleteBody(int count) { + return '$count tracks indexed'; + } + + @override + String notifLibraryScanExcluded(int count) { + return '$count excluded'; + } + + @override + String notifLibraryScanErrors(int count) { + return '$count errors'; + } + + @override + String get notifLibraryScanFailed => 'Library scan failed'; + + @override + String get notifLibraryScanCancelled => 'Library scan cancelled'; + + @override + String get notifLibraryScanStopped => 'Scan stopped before completion.'; + + @override + String notifDownloadingUpdate(String version) { + return 'Downloading SpotiFLAC v$version'; + } + + @override + String notifUpdateProgress(String received, String total, int percentage) { + return '$received / $total MB • $percentage%'; + } + + @override + String get notifUpdateReady => 'Update Ready'; + + @override + String notifUpdateReadyBody(String version) { + return 'SpotiFLAC v$version downloaded. Tap to install.'; + } + + @override + String get notifUpdateFailed => 'Update Failed'; + + @override + String get notifUpdateFailedBody => + 'Could not download update. Try again later.'; } diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index cc49bda1..c1f42368 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -3092,6 +3092,194 @@ class AppLocalizationsPt extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + + @override + String extensionsSearchWith(String providerName) { + return 'Search with $providerName'; + } + + @override + String get extensionsHomeFeedProvider => 'Home Feed Provider'; + + @override + String get extensionsHomeFeedDescription => + 'Choose which extension provides the home feed on the main screen'; + + @override + String get extensionsHomeFeedAuto => 'Auto'; + + @override + String get extensionsHomeFeedAutoSubtitle => + 'Automatically select the best available'; + + @override + String extensionsHomeFeedUse(String extensionName) { + return 'Use $extensionName home feed'; + } + + @override + String get extensionsNoHomeFeedExtensions => 'No extensions with home feed'; + + @override + String get sortAlphaAsc => 'A-Z'; + + @override + String get sortAlphaDesc => 'Z-A'; + + @override + String get cancelDownloadTitle => 'Cancel download?'; + + @override + String cancelDownloadContent(String trackName) { + return 'This will cancel the active download for \"$trackName\".'; + } + + @override + String get cancelDownloadKeep => 'Keep'; + + @override + String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg'; + + @override + String get metadataSaveFailedStorage => + 'Failed to write metadata back to storage'; + + @override + String snackbarFolderPickerFailed(String error) { + return 'Failed to open folder picker: $error'; + } + + @override + String get errorLoadAlbum => 'Failed to load album'; + + @override + String get errorLoadPlaylist => 'Failed to load playlist'; + + @override + String get errorLoadArtist => 'Failed to load artist'; + + @override + String get notifChannelDownloadName => 'Download Progress'; + + @override + String get notifChannelDownloadDesc => 'Shows download progress for tracks'; + + @override + String get notifChannelLibraryScanName => 'Library Scan'; + + @override + String get notifChannelLibraryScanDesc => 'Shows local library scan progress'; + + @override + String notifDownloadingTrack(String trackName) { + return 'Downloading $trackName'; + } + + @override + String notifFinalizingTrack(String trackName) { + return 'Finalizing $trackName'; + } + + @override + String get notifEmbeddingMetadata => 'Embedding metadata...'; + + @override + String notifAlreadyInLibraryCount(int completed, int total) { + return 'Already in Library ($completed/$total)'; + } + + @override + String get notifAlreadyInLibrary => 'Already in Library'; + + @override + String notifDownloadCompleteCount(int completed, int total) { + return 'Download Complete ($completed/$total)'; + } + + @override + String get notifDownloadComplete => 'Download Complete'; + + @override + String notifDownloadsFinished(int completed, int failed) { + return 'Downloads Finished ($completed done, $failed failed)'; + } + + @override + String get notifAllDownloadsComplete => 'All Downloads Complete'; + + @override + String notifTracksDownloadedSuccess(int count) { + return '$count tracks downloaded successfully'; + } + + @override + String get notifScanningLibrary => 'Scanning local library'; + + @override + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ) { + return '$scanned/$total files • $percentage%'; + } + + @override + String notifLibraryScanProgressNoTotal(int scanned, int percentage) { + return '$scanned files scanned • $percentage%'; + } + + @override + String get notifLibraryScanComplete => 'Library scan complete'; + + @override + String notifLibraryScanCompleteBody(int count) { + return '$count tracks indexed'; + } + + @override + String notifLibraryScanExcluded(int count) { + return '$count excluded'; + } + + @override + String notifLibraryScanErrors(int count) { + return '$count errors'; + } + + @override + String get notifLibraryScanFailed => 'Library scan failed'; + + @override + String get notifLibraryScanCancelled => 'Library scan cancelled'; + + @override + String get notifLibraryScanStopped => 'Scan stopped before completion.'; + + @override + String notifDownloadingUpdate(String version) { + return 'Downloading SpotiFLAC v$version'; + } + + @override + String notifUpdateProgress(String received, String total, int percentage) { + return '$received / $total MB • $percentage%'; + } + + @override + String get notifUpdateReady => 'Update Ready'; + + @override + String notifUpdateReadyBody(String version) { + return 'SpotiFLAC v$version downloaded. Tap to install.'; + } + + @override + String get notifUpdateFailed => 'Update Failed'; + + @override + String get notifUpdateFailedBody => + 'Could not download update. Try again later.'; } /// The translations for Portuguese, as used in Portugal (`pt_PT`). diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index c987bfb4..d411320a 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -3151,4 +3151,192 @@ class AppLocalizationsRu extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + + @override + String extensionsSearchWith(String providerName) { + return 'Search with $providerName'; + } + + @override + String get extensionsHomeFeedProvider => 'Home Feed Provider'; + + @override + String get extensionsHomeFeedDescription => + 'Choose which extension provides the home feed on the main screen'; + + @override + String get extensionsHomeFeedAuto => 'Auto'; + + @override + String get extensionsHomeFeedAutoSubtitle => + 'Automatically select the best available'; + + @override + String extensionsHomeFeedUse(String extensionName) { + return 'Use $extensionName home feed'; + } + + @override + String get extensionsNoHomeFeedExtensions => 'No extensions with home feed'; + + @override + String get sortAlphaAsc => 'A-Z'; + + @override + String get sortAlphaDesc => 'Z-A'; + + @override + String get cancelDownloadTitle => 'Cancel download?'; + + @override + String cancelDownloadContent(String trackName) { + return 'This will cancel the active download for \"$trackName\".'; + } + + @override + String get cancelDownloadKeep => 'Keep'; + + @override + String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg'; + + @override + String get metadataSaveFailedStorage => + 'Failed to write metadata back to storage'; + + @override + String snackbarFolderPickerFailed(String error) { + return 'Failed to open folder picker: $error'; + } + + @override + String get errorLoadAlbum => 'Failed to load album'; + + @override + String get errorLoadPlaylist => 'Failed to load playlist'; + + @override + String get errorLoadArtist => 'Failed to load artist'; + + @override + String get notifChannelDownloadName => 'Download Progress'; + + @override + String get notifChannelDownloadDesc => 'Shows download progress for tracks'; + + @override + String get notifChannelLibraryScanName => 'Library Scan'; + + @override + String get notifChannelLibraryScanDesc => 'Shows local library scan progress'; + + @override + String notifDownloadingTrack(String trackName) { + return 'Downloading $trackName'; + } + + @override + String notifFinalizingTrack(String trackName) { + return 'Finalizing $trackName'; + } + + @override + String get notifEmbeddingMetadata => 'Embedding metadata...'; + + @override + String notifAlreadyInLibraryCount(int completed, int total) { + return 'Already in Library ($completed/$total)'; + } + + @override + String get notifAlreadyInLibrary => 'Already in Library'; + + @override + String notifDownloadCompleteCount(int completed, int total) { + return 'Download Complete ($completed/$total)'; + } + + @override + String get notifDownloadComplete => 'Download Complete'; + + @override + String notifDownloadsFinished(int completed, int failed) { + return 'Downloads Finished ($completed done, $failed failed)'; + } + + @override + String get notifAllDownloadsComplete => 'All Downloads Complete'; + + @override + String notifTracksDownloadedSuccess(int count) { + return '$count tracks downloaded successfully'; + } + + @override + String get notifScanningLibrary => 'Scanning local library'; + + @override + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ) { + return '$scanned/$total files • $percentage%'; + } + + @override + String notifLibraryScanProgressNoTotal(int scanned, int percentage) { + return '$scanned files scanned • $percentage%'; + } + + @override + String get notifLibraryScanComplete => 'Library scan complete'; + + @override + String notifLibraryScanCompleteBody(int count) { + return '$count tracks indexed'; + } + + @override + String notifLibraryScanExcluded(int count) { + return '$count excluded'; + } + + @override + String notifLibraryScanErrors(int count) { + return '$count errors'; + } + + @override + String get notifLibraryScanFailed => 'Library scan failed'; + + @override + String get notifLibraryScanCancelled => 'Library scan cancelled'; + + @override + String get notifLibraryScanStopped => 'Scan stopped before completion.'; + + @override + String notifDownloadingUpdate(String version) { + return 'Downloading SpotiFLAC v$version'; + } + + @override + String notifUpdateProgress(String received, String total, int percentage) { + return '$received / $total MB • $percentage%'; + } + + @override + String get notifUpdateReady => 'Update Ready'; + + @override + String notifUpdateReadyBody(String version) { + return 'SpotiFLAC v$version downloaded. Tap to install.'; + } + + @override + String get notifUpdateFailed => 'Update Failed'; + + @override + String get notifUpdateFailedBody => + 'Could not download update. Try again later.'; } diff --git a/lib/l10n/app_localizations_tr.dart b/lib/l10n/app_localizations_tr.dart index 5d3eab8e..4bc02ea7 100644 --- a/lib/l10n/app_localizations_tr.dart +++ b/lib/l10n/app_localizations_tr.dart @@ -3097,4 +3097,192 @@ class AppLocalizationsTr extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + + @override + String extensionsSearchWith(String providerName) { + return 'Search with $providerName'; + } + + @override + String get extensionsHomeFeedProvider => 'Home Feed Provider'; + + @override + String get extensionsHomeFeedDescription => + 'Choose which extension provides the home feed on the main screen'; + + @override + String get extensionsHomeFeedAuto => 'Auto'; + + @override + String get extensionsHomeFeedAutoSubtitle => + 'Automatically select the best available'; + + @override + String extensionsHomeFeedUse(String extensionName) { + return 'Use $extensionName home feed'; + } + + @override + String get extensionsNoHomeFeedExtensions => 'No extensions with home feed'; + + @override + String get sortAlphaAsc => 'A-Z'; + + @override + String get sortAlphaDesc => 'Z-A'; + + @override + String get cancelDownloadTitle => 'Cancel download?'; + + @override + String cancelDownloadContent(String trackName) { + return 'This will cancel the active download for \"$trackName\".'; + } + + @override + String get cancelDownloadKeep => 'Keep'; + + @override + String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg'; + + @override + String get metadataSaveFailedStorage => + 'Failed to write metadata back to storage'; + + @override + String snackbarFolderPickerFailed(String error) { + return 'Failed to open folder picker: $error'; + } + + @override + String get errorLoadAlbum => 'Failed to load album'; + + @override + String get errorLoadPlaylist => 'Failed to load playlist'; + + @override + String get errorLoadArtist => 'Failed to load artist'; + + @override + String get notifChannelDownloadName => 'Download Progress'; + + @override + String get notifChannelDownloadDesc => 'Shows download progress for tracks'; + + @override + String get notifChannelLibraryScanName => 'Library Scan'; + + @override + String get notifChannelLibraryScanDesc => 'Shows local library scan progress'; + + @override + String notifDownloadingTrack(String trackName) { + return 'Downloading $trackName'; + } + + @override + String notifFinalizingTrack(String trackName) { + return 'Finalizing $trackName'; + } + + @override + String get notifEmbeddingMetadata => 'Embedding metadata...'; + + @override + String notifAlreadyInLibraryCount(int completed, int total) { + return 'Already in Library ($completed/$total)'; + } + + @override + String get notifAlreadyInLibrary => 'Already in Library'; + + @override + String notifDownloadCompleteCount(int completed, int total) { + return 'Download Complete ($completed/$total)'; + } + + @override + String get notifDownloadComplete => 'Download Complete'; + + @override + String notifDownloadsFinished(int completed, int failed) { + return 'Downloads Finished ($completed done, $failed failed)'; + } + + @override + String get notifAllDownloadsComplete => 'All Downloads Complete'; + + @override + String notifTracksDownloadedSuccess(int count) { + return '$count tracks downloaded successfully'; + } + + @override + String get notifScanningLibrary => 'Scanning local library'; + + @override + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ) { + return '$scanned/$total files • $percentage%'; + } + + @override + String notifLibraryScanProgressNoTotal(int scanned, int percentage) { + return '$scanned files scanned • $percentage%'; + } + + @override + String get notifLibraryScanComplete => 'Library scan complete'; + + @override + String notifLibraryScanCompleteBody(int count) { + return '$count tracks indexed'; + } + + @override + String notifLibraryScanExcluded(int count) { + return '$count excluded'; + } + + @override + String notifLibraryScanErrors(int count) { + return '$count errors'; + } + + @override + String get notifLibraryScanFailed => 'Library scan failed'; + + @override + String get notifLibraryScanCancelled => 'Library scan cancelled'; + + @override + String get notifLibraryScanStopped => 'Scan stopped before completion.'; + + @override + String notifDownloadingUpdate(String version) { + return 'Downloading SpotiFLAC v$version'; + } + + @override + String notifUpdateProgress(String received, String total, int percentage) { + return '$received / $total MB • $percentage%'; + } + + @override + String get notifUpdateReady => 'Update Ready'; + + @override + String notifUpdateReadyBody(String version) { + return 'SpotiFLAC v$version downloaded. Tap to install.'; + } + + @override + String get notifUpdateFailed => 'Update Failed'; + + @override + String get notifUpdateFailedBody => + 'Could not download update. Try again later.'; } diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 69dd7d32..cc17d8dc 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -3092,6 +3092,194 @@ class AppLocalizationsZh extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + + @override + String extensionsSearchWith(String providerName) { + return 'Search with $providerName'; + } + + @override + String get extensionsHomeFeedProvider => 'Home Feed Provider'; + + @override + String get extensionsHomeFeedDescription => + 'Choose which extension provides the home feed on the main screen'; + + @override + String get extensionsHomeFeedAuto => 'Auto'; + + @override + String get extensionsHomeFeedAutoSubtitle => + 'Automatically select the best available'; + + @override + String extensionsHomeFeedUse(String extensionName) { + return 'Use $extensionName home feed'; + } + + @override + String get extensionsNoHomeFeedExtensions => 'No extensions with home feed'; + + @override + String get sortAlphaAsc => 'A-Z'; + + @override + String get sortAlphaDesc => 'Z-A'; + + @override + String get cancelDownloadTitle => 'Cancel download?'; + + @override + String cancelDownloadContent(String trackName) { + return 'This will cancel the active download for \"$trackName\".'; + } + + @override + String get cancelDownloadKeep => 'Keep'; + + @override + String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg'; + + @override + String get metadataSaveFailedStorage => + 'Failed to write metadata back to storage'; + + @override + String snackbarFolderPickerFailed(String error) { + return 'Failed to open folder picker: $error'; + } + + @override + String get errorLoadAlbum => 'Failed to load album'; + + @override + String get errorLoadPlaylist => 'Failed to load playlist'; + + @override + String get errorLoadArtist => 'Failed to load artist'; + + @override + String get notifChannelDownloadName => 'Download Progress'; + + @override + String get notifChannelDownloadDesc => 'Shows download progress for tracks'; + + @override + String get notifChannelLibraryScanName => 'Library Scan'; + + @override + String get notifChannelLibraryScanDesc => 'Shows local library scan progress'; + + @override + String notifDownloadingTrack(String trackName) { + return 'Downloading $trackName'; + } + + @override + String notifFinalizingTrack(String trackName) { + return 'Finalizing $trackName'; + } + + @override + String get notifEmbeddingMetadata => 'Embedding metadata...'; + + @override + String notifAlreadyInLibraryCount(int completed, int total) { + return 'Already in Library ($completed/$total)'; + } + + @override + String get notifAlreadyInLibrary => 'Already in Library'; + + @override + String notifDownloadCompleteCount(int completed, int total) { + return 'Download Complete ($completed/$total)'; + } + + @override + String get notifDownloadComplete => 'Download Complete'; + + @override + String notifDownloadsFinished(int completed, int failed) { + return 'Downloads Finished ($completed done, $failed failed)'; + } + + @override + String get notifAllDownloadsComplete => 'All Downloads Complete'; + + @override + String notifTracksDownloadedSuccess(int count) { + return '$count tracks downloaded successfully'; + } + + @override + String get notifScanningLibrary => 'Scanning local library'; + + @override + String notifLibraryScanProgressWithTotal( + int scanned, + int total, + int percentage, + ) { + return '$scanned/$total files • $percentage%'; + } + + @override + String notifLibraryScanProgressNoTotal(int scanned, int percentage) { + return '$scanned files scanned • $percentage%'; + } + + @override + String get notifLibraryScanComplete => 'Library scan complete'; + + @override + String notifLibraryScanCompleteBody(int count) { + return '$count tracks indexed'; + } + + @override + String notifLibraryScanExcluded(int count) { + return '$count excluded'; + } + + @override + String notifLibraryScanErrors(int count) { + return '$count errors'; + } + + @override + String get notifLibraryScanFailed => 'Library scan failed'; + + @override + String get notifLibraryScanCancelled => 'Library scan cancelled'; + + @override + String get notifLibraryScanStopped => 'Scan stopped before completion.'; + + @override + String notifDownloadingUpdate(String version) { + return 'Downloading SpotiFLAC v$version'; + } + + @override + String notifUpdateProgress(String received, String total, int percentage) { + return '$received / $total MB • $percentage%'; + } + + @override + String get notifUpdateReady => 'Update Ready'; + + @override + String notifUpdateReadyBody(String version) { + return 'SpotiFLAC v$version downloaded. Tap to install.'; + } + + @override + String get notifUpdateFailed => 'Update Failed'; + + @override + String get notifUpdateFailedBody => + 'Could not download update. Try again later.'; } /// The translations for Chinese, as used in China (`zh_CN`). diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 337fbfa6..8a5de0d6 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -4063,5 +4063,317 @@ "audioAnalysisSamples": "Samples", "@audioAnalysisSamples": { "description": "Total samples metric label" + }, + + "extensionsSearchWith": "Search with {providerName}", + "@extensionsSearchWith": { + "description": "Extensions page - subtitle for built-in search provider option", + "placeholders": { + "providerName": { + "type": "String" + } + } + }, + "extensionsHomeFeedProvider": "Home Feed Provider", + "@extensionsHomeFeedProvider": { + "description": "Extensions page - label for home feed provider selector" + }, + "extensionsHomeFeedDescription": "Choose which extension provides the home feed on the main screen", + "@extensionsHomeFeedDescription": { + "description": "Extensions page - description for home feed provider picker" + }, + "extensionsHomeFeedAuto": "Auto", + "@extensionsHomeFeedAuto": { + "description": "Extensions page - home feed provider option: auto" + }, + "extensionsHomeFeedAutoSubtitle": "Automatically select the best available", + "@extensionsHomeFeedAutoSubtitle": { + "description": "Extensions page - subtitle for auto home feed option" + }, + "extensionsHomeFeedUse": "Use {extensionName} home feed", + "@extensionsHomeFeedUse": { + "description": "Extensions page - subtitle for a specific extension home feed option", + "placeholders": { + "extensionName": { + "type": "String" + } + } + }, + "extensionsNoHomeFeedExtensions": "No extensions with home feed", + "@extensionsNoHomeFeedExtensions": { + "description": "Extensions page - shown when no installed extension has home feed" + }, + + "sortAlphaAsc": "A-Z", + "@sortAlphaAsc": { + "description": "Sort option - alphabetical ascending" + }, + "sortAlphaDesc": "Z-A", + "@sortAlphaDesc": { + "description": "Sort option - alphabetical descending" + }, + "cancelDownloadTitle": "Cancel download?", + "@cancelDownloadTitle": { + "description": "Dialog title when confirming cancellation of an active download" + }, + "cancelDownloadContent": "This will cancel the active download for \"{trackName}\".", + "@cancelDownloadContent": { + "description": "Dialog body when confirming cancellation of an active download", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "cancelDownloadKeep": "Keep", + "@cancelDownloadKeep": { + "description": "Dialog button - keep the active download (do not cancel)" + }, + + "metadataSaveFailedFfmpeg": "Failed to save metadata via FFmpeg", + "@metadataSaveFailedFfmpeg": { + "description": "Snackbar error when FFmpeg fails to write metadata" + }, + "metadataSaveFailedStorage": "Failed to write metadata back to storage", + "@metadataSaveFailedStorage": { + "description": "Snackbar error when writing metadata file back to storage fails" + }, + + "snackbarFolderPickerFailed": "Failed to open folder picker: {error}", + "@snackbarFolderPickerFailed": { + "description": "Snackbar shown when folder picker fails to open", + "placeholders": { + "error": { + "type": "String" + } + } + }, + + "errorLoadAlbum": "Failed to load album", + "@errorLoadAlbum": { + "description": "Error state shown when album fails to load" + }, + "errorLoadPlaylist": "Failed to load playlist", + "@errorLoadPlaylist": { + "description": "Error state shown when playlist fails to load" + }, + "errorLoadArtist": "Failed to load artist", + "@errorLoadArtist": { + "description": "Error state shown when artist fails to load" + }, + + "notifChannelDownloadName": "Download Progress", + "@notifChannelDownloadName": { + "description": "Android notification channel name for download progress" + }, + "notifChannelDownloadDesc": "Shows download progress for tracks", + "@notifChannelDownloadDesc": { + "description": "Android notification channel description for download progress" + }, + "notifChannelLibraryScanName": "Library Scan", + "@notifChannelLibraryScanName": { + "description": "Android notification channel name for library scan" + }, + "notifChannelLibraryScanDesc": "Shows local library scan progress", + "@notifChannelLibraryScanDesc": { + "description": "Android notification channel description for library scan" + }, + "notifDownloadingTrack": "Downloading {trackName}", + "@notifDownloadingTrack": { + "description": "Notification title while downloading a track", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "notifFinalizingTrack": "Finalizing {trackName}", + "@notifFinalizingTrack": { + "description": "Notification title while finalizing (embedding metadata) a track", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "notifEmbeddingMetadata": "Embedding metadata...", + "@notifEmbeddingMetadata": { + "description": "Notification body while embedding metadata into a downloaded track" + }, + "notifAlreadyInLibraryCount": "Already in Library ({completed}/{total})", + "@notifAlreadyInLibraryCount": { + "description": "Notification title when track is already in library, with count", + "placeholders": { + "completed": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "notifAlreadyInLibrary": "Already in Library", + "@notifAlreadyInLibrary": { + "description": "Notification title when track is already in library" + }, + "notifDownloadCompleteCount": "Download Complete ({completed}/{total})", + "@notifDownloadCompleteCount": { + "description": "Notification title when download is complete, with count", + "placeholders": { + "completed": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "notifDownloadComplete": "Download Complete", + "@notifDownloadComplete": { + "description": "Notification title when a single download is complete" + }, + "notifDownloadsFinished": "Downloads Finished ({completed} done, {failed} failed)", + "@notifDownloadsFinished": { + "description": "Notification title when queue finishes with some failures", + "placeholders": { + "completed": { + "type": "int" + }, + "failed": { + "type": "int" + } + } + }, + "notifAllDownloadsComplete": "All Downloads Complete", + "@notifAllDownloadsComplete": { + "description": "Notification title when all downloads finish successfully" + }, + "notifTracksDownloadedSuccess": "{count} tracks downloaded successfully", + "@notifTracksDownloadedSuccess": { + "description": "Notification body for queue complete - how many tracks were downloaded", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifScanningLibrary": "Scanning local library", + "@notifScanningLibrary": { + "description": "Notification title while scanning local library" + }, + "notifLibraryScanProgressWithTotal": "{scanned}/{total} files • {percentage}%", + "@notifLibraryScanProgressWithTotal": { + "description": "Notification body for library scan progress when total is known", + "placeholders": { + "scanned": { + "type": "int" + }, + "total": { + "type": "int" + }, + "percentage": { + "type": "int" + } + } + }, + "notifLibraryScanProgressNoTotal": "{scanned} files scanned • {percentage}%", + "@notifLibraryScanProgressNoTotal": { + "description": "Notification body for library scan progress when total is unknown", + "placeholders": { + "scanned": { + "type": "int" + }, + "percentage": { + "type": "int" + } + } + }, + "notifLibraryScanComplete": "Library scan complete", + "@notifLibraryScanComplete": { + "description": "Notification title when library scan finishes" + }, + "notifLibraryScanCompleteBody": "{count} tracks indexed", + "@notifLibraryScanCompleteBody": { + "description": "Notification body for library scan complete - number of indexed tracks", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifLibraryScanExcluded": "{count} excluded", + "@notifLibraryScanExcluded": { + "description": "Library scan complete suffix - excluded track count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifLibraryScanErrors": "{count} errors", + "@notifLibraryScanErrors": { + "description": "Library scan complete suffix - error count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifLibraryScanFailed": "Library scan failed", + "@notifLibraryScanFailed": { + "description": "Notification title when library scan fails" + }, + "notifLibraryScanCancelled": "Library scan cancelled", + "@notifLibraryScanCancelled": { + "description": "Notification title when library scan is cancelled by the user" + }, + "notifLibraryScanStopped": "Scan stopped before completion.", + "@notifLibraryScanStopped": { + "description": "Notification body when library scan is cancelled" + }, + "notifDownloadingUpdate": "Downloading SpotiFLAC v{version}", + "@notifDownloadingUpdate": { + "description": "Notification title while downloading an app update", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "notifUpdateProgress": "{received} / {total} MB • {percentage}%", + "@notifUpdateProgress": { + "description": "Notification body showing update download progress", + "placeholders": { + "received": { + "type": "String" + }, + "total": { + "type": "String" + }, + "percentage": { + "type": "int" + } + } + }, + "notifUpdateReady": "Update Ready", + "@notifUpdateReady": { + "description": "Notification title when app update download is complete" + }, + "notifUpdateReadyBody": "SpotiFLAC v{version} downloaded. Tap to install.", + "@notifUpdateReadyBody": { + "description": "Notification body when app update is ready to install", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "notifUpdateFailed": "Update Failed", + "@notifUpdateFailed": { + "description": "Notification title when app update download fails" + }, + "notifUpdateFailedBody": "Could not download update. Try again later.", + "@notifUpdateFailedBody": { + "description": "Notification body when app update download fails" } } diff --git a/lib/screens/home_tab.dart b/lib/screens/home_tab.dart index 527c1475..ca6c18ae 100644 --- a/lib/screens/home_tab.dart +++ b/lib/screens/home_tab.dart @@ -3124,7 +3124,7 @@ class _HomeTabState extends ConsumerState Padding( padding: const EdgeInsets.only(right: 8), child: FilterChip( - label: const Text('All'), + label: Text(context.l10n.historyFilterAll), selected: selectedFilter == null, onSelected: (_) { ref.read(trackProvider.notifier).setSearchFilter(null); @@ -4212,7 +4212,7 @@ class _ExtensionAlbumScreenState extends ConsumerState { if (result == null) { setState(() { - _error = 'Failed to load album'; + _error = context.l10n.errorLoadAlbum; _isLoading = false; }); return; @@ -4221,7 +4221,7 @@ class _ExtensionAlbumScreenState extends ConsumerState { final trackList = result['tracks'] as List?; if (trackList == null) { setState(() { - _error = 'No tracks found'; + _error = context.l10n.errorNoTracksFound; _isLoading = false; }); return; @@ -4243,7 +4243,7 @@ class _ExtensionAlbumScreenState extends ConsumerState { } catch (e) { if (!mounted) return; setState(() { - _error = 'Error: $e'; + _error = context.l10n.snackbarError(e.toString()); _isLoading = false; }); } @@ -4376,7 +4376,7 @@ class _ExtensionPlaylistScreenState if (result == null) { setState(() { - _error = 'Failed to load playlist'; + _error = context.l10n.errorLoadPlaylist; _isLoading = false; }); return; @@ -4385,7 +4385,7 @@ class _ExtensionPlaylistScreenState final trackList = result['tracks'] as List?; if (trackList == null) { setState(() { - _error = 'No tracks found'; + _error = context.l10n.errorNoTracksFound; _isLoading = false; }); return; @@ -4402,7 +4402,7 @@ class _ExtensionPlaylistScreenState } catch (e) { if (!mounted) return; setState(() { - _error = 'Error: $e'; + _error = context.l10n.snackbarError(e.toString()); _isLoading = false; }); } @@ -4528,7 +4528,7 @@ class _ExtensionArtistScreenState extends ConsumerState { if (result == null) { setState(() { - _error = 'Failed to load artist'; + _error = context.l10n.errorLoadArtist; _isLoading = false; }); return; @@ -4562,7 +4562,7 @@ class _ExtensionArtistScreenState extends ConsumerState { } catch (e) { if (!mounted) return; setState(() { - _error = 'Error: $e'; + _error = context.l10n.snackbarError(e.toString()); _isLoading = false; }); } diff --git a/lib/screens/main_shell.dart b/lib/screens/main_shell.dart index 91cc568e..898c5056 100644 --- a/lib/screens/main_shell.dart +++ b/lib/screens/main_shell.dart @@ -18,6 +18,7 @@ import 'package:spotiflac_android/screens/settings/settings_tab.dart'; import 'package:spotiflac_android/services/platform_bridge.dart'; import 'package:spotiflac_android/services/shell_navigation_service.dart'; import 'package:spotiflac_android/services/share_intent_service.dart'; +import 'package:spotiflac_android/services/notification_service.dart'; import 'package:spotiflac_android/services/update_checker.dart'; import 'package:spotiflac_android/widgets/update_dialog.dart'; import 'package:spotiflac_android/widgets/animation_utils.dart'; @@ -47,6 +48,12 @@ class _MainShellState extends ConsumerState final GlobalKey _repoTabNavigatorKey = ShellNavigationService.repoTabNavigatorKey; + @override + void didChangeDependencies() { + super.didChangeDependencies(); + NotificationService().updateStrings(context.l10n); + } + @override void initState() { super.initState(); diff --git a/lib/screens/queue_tab.dart b/lib/screens/queue_tab.dart index 0f0f1d33..0dbf003e 100644 --- a/lib/screens/queue_tab.dart +++ b/lib/screens/queue_tab.dart @@ -2174,13 +2174,13 @@ class _QueueTabState extends ConsumerState { ), ), FilterChip( - label: const Text('A-Z'), + label: Text(context.l10n.sortAlphaAsc), selected: tempSortMode == 'a-z', onSelected: (_) => setSheetState(() => tempSortMode = 'a-z'), ), FilterChip( - label: const Text('Z-A'), + label: Text(context.l10n.sortAlphaDesc), selected: tempSortMode == 'z-a', onSelected: (_) => setSheetState(() => tempSortMode = 'z-a'), @@ -5461,18 +5461,18 @@ class _QueueTabState extends ConsumerState { return await showDialog( context: context, builder: (ctx) => AlertDialog( - title: const Text('Cancel download?'), + title: Text(context.l10n.cancelDownloadTitle), content: Text( - 'This will cancel the active download for "${item.track.name}".', + context.l10n.cancelDownloadContent(item.track.name), ), actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop(false), - child: const Text('Keep'), + child: Text(context.l10n.cancelDownloadKeep), ), TextButton( onPressed: () => Navigator.of(ctx).pop(true), - child: const Text('Cancel'), + child: Text(context.l10n.dialogCancel), ), ], ), diff --git a/lib/screens/settings/download_settings_page.dart b/lib/screens/settings/download_settings_page.dart index 39b116e6..a330ac51 100644 --- a/lib/screens/settings/download_settings_page.dart +++ b/lib/screens/settings/download_settings_page.dart @@ -1359,7 +1359,9 @@ class _DownloadSettingsPageState extends ConsumerState { if (ctx.mounted) { ScaffoldMessenger.of(ctx).showSnackBar( SnackBar( - content: Text('Failed to open folder picker: $e'), + content: Text( + ctx.l10n.snackbarFolderPickerFailed(e.toString()), + ), backgroundColor: Theme.of(ctx).colorScheme.error, duration: const Duration(seconds: 4), ), diff --git a/lib/screens/settings/extensions_page.dart b/lib/screens/settings/extensions_page.dart index 9fec23ee..b782954f 100644 --- a/lib/screens/settings/extensions_page.dart +++ b/lib/screens/settings/extensions_page.dart @@ -735,7 +735,7 @@ class _SearchProviderSelector extends ConsumerWidget { (entry) => ListTile( leading: Icon(Icons.search, color: colorScheme.tertiary), title: Text(entry.value), - subtitle: Text('Search with ${entry.value}'), + subtitle: Text(ctx.l10n.extensionsSearchWith(entry.value)), trailing: settings.searchProvider == entry.key ? Icon(Icons.check_circle, color: colorScheme.primary) : Icon(Icons.circle_outlined, color: colorScheme.outline), @@ -791,7 +791,7 @@ class _HomeFeedProviderSelector extends ConsumerWidget { final hasAnyProvider = homeFeedProviders.isNotEmpty; - String currentProviderName = 'Auto'; + String currentProviderName = context.l10n.extensionsHomeFeedAuto; if (settings.homeFeedProvider != null && settings.homeFeedProvider!.isNotEmpty) { final ext = homeFeedProviders @@ -828,7 +828,7 @@ class _HomeFeedProviderSelector extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Home Feed Provider', + context.l10n.extensionsHomeFeedProvider, style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: !hasAnyProvider ? colorScheme.outline : null, ), @@ -836,7 +836,7 @@ class _HomeFeedProviderSelector extends ConsumerWidget { const SizedBox(height: 2), Text( !hasAnyProvider - ? 'No extensions with home feed' + ? context.l10n.extensionsNoHomeFeedExtensions : currentProviderName, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: colorScheme.onSurfaceVariant, @@ -883,7 +883,7 @@ class _HomeFeedProviderSelector extends ConsumerWidget { Padding( padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), child: Text( - 'Home Feed Provider', + ctx.l10n.extensionsHomeFeedProvider, style: Theme.of( context, ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), @@ -892,7 +892,7 @@ class _HomeFeedProviderSelector extends ConsumerWidget { Padding( padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), child: Text( - 'Choose which extension provides the home feed on the main screen', + ctx.l10n.extensionsHomeFeedDescription, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: colorScheme.onSurfaceVariant, ), @@ -900,8 +900,8 @@ class _HomeFeedProviderSelector extends ConsumerWidget { ), ListTile( leading: Icon(Icons.auto_awesome, color: colorScheme.primary), - title: const Text('Auto'), - subtitle: const Text('Automatically select the best available'), + title: Text(ctx.l10n.extensionsHomeFeedAuto), + subtitle: Text(ctx.l10n.extensionsHomeFeedAutoSubtitle), trailing: (settings.homeFeedProvider == null || settings.homeFeedProvider!.isEmpty) @@ -917,7 +917,9 @@ class _HomeFeedProviderSelector extends ConsumerWidget { (ext) => ListTile( leading: Icon(Icons.extension, color: colorScheme.secondary), title: Text(ext.displayName), - subtitle: Text('Use ${ext.displayName} home feed'), + subtitle: Text( + ctx.l10n.extensionsHomeFeedUse(ext.displayName), + ), trailing: settings.homeFeedProvider == ext.id ? Icon(Icons.check_circle, color: colorScheme.primary) : Icon(Icons.circle_outlined, color: colorScheme.outline), diff --git a/lib/screens/settings/options_settings_page.dart b/lib/screens/settings/options_settings_page.dart index 81f81f53..6484424e 100644 --- a/lib/screens/settings/options_settings_page.dart +++ b/lib/screens/settings/options_settings_page.dart @@ -307,9 +307,9 @@ class OptionsSettingsPage extends ConsumerWidget { } catch (e) { if (context.mounted) { Navigator.pop(context); // Close loading dialog - ScaffoldMessenger.of( - context, - ).showSnackBar(SnackBar(content: Text('Error: $e'))); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(context.l10n.snackbarError(e.toString()))), + ); } } } diff --git a/lib/screens/setup_screen.dart b/lib/screens/setup_screen.dart index 9a9ddd9f..444c7c30 100644 --- a/lib/screens/setup_screen.dart +++ b/lib/screens/setup_screen.dart @@ -339,7 +339,9 @@ class _SetupScreenState extends ConsumerState { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Failed to open folder picker: $e'), + content: Text( + context.l10n.snackbarFolderPickerFailed(e.toString()), + ), backgroundColor: Theme.of(context).colorScheme.error, duration: const Duration(seconds: 4), ), @@ -430,9 +432,9 @@ class _SetupScreenState extends ConsumerState { if (mounted) context.go('/tutorial'); } catch (e) { if (mounted) { - ScaffoldMessenger.of( - context, - ).showSnackBar(SnackBar(content: Text('Error: $e'))); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(context.l10n.snackbarError(e.toString()))), + ); } } finally { setState(() => _isLoading = false); diff --git a/lib/screens/track_metadata_screen.dart b/lib/screens/track_metadata_screen.dart index 0e3538a8..55c7316c 100644 --- a/lib/screens/track_metadata_screen.dart +++ b/lib/screens/track_metadata_screen.dart @@ -5025,9 +5025,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> { if (ffmpegResult == null) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Failed to save metadata via FFmpeg'), - ), + SnackBar(content: Text(context.l10n.metadataSaveFailedFfmpeg)), ); } setState(() => _saving = false); @@ -5038,9 +5036,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> { final ok = await PlatformBridge.writeTempToSaf(ffmpegResult, safUri); if (!ok && mounted) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Failed to write metadata back to storage'), - ), + SnackBar(content: Text(context.l10n.metadataSaveFailedStorage)), ); setState(() => _saving = false); return; @@ -5094,7 +5090,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> { children: [ Expanded( child: Text( - 'Edit Metadata', + context.l10n.trackEditMetadata, style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), @@ -5107,7 +5103,10 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> { child: CircularProgressIndicator(strokeWidth: 2), ) else - FilledButton(onPressed: _save, child: const Text('Save')), + FilledButton( + onPressed: _save, + child: Text(context.l10n.dialogSave), + ), ], ), ), diff --git a/lib/services/notification_service.dart b/lib/services/notification_service.dart index 76067461..4ac3c392 100644 --- a/lib/services/notification_service.dart +++ b/lib/services/notification_service.dart @@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:spotiflac_android/l10n/app_localizations.dart'; class NotificationService { static final NotificationService _instance = NotificationService._internal(); @@ -13,6 +14,13 @@ class NotificationService { FlutterLocalNotificationsPlugin(); bool _isInitialized = false; bool _notificationPermissionRequested = false; + AppLocalizations? _l10n; + + /// Call this from the widget tree (e.g. didChangeDependencies) whenever the + /// app locale changes so that notification strings stay in sync. + void updateStrings(AppLocalizations l10n) { + _l10n = l10n; + } static const int downloadProgressId = 1; static const int updateDownloadId = 2; @@ -165,7 +173,8 @@ class NotificationService { await _showSafely( id: downloadProgressId, - title: 'Downloading $trackName', + title: + _l10n?.notifDownloadingTrack(trackName) ?? 'Downloading $trackName', body: '$artistName • $percentage%', details: details, ); @@ -208,8 +217,9 @@ class NotificationService { await _showSafely( id: downloadProgressId, - title: 'Finalizing $trackName', - body: '$artistName • Embedding metadata...', + title: _l10n?.notifFinalizingTrack(trackName) ?? 'Finalizing $trackName', + body: + '$artistName • ${_l10n?.notifEmbeddingMetadata ?? 'Embedding metadata...'}', details: details, ); } @@ -226,12 +236,14 @@ class NotificationService { String title; if (alreadyInLibrary) { title = completedCount != null && totalCount != null - ? 'Already in Library ($completedCount/$totalCount)' - : 'Already in Library'; + ? (_l10n?.notifAlreadyInLibraryCount(completedCount, totalCount) ?? + 'Already in Library ($completedCount/$totalCount)') + : (_l10n?.notifAlreadyInLibrary ?? 'Already in Library'); } else { title = completedCount != null && totalCount != null - ? 'Download Complete ($completedCount/$totalCount)' - : 'Download Complete'; + ? (_l10n?.notifDownloadCompleteCount(completedCount, totalCount) ?? + 'Download Complete ($completedCount/$totalCount)') + : (_l10n?.notifDownloadComplete ?? 'Download Complete'); } const androidDetails = AndroidNotificationDetails( @@ -271,8 +283,9 @@ class NotificationService { if (!_isInitialized) await initialize(); final title = failedCount > 0 - ? 'Downloads Finished ($completedCount done, $failedCount failed)' - : 'All Downloads Complete'; + ? (_l10n?.notifDownloadsFinished(completedCount, failedCount) ?? + 'Downloads Finished ($completedCount done, $failedCount failed)') + : (_l10n?.notifAllDownloadsComplete ?? 'All Downloads Complete'); const androidDetails = AndroidNotificationDetails( channelId, @@ -299,7 +312,9 @@ class NotificationService { await _showSafely( id: downloadProgressId, title: title, - body: '$completedCount tracks downloaded successfully', + body: + _l10n?.notifTracksDownloadedSuccess(completedCount) ?? + '$completedCount tracks downloaded successfully', details: details, ); } @@ -319,8 +334,14 @@ class NotificationService { final clampedProgress = progress.clamp(0.0, 100.0); final percentage = clampedProgress.round(); final progressBody = totalFiles > 0 - ? '$scannedFiles/$totalFiles files • $percentage%' - : '$scannedFiles files scanned • $percentage%'; + ? (_l10n?.notifLibraryScanProgressWithTotal( + scannedFiles, + totalFiles, + percentage, + ) ?? + '$scannedFiles/$totalFiles files • $percentage%') + : (_l10n?.notifLibraryScanProgressNoTotal(scannedFiles, percentage) ?? + '$scannedFiles files scanned • $percentage%'); final body = (currentFile != null && currentFile.isNotEmpty) ? '$progressBody\n$currentFile' : progressBody; @@ -355,7 +376,7 @@ class NotificationService { await _showSafely( id: libraryScanId, - title: 'Scanning local library', + title: _l10n?.notifScanningLibrary ?? 'Scanning local library', body: body, details: details, ); @@ -370,10 +391,15 @@ class NotificationService { final extras = []; if (excludedDownloadedCount > 0) { - extras.add('$excludedDownloadedCount excluded'); + extras.add( + _l10n?.notifLibraryScanExcluded(excludedDownloadedCount) ?? + '$excludedDownloadedCount excluded', + ); } if (errorCount > 0) { - extras.add('$errorCount errors'); + extras.add( + _l10n?.notifLibraryScanErrors(errorCount) ?? '$errorCount errors', + ); } final suffix = extras.isEmpty ? '' : ' (${extras.join(', ')})'; @@ -401,8 +427,9 @@ class NotificationService { await _showSafely( id: libraryScanId, - title: 'Library scan complete', - body: '$totalTracks tracks indexed$suffix', + title: _l10n?.notifLibraryScanComplete ?? 'Library scan complete', + body: + '${_l10n?.notifLibraryScanCompleteBody(totalTracks) ?? '$totalTracks tracks indexed'}$suffix', details: details, ); } @@ -434,7 +461,7 @@ class NotificationService { await _showSafely( id: libraryScanId, - title: 'Library scan failed', + title: _l10n?.notifLibraryScanFailed ?? 'Library scan failed', body: message, details: details, ); @@ -467,8 +494,8 @@ class NotificationService { await _showSafely( id: libraryScanId, - title: 'Library scan cancelled', - body: 'Scan stopped before completion.', + title: _l10n?.notifLibraryScanCancelled ?? 'Library scan cancelled', + body: _l10n?.notifLibraryScanStopped ?? 'Scan stopped before completion.', details: details, ); } @@ -518,8 +545,12 @@ class NotificationService { await _showSafely( id: updateDownloadId, - title: 'Downloading SpotiFLAC v$version', - body: '$receivedMB / $totalMB MB • $percentage%', + title: + _l10n?.notifDownloadingUpdate(version) ?? + 'Downloading SpotiFLAC v$version', + body: + _l10n?.notifUpdateProgress(receivedMB, totalMB, percentage) ?? + '$receivedMB / $totalMB MB • $percentage%', details: details, ); } @@ -551,8 +582,10 @@ class NotificationService { await _showSafely( id: updateDownloadId, - title: 'Update Ready', - body: 'SpotiFLAC v$version downloaded. Tap to install.', + title: _l10n?.notifUpdateReady ?? 'Update Ready', + body: + _l10n?.notifUpdateReadyBody(version) ?? + 'SpotiFLAC v$version downloaded. Tap to install.', details: details, ); } @@ -583,8 +616,10 @@ class NotificationService { await _showSafely( id: updateDownloadId, - title: 'Update Failed', - body: 'Could not download update. Try again later.', + title: _l10n?.notifUpdateFailed ?? 'Update Failed', + body: + _l10n?.notifUpdateFailedBody ?? + 'Could not download update. Try again later.', details: details, ); }