mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-11 02:08:44 +02:00
feat(i18n): replace all hardcoded strings with l10n keys across 13 screens
- Added 80+ new keys to app_en.arb covering lyrics, SAF, download settings, snackbars, dialogs, home, cache, and store screens - Replaced hardcoded strings in main_shell, album_screen, playlist_screen, library_tracks_folder_screen, home_tab, settings_tab, download_settings_page, lyrics_provider_priority_page, track_metadata_screen, extension_detail_page, cache_management_page, local_album_screen, downloaded_album_screen, search_screen - Fixed structural bug in track_metadata_screen (duplicate closing brace) - Added missing l10n.dart import to search_screen.dart - Regenerated all app_localizations*.dart files via flutter gen-l10n
This commit is contained in:
1 parent
16669d8b7a
commit
48f614359e
29 files changed
+5831
-161
No files matched your search
@@ -619,7 +619,7 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
size: 22,
|
||||
color: allLoved ? Colors.redAccent : Colors.white,
|
||||
),
|
||||
tooltip: allLoved ? 'Remove from Loved' : 'Love All',
|
||||
tooltip: allLoved ? context.l10n.trackOptionRemoveFromLoved : context.l10n.tooltipLoveAll,
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
);
|
||||
@@ -642,7 +642,7 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
? null
|
||||
: () => showAddTracksToPlaylistSheet(context, ref, _tracks!),
|
||||
icon: const Icon(Icons.add, size: 22, color: Colors.white),
|
||||
tooltip: 'Add to Playlist',
|
||||
tooltip: context.l10n.tooltipAddToPlaylist,
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
);
|
||||
@@ -660,7 +660,11 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
}
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Removed ${tracks.length} tracks from Loved')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.snackbarRemovedTracksFromLoved(tracks.length),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -673,7 +677,11 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
}
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Added $addedCount tracks to Loved')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.snackbarAddedTracksToLoved(addedCount),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,7 +363,7 @@ class _DownloadedAlbumScreenState extends ConsumerState<DownloadedAlbumScreen> {
|
||||
if (tracks.isEmpty) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(widget.albumName)),
|
||||
body: Center(child: Text('No tracks found for this album')),
|
||||
body: Center(child: Text(context.l10n.noTracksFoundForAlbum)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -816,7 +816,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
const SizedBox(height: 12),
|
||||
CheckboxListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: const Text('Skip already downloaded songs'),
|
||||
title: Text(l10n.homeSkipAlreadyDownloaded),
|
||||
value: skipDownloaded,
|
||||
onChanged: (value) {
|
||||
setDialogState(() {
|
||||
@@ -1750,7 +1750,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.album, color: colorScheme.onSurfaceVariant),
|
||||
title: const Text('Go to Album'),
|
||||
title: Text(context.l10n.homeGoToAlbum),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
_navigateToTrackAlbum(item);
|
||||
@@ -1824,7 +1824,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
} else {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('Album info not available')));
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.homeAlbumInfoUnavailable)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -850,8 +850,8 @@ class _LibraryTracksFolderScreenState
|
||||
final colorScheme = Theme.of(dialogContext).colorScheme;
|
||||
return AlertDialog(
|
||||
backgroundColor: colorScheme.surfaceContainerHigh,
|
||||
title: const Text('Download All'),
|
||||
content: Text('Download ${tracks.length} tracks?'),
|
||||
title: Text(context.l10n.dialogDownloadAllTitle),
|
||||
content: Text(context.l10n.dialogDownloadAllMessage(tracks.length)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext),
|
||||
@@ -862,7 +862,7 @@ class _LibraryTracksFolderScreenState
|
||||
Navigator.pop(dialogContext);
|
||||
_downloadAll(tracks);
|
||||
},
|
||||
child: const Text('Download'),
|
||||
child: Text(context.l10n.dialogDownload),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -247,7 +247,7 @@ class _LocalAlbumScreenState extends ConsumerState<LocalAlbumScreen> {
|
||||
if (tracks.isEmpty) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(widget.albumName)),
|
||||
body: const Center(child: Text('No tracks found for this album')),
|
||||
body: Center(child: Text(context.l10n.noTracksFoundForAlbum)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -181,25 +181,20 @@ class _MainShellState extends ConsumerState<MainShell> {
|
||||
size: 32,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
title: const Text('Storage Update Required'),
|
||||
content: const Column(
|
||||
title: Text(context.l10n.safMigrationTitle),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'SpotiFLAC now uses Android Storage Access Framework (SAF) for downloads. '
|
||||
'This fixes "permission denied" errors on Android 10+.',
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Text(
|
||||
'Please select your download folder again to switch to the new storage system.',
|
||||
),
|
||||
Text(context.l10n.safMigrationMessage1),
|
||||
const SizedBox(height: 12),
|
||||
Text(context.l10n.safMigrationMessage2),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx),
|
||||
child: const Text('Later'),
|
||||
child: Text(context.l10n.updateLater),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () async {
|
||||
@@ -219,15 +214,15 @@ class _MainShellState extends ConsumerState<MainShell> {
|
||||
);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Download folder updated to SAF mode'),
|
||||
SnackBar(
|
||||
content: Text(context.l10n.safMigrationSuccess),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
child: const Text('Select Folder'),
|
||||
child: Text(context.l10n.setupSelectFolder),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -482,7 +482,7 @@ class _PlaylistScreenState extends ConsumerState<PlaylistScreen> {
|
||||
size: 22,
|
||||
color: allLoved ? Colors.redAccent : Colors.white,
|
||||
),
|
||||
tooltip: allLoved ? 'Remove from Loved' : 'Love All',
|
||||
tooltip: allLoved ? context.l10n.trackOptionRemoveFromLoved : context.l10n.tooltipLoveAll,
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
);
|
||||
@@ -505,7 +505,7 @@ class _PlaylistScreenState extends ConsumerState<PlaylistScreen> {
|
||||
Widget _buildAddToPlaylistButton(BuildContext context) {
|
||||
return _buildCircleButton(
|
||||
icon: Icons.playlist_add,
|
||||
tooltip: 'Add to Playlist',
|
||||
tooltip: context.l10n.tooltipAddToPlaylist,
|
||||
onPressed: _tracks.isEmpty
|
||||
? null
|
||||
: () => showAddTracksToPlaylistSheet(context, ref, _tracks),
|
||||
@@ -520,8 +520,8 @@ class _PlaylistScreenState extends ConsumerState<PlaylistScreen> {
|
||||
final colorScheme = Theme.of(dialogContext).colorScheme;
|
||||
return AlertDialog(
|
||||
backgroundColor: colorScheme.surfaceContainerHigh,
|
||||
title: const Text('Download All'),
|
||||
content: Text('Download ${_tracks.length} tracks?'),
|
||||
title: Text(context.l10n.dialogDownloadAllTitle),
|
||||
content: Text(context.l10n.dialogDownloadAllMessage(_tracks.length)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext),
|
||||
@@ -532,7 +532,7 @@ class _PlaylistScreenState extends ConsumerState<PlaylistScreen> {
|
||||
Navigator.pop(dialogContext);
|
||||
_downloadAll(context);
|
||||
},
|
||||
child: const Text('Download'),
|
||||
child: Text(context.l10n.dialogDownload),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -552,7 +552,11 @@ class _PlaylistScreenState extends ConsumerState<PlaylistScreen> {
|
||||
}
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Removed ${tracks.length} tracks from Loved')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.snackbarRemovedTracksFromLoved(tracks.length),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -565,7 +569,11 @@ class _PlaylistScreenState extends ConsumerState<PlaylistScreen> {
|
||||
}
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Added $addedCount tracks to Loved')),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.snackbarAddedTracksToLoved(addedCount),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/services/cover_cache_manager.dart';
|
||||
import 'package:spotiflac_android/models/track.dart';
|
||||
import 'package:spotiflac_android/providers/track_provider.dart';
|
||||
@@ -52,7 +53,7 @@ class _SearchScreenState extends ConsumerState<SearchScreen> {
|
||||
.addToQueue(track, settings.defaultService);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Added "${track.name}" to queue')));
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.snackbarAddedToQueue(track.name))));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -56,7 +56,7 @@ class _CacheManagementPageState extends ConsumerState<CacheManagementPage> {
|
||||
setState(() => _isLoading = false);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Error: $e')));
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.snackbarError(e.toString()))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ class _CacheManagementPageState extends ConsumerState<CacheManagementPage> {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Error: $e')));
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.snackbarError(e.toString()))));
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() => _busyAction = null);
|
||||
@@ -394,7 +394,7 @@ class _CacheManagementPageState extends ConsumerState<CacheManagementPage> {
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: 'Refresh',
|
||||
tooltip: context.l10n.cacheRefresh,
|
||||
onPressed: _isBusy ? null : _refreshOverview,
|
||||
icon: const Icon(Icons.refresh),
|
||||
),
|
||||
|
||||
@@ -376,7 +376,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
title: context.l10n.downloadAskBeforeDownload,
|
||||
subtitle: isBuiltInService
|
||||
? context.l10n.downloadAskQualitySubtitle
|
||||
: 'Select a built-in service to enable',
|
||||
: context.l10n.downloadSelectServiceToEnable,
|
||||
value: settings.askQualityBeforeDownload,
|
||||
enabled: isBuiltInService,
|
||||
onChanged: (value) => ref
|
||||
@@ -413,7 +413,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
// Lossy 320kbps option (Tidal only) - downloads M4A, converts to MP3/Opus
|
||||
if (isTidalService)
|
||||
_QualityOption(
|
||||
title: 'Lossy 320kbps',
|
||||
title: context.l10n.downloadLossy320,
|
||||
subtitle: _getTidalHighFormatLabel(
|
||||
settings.tidalHighFormat,
|
||||
),
|
||||
@@ -426,7 +426,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
if (isTidalService && settings.audioQuality == 'HIGH')
|
||||
SettingsItem(
|
||||
icon: Icons.tune,
|
||||
title: 'Lossy Format',
|
||||
title: context.l10n.downloadLossyFormat,
|
||||
subtitle: _getTidalHighFormatLabel(
|
||||
settings.tidalHighFormat,
|
||||
),
|
||||
@@ -451,7 +451,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Select Tidal or Qobuz above to configure quality',
|
||||
context.l10n.downloadSelectTidalQobuz,
|
||||
style: Theme.of(context).textTheme.bodySmall
|
||||
?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
@@ -504,7 +504,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
title: context.l10n.optionsEmbedLyrics,
|
||||
subtitle: settings.embedMetadata
|
||||
? context.l10n.optionsEmbedLyricsSubtitle
|
||||
: 'Disabled while Embed Metadata is turned off',
|
||||
: context.l10n.downloadEmbedLyricsDisabled,
|
||||
value: settings.embedLyrics,
|
||||
enabled: settings.embedMetadata,
|
||||
onChanged: (value) => ref
|
||||
@@ -528,7 +528,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
),
|
||||
SettingsItem(
|
||||
icon: Icons.source_outlined,
|
||||
title: 'Lyrics Providers',
|
||||
title: context.l10n.lyricsProvidersTitle,
|
||||
subtitle: _getLyricsProvidersSubtitle(
|
||||
settings.lyricsProviders,
|
||||
),
|
||||
@@ -541,10 +541,10 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.translate_outlined,
|
||||
title: 'Netease: Include Translation',
|
||||
title: context.l10n.downloadNeteaseIncludeTranslation,
|
||||
subtitle: settings.lyricsIncludeTranslationNetease
|
||||
? 'Append translated lyrics when available'
|
||||
: 'Use original lyrics only',
|
||||
? context.l10n.downloadNeteaseIncludeTranslationEnabled
|
||||
: context.l10n.downloadNeteaseIncludeTranslationDisabled,
|
||||
value: settings.lyricsIncludeTranslationNetease,
|
||||
onChanged: (value) => ref
|
||||
.read(settingsProvider.notifier)
|
||||
@@ -552,10 +552,10 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.text_fields_outlined,
|
||||
title: 'Netease: Include Romanization',
|
||||
title: context.l10n.downloadNeteaseIncludeRomanization,
|
||||
subtitle: settings.lyricsIncludeRomanizationNetease
|
||||
? 'Append romanized lyrics when available'
|
||||
: 'Disabled',
|
||||
? context.l10n.downloadNeteaseIncludeRomanizationEnabled
|
||||
: context.l10n.downloadNeteaseIncludeRomanizationDisabled,
|
||||
value: settings.lyricsIncludeRomanizationNetease,
|
||||
onChanged: (value) => ref
|
||||
.read(settingsProvider.notifier)
|
||||
@@ -563,10 +563,10 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.record_voice_over_outlined,
|
||||
title: 'Apple/QQ Multi-Person Word-by-Word',
|
||||
title: context.l10n.downloadAppleQqMultiPerson,
|
||||
subtitle: settings.lyricsMultiPersonWordByWord
|
||||
? 'Enable v1/v2 speaker and [bg:] tags'
|
||||
: 'Simplified word-by-word formatting',
|
||||
? context.l10n.downloadAppleQqMultiPersonEnabled
|
||||
: context.l10n.downloadAppleQqMultiPersonDisabled,
|
||||
value: settings.lyricsMultiPersonWordByWord,
|
||||
onChanged: (value) => ref
|
||||
.read(settingsProvider.notifier)
|
||||
@@ -574,9 +574,9 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
),
|
||||
SettingsItem(
|
||||
icon: Icons.language_outlined,
|
||||
title: 'Musixmatch Language',
|
||||
title: context.l10n.downloadMusixmatchLanguage,
|
||||
subtitle: settings.musixmatchLanguage.isEmpty
|
||||
? 'Auto (original)'
|
||||
? context.l10n.downloadMusixmatchLanguageAuto
|
||||
: settings.musixmatchLanguage.toUpperCase(),
|
||||
onTap: () => _showMusixmatchLanguagePicker(
|
||||
context,
|
||||
@@ -622,8 +622,8 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
icon: Icons.library_music_outlined,
|
||||
title: context.l10n.downloadSeparateSinglesFolder,
|
||||
subtitle: settings.separateSingles
|
||||
? 'Albums/ and Singles/ folders'
|
||||
: 'All files in same structure',
|
||||
? context.l10n.downloadSeparateSinglesEnabled
|
||||
: context.l10n.downloadSeparateSinglesDisabled,
|
||||
value: settings.separateSingles,
|
||||
onChanged: (value) => ref
|
||||
.read(settingsProvider.notifier)
|
||||
@@ -670,9 +670,9 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
.read(settingsProvider.notifier)
|
||||
.setUseAlbumArtistForFolders(value),
|
||||
),
|
||||
SettingsItem(
|
||||
SettingsItem(
|
||||
icon: Icons.filter_alt_outlined,
|
||||
title: 'Artist Name Filters',
|
||||
title: context.l10n.downloadArtistNameFilters,
|
||||
subtitle: _getArtistFolderFilterSubtitle(
|
||||
context,
|
||||
usePrimaryArtistOnly: settings.usePrimaryArtistOnly,
|
||||
@@ -707,28 +707,16 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
if (_artistFolderFiltersExpanded)
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.group_remove_outlined,
|
||||
title: 'Filter contributing artists in Album Artist',
|
||||
title: context.l10n.downloadFilterContributing,
|
||||
subtitle: settings.filterContributingArtistsInAlbumArtist
|
||||
? 'Album Artist metadata uses primary artist only'
|
||||
: 'Keep full Album Artist metadata value',
|
||||
? context.l10n.downloadFilterContributingEnabled
|
||||
: context.l10n.downloadFilterContributingDisabled,
|
||||
value: settings.filterContributingArtistsInAlbumArtist,
|
||||
onChanged: (value) => ref
|
||||
.read(settingsProvider.notifier)
|
||||
.setFilterContributingArtistsInAlbumArtist(value),
|
||||
showDivider: false,
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.person_outline,
|
||||
title: context.l10n.downloadUsePrimaryArtistOnly,
|
||||
subtitle: settings.usePrimaryArtistOnly
|
||||
? context.l10n.downloadUsePrimaryArtistOnlyEnabled
|
||||
: context.l10n.downloadUsePrimaryArtistOnlyDisabled,
|
||||
value: settings.usePrimaryArtistOnly,
|
||||
onChanged: (value) => ref
|
||||
.read(settingsProvider.notifier)
|
||||
.setUsePrimaryArtistOnly(value),
|
||||
showDivider: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -753,7 +741,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
),
|
||||
SettingsItem(
|
||||
icon: Icons.public,
|
||||
title: 'SongLink Region',
|
||||
title: context.l10n.downloadSongLinkRegion,
|
||||
subtitle: _getSongLinkRegionLabel(settings.songLinkRegion),
|
||||
onTap: () => _showSongLinkRegionPicker(
|
||||
context,
|
||||
@@ -763,10 +751,10 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.security_outlined,
|
||||
title: 'Network compatibility mode',
|
||||
title: context.l10n.downloadNetworkCompatibilityMode,
|
||||
subtitle: settings.networkCompatibilityMode
|
||||
? 'Enabled: try HTTP + accept invalid TLS certificates (unsafe)'
|
||||
: 'Off: strict HTTPS certificate validation (recommended)',
|
||||
? context.l10n.downloadNetworkCompatibilityModeEnabled
|
||||
: context.l10n.downloadNetworkCompatibilityModeDisabled,
|
||||
value: settings.networkCompatibilityMode,
|
||||
onChanged: (value) {
|
||||
ref
|
||||
@@ -1045,7 +1033,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Customize how your files are named.',
|
||||
context.l10n.downloadFilenameDescription,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -1070,7 +1058,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
const SizedBox(height: 24),
|
||||
|
||||
Text(
|
||||
'Tap to insert tag:',
|
||||
context.l10n.downloadFilenameInsertTag,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
@@ -1238,7 +1226,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
|
||||
child: Text(
|
||||
'Download Location',
|
||||
context.l10n.setupDownloadLocationTitle,
|
||||
style: Theme.of(
|
||||
ctx,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
@@ -1247,7 +1235,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
|
||||
child: Text(
|
||||
'Choose storage mode for downloaded files.',
|
||||
context.l10n.downloadLocationSubtitle,
|
||||
style: Theme.of(ctx).textTheme.bodyMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -1255,8 +1243,8 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.folder_special, color: colorScheme.primary),
|
||||
title: const Text('App folder (non-SAF)'),
|
||||
subtitle: const Text('Use default Music/SpotiFLAC path'),
|
||||
title: Text(context.l10n.storageModeAppFolder),
|
||||
subtitle: Text(context.l10n.storageModeAppFolderSubtitle),
|
||||
trailing: !isSafMode ? const Icon(Icons.check) : null,
|
||||
onTap: () async {
|
||||
Navigator.pop(ctx);
|
||||
@@ -1269,10 +1257,8 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.folder_open, color: colorScheme.primary),
|
||||
title: const Text('SAF folder'),
|
||||
subtitle: const Text(
|
||||
'Pick folder via Android Storage Access Framework',
|
||||
),
|
||||
title: Text(context.l10n.storageModeSaf),
|
||||
subtitle: Text(context.l10n.storageModeSafSubtitle),
|
||||
trailing: isSafMode ? const Icon(Icons.check) : null,
|
||||
onTap: () async {
|
||||
Navigator.pop(ctx);
|
||||
@@ -1546,7 +1532,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
};
|
||||
|
||||
String _getLyricsProvidersSubtitle(List<String> providers) {
|
||||
if (providers.isEmpty) return 'None enabled';
|
||||
if (providers.isEmpty) return context.l10n.downloadProvidersNoneEnabled;
|
||||
return providers.map((p) => _providerDisplayNames[p] ?? p).join(' > ');
|
||||
}
|
||||
|
||||
@@ -1645,14 +1631,14 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Musixmatch Language',
|
||||
context.l10n.downloadMusixmatchLanguage,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Set preferred language code (example: en, es, ja). Leave empty for auto.',
|
||||
context.l10n.downloadMusixmatchLanguageDesc,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -1661,9 +1647,9 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
TextField(
|
||||
controller: controller,
|
||||
textInputAction: TextInputAction.done,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Language code',
|
||||
hintText: 'auto / en / es / ja',
|
||||
decoration: InputDecoration(
|
||||
labelText: context.l10n.downloadMusixmatchLanguageCode,
|
||||
hintText: context.l10n.downloadMusixmatchLanguageHint,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@@ -1682,7 +1668,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
.setMusixmatchLanguage('');
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Auto'),
|
||||
child: Text(context.l10n.downloadMusixmatchAuto),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
FilledButton(
|
||||
@@ -1739,7 +1725,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
|
||||
child: Text(
|
||||
'Lossy 320kbps Format',
|
||||
context.l10n.downloadLossy320Format,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
@@ -1748,7 +1734,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
|
||||
child: Text(
|
||||
'Choose the output format for Tidal 320kbps lossy downloads. The original AAC stream will be converted to your selected format.',
|
||||
context.l10n.downloadLossy320FormatDesc,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -1756,8 +1742,8 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.audiotrack),
|
||||
title: const Text('MP3 320kbps'),
|
||||
subtitle: const Text('Best compatibility, ~10MB per track'),
|
||||
title: Text(context.l10n.downloadLossyMp3),
|
||||
subtitle: Text(context.l10n.downloadLossyMp3Subtitle),
|
||||
trailing: current == 'mp3_320'
|
||||
? Icon(Icons.check, color: colorScheme.primary)
|
||||
: null,
|
||||
@@ -1770,8 +1756,8 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.graphic_eq),
|
||||
title: const Text('Opus 256kbps'),
|
||||
subtitle: const Text('Best quality Opus, ~8MB per track'),
|
||||
title: Text(context.l10n.downloadLossyOpus256),
|
||||
subtitle: Text(context.l10n.downloadLossyOpus256Subtitle),
|
||||
trailing: current == 'opus_256'
|
||||
? Icon(Icons.check, color: colorScheme.primary)
|
||||
: null,
|
||||
@@ -1784,8 +1770,8 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.graphic_eq),
|
||||
title: const Text('Opus 128kbps'),
|
||||
subtitle: const Text('Smallest size, ~4MB per track'),
|
||||
title: Text(context.l10n.downloadLossyOpus128),
|
||||
subtitle: Text(context.l10n.downloadLossyOpus128Subtitle),
|
||||
trailing: current == 'opus_128'
|
||||
? Icon(Icons.check, color: colorScheme.primary)
|
||||
: null,
|
||||
@@ -1842,7 +1828,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
ListTile(
|
||||
leading: const Icon(Icons.signal_cellular_alt),
|
||||
title: Text(context.l10n.settingsDownloadNetworkAny),
|
||||
subtitle: const Text('WiFi + Mobile Data'),
|
||||
subtitle: Text(context.l10n.downloadNetworkAnySubtitle),
|
||||
trailing: current == 'any'
|
||||
? Icon(Icons.check, color: colorScheme.primary)
|
||||
: null,
|
||||
@@ -1856,7 +1842,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
ListTile(
|
||||
leading: const Icon(Icons.wifi),
|
||||
title: Text(context.l10n.settingsDownloadNetworkWifiOnly),
|
||||
subtitle: const Text('Pause downloads on mobile data'),
|
||||
subtitle: Text(context.l10n.downloadNetworkWifiOnlySubtitle),
|
||||
trailing: current == 'wifi_only'
|
||||
? Icon(Icons.check, color: colorScheme.primary)
|
||||
: null,
|
||||
@@ -1897,17 +1883,17 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
|
||||
child: Text(
|
||||
'SongLink Region',
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
child: Text(
|
||||
context.l10n.downloadSongLinkRegion,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
|
||||
child: Text(
|
||||
'Used as userCountry for SongLink API lookup.',
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
|
||||
child: Text(
|
||||
context.l10n.downloadSongLinkRegionDesc,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -1968,12 +1954,12 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
|
||||
child: Text(
|
||||
'Folder Organization',
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
child: Text(
|
||||
context.l10n.downloadFolderOrganization,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
|
||||
|
||||
@@ -801,7 +801,7 @@ class _SettingItemState extends State<_SettingItem> {
|
||||
Future<void> _invokeAction(BuildContext context) async {
|
||||
if (widget.setting.action == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('No action defined for this button')),
|
||||
SnackBar(content: Text(context.l10n.snackbarNoActionDefined)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -834,7 +834,7 @@ class _SettingItemState extends State<_SettingItem> {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Error: $e')));
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.snackbarError(e.toString()))));
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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/widgets/priority_settings_scaffold.dart';
|
||||
import 'package:spotiflac_android/widgets/settings_group.dart';
|
||||
@@ -55,18 +56,18 @@ class _LyricsProviderPriorityPageState
|
||||
|
||||
return PrioritySettingsScaffold(
|
||||
hasChanges: _hasChanges,
|
||||
title: 'Lyrics Providers',
|
||||
description:
|
||||
'Enable, disable and reorder lyrics sources. Providers are tried top-to-bottom until lyrics are found.',
|
||||
infoText:
|
||||
'Extension lyrics providers always run before built-in providers. At least one provider must remain enabled.',
|
||||
title: context.l10n.lyricsProvidersTitle,
|
||||
description: context.l10n.lyricsProvidersDescription,
|
||||
infoText: context.l10n.lyricsProvidersInfoText,
|
||||
onSave: _saveChanges,
|
||||
onConfirmDiscard: _confirmDiscard,
|
||||
slivers: [
|
||||
if (_enabledProviders.isNotEmpty)
|
||||
SliverToBoxAdapter(
|
||||
child: SettingsSectionHeader(
|
||||
title: 'Enabled (${_enabledProviders.length})',
|
||||
title: context.l10n.lyricsProvidersEnabledSection(
|
||||
_enabledProviders.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_enabledProviders.isNotEmpty)
|
||||
@@ -76,7 +77,7 @@ class _LyricsProviderPriorityPageState
|
||||
itemCount: _enabledProviders.length,
|
||||
itemBuilder: (context, index) {
|
||||
final id = _enabledProviders[index];
|
||||
final info = _getLyricsProviderInfo(id);
|
||||
final info = _getLyricsProviderInfo(id, context);
|
||||
return _EnabledProviderItem(
|
||||
key: ValueKey(id),
|
||||
providerId: id,
|
||||
@@ -99,7 +100,9 @@ class _LyricsProviderPriorityPageState
|
||||
if (disabled.isNotEmpty)
|
||||
SliverToBoxAdapter(
|
||||
child: SettingsSectionHeader(
|
||||
title: 'Disabled (${disabled.length})',
|
||||
title: context.l10n.lyricsProvidersDisabledSection(
|
||||
disabled.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (disabled.isNotEmpty)
|
||||
@@ -108,7 +111,7 @@ class _LyricsProviderPriorityPageState
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
final id = disabled[index];
|
||||
final info = _getLyricsProviderInfo(id);
|
||||
final info = _getLyricsProviderInfo(id, context);
|
||||
return _DisabledProviderItem(
|
||||
key: ValueKey(id),
|
||||
providerId: id,
|
||||
@@ -130,8 +133,8 @@ class _LyricsProviderPriorityPageState
|
||||
void _disableProvider(String id) {
|
||||
if (_enabledProviders.length <= 1) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('At least one provider must remain enabled'),
|
||||
SnackBar(
|
||||
content: Text(context.l10n.lyricsProvidersAtLeastOne),
|
||||
),
|
||||
);
|
||||
return;
|
||||
@@ -150,7 +153,7 @@ class _LyricsProviderPriorityPageState
|
||||
});
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Lyrics provider priority saved')),
|
||||
SnackBar(content: Text(context.l10n.lyricsProvidersSaved)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -159,16 +162,16 @@ class _LyricsProviderPriorityPageState
|
||||
final result = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Discard changes?'),
|
||||
content: const Text('You have unsaved changes that will be lost.'),
|
||||
title: Text(context.l10n.dialogDiscardChanges),
|
||||
content: Text(context.l10n.lyricsProvidersDiscardContent),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(context.l10n.dialogCancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: const Text('Discard'),
|
||||
child: Text(context.l10n.dialogDiscard),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -176,48 +179,51 @@ class _LyricsProviderPriorityPageState
|
||||
return result ?? false;
|
||||
}
|
||||
|
||||
static _LyricsProviderInfo _getLyricsProviderInfo(String id) {
|
||||
static _LyricsProviderInfo _getLyricsProviderInfo(
|
||||
String id,
|
||||
BuildContext context,
|
||||
) {
|
||||
switch (id) {
|
||||
case 'spotify_api':
|
||||
return _LyricsProviderInfo(
|
||||
name: 'Spotify Lyrics API',
|
||||
description: 'Spotify-sourced synced lyrics via community API',
|
||||
description: context.l10n.lyricsProviderSpotifyApiDesc,
|
||||
icon: Icons.music_note_outlined,
|
||||
);
|
||||
case 'lrclib':
|
||||
return _LyricsProviderInfo(
|
||||
name: 'LRCLIB',
|
||||
description: 'Open-source synced lyrics database',
|
||||
description: context.l10n.lyricsProviderLrclibDesc,
|
||||
icon: Icons.subtitles_outlined,
|
||||
);
|
||||
case 'netease':
|
||||
return _LyricsProviderInfo(
|
||||
name: 'Netease',
|
||||
description: 'NetEase Cloud Music (good for Asian songs)',
|
||||
description: context.l10n.lyricsProviderNeteaseDesc,
|
||||
icon: Icons.cloud_outlined,
|
||||
);
|
||||
case 'musixmatch':
|
||||
return _LyricsProviderInfo(
|
||||
name: 'Musixmatch',
|
||||
description: 'Largest lyrics database (multi-language)',
|
||||
description: context.l10n.lyricsProviderMusixmatchDesc,
|
||||
icon: Icons.translate,
|
||||
);
|
||||
case 'apple_music':
|
||||
return _LyricsProviderInfo(
|
||||
name: 'Apple Music',
|
||||
description: 'Word-by-word synced lyrics (via proxy)',
|
||||
description: context.l10n.lyricsProviderAppleMusicDesc,
|
||||
icon: Icons.music_note,
|
||||
);
|
||||
case 'qqmusic':
|
||||
return _LyricsProviderInfo(
|
||||
name: 'QQ Music',
|
||||
description: 'QQ Music (good for Chinese songs, via proxy)',
|
||||
description: context.l10n.lyricsProviderQqMusicDesc,
|
||||
icon: Icons.queue_music,
|
||||
);
|
||||
default:
|
||||
return _LyricsProviderInfo(
|
||||
name: id,
|
||||
description: 'Extension provider',
|
||||
description: context.l10n.lyricsProviderExtensionDesc,
|
||||
icon: Icons.extension,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -107,8 +107,8 @@ class SettingsTab extends ConsumerWidget {
|
||||
),
|
||||
SettingsItem(
|
||||
icon: Icons.favorite_outline,
|
||||
title: 'Donate',
|
||||
subtitle: 'Support SpotiFLAC-Mobile development',
|
||||
title: l10n.settingsDonate,
|
||||
subtitle: l10n.settingsDonateSubtitle,
|
||||
onTap: () => _navigateTo(context, const DonatePage()),
|
||||
showDivider: false,
|
||||
),
|
||||
|
||||
@@ -1731,13 +1731,13 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
);
|
||||
success = ok;
|
||||
if (!ok) {
|
||||
error = 'Failed to write back to storage';
|
||||
error = context.l10n.snackbarFailedToWriteStorage;
|
||||
}
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
} else {
|
||||
error = result['error']?.toString() ?? 'Failed to embed lyrics';
|
||||
error = result['error']?.toString() ?? context.l10n.snackbarFailedToEmbedLyrics;
|
||||
}
|
||||
} else if (isMp3 || isOpus) {
|
||||
final metadata = _buildFallbackMetadata();
|
||||
@@ -1783,7 +1783,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
}
|
||||
|
||||
if (ffmpegResult == null) {
|
||||
error = 'Failed to embed lyrics';
|
||||
error = context.l10n.snackbarFailedToEmbedLyrics;
|
||||
} else if (_isSafFile) {
|
||||
final ok = await PlatformBridge.writeTempToSaf(
|
||||
ffmpegResult,
|
||||
@@ -1791,13 +1791,13 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
);
|
||||
success = ok;
|
||||
if (!ok) {
|
||||
error = 'Failed to write back to storage';
|
||||
error = context.l10n.snackbarFailedToWriteStorage;
|
||||
}
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
} else {
|
||||
error = 'Unsupported audio format';
|
||||
error = context.l10n.snackbarUnsupportedAudioFormat;
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
@@ -1812,7 +1812,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
} else {
|
||||
setState(() => _isEmbedding = false);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(error ?? 'Failed to embed lyrics')),
|
||||
SnackBar(content: Text(error ?? context.l10n.snackbarFailedToEmbedLyrics)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1821,7 +1821,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
setState(() => _isEmbedding = false);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Error: $e')));
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.snackbarError(e.toString()))));
|
||||
}
|
||||
} finally {
|
||||
if (coverPath != null) {
|
||||
@@ -2271,7 +2271,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.trackSaveFailed(
|
||||
'Failed to write back to storage',
|
||||
context.l10n.snackbarFailedToWriteStorage,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -2873,7 +2873,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
|
||||
// Show loading indicator
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Loading CUE sheet...')),
|
||||
SnackBar(content: Text(context.l10n.snackbarLoadingCueSheet)),
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -3644,7 +3644,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
|
||||
if (saved == true && mounted) {
|
||||
ScaffoldMessenger.of(this.context).showSnackBar(
|
||||
const SnackBar(content: Text('Metadata saved successfully')),
|
||||
SnackBar(content: Text(this.context.l10n.snackbarMetadataSaved)),
|
||||
);
|
||||
// Re-read metadata from file to refresh the display
|
||||
try {
|
||||
@@ -4066,7 +4066,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Failed to pick cover: $e')));
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.snackbarError(e.toString()))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4290,7 +4290,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Failed to save metadata: $e')));
|
||||
).showSnackBar(SnackBar(content: Text(context.l10n.snackbarError(e.toString()))));
|
||||
}
|
||||
} finally {
|
||||
if (mounted) setState(() => _saving = false);
|
||||
|
||||
Reference in new issue
Block a user