Merge remote-tracking branch 'spotiflacapp/main'

# Conflicts:
#	android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png
#	android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png
#	android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png
#	android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png
#	android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png
#	android/app/src/main/res/mipmap-hdpi/ic_launcher.png
#	android/app/src/main/res/mipmap-mdpi/ic_launcher.png
#	android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
#	android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
#	android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
#	crowdin.yml
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
#	ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
#	lib/l10n/app_localizations_de.dart
#	lib/l10n/app_localizations_fr.dart
#	lib/l10n/app_localizations_id.dart
#	lib/l10n/app_localizations_tr.dart
#	lib/l10n/arb/app_en.arb
#	lib/l10n/arb/app_id.arb
#	lib/l10n/arb/app_ja.arb
#	lib/l10n/arb/app_tr.arb
#	lib/screens/settings/download_settings_page.dart
#	lib/screens/settings/options_settings_page.dart
This commit is contained in:
zarzet
2026-05-03 01:00:08 +07:00
78 changed files with 44993 additions and 5312 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 828 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

+1 -1
View File
@@ -12,7 +12,7 @@
"downloadURL": "https://github.com/zarzet/SpotiFLAC-Mobile/releases/download/v4.3.1/SpotiFLAC-v4.3.1-ios-unsigned.ipa",
"localizedDescription": "Mobile version of SpotiFLAC written in Flutter. Download Tracks in true FLAC from Tidal, Qobuz, & Amazon Music.",
"iconURL": "https://raw.githubusercontent.com/zarzet/SpotiFLAC-Mobile/main/assets/images/logo.png",
"size": 34773598
"size": 34773644
}
]
}
+1
View File
@@ -17,6 +17,7 @@ files:
pt-PT: pt_PT
ru: ru
tr: tr
uk: uk
zh: zh
# Full codes for Chinese variants
zh-CN: zh_CN
Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 872 B

After

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 648 B

After

Width:  |  Height:  |  Size: 783 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 872 B

After

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

+229 -96
View File
@@ -17,6 +17,7 @@ import 'app_localizations_nl.dart';
import 'app_localizations_pt.dart';
import 'app_localizations_ru.dart';
import 'app_localizations_tr.dart';
import 'app_localizations_uk.dart';
import 'app_localizations_zh.dart';
// ignore_for_file: type=lint
@@ -119,6 +120,7 @@ abstract class AppLocalizations {
Locale('pt', 'PT'),
Locale('ru'),
Locale('tr'),
Locale('uk'),
Locale('zh'),
Locale('zh', 'CN'),
Locale('zh', 'TW'),
@@ -355,7 +357,7 @@ abstract class AppLocalizations {
/// Subtitle for primary provider
///
/// In en, this message translates to:
/// **'Service used when searching by track name.'**
/// **'Service used for searching by track or album name'**
String get optionsPrimaryProviderSubtitle;
/// Shows active extension name
@@ -376,10 +378,10 @@ abstract class AppLocalizations {
/// **'Choose which tab opens first for new search results.'**
String get optionsDefaultSearchTabSubtitle;
/// Hint to switch away from the current provider selection
/// Hint to switch back to built-in providers
///
/// In en, this message translates to:
/// **'Choose another provider below to stop using the current extension'**
/// **'Tap Deezer or Spotify to switch back from extension'**
String get optionsSwitchBack;
/// Auto-retry with other services
@@ -406,10 +408,10 @@ abstract class AppLocalizations {
/// **'Extensions will be tried first'**
String get optionsUseExtensionProvidersOn;
/// Status when extension providers are disabled
/// Status when extension providers disabled
///
/// In en, this message translates to:
/// **'Extensions are turned off'**
/// **'Using built-in providers only'**
String get optionsUseExtensionProvidersOff;
/// Embed lyrics in audio files
@@ -421,7 +423,7 @@ abstract class AppLocalizations {
/// Subtitle for embed lyrics
///
/// In en, this message translates to:
/// **'Embed synced lyrics into FLAC files'**
/// **'Save synced lyrics alongside your downloaded tracks'**
String get optionsEmbedLyricsSubtitle;
/// Download highest quality album art
@@ -1795,7 +1797,7 @@ abstract class AppLocalizations {
/// Section description for extension fallback selection
///
/// In en, this message translates to:
/// **'Choose which installed download extensions can be used during automatic fallback.'**
/// **'Choose which installed download extensions can be used during automatic fallback. Built-in providers still follow the priority order above.'**
String get providerPriorityFallbackExtensionsDescription;
/// Hint below the extension fallback selection list
@@ -1804,10 +1806,10 @@ abstract class AppLocalizations {
/// **'Only enabled extensions with download-provider capability are listed here.'**
String get providerPriorityFallbackExtensionsHint;
/// Label for legacy providers kept for compatibility
/// Label for built-in providers (Tidal/Qobuz)
///
/// In en, this message translates to:
/// **'Legacy'**
/// **'Built-in'**
String get providerBuiltIn;
/// Label for extension-provided providers
@@ -2125,13 +2127,13 @@ abstract class AppLocalizations {
/// Download settings description
///
/// In en, this message translates to:
/// **'Service, quality, filename format'**
/// **'Service, quality, fallback'**
String get settingsDownloadSubtitle;
/// Options settings description
///
/// In en, this message translates to:
/// **'Fallback, lyrics, cover art, updates'**
/// **'Fallback, metadata, lyrics, cover art'**
String get settingsOptionsSubtitle;
/// Extensions settings description
@@ -4803,22 +4805,22 @@ abstract class AppLocalizations {
/// **'{count} downloaded'**
String downloadedAlbumDownloadedCount(int count);
/// Subtitle when Album Artist is used for folder naming
/// Subtitle when album artist is used for folder names
///
/// In en, this message translates to:
/// **'Artist folders use Album Artist when available'**
/// **'Folder named after Album Artist tag'**
String get downloadUseAlbumArtistForFoldersAlbumSubtitle;
/// Subtitle when Track Artist is used for folder naming
/// Subtitle when track artist is used for folder names
///
/// In en, this message translates to:
/// **'Artist folders use Track Artist only'**
/// **'Folder named after Track Artist tag'**
String get downloadUseAlbumArtistForFoldersTrackSubtitle;
/// Title for the lyrics provider priority page
/// Settings item title for lyrics provider order
///
/// In en, this message translates to:
/// **'Lyrics Providers'**
/// **'Lyrics Provider Priority'**
String get lyricsProvidersTitle;
/// Description on the lyrics provider priority page
@@ -4923,16 +4925,16 @@ abstract class AppLocalizations {
/// **'Download folder updated to SAF mode'**
String get safMigrationSuccess;
/// Settings menu item - donate
/// Settings menu item - donate page
///
/// In en, this message translates to:
/// **'Donate'**
/// **'Support Development'**
String get settingsDonate;
/// Subtitle for donate menu item
///
/// In en, this message translates to:
/// **'Support SpotiFLAC-Mobile development'**
/// **'Buy the developer a coffee'**
String get settingsDonateSubtitle;
/// Tooltip for the Love All button on album/playlist screens
@@ -5031,41 +5033,49 @@ abstract class AppLocalizations {
/// **'No tracks found for this album'**
String get noTracksFoundForAlbum;
/// Subtitle text in Android download location bottom sheet
/// Subtitle shown in the download location picker sheet
///
/// In en, this message translates to:
/// **'Choose storage mode for downloaded files.'**
/// **'Choose where to save your downloaded tracks'**
String get downloadLocationSubtitle;
/// Storage mode option - use legacy app folder
/// Storage mode option - app-managed folder
///
/// In en, this message translates to:
/// **'App folder (non-SAF)'**
/// **'App Folder (Recommended)'**
String get storageModeAppFolder;
/// Subtitle for app folder storage mode
///
/// In en, this message translates to:
/// **'Use default Music/SpotiFLAC path'**
/// **'Saves to Music/SpotiFLAC by default'**
String get storageModeAppFolderSubtitle;
/// Storage mode option - use Android SAF picker
/// Storage mode option - Storage Access Framework
///
/// In en, this message translates to:
/// **'SAF folder'**
/// **'Custom Folder (SAF)'**
String get storageModeSaf;
/// Subtitle for SAF storage mode
///
/// In en, this message translates to:
/// **'Pick folder via Android Storage Access Framework'**
/// **'Pick any folder, including SD card'**
String get storageModeSafSubtitle;
/// Description text in filename format bottom sheet
/// Description shown in filename format editor
///
/// In en, this message translates to:
/// **'Customize how your files are named.'**
String get downloadFilenameDescription;
/// **'Use {artist}, {title}, {album}, {track}, {year}, {date}, {disc} as placeholders.'**
String downloadFilenameDescription(
Object album,
Object artist,
Object date,
Object disc,
Object title,
Object track,
Object year,
);
/// Label above filename tag chips
///
@@ -5073,16 +5083,16 @@ abstract class AppLocalizations {
/// **'Tap to insert tag:'**
String get downloadFilenameInsertTag;
/// Subtitle when separate singles folder is enabled
/// Subtitle when separate singles folder is on
///
/// In en, this message translates to:
/// **'Albums/ and Singles/ folders'**
/// **'Singles and EPs saved in a separate folder'**
String get downloadSeparateSinglesEnabled;
/// Subtitle when separate singles folder is disabled
/// Subtitle when separate singles folder is off
///
/// In en, this message translates to:
/// **'All files in same structure'**
/// **'Singles and albums saved in the same folder'**
String get downloadSeparateSinglesDisabled;
/// Setting title for artist folder filter options
@@ -5091,202 +5101,202 @@ abstract class AppLocalizations {
/// **'Artist Name Filters'**
String get downloadArtistNameFilters;
/// Setting title for adding a playlist folder prefix before the normal organization structure
/// Setting to create a subfolder per playlist source
///
/// In en, this message translates to:
/// **'Create playlist source folder'**
/// **'Playlist Source Folder'**
String get downloadCreatePlaylistSourceFolder;
/// Subtitle when playlist source folder prefix is enabled
/// Subtitle when playlist folder is enabled
///
/// In en, this message translates to:
/// **'Playlist downloads use Playlist/ plus your normal folder structure.'**
/// **'A subfolder is created for each playlist'**
String get downloadCreatePlaylistSourceFolderEnabled;
/// Subtitle when playlist source folder prefix is disabled
/// Subtitle when playlist folder is disabled
///
/// In en, this message translates to:
/// **'Playlist downloads use the normal folder structure only.'**
/// **'All tracks saved directly to download folder'**
String get downloadCreatePlaylistSourceFolderDisabled;
/// Subtitle when playlist folder prefix setting is redundant because folder organization is already by playlist
/// Subtitle when folder organization is already set to playlist
///
/// In en, this message translates to:
/// **'By Playlist already places downloads inside a playlist folder.'**
/// **'Handled by folder organization setting'**
String get downloadCreatePlaylistSourceFolderRedundant;
/// Setting title for SongLink country region
/// Setting for SongLink region used during fallback resolution
///
/// In en, this message translates to:
/// **'SongLink Region'**
String get downloadSongLinkRegion;
/// Setting title for network compatibility toggle
/// Setting for legacy TLS/network handling
///
/// In en, this message translates to:
/// **'Network compatibility mode'**
/// **'Network Compatibility Mode'**
String get downloadNetworkCompatibilityMode;
/// Subtitle when network compatibility mode is enabled
/// Subtitle when network compatibility mode is on
///
/// In en, this message translates to:
/// **'Enabled: try HTTP + accept invalid TLS certificates (unsafe)'**
/// **'Using legacy TLS settings for older networks'**
String get downloadNetworkCompatibilityModeEnabled;
/// Subtitle when network compatibility mode is disabled
/// Subtitle when network compatibility mode is off
///
/// In en, this message translates to:
/// **'Off: strict HTTPS certificate validation (recommended)'**
/// **'Using standard network settings'**
String get downloadNetworkCompatibilityModeDisabled;
/// Hint shown instead of Ask-quality subtitle when the selected provider does not expose built-in quality controls
/// Subtitle when quality picker is disabled due to extension service
///
/// In en, this message translates to:
/// **'Select a compatible download provider to enable quality options'**
/// **'Select Tidal or Qobuz to enable this option'**
String get downloadSelectServiceToEnable;
/// Info hint when the selected provider does not expose built-in quality controls
/// Info shown when a non-built-in service is selected
///
/// In en, this message translates to:
/// **'Select a compatible provider above to configure quality'**
/// **'Select Tidal or Qobuz to choose audio quality'**
String get downloadSelectTidalQobuz;
/// Subtitle for Embed Lyrics when Embed Metadata is disabled
/// Subtitle when lyrics embedding is blocked by metadata toggle
///
/// In en, this message translates to:
/// **'Disabled while Embed Metadata is turned off'**
/// **'Enable metadata embedding first'**
String get downloadEmbedLyricsDisabled;
/// Toggle title for including Netease translated lyrics
/// Setting to include translated lyrics from Netease
///
/// In en, this message translates to:
/// **'Netease: Include Translation'**
String get downloadNeteaseIncludeTranslation;
/// Subtitle when Netease translation is enabled
/// Subtitle when Netease translation is on
///
/// In en, this message translates to:
/// **'Append translated lyrics when available'**
/// **'Chinese translation lines included'**
String get downloadNeteaseIncludeTranslationEnabled;
/// Subtitle when Netease translation is disabled
/// Subtitle when Netease translation is off
///
/// In en, this message translates to:
/// **'Use original lyrics only'**
/// **'Original lyrics only'**
String get downloadNeteaseIncludeTranslationDisabled;
/// Toggle title for including Netease romanized lyrics
/// Setting to include romanized lyrics from Netease
///
/// In en, this message translates to:
/// **'Netease: Include Romanization'**
String get downloadNeteaseIncludeRomanization;
/// Subtitle when Netease romanization is enabled
/// Subtitle when Netease romanization is on
///
/// In en, this message translates to:
/// **'Append romanized lyrics when available'**
/// **'Romanization lines included'**
String get downloadNeteaseIncludeRomanizationEnabled;
/// Subtitle when Netease romanization is disabled
/// Subtitle when Netease romanization is off
///
/// In en, this message translates to:
/// **'Disabled'**
/// **'No romanization'**
String get downloadNeteaseIncludeRomanizationDisabled;
/// Toggle title for Apple/QQ multi-person word-by-word lyrics
/// Setting for word-by-word multi-person lyrics from Apple Music and QQ Music
///
/// In en, this message translates to:
/// **'Apple/QQ Multi-Person Word-by-Word'**
/// **'Apple / QQ: Multi-Person Lyrics'**
String get downloadAppleQqMultiPerson;
/// Subtitle when multi-person word-by-word is enabled
/// Subtitle when multi-person lyrics is on
///
/// In en, this message translates to:
/// **'Enable v1/v2 speaker and [bg:] tags'**
/// **'Speaker labels included for duets and group tracks'**
String get downloadAppleQqMultiPersonEnabled;
/// Subtitle when multi-person word-by-word is disabled
/// Subtitle when multi-person lyrics is off
///
/// In en, this message translates to:
/// **'Simplified word-by-word formatting'**
/// **'Standard lyrics without speaker labels'**
String get downloadAppleQqMultiPersonDisabled;
/// Setting title for Musixmatch language preference
/// Setting for Musixmatch lyrics translation language
///
/// In en, this message translates to:
/// **'Musixmatch Language'**
String get downloadMusixmatchLanguage;
/// Option label when Musixmatch uses original language
/// Subtitle when no language is set
///
/// In en, this message translates to:
/// **'Auto (original)'**
/// **'Auto (original language)'**
String get downloadMusixmatchLanguageAuto;
/// Toggle title for filtering contributing artists in Album Artist metadata
/// Setting to strip contributing artists from Album Artist folder name
///
/// In en, this message translates to:
/// **'Filter contributing artists in Album Artist'**
/// **'Filter Contributing Artists'**
String get downloadFilterContributing;
/// Subtitle when contributing artist filter is enabled
/// Subtitle when contributing artist filter is on
///
/// In en, this message translates to:
/// **'Album Artist metadata uses primary artist only'**
/// **'Contributing artists removed from Album Artist folder name'**
String get downloadFilterContributingEnabled;
/// Subtitle when contributing artist filter is disabled
/// Subtitle when contributing artist filter is off
///
/// In en, this message translates to:
/// **'Keep full Album Artist metadata value'**
/// **'Full Album Artist string used'**
String get downloadFilterContributingDisabled;
/// Subtitle for lyrics providers setting when no providers are enabled
/// Shown when no lyrics providers are active
///
/// In en, this message translates to:
/// **'None enabled'**
/// **'No providers enabled'**
String get downloadProvidersNoneEnabled;
/// Label for the Musixmatch language code text field
/// Label for Musixmatch language input field
///
/// In en, this message translates to:
/// **'Language code'**
String get downloadMusixmatchLanguageCode;
/// Hint text for the Musixmatch language code field
/// Placeholder for Musixmatch language input
///
/// In en, this message translates to:
/// **'auto / en / es / ja'**
/// **'e.g. en, de, ja'**
String get downloadMusixmatchLanguageHint;
/// Description in the Musixmatch language picker
/// Description in Musixmatch language picker
///
/// In en, this message translates to:
/// **'Set preferred language code (example: en, es, ja). Leave empty for auto.'**
/// **'Enter a BCP-47 language code (e.g. en, de, ja) to request translated lyrics from Musixmatch.'**
String get downloadMusixmatchLanguageDesc;
/// Button to reset Musixmatch language to automatic
/// Button to clear Musixmatch language (use auto)
///
/// In en, this message translates to:
/// **'Auto'**
String get downloadMusixmatchAuto;
/// Subtitle for 'Any' network mode option
/// Subtitle for any-network option in picker
///
/// In en, this message translates to:
/// **'WiFi + Mobile Data'**
/// **'Use WiFi or mobile data'**
String get downloadNetworkAnySubtitle;
/// Subtitle for 'WiFi only' network mode option
/// Subtitle for WiFi-only option in picker
///
/// In en, this message translates to:
/// **'Pause downloads on mobile data'**
/// **'Downloads pause when on mobile data'**
String get downloadNetworkWifiOnlySubtitle;
/// Description in the SongLink region picker
/// Description in SongLink region picker
///
/// In en, this message translates to:
/// **'Used as userCountry for SongLink API lookup.'**
/// **'Region used when resolving track links via SongLink. Choose the country where your streaming services are available.'**
String get downloadSongLinkRegionDesc;
/// Snackbar when the audio format is not supported for the requested operation
@@ -5637,7 +5647,7 @@ abstract class AppLocalizations {
/// **'Choose which extension provides the home feed on the main screen'**
String get extensionsHomeFeedDescription;
/// Extensions page - home feed provider option: auto
/// Label for auto-selected search provider
///
/// In en, this message translates to:
/// **'Auto'**
@@ -6280,6 +6290,126 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Open album {albumName}'**
String a11yOpenAlbum(String albumName);
/// Default search tab option - Albums tab
///
/// In en, this message translates to:
/// **'Albums'**
String get optionsDefaultSearchTabAlbums;
/// Default search tab option - Tracks tab
///
/// In en, this message translates to:
/// **'Tracks'**
String get optionsDefaultSearchTabTracks;
/// Settings menu item - file and folder settings
///
/// In en, this message translates to:
/// **'Files & Folders'**
String get settingsFiles;
/// Subtitle for files & folders settings
///
/// In en, this message translates to:
/// **'Download location, filename, folder structure'**
String get settingsFilesSubtitle;
/// Settings menu item - metadata settings
///
/// In en, this message translates to:
/// **'Metadata'**
String get settingsMetadata;
/// Subtitle for metadata settings
///
/// In en, this message translates to:
/// **'Cover art, tags, ReplayGain, providers'**
String get settingsMetadataSubtitle;
/// Settings menu item - lyrics settings
///
/// In en, this message translates to:
/// **'Lyrics'**
String get settingsLyrics;
/// Subtitle for lyrics settings
///
/// In en, this message translates to:
/// **'Embed, mode, providers, language options'**
String get settingsLyricsSubtitle;
/// Settings menu item - app settings
///
/// In en, this message translates to:
/// **'App'**
String get settingsApp;
/// Subtitle for app settings
///
/// In en, this message translates to:
/// **'Updates, data, extension repo, debug'**
String get settingsAppSubtitle;
/// Settings section header for metadata providers
///
/// In en, this message translates to:
/// **'Providers'**
String get sectionMetadataProviders;
/// Settings section header for deduplication
///
/// In en, this message translates to:
/// **'Duplicates'**
String get sectionDuplicates;
/// Settings section header for per-provider lyrics options
///
/// In en, this message translates to:
/// **'Provider Options'**
String get sectionLyricsProviderOptions;
/// Settings item title for metadata provider order
///
/// In en, this message translates to:
/// **'Metadata Provider Priority'**
String get metadataProvidersTitle;
/// Subtitle for metadata provider priority item
///
/// In en, this message translates to:
/// **'Drag to set search and metadata source order'**
String get metadataProvidersSubtitle;
/// Setting - skip tracks already in download history
///
/// In en, this message translates to:
/// **'Skip Duplicate Downloads'**
String get downloadDeduplication;
/// Subtitle when deduplication is on
///
/// In en, this message translates to:
/// **'Already-downloaded tracks will be skipped'**
String get downloadDeduplicationEnabled;
/// Subtitle when deduplication is off
///
/// In en, this message translates to:
/// **'All tracks will be downloaded regardless of history'**
String get downloadDeduplicationDisabled;
/// Settings item for configuring fallback extension providers
///
/// In en, this message translates to:
/// **'Fallback Extensions'**
String get downloadFallbackExtensions;
/// Subtitle for fallback extensions item
///
/// In en, this message translates to:
/// **'Choose which extensions can be used as fallback'**
String get downloadFallbackExtensionsSubtitle;
}
class _AppLocalizationsDelegate
@@ -6305,6 +6435,7 @@ class _AppLocalizationsDelegate
'pt',
'ru',
'tr',
'uk',
'zh',
].contains(locale.languageCode);
@@ -6369,6 +6500,8 @@ AppLocalizations lookupAppLocalizations(Locale locale) {
return AppLocalizationsRu();
case 'tr':
return AppLocalizationsTr();
case 'uk':
return AppLocalizationsUk();
case 'zh':
return AppLocalizationsZh();
}
File diff suppressed because it is too large Load Diff
+128 -53
View File
@@ -126,7 +126,7 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get optionsPrimaryProviderSubtitle =>
'Service used when searching by track name.';
'Service used for searching by track or album name';
@override
String optionsUsingExtension(String extensionName) {
@@ -142,7 +142,7 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get optionsSwitchBack =>
'Choose another provider below to stop using the current extension';
'Tap Deezer or Spotify to switch back from extension';
@override
String get optionsAutoFallback => 'Auto Fallback';
@@ -158,14 +158,14 @@ class AppLocalizationsEn extends AppLocalizations {
String get optionsUseExtensionProvidersOn => 'Extensions will be tried first';
@override
String get optionsUseExtensionProvidersOff => 'Extensions are turned off';
String get optionsUseExtensionProvidersOff => 'Using built-in providers only';
@override
String get optionsEmbedLyrics => 'Embed Lyrics';
@override
String get optionsEmbedLyricsSubtitle =>
'Embed synced lyrics into FLAC files';
'Save synced lyrics alongside your downloaded tracks';
@override
String get optionsMaxQualityCover => 'Max Quality Cover';
@@ -957,14 +957,14 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get providerPriorityFallbackExtensionsDescription =>
'Choose which installed download extensions can be used during automatic fallback.';
'Choose which installed download extensions can be used during automatic fallback. Built-in providers still follow the priority order above.';
@override
String get providerPriorityFallbackExtensionsHint =>
'Only enabled extensions with download-provider capability are listed here.';
@override
String get providerBuiltIn => 'Legacy';
String get providerBuiltIn => 'Built-in';
@override
String get providerExtension => 'Extension';
@@ -1132,10 +1132,10 @@ class AppLocalizationsEn extends AppLocalizations {
String get settingsAppearanceSubtitle => 'Theme, colors, display';
@override
String get settingsDownloadSubtitle => 'Service, quality, filename format';
String get settingsDownloadSubtitle => 'Service, quality, fallback';
@override
String get settingsOptionsSubtitle => 'Fallback, lyrics, cover art, updates';
String get settingsOptionsSubtitle => 'Fallback, metadata, lyrics, cover art';
@override
String get settingsExtensionsSubtitle => 'Manage download providers';
@@ -2765,14 +2765,14 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get downloadUseAlbumArtistForFoldersAlbumSubtitle =>
'Artist folders use Album Artist when available';
'Folder named after Album Artist tag';
@override
String get downloadUseAlbumArtistForFoldersTrackSubtitle =>
'Artist folders use Track Artist only';
'Folder named after Track Artist tag';
@override
String get lyricsProvidersTitle => 'Lyrics Providers';
String get lyricsProvidersTitle => 'Lyrics Provider Priority';
@override
String get lyricsProvidersDescription =>
@@ -2840,10 +2840,10 @@ class AppLocalizationsEn extends AppLocalizations {
String get safMigrationSuccess => 'Download folder updated to SAF mode';
@override
String get settingsDonate => 'Donate';
String get settingsDonate => 'Support Development';
@override
String get settingsDonateSubtitle => 'Support SpotiFLAC-Mobile development';
String get settingsDonateSubtitle => 'Buy the developer a coffee';
@override
String get tooltipLoveAll => 'Love All';
@@ -2903,78 +2903,87 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get downloadLocationSubtitle =>
'Choose storage mode for downloaded files.';
'Choose where to save your downloaded tracks';
@override
String get storageModeAppFolder => 'App folder (non-SAF)';
String get storageModeAppFolder => 'App Folder (Recommended)';
@override
String get storageModeAppFolderSubtitle => 'Use default Music/SpotiFLAC path';
String get storageModeAppFolderSubtitle =>
'Saves to Music/SpotiFLAC by default';
@override
String get storageModeSaf => 'SAF folder';
String get storageModeSaf => 'Custom Folder (SAF)';
@override
String get storageModeSafSubtitle =>
'Pick folder via Android Storage Access Framework';
String get storageModeSafSubtitle => 'Pick any folder, including SD card';
@override
String get downloadFilenameDescription =>
'Customize how your files are named.';
String downloadFilenameDescription(
Object album,
Object artist,
Object date,
Object disc,
Object title,
Object track,
Object year,
) {
return 'Use $artist, $title, $album, $track, $year, $date, $disc as placeholders.';
}
@override
String get downloadFilenameInsertTag => 'Tap to insert tag:';
@override
String get downloadSeparateSinglesEnabled => 'Albums/ and Singles/ folders';
String get downloadSeparateSinglesEnabled =>
'Singles and EPs saved in a separate folder';
@override
String get downloadSeparateSinglesDisabled => 'All files in same structure';
String get downloadSeparateSinglesDisabled =>
'Singles and albums saved in the same folder';
@override
String get downloadArtistNameFilters => 'Artist Name Filters';
@override
String get downloadCreatePlaylistSourceFolder =>
'Create playlist source folder';
String get downloadCreatePlaylistSourceFolder => 'Playlist Source Folder';
@override
String get downloadCreatePlaylistSourceFolderEnabled =>
'Playlist downloads use Playlist/ plus your normal folder structure.';
'A subfolder is created for each playlist';
@override
String get downloadCreatePlaylistSourceFolderDisabled =>
'Playlist downloads use the normal folder structure only.';
'All tracks saved directly to download folder';
@override
String get downloadCreatePlaylistSourceFolderRedundant =>
'By Playlist already places downloads inside a playlist folder.';
'Handled by folder organization setting';
@override
String get downloadSongLinkRegion => 'SongLink Region';
@override
String get downloadNetworkCompatibilityMode => 'Network compatibility mode';
String get downloadNetworkCompatibilityMode => 'Network Compatibility Mode';
@override
String get downloadNetworkCompatibilityModeEnabled =>
'Enabled: try HTTP + accept invalid TLS certificates (unsafe)';
'Using legacy TLS settings for older networks';
@override
String get downloadNetworkCompatibilityModeDisabled =>
'Off: strict HTTPS certificate validation (recommended)';
'Using standard network settings';
@override
String get downloadSelectServiceToEnable =>
'Select a compatible download provider to enable quality options';
'Select Tidal or Qobuz to enable this option';
@override
String get downloadSelectTidalQobuz =>
'Select a compatible provider above to configure quality';
'Select Tidal or Qobuz to choose audio quality';
@override
String get downloadEmbedLyricsDisabled =>
'Disabled while Embed Metadata is turned off';
String get downloadEmbedLyricsDisabled => 'Enable metadata embedding first';
@override
String get downloadNeteaseIncludeTranslation =>
@@ -2982,11 +2991,11 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get downloadNeteaseIncludeTranslationEnabled =>
'Append translated lyrics when available';
'Chinese translation lines included';
@override
String get downloadNeteaseIncludeTranslationDisabled =>
'Use original lyrics only';
'Original lyrics only';
@override
String get downloadNeteaseIncludeRomanization =>
@@ -2994,66 +3003,65 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get downloadNeteaseIncludeRomanizationEnabled =>
'Append romanized lyrics when available';
'Romanization lines included';
@override
String get downloadNeteaseIncludeRomanizationDisabled => 'Disabled';
String get downloadNeteaseIncludeRomanizationDisabled => 'No romanization';
@override
String get downloadAppleQqMultiPerson => 'Apple/QQ Multi-Person Word-by-Word';
String get downloadAppleQqMultiPerson => 'Apple / QQ: Multi-Person Lyrics';
@override
String get downloadAppleQqMultiPersonEnabled =>
'Enable v1/v2 speaker and [bg:] tags';
'Speaker labels included for duets and group tracks';
@override
String get downloadAppleQqMultiPersonDisabled =>
'Simplified word-by-word formatting';
'Standard lyrics without speaker labels';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@override
String get downloadMusixmatchLanguageAuto => 'Auto (original)';
String get downloadMusixmatchLanguageAuto => 'Auto (original language)';
@override
String get downloadFilterContributing =>
'Filter contributing artists in Album Artist';
String get downloadFilterContributing => 'Filter Contributing Artists';
@override
String get downloadFilterContributingEnabled =>
'Album Artist metadata uses primary artist only';
'Contributing artists removed from Album Artist folder name';
@override
String get downloadFilterContributingDisabled =>
'Keep full Album Artist metadata value';
'Full Album Artist string used';
@override
String get downloadProvidersNoneEnabled => 'None enabled';
String get downloadProvidersNoneEnabled => 'No providers enabled';
@override
String get downloadMusixmatchLanguageCode => 'Language code';
@override
String get downloadMusixmatchLanguageHint => 'auto / en / es / ja';
String get downloadMusixmatchLanguageHint => 'e.g. en, de, ja';
@override
String get downloadMusixmatchLanguageDesc =>
'Set preferred language code (example: en, es, ja). Leave empty for auto.';
'Enter a BCP-47 language code (e.g. en, de, ja) to request translated lyrics from Musixmatch.';
@override
String get downloadMusixmatchAuto => 'Auto';
@override
String get downloadNetworkAnySubtitle => 'WiFi + Mobile Data';
String get downloadNetworkAnySubtitle => 'Use WiFi or mobile data';
@override
String get downloadNetworkWifiOnlySubtitle =>
'Pause downloads on mobile data';
'Downloads pause when on mobile data';
@override
String get downloadSongLinkRegionDesc =>
'Used as userCountry for SongLink API lookup.';
'Region used when resolving track links via SongLink. Choose the country where your streaming services are available.';
@override
String get snackbarUnsupportedAudioFormat => 'Unsupported audio format';
@@ -3696,4 +3704,71 @@ class AppLocalizationsEn extends AppLocalizations {
String a11yOpenAlbum(String albumName) {
return 'Open album $albumName';
}
@override
String get optionsDefaultSearchTabAlbums => 'Albums';
@override
String get optionsDefaultSearchTabTracks => 'Tracks';
@override
String get settingsFiles => 'Files & Folders';
@override
String get settingsFilesSubtitle =>
'Download location, filename, folder structure';
@override
String get settingsMetadata => 'Metadata';
@override
String get settingsMetadataSubtitle =>
'Cover art, tags, ReplayGain, providers';
@override
String get settingsLyrics => 'Lyrics';
@override
String get settingsLyricsSubtitle =>
'Embed, mode, providers, language options';
@override
String get settingsApp => 'App';
@override
String get settingsAppSubtitle => 'Updates, data, extension repo, debug';
@override
String get sectionMetadataProviders => 'Providers';
@override
String get sectionDuplicates => 'Duplicates';
@override
String get sectionLyricsProviderOptions => 'Provider Options';
@override
String get metadataProvidersTitle => 'Metadata Provider Priority';
@override
String get metadataProvidersSubtitle =>
'Drag to set search and metadata source order';
@override
String get downloadDeduplication => 'Skip Duplicate Downloads';
@override
String get downloadDeduplicationEnabled =>
'Already-downloaded tracks will be skipped';
@override
String get downloadDeduplicationDisabled =>
'All tracks will be downloaded regardless of history';
@override
String get downloadFallbackExtensions => 'Fallback Extensions';
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
}
File diff suppressed because it is too large Load Diff
+116 -38
View File
@@ -21,13 +21,13 @@ class AppLocalizationsFr extends AppLocalizations {
String get navSettings => 'Paramètres';
@override
String get navStore => 'Magasin';
String get navStore => 'Repo';
@override
String get homeTitle => 'Accueil';
@override
String get homeSubtitle => 'Coller un lien Spotify ou rechercher par nom';
String get homeSubtitle => 'Paste a supported URL or search by name';
@override
String get homeEmptyTitle => 'No search providers yet';
@@ -224,10 +224,10 @@ class AppLocalizationsFr extends AppLocalizations {
'Parallel downloads may trigger rate limiting';
@override
String get optionsExtensionStore => 'Extension Store';
String get optionsExtensionStore => 'Extension Repo';
@override
String get optionsExtensionStoreSubtitle => 'Show Store tab in navigation';
String get optionsExtensionStoreSubtitle => 'Show Repo tab in navigation';
@override
String get optionsCheckUpdates => 'Check for Updates';
@@ -288,7 +288,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get extensionsTitle => 'Extensions';
@override
String get extensionsDisabled => 'Disabled';
String get extensionsDisabled => 'Désactivée';
@override
String extensionsVersion(String version) {
@@ -297,38 +297,38 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String extensionsAuthor(String author) {
return 'by $author';
return 'par $author';
}
@override
String get extensionsUninstall => 'Désinstaller';
@override
String get storeTitle => 'Magasin d\'extension';
String get storeTitle => 'Extension Repo';
@override
String get storeSearch => 'Recherche d\'extensions...';
@override
String get storeInstall => 'Install';
String get storeInstall => 'Installer';
@override
String get storeInstalled => 'Installed';
String get storeInstalled => 'Installé';
@override
String get storeUpdate => 'Update';
String get storeUpdate => 'Mettre à jour';
@override
String get aboutTitle => 'About';
String get aboutTitle => 'À propos de';
@override
String get aboutContributors => 'Contributors';
String get aboutContributors => 'Contributeurs';
@override
String get aboutMobileDeveloper => 'Mobile version developer';
String get aboutMobileDeveloper => 'Développeur de la version mobile';
@override
String get aboutOriginalCreator => 'Creator of the original SpotiFLAC';
String get aboutOriginalCreator => 'Créateur de SpotiFLAC original';
@override
String get aboutLogoArtist =>
@@ -368,7 +368,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get aboutTelegramChannel => 'Telegram Channel';
@override
String get aboutTelegramChannelSubtitle => 'Announcements and updates';
String get aboutTelegramChannelSubtitle => 'Annonces et mises à jour';
@override
String get aboutTelegramChat => 'Telegram Community';
@@ -526,10 +526,11 @@ class AppLocalizationsFr extends AppLocalizations {
'SpotiFLAC needs storage permission to save your downloaded music files.';
@override
String get setupNotificationGranted => 'Notification Permission Granted!';
String get setupNotificationGranted =>
'Autorisation de notifications accordée!';
@override
String get setupNotificationEnable => 'Enable Notifications';
String get setupNotificationEnable => 'Activer les notifications';
@override
String get setupFolderChoose => 'Choisissez le dossier pour télécharger';
@@ -539,27 +540,27 @@ class AppLocalizationsFr extends AppLocalizations {
'Sélectionnez un dossier dans lequel votre musique téléchargée sera enregistrée.';
@override
String get setupSelectFolder => 'Select Folder';
String get setupSelectFolder => 'Sélectionner un dossier';
@override
String get setupEnableNotifications => 'Enable Notifications';
String get setupEnableNotifications => 'Activer les notifications';
@override
String get setupNotificationBackgroundDescription =>
'Get notified about download progress and completion. This helps you track downloads when the app is in background.';
@override
String get setupSkipForNow => 'Skip for now';
String get setupSkipForNow => 'Ignorer pour le moment';
@override
String get setupNext => 'Next';
String get setupNext => 'Suivant';
@override
String get setupGetStarted => 'Get Started';
String get setupGetStarted => 'Démarrer';
@override
String get setupAllowAccessToManageFiles =>
'Please enable \"Allow access to manage all files\" in the next screen.';
'Veuillez activer \"Autoriser l\'accès à tous les fichiers\" sur l\'écran suivant.';
@override
String get setupLanguageTitle => 'Choose Language';
@@ -572,16 +573,16 @@ class AppLocalizationsFr extends AppLocalizations {
String get setupLanguageSystemDefault => 'System Default';
@override
String get dialogCancel => 'Cancel';
String get dialogCancel => 'Annuler';
@override
String get dialogSave => 'Save';
String get dialogSave => 'Sauvegarder';
@override
String get dialogDelete => 'Delete';
String get dialogDelete => 'Supprimer';
@override
String get dialogRetry => 'Retry';
String get dialogRetry => 'Réessayer';
@override
String get dialogClear => 'Clear';
@@ -593,7 +594,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get dialogImport => 'Import';
@override
String get dialogDownload => 'Download';
String get dialogDownload => 'Télécharger';
@override
String get dialogDiscard => 'Discard';
@@ -602,10 +603,10 @@ class AppLocalizationsFr extends AppLocalizations {
String get dialogRemove => 'Remove';
@override
String get dialogUninstall => 'Uninstall';
String get dialogUninstall => 'Désinstaller';
@override
String get dialogDiscardChanges => 'Discard Changes?';
String get dialogDiscardChanges => 'Ignorer les modifications ?';
@override
String get dialogUnsavedChanges =>
@@ -959,7 +960,7 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get providerPriorityFallbackExtensionsDescription =>
'Choose which installed download extensions can be used during automatic fallback.';
'Choose which installed download extensions can be used during automatic fallback. Built-in providers still follow the priority order above.';
@override
String get providerPriorityFallbackExtensionsHint =>
@@ -1354,7 +1355,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get storeEmptyNoResults => 'No extensions found';
@override
String get extensionDefaultProvider => 'Default (Deezer/Spotify)';
String get extensionDefaultProvider => 'Default (Deezer)';
@override
String get extensionDefaultProviderSubtitle => 'Use built-in search';
@@ -2132,7 +2133,7 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get tutorialExtensionsTip1 =>
'Browse the Store tab to discover useful extensions';
'Browse the Repo tab to discover useful extensions';
@override
String get tutorialExtensionsTip2 =>
@@ -2413,7 +2414,8 @@ class AppLocalizationsFr extends AppLocalizations {
String get trackConvertFormat => 'Convert Format';
@override
String get trackConvertFormatSubtitle => 'Convert to MP3 or Opus';
String get trackConvertFormatSubtitle =>
'Convert to MP3, Opus, ALAC, or FLAC';
@override
String get trackConvertTitle => 'Convert Audio';
@@ -2920,8 +2922,17 @@ class AppLocalizationsFr extends AppLocalizations {
'Pick folder via Android Storage Access Framework';
@override
String get downloadFilenameDescription =>
'Customize how your files are named.';
String downloadFilenameDescription(
Object album,
Object artist,
Object date,
Object disc,
Object title,
Object track,
Object year,
) {
return 'Customize how your files are named.';
}
@override
String get downloadFilenameInsertTag => 'Tap to insert tag:';
@@ -2967,11 +2978,11 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get downloadSelectServiceToEnable =>
'Select a compatible download provider to enable quality options';
'Select a built-in service to enable';
@override
String get downloadSelectTidalQobuz =>
'Select a compatible provider above to configure quality';
'Select Tidal or Qobuz above to configure quality';
@override
String get downloadEmbedLyricsDisabled =>
@@ -3697,4 +3708,71 @@ class AppLocalizationsFr extends AppLocalizations {
String a11yOpenAlbum(String albumName) {
return 'Open album $albumName';
}
@override
String get optionsDefaultSearchTabAlbums => 'Albums';
@override
String get optionsDefaultSearchTabTracks => 'Tracks';
@override
String get settingsFiles => 'Files & Folders';
@override
String get settingsFilesSubtitle =>
'Download location, filename, folder structure';
@override
String get settingsMetadata => 'Metadata';
@override
String get settingsMetadataSubtitle =>
'Cover art, tags, ReplayGain, providers';
@override
String get settingsLyrics => 'Lyrics';
@override
String get settingsLyricsSubtitle =>
'Embed, mode, providers, language options';
@override
String get settingsApp => 'App';
@override
String get settingsAppSubtitle => 'Updates, data, extension repo, debug';
@override
String get sectionMetadataProviders => 'Providers';
@override
String get sectionDuplicates => 'Duplicates';
@override
String get sectionLyricsProviderOptions => 'Provider Options';
@override
String get metadataProvidersTitle => 'Metadata Provider Priority';
@override
String get metadataProvidersSubtitle =>
'Drag to set search and metadata source order';
@override
String get downloadDeduplication => 'Skip Duplicate Downloads';
@override
String get downloadDeduplicationEnabled =>
'Already-downloaded tracks will be skipped';
@override
String get downloadDeduplicationDisabled =>
'All tracks will be downloaded regardless of history';
@override
String get downloadFallbackExtensions => 'Fallback Extensions';
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
}
+90 -13
View File
@@ -21,13 +21,13 @@ class AppLocalizationsHi extends AppLocalizations {
String get navSettings => 'विकल्प';
@override
String get navStore => 'Store';
String get navStore => 'Repo';
@override
String get homeTitle => 'Home';
@override
String get homeSubtitle => 'Paste a Spotify link or search by name';
String get homeSubtitle => 'Paste a supported URL or search by name';
@override
String get homeEmptyTitle => 'No search providers yet';
@@ -222,10 +222,10 @@ class AppLocalizationsHi extends AppLocalizations {
'Parallel downloads may trigger rate limiting';
@override
String get optionsExtensionStore => 'Extension Store';
String get optionsExtensionStore => 'Extension Repo';
@override
String get optionsExtensionStoreSubtitle => 'Show Store tab in navigation';
String get optionsExtensionStoreSubtitle => 'Show Repo tab in navigation';
@override
String get optionsCheckUpdates => 'Check for Updates';
@@ -302,7 +302,7 @@ class AppLocalizationsHi extends AppLocalizations {
String get extensionsUninstall => 'Uninstall';
@override
String get storeTitle => 'Extension Store';
String get storeTitle => 'Extension Repo';
@override
String get storeSearch => 'Search extensions...';
@@ -957,7 +957,7 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get providerPriorityFallbackExtensionsDescription =>
'Choose which installed download extensions can be used during automatic fallback.';
'Choose which installed download extensions can be used during automatic fallback. Built-in providers still follow the priority order above.';
@override
String get providerPriorityFallbackExtensionsHint =>
@@ -1352,7 +1352,7 @@ class AppLocalizationsHi extends AppLocalizations {
String get storeEmptyNoResults => 'No extensions found';
@override
String get extensionDefaultProvider => 'Default (Deezer/Spotify)';
String get extensionDefaultProvider => 'Default (Deezer)';
@override
String get extensionDefaultProviderSubtitle => 'Use built-in search';
@@ -2130,7 +2130,7 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get tutorialExtensionsTip1 =>
'Browse the Store tab to discover useful extensions';
'Browse the Repo tab to discover useful extensions';
@override
String get tutorialExtensionsTip2 =>
@@ -2411,7 +2411,8 @@ class AppLocalizationsHi extends AppLocalizations {
String get trackConvertFormat => 'Convert Format';
@override
String get trackConvertFormatSubtitle => 'Convert to MP3 or Opus';
String get trackConvertFormatSubtitle =>
'Convert to MP3, Opus, ALAC, or FLAC';
@override
String get trackConvertTitle => 'Convert Audio';
@@ -2918,8 +2919,17 @@ class AppLocalizationsHi extends AppLocalizations {
'Pick folder via Android Storage Access Framework';
@override
String get downloadFilenameDescription =>
'Customize how your files are named.';
String downloadFilenameDescription(
Object album,
Object artist,
Object date,
Object disc,
Object title,
Object track,
Object year,
) {
return 'Customize how your files are named.';
}
@override
String get downloadFilenameInsertTag => 'Tap to insert tag:';
@@ -2965,11 +2975,11 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get downloadSelectServiceToEnable =>
'Select a compatible download provider to enable quality options';
'Select a built-in service to enable';
@override
String get downloadSelectTidalQobuz =>
'Select a compatible provider above to configure quality';
'Select Tidal or Qobuz above to configure quality';
@override
String get downloadEmbedLyricsDisabled =>
@@ -3695,4 +3705,71 @@ class AppLocalizationsHi extends AppLocalizations {
String a11yOpenAlbum(String albumName) {
return 'Open album $albumName';
}
@override
String get optionsDefaultSearchTabAlbums => 'Albums';
@override
String get optionsDefaultSearchTabTracks => 'Tracks';
@override
String get settingsFiles => 'Files & Folders';
@override
String get settingsFilesSubtitle =>
'Download location, filename, folder structure';
@override
String get settingsMetadata => 'Metadata';
@override
String get settingsMetadataSubtitle =>
'Cover art, tags, ReplayGain, providers';
@override
String get settingsLyrics => 'Lyrics';
@override
String get settingsLyricsSubtitle =>
'Embed, mode, providers, language options';
@override
String get settingsApp => 'App';
@override
String get settingsAppSubtitle => 'Updates, data, extension repo, debug';
@override
String get sectionMetadataProviders => 'Providers';
@override
String get sectionDuplicates => 'Duplicates';
@override
String get sectionLyricsProviderOptions => 'Provider Options';
@override
String get metadataProvidersTitle => 'Metadata Provider Priority';
@override
String get metadataProvidersSubtitle =>
'Drag to set search and metadata source order';
@override
String get downloadDeduplication => 'Skip Duplicate Downloads';
@override
String get downloadDeduplicationEnabled =>
'Already-downloaded tracks will be skipped';
@override
String get downloadDeduplicationDisabled =>
'All tracks will be downloaded regardless of history';
@override
String get downloadFallbackExtensions => 'Fallback Extensions';
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
}
+112 -36
View File
@@ -27,8 +27,7 @@ class AppLocalizationsId extends AppLocalizations {
String get homeTitle => 'Beranda';
@override
String get homeSubtitle =>
'Tempel URL yang didukung atau cari berdasarkan nama';
String get homeSubtitle => 'Paste a supported URL or search by name';
@override
String get homeEmptyTitle => 'Belum ada provider pencarian';
@@ -136,15 +135,15 @@ class AppLocalizationsId extends AppLocalizations {
}
@override
String get optionsDefaultSearchTab => 'Tab Pencarian Default';
String get optionsDefaultSearchTab => 'Default Search Tab';
@override
String get optionsDefaultSearchTabSubtitle =>
'Pilih tab yang dibuka lebih dulu untuk hasil pencarian baru.';
'Choose which tab opens first for new search results.';
@override
String get optionsSwitchBack =>
'Pilih provider lain di bawah untuk berhenti memakai ekstensi saat ini';
'Ketuk Deezer atau Spotify untuk beralih dari ekstensi';
@override
String get optionsAutoFallback => 'Cadangan Otomatis';
@@ -161,7 +160,8 @@ class AppLocalizationsId extends AppLocalizations {
'Ekstensi akan dicoba terlebih dahulu';
@override
String get optionsUseExtensionProvidersOff => 'Ekstensi dimatikan';
String get optionsUseExtensionProvidersOff =>
'Hanya menggunakan provider bawaan';
@override
String get optionsEmbedLyrics => 'Sematkan Lirik';
@@ -225,10 +225,10 @@ class AppLocalizationsId extends AppLocalizations {
'Unduhan paralel dapat memicu pembatasan rate';
@override
String get optionsExtensionStore => 'Repo Ekstensi';
String get optionsExtensionStore => 'Extension Repo';
@override
String get optionsExtensionStoreSubtitle => 'Tampilkan tab Repo di navigasi';
String get optionsExtensionStoreSubtitle => 'Show Repo tab in navigation';
@override
String get optionsCheckUpdates => 'Periksa Pembaruan';
@@ -304,7 +304,7 @@ class AppLocalizationsId extends AppLocalizations {
String get extensionsUninstall => 'Copot';
@override
String get storeTitle => 'Repo Ekstensi';
String get storeTitle => 'Extension Repo';
@override
String get storeSearch => 'Cari ekstensi...';
@@ -760,15 +760,15 @@ class AppLocalizationsId extends AppLocalizations {
String get errorNoTracksFound => 'Tidak ada lagu ditemukan';
@override
String get errorUrlNotRecognized => 'Link tidak dikenali';
String get errorUrlNotRecognized => 'Tautan tidak dikenali';
@override
String get errorUrlNotRecognizedMessage =>
'Link ini tidak didukung. Pastikan URL benar dan ekstensi yang kompatibel sudah terpasang.';
'Tautan ini tidak didukung. Pastikan URL sudah benar dan ekstensi yang kompatibel telah terpasang.';
@override
String get errorUrlFetchFailed =>
'Gagal memuat konten dari link ini. Silakan coba lagi.';
'Konten dari tautan ini gagal dimuat. Silakan coba lagi.';
@override
String errorMissingExtensionSource(String item) {
@@ -956,18 +956,18 @@ class AppLocalizationsId extends AppLocalizations {
'Jika lagu tidak tersedia di provider pertama, aplikasi akan otomatis mencoba yang berikutnya.';
@override
String get providerPriorityFallbackExtensionsTitle => 'Fallback Ekstensi';
String get providerPriorityFallbackExtensionsTitle => 'Extension Fallback';
@override
String get providerPriorityFallbackExtensionsDescription =>
'Pilih ekstensi unduhan terpasang mana yang boleh dipakai saat fallback otomatis.';
'Choose which installed download extensions can be used during automatic fallback. Built-in providers still follow the priority order above.';
@override
String get providerPriorityFallbackExtensionsHint =>
'Hanya ekstensi aktif dengan kemampuan download provider yang ditampilkan di sini.';
'Only enabled extensions with download-provider capability are listed here.';
@override
String get providerBuiltIn => 'Legacy';
String get providerBuiltIn => 'Bawaan';
@override
String get providerExtension => 'Ekstensi';
@@ -1349,7 +1349,7 @@ class AppLocalizationsId extends AppLocalizations {
String get storeNewRepoUrlLabel => 'New Repository URL';
@override
String get storeLoadError => 'Gagal memuat repo';
String get storeLoadError => 'Failed to load repository';
@override
String get storeEmptyNoExtensions => 'No extensions available';
@@ -1358,7 +1358,7 @@ class AppLocalizationsId extends AppLocalizations {
String get storeEmptyNoResults => 'No extensions found';
@override
String get extensionDefaultProvider => 'Bawaan (Deezer/Spotify)';
String get extensionDefaultProvider => 'Default (Deezer)';
@override
String get extensionDefaultProviderSubtitle => 'Gunakan pencarian bawaan';
@@ -1464,7 +1464,7 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get extensionsFallbackSubtitle =>
'Pilih ekstensi unduhan terpasang yang boleh dipakai saat fallback';
'Choose which installed download extensions can be used as fallback';
@override
String get extensionsNoDownloadProvider =>
@@ -2139,7 +2139,7 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get tutorialExtensionsTip1 =>
'Buka tab Repo untuk menemukan ekstensi yang berguna';
'Browse the Repo tab to discover useful extensions';
@override
String get tutorialExtensionsTip2 =>
@@ -2390,25 +2390,25 @@ class AppLocalizationsId extends AppLocalizations {
String get trackReEnrichFfmpegFailed => 'FFmpeg metadata embed failed';
@override
String get queueFlacAction => 'Antrekan FLAC';
String get queueFlacAction => 'Queue FLAC';
@override
String queueFlacConfirmMessage(int count) {
return 'Cari kecocokan online untuk track yang dipilih lalu antrekan download FLAC.\n\nFile yang sudah ada tidak akan diubah atau dihapus.\n\nHanya kecocokan dengan keyakinan tinggi yang akan diantrikan otomatis.\n\n$count dipilih';
return 'Search online matches for the selected tracks and queue FLAC downloads.\n\nExisting files will not be modified or deleted.\n\nOnly high-confidence matches are queued automatically.\n\n$count selected';
}
@override
String queueFlacFindingProgress(int current, int total) {
return 'Mencari kecocokan FLAC... ($current/$total)';
return 'Finding FLAC matches... ($current/$total)';
}
@override
String get queueFlacNoReliableMatches =>
'Tidak ada kecocokan online yang cukup meyakinkan untuk pilihan ini';
'No reliable online matches found for the selection';
@override
String queueFlacQueuedWithSkipped(int addedCount, int skippedCount) {
return 'Menambahkan $addedCount track ke antrean, melewati $skippedCount';
return 'Added $addedCount tracks to queue, skipped $skippedCount';
}
@override
@@ -2421,7 +2421,7 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get trackConvertFormatSubtitle =>
'Konversi ke MP3, Opus, ALAC, atau FLAC';
'Convert to MP3, Opus, ALAC, or FLAC';
@override
String get trackConvertTitle => 'Convert Audio';
@@ -2449,12 +2449,12 @@ class AppLocalizationsId extends AppLocalizations {
String sourceFormat,
String targetFormat,
) {
return 'Konversi dari $sourceFormat ke $targetFormat? (Lossless — tanpa kehilangan kualitas)\n\nFile asli akan dihapus setelah konversi.';
return 'Convert from $sourceFormat to $targetFormat? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.';
}
@override
String get trackConvertLosslessHint =>
'Konversi lossless — tanpa kehilangan kualitas';
'Lossless conversion — no quality loss';
@override
String get trackConvertConverting => 'Converting audio...';
@@ -2928,8 +2928,17 @@ class AppLocalizationsId extends AppLocalizations {
'Pick folder via Android Storage Access Framework';
@override
String get downloadFilenameDescription =>
'Customize how your files are named.';
String downloadFilenameDescription(
Object album,
Object artist,
Object date,
Object disc,
Object title,
Object track,
Object year,
) {
return 'Customize how your files are named.';
}
@override
String get downloadFilenameInsertTag => 'Tap to insert tag:';
@@ -2945,19 +2954,19 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get downloadCreatePlaylistSourceFolder =>
'Buat folder sumber playlist';
'Create playlist source folder';
@override
String get downloadCreatePlaylistSourceFolderEnabled =>
'Unduhan dari playlist memakai Playlist/ lalu struktur folder normal Anda.';
'Playlist downloads use Playlist/ plus your normal folder structure.';
@override
String get downloadCreatePlaylistSourceFolderDisabled =>
'Unduhan dari playlist hanya memakai struktur folder normal.';
'Playlist downloads use the normal folder structure only.';
@override
String get downloadCreatePlaylistSourceFolderRedundant =>
'Mode Berdasarkan Playlist sudah menaruh unduhan ke dalam folder playlist.';
'By Playlist already places downloads inside a playlist folder.';
@override
String get downloadSongLinkRegion => 'SongLink Region';
@@ -2975,11 +2984,11 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get downloadSelectServiceToEnable =>
'Select a compatible download provider to enable quality options';
'Select a built-in service to enable';
@override
String get downloadSelectTidalQobuz =>
'Select a compatible provider above to configure quality';
'Select Tidal or Qobuz above to configure quality';
@override
String get downloadEmbedLyricsDisabled =>
@@ -3705,4 +3714,71 @@ class AppLocalizationsId extends AppLocalizations {
String a11yOpenAlbum(String albumName) {
return 'Open album $albumName';
}
@override
String get optionsDefaultSearchTabAlbums => 'Albums';
@override
String get optionsDefaultSearchTabTracks => 'Tracks';
@override
String get settingsFiles => 'Files & Folders';
@override
String get settingsFilesSubtitle =>
'Download location, filename, folder structure';
@override
String get settingsMetadata => 'Metadata';
@override
String get settingsMetadataSubtitle =>
'Cover art, tags, ReplayGain, providers';
@override
String get settingsLyrics => 'Lyrics';
@override
String get settingsLyricsSubtitle =>
'Embed, mode, providers, language options';
@override
String get settingsApp => 'App';
@override
String get settingsAppSubtitle => 'Updates, data, extension repo, debug';
@override
String get sectionMetadataProviders => 'Providers';
@override
String get sectionDuplicates => 'Duplicates';
@override
String get sectionLyricsProviderOptions => 'Provider Options';
@override
String get metadataProvidersTitle => 'Metadata Provider Priority';
@override
String get metadataProvidersSubtitle =>
'Drag to set search and metadata source order';
@override
String get downloadDeduplication => 'Skip Duplicate Downloads';
@override
String get downloadDeduplicationEnabled =>
'Already-downloaded tracks will be skipped';
@override
String get downloadDeduplicationDisabled =>
'All tracks will be downloaded regardless of history';
@override
String get downloadFallbackExtensions => 'Fallback Extensions';
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
}
+93 -15
View File
@@ -21,13 +21,13 @@ class AppLocalizationsJa extends AppLocalizations {
String get navSettings => '設定';
@override
String get navStore => 'ストア';
String get navStore => 'Repo';
@override
String get homeTitle => 'ホーム';
@override
String get homeSubtitle => 'Spotify のリンクを貼り付けるか、名前で検索します';
String get homeSubtitle => 'Paste a supported URL or search by name';
@override
String get homeEmptyTitle => 'No search providers yet';
@@ -141,7 +141,8 @@ class AppLocalizationsJa extends AppLocalizations {
'Choose which tab opens first for new search results.';
@override
String get optionsSwitchBack => '現在の拡張の使用をやめるには、下から別のプロバイダーを選択してください';
String get optionsSwitchBack =>
'Tap Deezer or Spotify to switch back from extension';
@override
String get optionsAutoFallback => 'Auto Fallback';
@@ -157,7 +158,7 @@ class AppLocalizationsJa extends AppLocalizations {
String get optionsUseExtensionProvidersOn => '最初に拡張で試みます';
@override
String get optionsUseExtensionProvidersOff => '拡張はオフです';
String get optionsUseExtensionProvidersOff => '内蔵のプロバイダーのみを使用する';
@override
String get optionsEmbedLyrics => '歌詞を埋め込む';
@@ -219,10 +220,10 @@ class AppLocalizationsJa extends AppLocalizations {
'Parallel downloads may trigger rate limiting';
@override
String get optionsExtensionStore => '拡張ストア';
String get optionsExtensionStore => 'Extension Repo';
@override
String get optionsExtensionStoreSubtitle => 'ナビゲーションにストアタブを表示';
String get optionsExtensionStoreSubtitle => 'Show Repo tab in navigation';
@override
String get optionsCheckUpdates => '更新を確認';
@@ -298,7 +299,7 @@ class AppLocalizationsJa extends AppLocalizations {
String get extensionsUninstall => 'アンインストール';
@override
String get storeTitle => '拡張ストア';
String get storeTitle => 'Extension Repo';
@override
String get storeSearch => '拡張を検索...';
@@ -950,7 +951,7 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get providerPriorityFallbackExtensionsDescription =>
'Choose which installed download extensions can be used during automatic fallback.';
'Choose which installed download extensions can be used during automatic fallback. Built-in providers still follow the priority order above.';
@override
String get providerPriorityFallbackExtensionsHint =>
@@ -1345,7 +1346,7 @@ class AppLocalizationsJa extends AppLocalizations {
String get storeEmptyNoResults => 'No extensions found';
@override
String get extensionDefaultProvider => 'デフォルト (Deezer/Spotify)';
String get extensionDefaultProvider => 'Default (Deezer)';
@override
String get extensionDefaultProviderSubtitle => '内蔵の検索を使用する';
@@ -2116,7 +2117,7 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get tutorialExtensionsTip1 =>
'Browse the Store tab to discover useful extensions';
'Browse the Repo tab to discover useful extensions';
@override
String get tutorialExtensionsTip2 =>
@@ -2397,7 +2398,8 @@ class AppLocalizationsJa extends AppLocalizations {
String get trackConvertFormat => '変換の形式';
@override
String get trackConvertFormatSubtitle => 'MP3 または Opus に変換';
String get trackConvertFormatSubtitle =>
'Convert to MP3, Opus, ALAC, or FLAC';
@override
String get trackConvertTitle => 'オーディオを変換';
@@ -2904,8 +2906,17 @@ class AppLocalizationsJa extends AppLocalizations {
'Pick folder via Android Storage Access Framework';
@override
String get downloadFilenameDescription =>
'Customize how your files are named.';
String downloadFilenameDescription(
Object album,
Object artist,
Object date,
Object disc,
Object title,
Object track,
Object year,
) {
return 'Customize how your files are named.';
}
@override
String get downloadFilenameInsertTag => 'Tap to insert tag:';
@@ -2951,11 +2962,11 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get downloadSelectServiceToEnable =>
'Select a compatible download provider to enable quality options';
'Select a built-in service to enable';
@override
String get downloadSelectTidalQobuz =>
'Select a compatible provider above to configure quality';
'Select Tidal or Qobuz above to configure quality';
@override
String get downloadEmbedLyricsDisabled =>
@@ -3681,4 +3692,71 @@ class AppLocalizationsJa extends AppLocalizations {
String a11yOpenAlbum(String albumName) {
return 'Open album $albumName';
}
@override
String get optionsDefaultSearchTabAlbums => 'Albums';
@override
String get optionsDefaultSearchTabTracks => 'Tracks';
@override
String get settingsFiles => 'Files & Folders';
@override
String get settingsFilesSubtitle =>
'Download location, filename, folder structure';
@override
String get settingsMetadata => 'Metadata';
@override
String get settingsMetadataSubtitle =>
'Cover art, tags, ReplayGain, providers';
@override
String get settingsLyrics => 'Lyrics';
@override
String get settingsLyricsSubtitle =>
'Embed, mode, providers, language options';
@override
String get settingsApp => 'App';
@override
String get settingsAppSubtitle => 'Updates, data, extension repo, debug';
@override
String get sectionMetadataProviders => 'Providers';
@override
String get sectionDuplicates => 'Duplicates';
@override
String get sectionLyricsProviderOptions => 'Provider Options';
@override
String get metadataProvidersTitle => 'Metadata Provider Priority';
@override
String get metadataProvidersSubtitle =>
'Drag to set search and metadata source order';
@override
String get downloadDeduplication => 'Skip Duplicate Downloads';
@override
String get downloadDeduplicationEnabled =>
'Already-downloaded tracks will be skipped';
@override
String get downloadDeduplicationDisabled =>
'All tracks will be downloaded regardless of history';
@override
String get downloadFallbackExtensions => 'Fallback Extensions';
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
}
+90 -13
View File
@@ -21,13 +21,13 @@ class AppLocalizationsKo extends AppLocalizations {
String get navSettings => 'Settings';
@override
String get navStore => 'Store';
String get navStore => 'Repo';
@override
String get homeTitle => 'Home';
@override
String get homeSubtitle => 'Spotify URL을 붙여 넣거나 검색';
String get homeSubtitle => 'Paste a supported URL or search by name';
@override
String get homeEmptyTitle => 'No search providers yet';
@@ -215,10 +215,10 @@ class AppLocalizationsKo extends AppLocalizations {
String get optionsConcurrentWarning => '동시에 다수의 음반을 다운로드하면 속도 제한이 발생할 수 있습니다';
@override
String get optionsExtensionStore => '확장 기능 스토어';
String get optionsExtensionStore => 'Extension Repo';
@override
String get optionsExtensionStoreSubtitle => '탐색 메뉴에 스토어 탭 표시';
String get optionsExtensionStoreSubtitle => 'Show Repo tab in navigation';
@override
String get optionsCheckUpdates => '업데이트 확인';
@@ -292,7 +292,7 @@ class AppLocalizationsKo extends AppLocalizations {
String get extensionsUninstall => '삭제';
@override
String get storeTitle => '확장 기능 스토어';
String get storeTitle => 'Extension Repo';
@override
String get storeSearch => '확장 기능 검색';
@@ -939,7 +939,7 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get providerPriorityFallbackExtensionsDescription =>
'Choose which installed download extensions can be used during automatic fallback.';
'Choose which installed download extensions can be used during automatic fallback. Built-in providers still follow the priority order above.';
@override
String get providerPriorityFallbackExtensionsHint =>
@@ -1332,7 +1332,7 @@ class AppLocalizationsKo extends AppLocalizations {
String get storeEmptyNoResults => 'No extensions found';
@override
String get extensionDefaultProvider => 'Default (Deezer/Spotify)';
String get extensionDefaultProvider => 'Default (Deezer)';
@override
String get extensionDefaultProviderSubtitle => 'Use built-in search';
@@ -2110,7 +2110,7 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get tutorialExtensionsTip1 =>
'Browse the Store tab to discover useful extensions';
'Browse the Repo tab to discover useful extensions';
@override
String get tutorialExtensionsTip2 =>
@@ -2391,7 +2391,8 @@ class AppLocalizationsKo extends AppLocalizations {
String get trackConvertFormat => 'Convert Format';
@override
String get trackConvertFormatSubtitle => 'Convert to MP3 or Opus';
String get trackConvertFormatSubtitle =>
'Convert to MP3, Opus, ALAC, or FLAC';
@override
String get trackConvertTitle => 'Convert Audio';
@@ -2898,8 +2899,17 @@ class AppLocalizationsKo extends AppLocalizations {
'Pick folder via Android Storage Access Framework';
@override
String get downloadFilenameDescription =>
'Customize how your files are named.';
String downloadFilenameDescription(
Object album,
Object artist,
Object date,
Object disc,
Object title,
Object track,
Object year,
) {
return 'Customize how your files are named.';
}
@override
String get downloadFilenameInsertTag => 'Tap to insert tag:';
@@ -2945,11 +2955,11 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get downloadSelectServiceToEnable =>
'Select a compatible download provider to enable quality options';
'Select a built-in service to enable';
@override
String get downloadSelectTidalQobuz =>
'Select a compatible provider above to configure quality';
'Select Tidal or Qobuz above to configure quality';
@override
String get downloadEmbedLyricsDisabled =>
@@ -3675,4 +3685,71 @@ class AppLocalizationsKo extends AppLocalizations {
String a11yOpenAlbum(String albumName) {
return 'Open album $albumName';
}
@override
String get optionsDefaultSearchTabAlbums => 'Albums';
@override
String get optionsDefaultSearchTabTracks => 'Tracks';
@override
String get settingsFiles => 'Files & Folders';
@override
String get settingsFilesSubtitle =>
'Download location, filename, folder structure';
@override
String get settingsMetadata => 'Metadata';
@override
String get settingsMetadataSubtitle =>
'Cover art, tags, ReplayGain, providers';
@override
String get settingsLyrics => 'Lyrics';
@override
String get settingsLyricsSubtitle =>
'Embed, mode, providers, language options';
@override
String get settingsApp => 'App';
@override
String get settingsAppSubtitle => 'Updates, data, extension repo, debug';
@override
String get sectionMetadataProviders => 'Providers';
@override
String get sectionDuplicates => 'Duplicates';
@override
String get sectionLyricsProviderOptions => 'Provider Options';
@override
String get metadataProvidersTitle => 'Metadata Provider Priority';
@override
String get metadataProvidersSubtitle =>
'Drag to set search and metadata source order';
@override
String get downloadDeduplication => 'Skip Duplicate Downloads';
@override
String get downloadDeduplicationEnabled =>
'Already-downloaded tracks will be skipped';
@override
String get downloadDeduplicationDisabled =>
'All tracks will be downloaded regardless of history';
@override
String get downloadFallbackExtensions => 'Fallback Extensions';
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
}
+90 -13
View File
@@ -21,13 +21,13 @@ class AppLocalizationsNl extends AppLocalizations {
String get navSettings => 'Settings';
@override
String get navStore => 'Store';
String get navStore => 'Repo';
@override
String get homeTitle => 'Home';
@override
String get homeSubtitle => 'Paste a Spotify link or search by name';
String get homeSubtitle => 'Paste a supported URL or search by name';
@override
String get homeEmptyTitle => 'No search providers yet';
@@ -222,10 +222,10 @@ class AppLocalizationsNl extends AppLocalizations {
'Parallel downloaden kan leiden tot rate-limiting';
@override
String get optionsExtensionStore => 'Extension Store';
String get optionsExtensionStore => 'Extension Repo';
@override
String get optionsExtensionStoreSubtitle => 'Show Store tab in navigation';
String get optionsExtensionStoreSubtitle => 'Show Repo tab in navigation';
@override
String get optionsCheckUpdates => 'Check for Updates';
@@ -302,7 +302,7 @@ class AppLocalizationsNl extends AppLocalizations {
String get extensionsUninstall => 'Uninstall';
@override
String get storeTitle => 'Extension Store';
String get storeTitle => 'Extension Repo';
@override
String get storeSearch => 'Search extensions...';
@@ -957,7 +957,7 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get providerPriorityFallbackExtensionsDescription =>
'Choose which installed download extensions can be used during automatic fallback.';
'Choose which installed download extensions can be used during automatic fallback. Built-in providers still follow the priority order above.';
@override
String get providerPriorityFallbackExtensionsHint =>
@@ -1352,7 +1352,7 @@ class AppLocalizationsNl extends AppLocalizations {
String get storeEmptyNoResults => 'No extensions found';
@override
String get extensionDefaultProvider => 'Default (Deezer/Spotify)';
String get extensionDefaultProvider => 'Default (Deezer)';
@override
String get extensionDefaultProviderSubtitle => 'Use built-in search';
@@ -2130,7 +2130,7 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get tutorialExtensionsTip1 =>
'Browse the Store tab to discover useful extensions';
'Browse the Repo tab to discover useful extensions';
@override
String get tutorialExtensionsTip2 =>
@@ -2411,7 +2411,8 @@ class AppLocalizationsNl extends AppLocalizations {
String get trackConvertFormat => 'Convert Format';
@override
String get trackConvertFormatSubtitle => 'Convert to MP3 or Opus';
String get trackConvertFormatSubtitle =>
'Convert to MP3, Opus, ALAC, or FLAC';
@override
String get trackConvertTitle => 'Convert Audio';
@@ -2918,8 +2919,17 @@ class AppLocalizationsNl extends AppLocalizations {
'Pick folder via Android Storage Access Framework';
@override
String get downloadFilenameDescription =>
'Customize how your files are named.';
String downloadFilenameDescription(
Object album,
Object artist,
Object date,
Object disc,
Object title,
Object track,
Object year,
) {
return 'Customize how your files are named.';
}
@override
String get downloadFilenameInsertTag => 'Tap to insert tag:';
@@ -2965,11 +2975,11 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get downloadSelectServiceToEnable =>
'Select a compatible download provider to enable quality options';
'Select a built-in service to enable';
@override
String get downloadSelectTidalQobuz =>
'Select a compatible provider above to configure quality';
'Select Tidal or Qobuz above to configure quality';
@override
String get downloadEmbedLyricsDisabled =>
@@ -3695,4 +3705,71 @@ class AppLocalizationsNl extends AppLocalizations {
String a11yOpenAlbum(String albumName) {
return 'Open album $albumName';
}
@override
String get optionsDefaultSearchTabAlbums => 'Albums';
@override
String get optionsDefaultSearchTabTracks => 'Tracks';
@override
String get settingsFiles => 'Files & Folders';
@override
String get settingsFilesSubtitle =>
'Download location, filename, folder structure';
@override
String get settingsMetadata => 'Metadata';
@override
String get settingsMetadataSubtitle =>
'Cover art, tags, ReplayGain, providers';
@override
String get settingsLyrics => 'Lyrics';
@override
String get settingsLyricsSubtitle =>
'Embed, mode, providers, language options';
@override
String get settingsApp => 'App';
@override
String get settingsAppSubtitle => 'Updates, data, extension repo, debug';
@override
String get sectionMetadataProviders => 'Providers';
@override
String get sectionDuplicates => 'Duplicates';
@override
String get sectionLyricsProviderOptions => 'Provider Options';
@override
String get metadataProvidersTitle => 'Metadata Provider Priority';
@override
String get metadataProvidersSubtitle =>
'Drag to set search and metadata source order';
@override
String get downloadDeduplication => 'Skip Duplicate Downloads';
@override
String get downloadDeduplicationEnabled =>
'Already-downloaded tracks will be skipped';
@override
String get downloadDeduplicationDisabled =>
'All tracks will be downloaded regardless of history';
@override
String get downloadFallbackExtensions => 'Fallback Extensions';
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
}
File diff suppressed because it is too large Load Diff
+101 -25
View File
@@ -21,13 +21,13 @@ class AppLocalizationsRu extends AppLocalizations {
String get navSettings => 'Настройки';
@override
String get navStore => 'Магазин';
String get navStore => 'Repo';
@override
String get homeTitle => 'Главная';
@override
String get homeSubtitle => 'Вставьте ссылку Spotify или ищите по названию';
String get homeSubtitle => 'Paste a supported URL or search by name';
@override
String get homeEmptyTitle => 'No search providers yet';
@@ -227,11 +227,10 @@ class AppLocalizationsRu extends AppLocalizations {
'Параллельные загрузки могут вызвать ограничение скорости';
@override
String get optionsExtensionStore => 'Магазин расширений';
String get optionsExtensionStore => 'Extension Repo';
@override
String get optionsExtensionStoreSubtitle =>
'Показывать вкладку Магазин в гл. меню';
String get optionsExtensionStoreSubtitle => 'Show Repo tab in navigation';
@override
String get optionsCheckUpdates => 'Проверить обновления';
@@ -308,7 +307,7 @@ class AppLocalizationsRu extends AppLocalizations {
String get extensionsUninstall => 'Удалить';
@override
String get storeTitle => 'Магазин расширений';
String get storeTitle => 'Extension Repo';
@override
String get storeSearch => 'Поиск расширений...';
@@ -652,9 +651,9 @@ class AppLocalizationsRu extends AppLocalizations {
count,
locale: localeName,
other: 'треков',
one: 'трек',
many: 'треков',
few: 'трека',
one: 'трек',
);
return 'Удалить $count $_temp0 из истории?\n\nЭто также удалит файлы из хранилища.';
}
@@ -707,9 +706,9 @@ class AppLocalizationsRu extends AppLocalizations {
count,
locale: localeName,
other: 'треков',
one: 'трек',
many: 'треков',
few: 'трека',
one: 'трек',
);
return 'Удалено $count $_temp0';
}
@@ -971,7 +970,7 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get providerPriorityFallbackExtensionsDescription =>
'Choose which installed download extensions can be used during automatic fallback.';
'Choose which installed download extensions can be used during automatic fallback. Built-in providers still follow the priority order above.';
@override
String get providerPriorityFallbackExtensionsHint =>
@@ -1177,9 +1176,9 @@ class AppLocalizationsRu extends AppLocalizations {
count,
locale: localeName,
other: '$count треков',
one: '1 трек',
many: '$count треков',
few: '$count трека',
one: '$count трек',
);
return '$_temp0';
}
@@ -1373,7 +1372,7 @@ class AppLocalizationsRu extends AppLocalizations {
String get storeEmptyNoResults => 'No extensions found';
@override
String get extensionDefaultProvider => 'По умолчанию (Deezer/Spotify)';
String get extensionDefaultProvider => 'Default (Deezer)';
@override
String get extensionDefaultProviderSubtitle =>
@@ -1686,9 +1685,9 @@ class AppLocalizationsRu extends AppLocalizations {
count,
locale: localeName,
other: 'треков',
one: 'трек',
many: 'треков',
few: 'трека',
one: 'трек',
);
return 'Удалить $count $_temp0 из этого альбома?\n\nЭто также удалит файлы из хранилища.';
}
@@ -1710,9 +1709,9 @@ class AppLocalizationsRu extends AppLocalizations {
count,
locale: localeName,
other: 'треков',
one: 'трек',
many: 'треков',
few: 'трека',
one: 'трек',
);
return 'Удалить $count $_temp0';
}
@@ -1943,9 +1942,9 @@ class AppLocalizationsRu extends AppLocalizations {
count,
locale: localeName,
other: 'треков',
one: 'трек',
many: 'треков',
few: 'трека',
one: 'трек',
);
return '$_temp0';
}
@@ -2099,9 +2098,9 @@ class AppLocalizationsRu extends AppLocalizations {
count,
locale: localeName,
other: '$count минут',
one: '1 минуту',
many: '$count минут',
few: '$count минуты',
one: '$count минуту',
);
return '$_temp0 назад';
}
@@ -2112,9 +2111,9 @@ class AppLocalizationsRu extends AppLocalizations {
count,
locale: localeName,
other: '$count часов',
one: '1 час',
many: '$count часов',
few: '$count часа',
one: '$count час',
);
return '$_temp0 назад';
}
@@ -2180,7 +2179,7 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get tutorialExtensionsTip1 =>
'Просмотрите вкладку Магазина, чтобы найти полезные расширения';
'Browse the Repo tab to discover useful extensions';
@override
String get tutorialExtensionsTip2 =>
@@ -2464,7 +2463,8 @@ class AppLocalizationsRu extends AppLocalizations {
String get trackConvertFormat => 'Переконвертировать формат';
@override
String get trackConvertFormatSubtitle => 'Конвертировать в MP3 или Opus';
String get trackConvertFormatSubtitle =>
'Convert to MP3, Opus, ALAC, or FLAC';
@override
String get trackConvertTitle => 'Конвертировать аудио';
@@ -2598,9 +2598,9 @@ class AppLocalizationsRu extends AppLocalizations {
count,
locale: localeName,
other: '$count треков',
one: '1 трек',
many: '$count треков',
few: '$count трека',
one: '$count трек',
);
return '$_temp0';
}
@@ -2751,9 +2751,9 @@ class AppLocalizationsRu extends AppLocalizations {
count,
locale: localeName,
other: 'треков',
one: 'трек',
many: 'треков',
few: 'трека',
one: 'трек',
);
return 'Отправить $count $_temp0';
}
@@ -2768,9 +2768,9 @@ class AppLocalizationsRu extends AppLocalizations {
count,
locale: localeName,
other: 'треков',
one: 'трек',
many: 'треков',
few: 'трека',
one: 'трек',
);
return 'Конвертировать $count $_temp0';
}
@@ -2978,8 +2978,17 @@ class AppLocalizationsRu extends AppLocalizations {
'Pick folder via Android Storage Access Framework';
@override
String get downloadFilenameDescription =>
'Customize how your files are named.';
String downloadFilenameDescription(
Object album,
Object artist,
Object date,
Object disc,
Object title,
Object track,
Object year,
) {
return 'Customize how your files are named.';
}
@override
String get downloadFilenameInsertTag => 'Tap to insert tag:';
@@ -3025,11 +3034,11 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get downloadSelectServiceToEnable =>
'Select a compatible download provider to enable quality options';
'Select a built-in service to enable';
@override
String get downloadSelectTidalQobuz =>
'Select a compatible provider above to configure quality';
'Select Tidal or Qobuz above to configure quality';
@override
String get downloadEmbedLyricsDisabled =>
@@ -3755,4 +3764,71 @@ class AppLocalizationsRu extends AppLocalizations {
String a11yOpenAlbum(String albumName) {
return 'Open album $albumName';
}
@override
String get optionsDefaultSearchTabAlbums => 'Albums';
@override
String get optionsDefaultSearchTabTracks => 'Tracks';
@override
String get settingsFiles => 'Files & Folders';
@override
String get settingsFilesSubtitle =>
'Download location, filename, folder structure';
@override
String get settingsMetadata => 'Metadata';
@override
String get settingsMetadataSubtitle =>
'Cover art, tags, ReplayGain, providers';
@override
String get settingsLyrics => 'Lyrics';
@override
String get settingsLyricsSubtitle =>
'Embed, mode, providers, language options';
@override
String get settingsApp => 'App';
@override
String get settingsAppSubtitle => 'Updates, data, extension repo, debug';
@override
String get sectionMetadataProviders => 'Providers';
@override
String get sectionDuplicates => 'Duplicates';
@override
String get sectionLyricsProviderOptions => 'Provider Options';
@override
String get metadataProvidersTitle => 'Metadata Provider Priority';
@override
String get metadataProvidersSubtitle =>
'Drag to set search and metadata source order';
@override
String get downloadDeduplication => 'Skip Duplicate Downloads';
@override
String get downloadDeduplicationEnabled =>
'Already-downloaded tracks will be skipped';
@override
String get downloadDeduplicationDisabled =>
'All tracks will be downloaded regardless of history';
@override
String get downloadFallbackExtensions => 'Fallback Extensions';
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1429 -37
View File
File diff suppressed because it is too large Load Diff
+179 -130
View File
@@ -1,6 +1,6 @@
{
"@@locale": "en",
"@@last_modified": "2026-01-16",
"@@last_modified": "2026-04-28",
"appName": "SpotiFLAC",
"@appName": {
"description": "App name - DO NOT TRANSLATE"
@@ -107,7 +107,7 @@
},
"downloadFolderOrganization": "Folder Organization",
"@downloadFolderOrganization": {
"description": "Setting for folder structure"
"description": "Title of the folder organization picker bottom sheet"
},
"appearanceTitle": "Appearance",
"@appearanceTitle": {
@@ -153,7 +153,7 @@
"@optionsPrimaryProvider": {
"description": "Main search provider setting"
},
"optionsPrimaryProviderSubtitle": "Service used when searching by track name.",
"optionsPrimaryProviderSubtitle": "Service used for searching by track or album name",
"@optionsPrimaryProviderSubtitle": {
"description": "Subtitle for primary provider"
},
@@ -174,9 +174,9 @@
"@optionsDefaultSearchTabSubtitle": {
"description": "Subtitle for the preferred default search tab setting"
},
"optionsSwitchBack": "Choose another provider below to stop using the current extension",
"optionsSwitchBack": "Tap Deezer or Spotify to switch back from extension",
"@optionsSwitchBack": {
"description": "Hint to switch away from the current provider selection"
"description": "Hint to switch back to built-in providers"
},
"optionsAutoFallback": "Auto Fallback",
"@optionsAutoFallback": {
@@ -194,15 +194,15 @@
"@optionsUseExtensionProvidersOn": {
"description": "Status when extension providers enabled"
},
"optionsUseExtensionProvidersOff": "Extensions are turned off",
"optionsUseExtensionProvidersOff": "Using built-in providers only",
"@optionsUseExtensionProvidersOff": {
"description": "Status when extension providers are disabled"
"description": "Status when extension providers disabled"
},
"optionsEmbedLyrics": "Embed Lyrics",
"@optionsEmbedLyrics": {
"description": "Embed lyrics in audio files"
},
"optionsEmbedLyricsSubtitle": "Embed synced lyrics into FLAC files",
"optionsEmbedLyricsSubtitle": "Save synced lyrics alongside your downloaded tracks",
"@optionsEmbedLyricsSubtitle": {
"description": "Subtitle for embed lyrics"
},
@@ -749,7 +749,7 @@
},
"dialogDownload": "Download",
"@dialogDownload": {
"description": "Dialog button - download action"
"description": "Confirm button in Download All dialog"
},
"dialogDiscard": "Discard",
"@dialogDiscard": {
@@ -1239,7 +1239,7 @@
"@providerPriorityFallbackExtensionsTitle": {
"description": "Section title for choosing which download extensions can be used as fallback providers"
},
"providerPriorityFallbackExtensionsDescription": "Choose which installed download extensions can be used during automatic fallback.",
"providerPriorityFallbackExtensionsDescription": "Choose which installed download extensions can be used during automatic fallback. Built-in providers still follow the priority order above.",
"@providerPriorityFallbackExtensionsDescription": {
"description": "Section description for extension fallback selection"
},
@@ -1247,9 +1247,9 @@
"@providerPriorityFallbackExtensionsHint": {
"description": "Hint below the extension fallback selection list"
},
"providerBuiltIn": "Legacy",
"providerBuiltIn": "Built-in",
"@providerBuiltIn": {
"description": "Label for legacy providers kept for compatibility"
"description": "Label for built-in providers (Tidal/Qobuz)"
},
"providerExtension": "Extension",
"@providerExtension": {
@@ -1469,11 +1469,11 @@
"@settingsAppearanceSubtitle": {
"description": "Appearance settings description"
},
"settingsDownloadSubtitle": "Service, quality, filename format",
"settingsDownloadSubtitle": "Service, quality, fallback",
"@settingsDownloadSubtitle": {
"description": "Download settings description"
},
"settingsOptionsSubtitle": "Fallback, lyrics, cover art, updates",
"settingsOptionsSubtitle": "Fallback, metadata, lyrics, cover art",
"@settingsOptionsSubtitle": {
"description": "Options settings description"
},
@@ -3659,18 +3659,17 @@
}
}
},
"downloadUseAlbumArtistForFoldersAlbumSubtitle": "Artist folders use Album Artist when available",
"downloadUseAlbumArtistForFoldersAlbumSubtitle": "Folder named after Album Artist tag",
"@downloadUseAlbumArtistForFoldersAlbumSubtitle": {
"description": "Subtitle when Album Artist is used for folder naming"
"description": "Subtitle when album artist is used for folder names"
},
"downloadUseAlbumArtistForFoldersTrackSubtitle": "Artist folders use Track Artist only",
"downloadUseAlbumArtistForFoldersTrackSubtitle": "Folder named after Track Artist tag",
"@downloadUseAlbumArtistForFoldersTrackSubtitle": {
"description": "Subtitle when Track Artist is used for folder naming"
"description": "Subtitle when track artist is used for folder names"
},
"lyricsProvidersTitle": "Lyrics Providers",
"lyricsProvidersTitle": "Lyrics Provider Priority",
"@lyricsProvidersTitle": {
"description": "Title for the lyrics provider priority page"
"description": "Settings item title for lyrics provider order"
},
"lyricsProvidersDescription": "Enable, disable and reorder lyrics sources. Providers are tried top-to-bottom until lyrics are found.",
"@lyricsProvidersDescription": {
@@ -3734,7 +3733,6 @@
"@lyricsProviderExtensionDesc": {
"description": "Generic description for extension-based lyrics providers"
},
"safMigrationTitle": "Storage Update Required",
"@safMigrationTitle": {
"description": "Title of SAF migration dialog"
@@ -3751,16 +3749,14 @@
"@safMigrationSuccess": {
"description": "Snackbar after successfully migrating to SAF"
},
"settingsDonate": "Donate",
"settingsDonate": "Support Development",
"@settingsDonate": {
"description": "Settings menu item - donate"
"description": "Settings menu item - donate page"
},
"settingsDonateSubtitle": "Support SpotiFLAC-Mobile development",
"settingsDonateSubtitle": "Buy the developer a coffee",
"@settingsDonateSubtitle": {
"description": "Subtitle for donate menu item"
},
"tooltipLoveAll": "Love All",
"@tooltipLoveAll": {
"description": "Tooltip for the Love All button on album/playlist screens"
@@ -3787,10 +3783,9 @@
}
}
},
"dialogDownloadAllTitle": "Download All",
"@dialogDownloadAllTitle": {
"description": "Title of the Download All confirmation dialog"
"description": "Dialog title for bulk download confirmation"
},
"dialogDownloadAllMessage": "Download {count} tracks?",
"@dialogDownloadAllMessage": {
@@ -3801,11 +3796,6 @@
}
}
},
"dialogDownload": "Download",
"@dialogDownload": {
"description": "Confirm button in Download All dialog"
},
"homeSkipAlreadyDownloaded": "Skip already downloaded songs",
"@homeSkipAlreadyDownloaded": {
"description": "Checkbox label in import dialog to skip already-downloaded songs"
@@ -3818,7 +3808,6 @@
"@homeAlbumInfoUnavailable": {
"description": "Snackbar when album info cannot be loaded"
},
"snackbarLoadingCueSheet": "Loading CUE sheet...",
"@snackbarLoadingCueSheet": {
"description": "Snackbar while loading a CUE sheet file"
@@ -3848,190 +3837,181 @@
"@snackbarNoActionDefined": {
"description": "Snackbar when an extension button has no action configured"
},
"noTracksFoundForAlbum": "No tracks found for this album",
"@noTracksFoundForAlbum": {
"description": "Empty state message when an album has no tracks"
},
"downloadLocationSubtitle": "Choose storage mode for downloaded files.",
"downloadLocationSubtitle": "Choose where to save your downloaded tracks",
"@downloadLocationSubtitle": {
"description": "Subtitle text in Android download location bottom sheet"
"description": "Subtitle shown in the download location picker sheet"
},
"storageModeAppFolder": "App folder (non-SAF)",
"storageModeAppFolder": "App Folder (Recommended)",
"@storageModeAppFolder": {
"description": "Storage mode option - use legacy app folder"
"description": "Storage mode option - app-managed folder"
},
"storageModeAppFolderSubtitle": "Use default Music/SpotiFLAC path",
"storageModeAppFolderSubtitle": "Saves to Music/SpotiFLAC by default",
"@storageModeAppFolderSubtitle": {
"description": "Subtitle for app folder storage mode"
},
"storageModeSaf": "SAF folder",
"storageModeSaf": "Custom Folder (SAF)",
"@storageModeSaf": {
"description": "Storage mode option - use Android SAF picker"
"description": "Storage mode option - Storage Access Framework"
},
"storageModeSafSubtitle": "Pick folder via Android Storage Access Framework",
"storageModeSafSubtitle": "Pick any folder, including SD card",
"@storageModeSafSubtitle": {
"description": "Subtitle for SAF storage mode"
},
"downloadFilenameDescription": "Customize how your files are named.",
"downloadFilenameDescription": "Use {artist}, {title}, {album}, {track}, {year}, {date}, {disc} as placeholders.",
"@downloadFilenameDescription": {
"description": "Description text in filename format bottom sheet"
"description": "Description shown in filename format editor"
},
"downloadFilenameInsertTag": "Tap to insert tag:",
"@downloadFilenameInsertTag": {
"description": "Label above filename tag chips"
},
"downloadSeparateSinglesEnabled": "Albums/ and Singles/ folders",
"downloadSeparateSinglesEnabled": "Singles and EPs saved in a separate folder",
"@downloadSeparateSinglesEnabled": {
"description": "Subtitle when separate singles folder is enabled"
"description": "Subtitle when separate singles folder is on"
},
"downloadSeparateSinglesDisabled": "All files in same structure",
"downloadSeparateSinglesDisabled": "Singles and albums saved in the same folder",
"@downloadSeparateSinglesDisabled": {
"description": "Subtitle when separate singles folder is disabled"
"description": "Subtitle when separate singles folder is off"
},
"downloadArtistNameFilters": "Artist Name Filters",
"@downloadArtistNameFilters": {
"description": "Setting title for artist folder filter options"
},
"downloadCreatePlaylistSourceFolder": "Create playlist source folder",
"downloadCreatePlaylistSourceFolder": "Playlist Source Folder",
"@downloadCreatePlaylistSourceFolder": {
"description": "Setting title for adding a playlist folder prefix before the normal organization structure"
"description": "Setting to create a subfolder per playlist source"
},
"downloadCreatePlaylistSourceFolderEnabled": "Playlist downloads use Playlist/ plus your normal folder structure.",
"downloadCreatePlaylistSourceFolderEnabled": "A subfolder is created for each playlist",
"@downloadCreatePlaylistSourceFolderEnabled": {
"description": "Subtitle when playlist source folder prefix is enabled"
"description": "Subtitle when playlist folder is enabled"
},
"downloadCreatePlaylistSourceFolderDisabled": "Playlist downloads use the normal folder structure only.",
"downloadCreatePlaylistSourceFolderDisabled": "All tracks saved directly to download folder",
"@downloadCreatePlaylistSourceFolderDisabled": {
"description": "Subtitle when playlist source folder prefix is disabled"
"description": "Subtitle when playlist folder is disabled"
},
"downloadCreatePlaylistSourceFolderRedundant": "By Playlist already places downloads inside a playlist folder.",
"downloadCreatePlaylistSourceFolderRedundant": "Handled by folder organization setting",
"@downloadCreatePlaylistSourceFolderRedundant": {
"description": "Subtitle when playlist folder prefix setting is redundant because folder organization is already by playlist"
"description": "Subtitle when folder organization is already set to playlist"
},
"downloadSongLinkRegion": "SongLink Region",
"@downloadSongLinkRegion": {
"description": "Setting title for SongLink country region"
"description": "Setting for SongLink region used during fallback resolution"
},
"downloadNetworkCompatibilityMode": "Network compatibility mode",
"downloadNetworkCompatibilityMode": "Network Compatibility Mode",
"@downloadNetworkCompatibilityMode": {
"description": "Setting title for network compatibility toggle"
"description": "Setting for legacy TLS/network handling"
},
"downloadNetworkCompatibilityModeEnabled": "Enabled: try HTTP + accept invalid TLS certificates (unsafe)",
"downloadNetworkCompatibilityModeEnabled": "Using legacy TLS settings for older networks",
"@downloadNetworkCompatibilityModeEnabled": {
"description": "Subtitle when network compatibility mode is enabled"
"description": "Subtitle when network compatibility mode is on"
},
"downloadNetworkCompatibilityModeDisabled": "Off: strict HTTPS certificate validation (recommended)",
"downloadNetworkCompatibilityModeDisabled": "Using standard network settings",
"@downloadNetworkCompatibilityModeDisabled": {
"description": "Subtitle when network compatibility mode is disabled"
"description": "Subtitle when network compatibility mode is off"
},
"downloadSelectServiceToEnable": "Select a compatible download provider to enable quality options",
"downloadSelectServiceToEnable": "Select Tidal or Qobuz to enable this option",
"@downloadSelectServiceToEnable": {
"description": "Hint shown instead of Ask-quality subtitle when the selected provider does not expose built-in quality controls"
"description": "Subtitle when quality picker is disabled due to extension service"
},
"downloadSelectTidalQobuz": "Select a compatible provider above to configure quality",
"downloadSelectTidalQobuz": "Select Tidal or Qobuz to choose audio quality",
"@downloadSelectTidalQobuz": {
"description": "Info hint when the selected provider does not expose built-in quality controls"
"description": "Info shown when a non-built-in service is selected"
},
"downloadEmbedLyricsDisabled": "Disabled while Embed Metadata is turned off",
"downloadEmbedLyricsDisabled": "Enable metadata embedding first",
"@downloadEmbedLyricsDisabled": {
"description": "Subtitle for Embed Lyrics when Embed Metadata is disabled"
"description": "Subtitle when lyrics embedding is blocked by metadata toggle"
},
"downloadNeteaseIncludeTranslation": "Netease: Include Translation",
"@downloadNeteaseIncludeTranslation": {
"description": "Toggle title for including Netease translated lyrics"
"description": "Setting to include translated lyrics from Netease"
},
"downloadNeteaseIncludeTranslationEnabled": "Append translated lyrics when available",
"downloadNeteaseIncludeTranslationEnabled": "Chinese translation lines included",
"@downloadNeteaseIncludeTranslationEnabled": {
"description": "Subtitle when Netease translation is enabled"
"description": "Subtitle when Netease translation is on"
},
"downloadNeteaseIncludeTranslationDisabled": "Use original lyrics only",
"downloadNeteaseIncludeTranslationDisabled": "Original lyrics only",
"@downloadNeteaseIncludeTranslationDisabled": {
"description": "Subtitle when Netease translation is disabled"
"description": "Subtitle when Netease translation is off"
},
"downloadNeteaseIncludeRomanization": "Netease: Include Romanization",
"@downloadNeteaseIncludeRomanization": {
"description": "Toggle title for including Netease romanized lyrics"
"description": "Setting to include romanized lyrics from Netease"
},
"downloadNeteaseIncludeRomanizationEnabled": "Append romanized lyrics when available",
"downloadNeteaseIncludeRomanizationEnabled": "Romanization lines included",
"@downloadNeteaseIncludeRomanizationEnabled": {
"description": "Subtitle when Netease romanization is enabled"
"description": "Subtitle when Netease romanization is on"
},
"downloadNeteaseIncludeRomanizationDisabled": "Disabled",
"downloadNeteaseIncludeRomanizationDisabled": "No romanization",
"@downloadNeteaseIncludeRomanizationDisabled": {
"description": "Subtitle when Netease romanization is disabled"
"description": "Subtitle when Netease romanization is off"
},
"downloadAppleQqMultiPerson": "Apple/QQ Multi-Person Word-by-Word",
"downloadAppleQqMultiPerson": "Apple / QQ: Multi-Person Lyrics",
"@downloadAppleQqMultiPerson": {
"description": "Toggle title for Apple/QQ multi-person word-by-word lyrics"
"description": "Setting for word-by-word multi-person lyrics from Apple Music and QQ Music"
},
"downloadAppleQqMultiPersonEnabled": "Enable v1/v2 speaker and [bg:] tags",
"downloadAppleQqMultiPersonEnabled": "Speaker labels included for duets and group tracks",
"@downloadAppleQqMultiPersonEnabled": {
"description": "Subtitle when multi-person word-by-word is enabled"
"description": "Subtitle when multi-person lyrics is on"
},
"downloadAppleQqMultiPersonDisabled": "Simplified word-by-word formatting",
"downloadAppleQqMultiPersonDisabled": "Standard lyrics without speaker labels",
"@downloadAppleQqMultiPersonDisabled": {
"description": "Subtitle when multi-person word-by-word is disabled"
"description": "Subtitle when multi-person lyrics is off"
},
"downloadMusixmatchLanguage": "Musixmatch Language",
"@downloadMusixmatchLanguage": {
"description": "Setting title for Musixmatch language preference"
"description": "Setting for Musixmatch lyrics translation language"
},
"downloadMusixmatchLanguageAuto": "Auto (original)",
"downloadMusixmatchLanguageAuto": "Auto (original language)",
"@downloadMusixmatchLanguageAuto": {
"description": "Option label when Musixmatch uses original language"
"description": "Subtitle when no language is set"
},
"downloadFilterContributing": "Filter contributing artists in Album Artist",
"downloadFilterContributing": "Filter Contributing Artists",
"@downloadFilterContributing": {
"description": "Toggle title for filtering contributing artists in Album Artist metadata"
"description": "Setting to strip contributing artists from Album Artist folder name"
},
"downloadFilterContributingEnabled": "Album Artist metadata uses primary artist only",
"downloadFilterContributingEnabled": "Contributing artists removed from Album Artist folder name",
"@downloadFilterContributingEnabled": {
"description": "Subtitle when contributing artist filter is enabled"
"description": "Subtitle when contributing artist filter is on"
},
"downloadFilterContributingDisabled": "Keep full Album Artist metadata value",
"downloadFilterContributingDisabled": "Full Album Artist string used",
"@downloadFilterContributingDisabled": {
"description": "Subtitle when contributing artist filter is disabled"
"description": "Subtitle when contributing artist filter is off"
},
"downloadProvidersNoneEnabled": "None enabled",
"downloadProvidersNoneEnabled": "No providers enabled",
"@downloadProvidersNoneEnabled": {
"description": "Subtitle for lyrics providers setting when no providers are enabled"
"description": "Shown when no lyrics providers are active"
},
"downloadMusixmatchLanguageCode": "Language code",
"@downloadMusixmatchLanguageCode": {
"description": "Label for the Musixmatch language code text field"
"description": "Label for Musixmatch language input field"
},
"downloadMusixmatchLanguageHint": "auto / en / es / ja",
"downloadMusixmatchLanguageHint": "e.g. en, de, ja",
"@downloadMusixmatchLanguageHint": {
"description": "Hint text for the Musixmatch language code field"
"description": "Placeholder for Musixmatch language input"
},
"downloadMusixmatchLanguageDesc": "Set preferred language code (example: en, es, ja). Leave empty for auto.",
"downloadMusixmatchLanguageDesc": "Enter a BCP-47 language code (e.g. en, de, ja) to request translated lyrics from Musixmatch.",
"@downloadMusixmatchLanguageDesc": {
"description": "Description in the Musixmatch language picker"
"description": "Description in Musixmatch language picker"
},
"downloadMusixmatchAuto": "Auto",
"@downloadMusixmatchAuto": {
"description": "Button to reset Musixmatch language to automatic"
"description": "Button to clear Musixmatch language (use auto)"
},
"downloadNetworkAnySubtitle": "WiFi + Mobile Data",
"downloadNetworkAnySubtitle": "Use WiFi or mobile data",
"@downloadNetworkAnySubtitle": {
"description": "Subtitle for 'Any' network mode option"
"description": "Subtitle for any-network option in picker"
},
"downloadNetworkWifiOnlySubtitle": "Pause downloads on mobile data",
"downloadNetworkWifiOnlySubtitle": "Downloads pause when on mobile data",
"@downloadNetworkWifiOnlySubtitle": {
"description": "Subtitle for 'WiFi only' network mode option"
"description": "Subtitle for WiFi-only option in picker"
},
"downloadSongLinkRegionDesc": "Used as userCountry for SongLink API lookup.",
"downloadSongLinkRegionDesc": "Region used when resolving track links via SongLink. Choose the country where your streaming services are available.",
"@downloadSongLinkRegionDesc": {
"description": "Description in the SongLink region picker"
},
"downloadFolderOrganization": "Folder Organization",
"@downloadFolderOrganization": {
"description": "Title of the folder organization picker bottom sheet"
"description": "Description in SongLink region picker"
},
"snackbarUnsupportedAudioFormat": "Unsupported audio format",
"@snackbarUnsupportedAudioFormat": {
@@ -4041,10 +4021,6 @@
"@cacheRefresh": {
"description": "Tooltip for refresh button on cache management page"
},
"dialogDownloadAllTitle": "Download All",
"@dialogDownloadAllTitle": {
"description": "Dialog title for bulk download confirmation"
},
"dialogDownloadPlaylistsMessage": "Download {trackCount} {trackCount, plural, =1{track} other{tracks}} from {playlistCount} {playlistCount, plural, =1{playlist} other{playlists}}?",
"@dialogDownloadPlaylistsMessage": {
"description": "Dialog message for bulk playlist download confirmation",
@@ -4172,7 +4148,6 @@
"@editMetadataSelectEmpty": {
"description": "Button to select only fields that are currently empty"
},
"queueDownloadingCount": "Downloading ({count})",
"@queueDownloadingCount": {
"description": "Header for active downloads section with count",
@@ -4296,7 +4271,6 @@
"@audioAnalysisSamples": {
"description": "Total samples metric label"
},
"extensionsSearchWith": "Search with {providerName}",
"@extensionsSearchWith": {
"description": "Extensions page - subtitle for built-in search provider option",
@@ -4316,7 +4290,7 @@
},
"extensionsHomeFeedAuto": "Auto",
"@extensionsHomeFeedAuto": {
"description": "Extensions page - home feed provider option: auto"
"description": "Label for auto-selected search provider"
},
"extensionsHomeFeedAutoSubtitle": "Automatically select the best available",
"@extensionsHomeFeedAutoSubtitle": {
@@ -4343,7 +4317,6 @@
"@extensionsNoHomeFeedExtensions": {
"description": "Extensions page - shown when no installed extension has home feed"
},
"sortAlphaAsc": "A-Z",
"@sortAlphaAsc": {
"description": "Sort option - alphabetical ascending"
@@ -4369,7 +4342,6 @@
"@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"
@@ -4378,7 +4350,6 @@
"@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",
@@ -4388,7 +4359,6 @@
}
}
},
"errorLoadAlbum": "Failed to load album",
"@errorLoadAlbum": {
"description": "Error state shown when album fails to load"
@@ -4401,7 +4371,6 @@
"@errorLoadArtist": {
"description": "Error state shown when artist fails to load"
},
"notifChannelDownloadName": "Download Progress",
"@notifChannelDownloadName": {
"description": "Android notification channel name for download progress"
@@ -4919,5 +4888,85 @@
"type": "String"
}
}
},
"optionsDefaultSearchTabAlbums": "Albums",
"@optionsDefaultSearchTabAlbums": {
"description": "Default search tab option - Albums tab"
},
"optionsDefaultSearchTabTracks": "Tracks",
"@optionsDefaultSearchTabTracks": {
"description": "Default search tab option - Tracks tab"
},
"settingsFiles": "Files & Folders",
"@settingsFiles": {
"description": "Settings menu item - file and folder settings"
},
"settingsFilesSubtitle": "Download location, filename, folder structure",
"@settingsFilesSubtitle": {
"description": "Subtitle for files & folders settings"
},
"settingsMetadata": "Metadata",
"@settingsMetadata": {
"description": "Settings menu item - metadata settings"
},
"settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers",
"@settingsMetadataSubtitle": {
"description": "Subtitle for metadata settings"
},
"settingsLyrics": "Lyrics",
"@settingsLyrics": {
"description": "Settings menu item - lyrics settings"
},
"settingsLyricsSubtitle": "Embed, mode, providers, language options",
"@settingsLyricsSubtitle": {
"description": "Subtitle for lyrics settings"
},
"settingsApp": "App",
"@settingsApp": {
"description": "Settings menu item - app settings"
},
"settingsAppSubtitle": "Updates, data, extension repo, debug",
"@settingsAppSubtitle": {
"description": "Subtitle for app settings"
},
"sectionMetadataProviders": "Providers",
"@sectionMetadataProviders": {
"description": "Settings section header for metadata providers"
},
"sectionDuplicates": "Duplicates",
"@sectionDuplicates": {
"description": "Settings section header for deduplication"
},
"sectionLyricsProviderOptions": "Provider Options",
"@sectionLyricsProviderOptions": {
"description": "Settings section header for per-provider lyrics options"
},
"metadataProvidersTitle": "Metadata Provider Priority",
"@metadataProvidersTitle": {
"description": "Settings item title for metadata provider order"
},
"metadataProvidersSubtitle": "Drag to set search and metadata source order",
"@metadataProvidersSubtitle": {
"description": "Subtitle for metadata provider priority item"
},
"downloadDeduplication": "Skip Duplicate Downloads",
"@downloadDeduplication": {
"description": "Setting - skip tracks already in download history"
},
"downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped",
"@downloadDeduplicationEnabled": {
"description": "Subtitle when deduplication is on"
},
"downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history",
"@downloadDeduplicationDisabled": {
"description": "Subtitle when deduplication is off"
},
"downloadFallbackExtensions": "Fallback Extensions",
"@downloadFallbackExtensions": {
"description": "Settings item for configuring fallback extension providers"
},
"downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback",
"@downloadFallbackExtensionsSubtitle": {
"description": "Subtitle for fallback extensions item"
}
}
File diff suppressed because it is too large Load Diff
+1432 -40
View File
File diff suppressed because it is too large Load Diff
+1425 -33
View File
File diff suppressed because it is too large Load Diff
+1401 -9
View File
File diff suppressed because it is too large Load Diff
+1322 -35
View File
File diff suppressed because it is too large Load Diff
+1405 -13
View File
File diff suppressed because it is too large Load Diff
+1401 -9
View File
File diff suppressed because it is too large Load Diff
+1401 -9
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1407 -15
View File
File diff suppressed because it is too large Load Diff
+1412 -20
View File
File diff suppressed because it is too large Load Diff
+1227 -684
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1402 -10
View File
File diff suppressed because it is too large Load Diff
+1402 -10
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -20,6 +20,7 @@ const List<Locale> filteredSupportedLocales = <Locale>[
Locale('pt', 'PT'),
Locale('ja'),
Locale('tr'),
Locale('uk'),
];
/// Set of locale codes for quick lookup.
@@ -31,4 +32,5 @@ const Set<String> filteredLocaleCodes = <String>{
'pt_PT',
'ja',
'tr',
'uk',
};
+6
View File
@@ -85,6 +85,9 @@ class AppSettings {
final String
lastSeenVersion; // Last app version the user has acknowledged (e.g. '3.7.0')
final bool
deduplicateDownloads; // Skip downloading tracks already present in history
const AppSettings({
this.defaultService = '',
this.audioQuality = 'LOSSLESS',
@@ -148,6 +151,7 @@ class AppSettings {
this.lyricsMultiPersonWordByWord = false,
this.musixmatchLanguage = '',
this.lastSeenVersion = '',
this.deduplicateDownloads = true,
});
AppSettings copyWith({
@@ -210,6 +214,7 @@ class AppSettings {
bool? lyricsMultiPersonWordByWord,
String? musixmatchLanguage,
String? lastSeenVersion,
bool? deduplicateDownloads,
}) {
return AppSettings(
defaultService: defaultService ?? this.defaultService,
@@ -288,6 +293,7 @@ class AppSettings {
lyricsMultiPersonWordByWord ?? this.lyricsMultiPersonWordByWord,
musixmatchLanguage: musixmatchLanguage ?? this.musixmatchLanguage,
lastSeenVersion: lastSeenVersion ?? this.lastSeenVersion,
deduplicateDownloads: deduplicateDownloads ?? this.deduplicateDownloads,
);
}
+5
View File
@@ -563,6 +563,11 @@ class SettingsNotifier extends Notifier<AppSettings> {
state = state.copyWith(hasCompletedTutorial: true);
_saveSettings();
}
void setDeduplicateDownloads(bool enabled) {
state = state.copyWith(deduplicateDownloads: enabled);
_saveSettings();
}
}
final settingsProvider = NotifierProvider<SettingsNotifier, AppSettings>(
+364
View File
@@ -0,0 +1,364 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/providers/download_queue_provider.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/utils/app_bar_layout.dart';
import 'package:spotiflac_android/widgets/settings_group.dart';
class AppSettingsPage extends ConsumerWidget {
const AppSettingsPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final settings = ref.watch(settingsProvider);
final colorScheme = Theme.of(context).colorScheme;
final topPadding = normalizedHeaderTopPadding(context);
return PopScope(
canPop: true,
child: Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 120 + topPadding,
collapsedHeight: kToolbarHeight,
floating: false,
pinned: true,
backgroundColor: colorScheme.surface,
surfaceTintColor: Colors.transparent,
leading: IconButton(
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
),
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
final maxHeight = 120 + topPadding;
final minHeight = kToolbarHeight + topPadding;
final expandRatio =
((constraints.maxHeight - minHeight) /
(maxHeight - minHeight))
.clamp(0.0, 1.0);
final leftPadding = 56 - (32 * expandRatio);
return FlexibleSpaceBar(
expandedTitleScale: 1.0,
titlePadding: EdgeInsets.only(
left: leftPadding,
bottom: 16,
),
title: Text(
context.l10n.settingsApp,
style: TextStyle(
fontSize: 20 + (8 * expandRatio),
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
);
},
),
),
// ── Updates ────────────────────────────────────────────────
SliverToBoxAdapter(
child: SettingsSectionHeader(title: context.l10n.sectionApp),
),
SliverToBoxAdapter(
child: SettingsGroup(
children: [
SettingsSwitchItem(
icon: Icons.extension,
title: context.l10n.optionsExtensionStore,
subtitle: context.l10n.optionsExtensionStoreSubtitle,
value: settings.showExtensionStore,
onChanged: (v) => ref
.read(settingsProvider.notifier)
.setShowExtensionStore(v),
),
SettingsSwitchItem(
icon: Icons.system_update,
title: context.l10n.optionsCheckUpdates,
subtitle: context.l10n.optionsCheckUpdatesSubtitle,
value: settings.checkForUpdates,
onChanged: (v) => ref
.read(settingsProvider.notifier)
.setCheckForUpdates(v),
showDivider: settings.checkForUpdates,
),
if (settings.checkForUpdates)
_UpdateChannelSelector(
currentChannel: settings.updateChannel,
onChanged: (v) => ref
.read(settingsProvider.notifier)
.setUpdateChannel(v),
),
],
),
),
// ── Data ───────────────────────────────────────────────────
SliverToBoxAdapter(
child: SettingsSectionHeader(title: context.l10n.sectionData),
),
SliverToBoxAdapter(
child: SettingsGroup(
children: [
SettingsItem(
icon: Icons.cleaning_services_outlined,
title: context.l10n.cleanupOrphanedDownloads,
subtitle: context.l10n.cleanupOrphanedDownloadsSubtitle,
onTap: () => _cleanupOrphanedDownloads(context, ref),
),
SettingsItem(
icon: Icons.delete_forever,
title: context.l10n.optionsClearHistory,
subtitle: context.l10n.optionsClearHistorySubtitle,
onTap: () =>
_showClearHistoryDialog(context, ref, colorScheme),
showDivider: false,
),
],
),
),
// ── Debug ──────────────────────────────────────────────────
SliverToBoxAdapter(
child: SettingsSectionHeader(title: context.l10n.sectionDebug),
),
SliverToBoxAdapter(
child: SettingsGroup(
children: [
SettingsSwitchItem(
icon: Icons.bug_report,
title: context.l10n.optionsDetailedLogging,
subtitle: settings.enableLogging
? context.l10n.optionsDetailedLoggingOn
: context.l10n.optionsDetailedLoggingOff,
value: settings.enableLogging,
onChanged: (v) =>
ref.read(settingsProvider.notifier).setEnableLogging(v),
showDivider: false,
),
],
),
),
const SliverToBoxAdapter(child: SizedBox(height: 32)),
],
),
),
);
}
void _showClearHistoryDialog(
BuildContext context,
WidgetRef ref,
ColorScheme colorScheme,
) {
showDialog<void>(
context: context,
builder: (context) => AlertDialog(
title: Text(context.l10n.dialogClearHistoryTitle),
content: Text(context.l10n.dialogClearHistoryMessage),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(context.l10n.dialogCancel),
),
TextButton(
onPressed: () {
ref.read(downloadHistoryProvider.notifier).clearHistory();
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.l10n.snackbarHistoryCleared)),
);
},
child: Text(
context.l10n.dialogClear,
style: TextStyle(color: colorScheme.error),
),
),
],
),
);
}
Future<void> _cleanupOrphanedDownloads(
BuildContext context,
WidgetRef ref,
) async {
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
content: Row(
children: [
const CircularProgressIndicator(),
const SizedBox(width: 16),
Text(context.l10n.cleanupOrphanedDownloads),
],
),
),
);
try {
final removed = await ref
.read(downloadHistoryProvider.notifier)
.cleanupOrphanedDownloads();
if (context.mounted) {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
removed > 0
? context.l10n.cleanupOrphanedDownloadsResult(removed)
: context.l10n.cleanupOrphanedDownloadsNone,
),
),
);
}
} catch (e) {
if (context.mounted) {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.l10n.snackbarError(e.toString()))),
);
}
}
}
}
class _UpdateChannelSelector extends StatelessWidget {
final String currentChannel;
final ValueChanged<String> onChanged;
const _UpdateChannelSelector({
required this.currentChannel,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.new_releases,
color: colorScheme.onSurfaceVariant,
size: 24,
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.l10n.optionsUpdateChannel,
style: Theme.of(context).textTheme.bodyLarge,
),
const SizedBox(height: 2),
Text(
currentChannel == 'preview'
? context.l10n.optionsUpdateChannelPreview
: context.l10n.optionsUpdateChannelStable,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
],
),
),
],
),
const SizedBox(height: 16),
Row(
children: [
_ChannelChip(
label: context.l10n.channelStable,
isSelected: currentChannel == 'stable',
onTap: () => onChanged('stable'),
),
const SizedBox(width: 8),
_ChannelChip(
label: context.l10n.channelPreview,
isSelected: currentChannel == 'preview',
onTap: () => onChanged('preview'),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Icon(
Icons.info_outline,
size: 16,
color: colorScheme.onSurfaceVariant,
),
const SizedBox(width: 8),
Expanded(
child: Text(
context.l10n.optionsUpdateChannelWarning,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
),
],
),
],
),
);
}
}
class _ChannelChip extends StatelessWidget {
final String label;
final bool isSelected;
final VoidCallback onTap;
const _ChannelChip({
required this.label,
required this.isSelected,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
final unselectedColor = isDark
? Color.alphaBlend(
Colors.white.withValues(alpha: 0.05),
colorScheme.surface,
)
: colorScheme.surfaceContainerHigh;
return Expanded(
child: Material(
color: isSelected ? colorScheme.primaryContainer : unselectedColor,
borderRadius: BorderRadius.circular(12),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Center(
child: Text(
label,
style: TextStyle(
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
color: isSelected
? colorScheme.onPrimaryContainer
: colorScheme.onSurfaceVariant,
),
),
),
),
),
),
);
}
}
@@ -735,6 +735,7 @@ class _LanguageSelector extends StatelessWidget {
('pt_PT', 'Português (Brasil)', Icons.language),
('ru', 'Русский', Icons.language),
('tr', 'Türkçe', Icons.language),
('uk', 'Українська', Icons.language),
('zh', '简体中文', Icons.language),
('zh_CN', '简体中文 (中国)', Icons.language),
('zh_TW', '繁體中文', Icons.language),
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,373 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/utils/app_bar_layout.dart';
import 'package:spotiflac_android/screens/settings/lyrics_provider_priority_page.dart';
import 'package:spotiflac_android/widgets/settings_group.dart';
class LyricsSettingsPage extends ConsumerWidget {
const LyricsSettingsPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final settings = ref.watch(settingsProvider);
final colorScheme = Theme.of(context).colorScheme;
final topPadding = normalizedHeaderTopPadding(context);
return PopScope(
canPop: true,
child: Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 120 + topPadding,
collapsedHeight: kToolbarHeight,
floating: false,
pinned: true,
backgroundColor: colorScheme.surface,
surfaceTintColor: Colors.transparent,
leading: IconButton(
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
),
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
final maxHeight = 120 + topPadding;
final minHeight = kToolbarHeight + topPadding;
final expandRatio =
((constraints.maxHeight - minHeight) /
(maxHeight - minHeight))
.clamp(0.0, 1.0);
final leftPadding = 56 - (32 * expandRatio);
return FlexibleSpaceBar(
expandedTitleScale: 1.0,
titlePadding: EdgeInsets.only(
left: leftPadding,
bottom: 16,
),
title: Text(
context.l10n.settingsLyrics,
style: TextStyle(
fontSize: 20 + (8 * expandRatio),
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
);
},
),
),
// ── Lyrics Embedding ───────────────────────────────────────
SliverToBoxAdapter(
child: SettingsSectionHeader(title: context.l10n.sectionLyrics),
),
SliverToBoxAdapter(
child: SettingsGroup(
children: [
SettingsSwitchItem(
icon: Icons.subtitles_outlined,
title: context.l10n.optionsEmbedLyrics,
subtitle: settings.embedMetadata
? context.l10n.optionsEmbedLyricsSubtitle
: context.l10n.downloadEmbedLyricsDisabled,
value: settings.embedLyrics,
enabled: settings.embedMetadata,
onChanged: (value) => ref
.read(settingsProvider.notifier)
.setEmbedLyrics(value),
showDivider:
settings.embedMetadata && settings.embedLyrics,
),
if (settings.embedMetadata && settings.embedLyrics) ...[
SettingsItem(
icon: Icons.lyrics_outlined,
title: context.l10n.lyricsMode,
subtitle: _getLyricsModeLabel(
context,
settings.lyricsMode,
),
onTap: () =>
_showLyricsModePicker(context, ref, settings.lyricsMode),
),
SettingsItem(
icon: Icons.source_outlined,
title: context.l10n.lyricsProvidersTitle,
subtitle: _getLyricsProvidersSubtitle(
context,
settings.lyricsProviders,
),
onTap: () => Navigator.push(
context,
MaterialPageRoute<void>(
builder: (_) => const LyricsProviderPriorityPage(),
),
),
showDivider: false,
),
],
],
),
),
// ── Provider Options ───────────────────────────────────────
if (settings.embedMetadata && settings.embedLyrics) ...[
SliverToBoxAdapter(
child: SettingsSectionHeader(
title: context.l10n.sectionLyricsProviderOptions,
),
),
SliverToBoxAdapter(
child: SettingsGroup(
children: [
SettingsSwitchItem(
icon: Icons.translate_outlined,
title: context.l10n.downloadNeteaseIncludeTranslation,
subtitle: settings.lyricsIncludeTranslationNetease
? context.l10n.downloadNeteaseIncludeTranslationEnabled
: context.l10n.downloadNeteaseIncludeTranslationDisabled,
value: settings.lyricsIncludeTranslationNetease,
onChanged: (value) => ref
.read(settingsProvider.notifier)
.setLyricsIncludeTranslationNetease(value),
),
SettingsSwitchItem(
icon: Icons.text_fields_outlined,
title: context.l10n.downloadNeteaseIncludeRomanization,
subtitle: settings.lyricsIncludeRomanizationNetease
? context
.l10n
.downloadNeteaseIncludeRomanizationEnabled
: context
.l10n
.downloadNeteaseIncludeRomanizationDisabled,
value: settings.lyricsIncludeRomanizationNetease,
onChanged: (value) => ref
.read(settingsProvider.notifier)
.setLyricsIncludeRomanizationNetease(value),
),
SettingsSwitchItem(
icon: Icons.record_voice_over_outlined,
title: context.l10n.downloadAppleQqMultiPerson,
subtitle: settings.lyricsMultiPersonWordByWord
? context.l10n.downloadAppleQqMultiPersonEnabled
: context.l10n.downloadAppleQqMultiPersonDisabled,
value: settings.lyricsMultiPersonWordByWord,
onChanged: (value) => ref
.read(settingsProvider.notifier)
.setLyricsMultiPersonWordByWord(value),
),
SettingsItem(
icon: Icons.language_outlined,
title: context.l10n.downloadMusixmatchLanguage,
subtitle: settings.musixmatchLanguage.isEmpty
? context.l10n.downloadMusixmatchLanguageAuto
: settings.musixmatchLanguage.toUpperCase(),
onTap: () => _showMusixmatchLanguagePicker(
context,
ref,
settings.musixmatchLanguage,
),
showDivider: false,
),
],
),
),
],
const SliverToBoxAdapter(child: SizedBox(height: 32)),
],
),
),
);
}
String _getLyricsModeLabel(BuildContext context, String mode) {
switch (mode) {
case 'external':
return context.l10n.lyricsModeExternal;
case 'both':
return context.l10n.lyricsModeBoth;
default:
return context.l10n.lyricsModeEmbed;
}
}
static const _providerDisplayNames = <String, String>{
'lrclib': 'LRCLIB',
'netease': 'Netease',
'musixmatch': 'Musixmatch',
'apple_music': 'Apple Music',
'qqmusic': 'QQ Music',
};
String _getLyricsProvidersSubtitle(
BuildContext context,
List<String> providers,
) {
if (providers.isEmpty) return context.l10n.downloadProvidersNoneEnabled;
return providers
.map((p) => _providerDisplayNames[p] ?? p)
.join(' > ');
}
void _showLyricsModePicker(
BuildContext context,
WidgetRef ref,
String current,
) {
final colorScheme = Theme.of(context).colorScheme;
showModalBottomSheet<void>(
context: context,
useRootNavigator: true,
backgroundColor: colorScheme.surfaceContainerHigh,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(28)),
),
builder: (context) => SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
child: Text(
context.l10n.lyricsMode,
style: Theme.of(context).textTheme.titleLarge
?.copyWith(fontWeight: FontWeight.bold),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
child: Text(
context.l10n.lyricsModeDescription,
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(color: colorScheme.onSurfaceVariant),
),
),
ListTile(
leading: const Icon(Icons.audiotrack),
title: Text(context.l10n.lyricsModeEmbed),
subtitle: Text(context.l10n.lyricsModeEmbedSubtitle),
trailing: current == 'embed' ? const Icon(Icons.check) : null,
onTap: () {
ref.read(settingsProvider.notifier).setLyricsMode('embed');
Navigator.pop(context);
},
),
ListTile(
leading: const Icon(Icons.insert_drive_file_outlined),
title: Text(context.l10n.lyricsModeExternal),
subtitle: Text(context.l10n.lyricsModeExternalSubtitle),
trailing: current == 'external' ? const Icon(Icons.check) : null,
onTap: () {
ref.read(settingsProvider.notifier).setLyricsMode('external');
Navigator.pop(context);
},
),
ListTile(
leading: const Icon(Icons.library_music_outlined),
title: Text(context.l10n.lyricsModeBoth),
subtitle: Text(context.l10n.lyricsModeBothSubtitle),
trailing: current == 'both' ? const Icon(Icons.check) : null,
onTap: () {
ref.read(settingsProvider.notifier).setLyricsMode('both');
Navigator.pop(context);
},
),
const SizedBox(height: 16),
],
),
),
);
}
void _showMusixmatchLanguagePicker(
BuildContext context,
WidgetRef ref,
String currentLanguage,
) {
final colorScheme = Theme.of(context).colorScheme;
final controller = TextEditingController(text: currentLanguage);
showModalBottomSheet<void>(
context: context,
useRootNavigator: true,
backgroundColor: colorScheme.surfaceContainerHigh,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(28)),
),
isScrollControlled: true,
builder: (context) => Padding(
padding: EdgeInsets.only(
left: 24,
right: 24,
top: 24,
bottom: 24 + MediaQuery.of(context).viewInsets.bottom,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.l10n.downloadMusixmatchLanguage,
style: Theme.of(context).textTheme.titleLarge
?.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(
context.l10n.downloadMusixmatchLanguageDesc,
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(color: colorScheme.onSurfaceVariant),
),
const SizedBox(height: 16),
TextField(
controller: controller,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
labelText: context.l10n.downloadMusixmatchLanguageCode,
hintText: context.l10n.downloadMusixmatchLanguageHint,
),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(context.l10n.dialogCancel),
),
const SizedBox(width: 8),
TextButton(
onPressed: () {
ref
.read(settingsProvider.notifier)
.setMusixmatchLanguage('');
Navigator.pop(context);
},
child: Text(context.l10n.downloadMusixmatchAuto),
),
const SizedBox(width: 8),
FilledButton(
onPressed: () {
final normalized = controller.text
.trim()
.toLowerCase()
.replaceAll(RegExp(r'[^a-z0-9\-_]'), '');
ref
.read(settingsProvider.notifier)
.setMusixmatchLanguage(normalized);
Navigator.pop(context);
},
child: Text(context.l10n.dialogSave),
),
],
),
],
),
),
);
}
}
@@ -0,0 +1,253 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/utils/app_bar_layout.dart';
import 'package:spotiflac_android/utils/artist_utils.dart';
import 'package:spotiflac_android/screens/settings/metadata_provider_priority_page.dart';
import 'package:spotiflac_android/widgets/settings_group.dart';
class MetadataSettingsPage extends ConsumerWidget {
const MetadataSettingsPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final settings = ref.watch(settingsProvider);
final colorScheme = Theme.of(context).colorScheme;
final topPadding = normalizedHeaderTopPadding(context);
return PopScope(
canPop: true,
child: Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 120 + topPadding,
collapsedHeight: kToolbarHeight,
floating: false,
pinned: true,
backgroundColor: colorScheme.surface,
surfaceTintColor: Colors.transparent,
leading: IconButton(
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
),
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
final maxHeight = 120 + topPadding;
final minHeight = kToolbarHeight + topPadding;
final expandRatio =
((constraints.maxHeight - minHeight) /
(maxHeight - minHeight))
.clamp(0.0, 1.0);
final leftPadding = 56 - (32 * expandRatio);
return FlexibleSpaceBar(
expandedTitleScale: 1.0,
titlePadding: EdgeInsets.only(
left: leftPadding,
bottom: 16,
),
title: Text(
context.l10n.settingsMetadata,
style: TextStyle(
fontSize: 20 + (8 * expandRatio),
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
);
},
),
),
// ── Embedding ──────────────────────────────────────────────
SliverToBoxAdapter(
child: SettingsSectionHeader(title: context.l10n.sectionDownload),
),
SliverToBoxAdapter(
child: SettingsGroup(
children: [
SettingsSwitchItem(
icon: Icons.sell_outlined,
title: 'Embed Metadata',
subtitle: settings.embedMetadata
? 'Write metadata, cover art, and lyrics to files'
: 'Disabled (advanced): skip all metadata embedding',
value: settings.embedMetadata,
onChanged: (v) =>
ref.read(settingsProvider.notifier).setEmbedMetadata(v),
showDivider: settings.embedMetadata,
),
if (settings.embedMetadata) ...[
SettingsItem(
icon: Icons.people_alt_outlined,
title: context.l10n.optionsArtistTagMode,
subtitle: _getArtistTagModeLabel(
context,
settings.artistTagMode,
),
onTap: () =>
_showArtistTagModePicker(context, ref, settings.artistTagMode),
),
SettingsSwitchItem(
icon: Icons.image,
title: context.l10n.optionsMaxQualityCover,
subtitle: context.l10n.optionsMaxQualityCoverSubtitle,
value: settings.maxQualityCover,
onChanged: (v) => ref
.read(settingsProvider.notifier)
.setMaxQualityCover(v),
),
SettingsSwitchItem(
icon: Icons.graphic_eq,
title: context.l10n.optionsReplayGain,
subtitle: settings.embedReplayGain
? context.l10n.optionsReplayGainSubtitleOn
: context.l10n.optionsReplayGainSubtitleOff,
value: settings.embedReplayGain,
onChanged: (v) => ref
.read(settingsProvider.notifier)
.setEmbedReplayGain(v),
showDivider: false,
),
],
],
),
),
// ── Providers ─────────────────────────────────────────────
SliverToBoxAdapter(
child: SettingsSectionHeader(
title: context.l10n.sectionMetadataProviders,
),
),
SliverToBoxAdapter(
child: SettingsGroup(
children: [
SettingsItem(
icon: Icons.source_outlined,
title: context.l10n.metadataProvidersTitle,
subtitle: context.l10n.metadataProvidersSubtitle,
onTap: () => Navigator.push(
context,
MaterialPageRoute<void>(
builder: (_) => const MetadataProviderPriorityPage(),
),
),
showDivider: false,
),
],
),
),
// ── Deduplication ──────────────────────────────────────────
SliverToBoxAdapter(
child: SettingsSectionHeader(
title: context.l10n.sectionDuplicates,
),
),
SliverToBoxAdapter(
child: SettingsGroup(
children: [
SettingsSwitchItem(
icon: Icons.filter_list_outlined,
title: context.l10n.downloadDeduplication,
subtitle: settings.deduplicateDownloads
? context.l10n.downloadDeduplicationEnabled
: context.l10n.downloadDeduplicationDisabled,
value: settings.deduplicateDownloads,
onChanged: (value) => ref
.read(settingsProvider.notifier)
.setDeduplicateDownloads(value),
showDivider: false,
),
],
),
),
const SliverToBoxAdapter(child: SizedBox(height: 32)),
],
),
),
);
}
String _getArtistTagModeLabel(BuildContext context, String mode) {
switch (mode) {
case artistTagModeSplitVorbis:
return context.l10n.optionsArtistTagModeSplitVorbis;
default:
return context.l10n.optionsArtistTagModeJoined;
}
}
void _showArtistTagModePicker(
BuildContext context,
WidgetRef ref,
String currentMode,
) {
final colorScheme = Theme.of(context).colorScheme;
showModalBottomSheet<void>(
context: context,
useRootNavigator: true,
backgroundColor: colorScheme.surfaceContainerHigh,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(28)),
),
builder: (context) => SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
child: Text(
context.l10n.optionsArtistTagMode,
style: Theme.of(context).textTheme.titleLarge
?.copyWith(fontWeight: FontWeight.bold),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
child: Text(
context.l10n.optionsArtistTagModeDescription,
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(color: colorScheme.onSurfaceVariant),
),
),
ListTile(
leading: const Icon(Icons.segment_outlined),
title: Text(context.l10n.optionsArtistTagModeJoined),
subtitle: Text(context.l10n.optionsArtistTagModeJoinedSubtitle),
trailing: currentMode == artistTagModeJoined
? const Icon(Icons.check)
: null,
onTap: () {
ref
.read(settingsProvider.notifier)
.setArtistTagMode(artistTagModeJoined);
Navigator.pop(context);
},
),
ListTile(
leading: const Icon(Icons.library_music_outlined),
title: Text(context.l10n.optionsArtistTagModeSplitVorbis),
subtitle: Text(context.l10n.optionsArtistTagModeSplitVorbisSubtitle),
trailing: currentMode == artistTagModeSplitVorbis
? const Icon(Icons.check)
: null,
onTap: () {
ref
.read(settingsProvider.notifier)
.setArtistTagMode(artistTagModeSplitVorbis);
Navigator.pop(context);
},
),
const SizedBox(height: 16),
],
),
),
);
}
}
File diff suppressed because it is too large Load Diff
+74 -32
View File
@@ -4,9 +4,12 @@ import 'package:spotiflac_android/constants/app_info.dart';
import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/screens/settings/appearance_settings_page.dart';
import 'package:spotiflac_android/screens/settings/download_settings_page.dart';
import 'package:spotiflac_android/screens/settings/files_settings_page.dart';
import 'package:spotiflac_android/screens/settings/lyrics_settings_page.dart';
import 'package:spotiflac_android/screens/settings/metadata_settings_page.dart';
import 'package:spotiflac_android/screens/settings/extensions_page.dart';
import 'package:spotiflac_android/screens/settings/library_settings_page.dart';
import 'package:spotiflac_android/screens/settings/options_settings_page.dart';
import 'package:spotiflac_android/screens/settings/app_settings_page.dart';
import 'package:spotiflac_android/screens/settings/about_page.dart';
import 'package:spotiflac_android/screens/settings/cache_management_page.dart';
import 'package:spotiflac_android/screens/settings/donate_page.dart';
@@ -48,7 +51,7 @@ class SettingsTab extends ConsumerWidget {
title: Text(
context.l10n.settingsTitle,
style: TextStyle(
fontSize: 20 + (14 * expandRatio), // 20 -> 34
fontSize: 20 + (14 * expandRatio),
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
@@ -58,6 +61,7 @@ class SettingsTab extends ConsumerWidget {
),
),
// ── Group 1: Appearance & Content ──────────────────────────────
SliverToBoxAdapter(
child: Builder(
builder: (context) {
@@ -72,6 +76,34 @@ class SettingsTab extends ConsumerWidget {
onTap: () =>
_navigateTo(context, const AppearanceSettingsPage()),
),
SettingsItem(
icon: Icons.library_music_outlined,
title: l10n.settingsLocalLibrary,
subtitle: l10n.settingsLocalLibrarySubtitle,
onTap: () =>
_navigateTo(context, const LibrarySettingsPage()),
),
SettingsItem(
icon: Icons.extension_outlined,
title: l10n.settingsExtensions,
subtitle: l10n.settingsExtensionsSubtitle,
onTap: () => _navigateTo(context, const ExtensionsPage()),
showDivider: false,
),
],
);
},
),
),
// ── Group 2: Download ──────────────────────────────────────────
SliverToBoxAdapter(
child: Builder(
builder: (context) {
final l10n = context.l10n;
return SettingsGroup(
margin: const EdgeInsets.fromLTRB(16, 4, 16, 4),
children: [
SettingsItem(
icon: Icons.download_outlined,
title: l10n.settingsDownload,
@@ -80,12 +112,41 @@ class SettingsTab extends ConsumerWidget {
_navigateTo(context, const DownloadSettingsPage()),
),
SettingsItem(
icon: Icons.library_music_outlined,
title: l10n.settingsLocalLibrary,
subtitle: l10n.settingsLocalLibrarySubtitle,
icon: Icons.folder_outlined,
title: l10n.settingsFiles,
subtitle: l10n.settingsFilesSubtitle,
onTap: () =>
_navigateTo(context, const LibrarySettingsPage()),
_navigateTo(context, const FilesSettingsPage()),
),
SettingsItem(
icon: Icons.sell_outlined,
title: l10n.settingsMetadata,
subtitle: l10n.settingsMetadataSubtitle,
onTap: () =>
_navigateTo(context, const MetadataSettingsPage()),
),
SettingsItem(
icon: Icons.lyrics_outlined,
title: l10n.settingsLyrics,
subtitle: l10n.settingsLyricsSubtitle,
onTap: () =>
_navigateTo(context, const LyricsSettingsPage()),
showDivider: false,
),
],
);
},
),
),
// ── Group 3: App ───────────────────────────────────────────────
SliverToBoxAdapter(
child: Builder(
builder: (context) {
final l10n = context.l10n;
return SettingsGroup(
margin: const EdgeInsets.fromLTRB(16, 4, 16, 4),
children: [
SettingsItem(
icon: Icons.storage_outlined,
title: l10n.settingsCache,
@@ -95,41 +156,22 @@ class SettingsTab extends ConsumerWidget {
),
SettingsItem(
icon: Icons.tune_outlined,
title: l10n.settingsOptions,
subtitle: l10n.settingsOptionsSubtitle,
title: l10n.settingsApp,
subtitle: l10n.settingsAppSubtitle,
onTap: () =>
_navigateTo(context, const OptionsSettingsPage()),
_navigateTo(context, const AppSettingsPage()),
),
SettingsItem(
icon: Icons.extension_outlined,
title: l10n.settingsExtensions,
subtitle: l10n.settingsExtensionsSubtitle,
onTap: () => _navigateTo(context, const ExtensionsPage()),
icon: Icons.article_outlined,
title: l10n.logTitle,
subtitle: l10n.settingsLogsSubtitle,
onTap: () => _navigateTo(context, const LogScreen()),
),
SettingsItem(
icon: Icons.favorite_outline,
title: l10n.settingsDonate,
subtitle: l10n.settingsDonateSubtitle,
onTap: () => _navigateTo(context, const DonatePage()),
showDivider: false,
),
],
);
},
),
),
SliverToBoxAdapter(
child: Builder(
builder: (context) {
final l10n = context.l10n;
return SettingsGroup(
children: [
SettingsItem(
icon: Icons.article_outlined,
title: l10n.logTitle,
subtitle: l10n.settingsLogsSubtitle,
onTap: () => _navigateTo(context, const LogScreen()),
),
SettingsItem(
icon: Icons.info_outline,
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 71 KiB