feat: add AAC lossy target and toggle for Apple Music eLRC word sync

The HIGH-quality lossy format picker can now produce an AAC/M4A 320 kbps output alongside MP3 and Opus. FFmpegService.convertM4aToLossy/convertAudioFormat, the Dart queue pipeline, the Kotlin finalizer, and the library database format helper all route .m4a through a unified aac codec path and tag the resulting file with the M4A metadata writer. The Lossy Format setting gains a new option, and the track metadata convert dialog lists AAC next to the other targets.

Apple Music lyrics gain a 'eLRC word sync' switch (default off). When disabled the pax-to-LRC formatter strips inline word timestamps, producing line-synced LRC that is safer for players that choke on eLRC; enabling it restores the previous word-by-word behaviour. The change propagates through SetLyricsFetchOptions and invalidates the global lyrics cache on toggle.

Broad l10n migration: roughly 400 previously hardcoded English strings across queue, settings, track metadata, repo, audio analysis, setup and extension screens now live in the ARB catalog, with matching plural/placeholder forms. No behaviour change beyond localisation. Existing and new unit tests (lyrics eLRC toggle and Dart settings round-trip) pass.
This commit is contained in:
zarzet
2026-05-12 02:20:42 +07:00
parent 7845ac8be5
commit 2a2d817314
47 changed files with 6428 additions and 277 deletions
@@ -476,13 +476,23 @@ object NativeDownloadFinalizer {
if (!looksLikeM4a(state.filePath, state.fileName)) return
val tidalHighFormat = input.request.optString("tidal_high_format", "").ifBlank { "mp3_320" }
val format = if (tidalHighFormat.startsWith("opus")) "opus" else "mp3"
val format = when {
tidalHighFormat.startsWith("opus") -> "opus"
tidalHighFormat.startsWith("aac") || tidalHighFormat.startsWith("m4a") -> "aac"
else -> "mp3"
}
val metadataFormat = if (format == "aac") "m4a" else format
val displayFormat = if (format == "aac") "AAC" else format.uppercase(Locale.ROOT)
val bitrate = if (tidalHighFormat.contains("_")) {
"${tidalHighFormat.substringAfterLast("_")}k"
} else {
if (format == "opus") "128k" else "320k"
}
val ext = if (format == "opus") ".opus" else ".mp3"
val ext = when (format) {
"opus" -> ".opus"
"aac" -> ".m4a"
else -> ".mp3"
}
val localInput = materializeForFFmpeg(context, input, state)
val deleteLocalInput = state.filePath.startsWith("content://")
val output = buildOutputPath(localInput, ext)
@@ -490,6 +500,8 @@ object NativeDownloadFinalizer {
try {
val command = if (format == "opus") {
"-v error -hide_banner -i ${q(localInput)} -codec:a libopus -b:a $bitrate -vbr on -compression_level 10 -map 0:a ${q(output)} -y"
} else if (format == "aac") {
"-v error -hide_banner -i ${q(localInput)} -codec:a aac -b:a $bitrate -map 0:a -f mp4 ${q(output)} -y"
} else {
"-v error -hide_banner -i ${q(localInput)} -codec:a libmp3lame -b:a $bitrate -map 0:a -id3v2_version 3 ${q(output)} -y"
}
@@ -497,14 +509,14 @@ object NativeDownloadFinalizer {
if (!result.first || !File(output).exists()) {
throw IllegalStateException("HIGH conversion failed: ${result.second}")
}
embedBasicMetadata(context, output, input, format)
embedBasicMetadata(context, output, input, metadataFormat)
replaceStatePath(context, input, state, output, deleteOld = true)
adoptedOutput = true
} finally {
if (!adoptedOutput) File(output).delete()
if (deleteLocalInput) File(localInput).delete()
}
state.quality = "${format.uppercase(Locale.ROOT)} ${bitrate.removeSuffix("k")}kbps"
state.quality = "$displayFormat ${bitrate.removeSuffix("k")}kbps"
state.bitDepth = null
state.sampleRate = null
}
@@ -1380,7 +1392,7 @@ object NativeDownloadFinalizer {
val rawName = input.request.optString("saf_file_name", "")
.ifBlank { state.fileName }
.ifBlank { "${trackString(input, "artistName", input.request.optString("artist_name", "Artist"))} - ${trackString(input, "name", input.request.optString("track_name", "Track"))}" }
val knownExts = listOf(".flac", ".m4a", ".mp4", ".mp3", ".opus", ".ogg", ".lrc")
val knownExts = listOf(".flac", ".m4a", ".mp4", ".aac", ".mp3", ".opus", ".ogg", ".lrc")
var base = rawName.trim()
val lower = base.lowercase(Locale.ROOT)
for (knownExt in knownExts) {
+11 -3
View File
@@ -68,6 +68,7 @@ type LyricsFetchOptions struct {
IncludeTranslationNetease bool `json:"include_translation_netease"`
IncludeRomanizationNetease bool `json:"include_romanization_netease"`
MultiPersonWordByWord bool `json:"multi_person_word_by_word"`
AppleElrcWordSync bool `json:"apple_elrc_word_sync"`
MusixmatchLanguage string `json:"musixmatch_language,omitempty"`
}
@@ -75,6 +76,7 @@ var defaultLyricsFetchOptions = LyricsFetchOptions{
IncludeTranslationNetease: false,
IncludeRomanizationNetease: false,
MultiPersonWordByWord: true,
AppleElrcWordSync: false,
MusixmatchLanguage: "",
}
@@ -151,12 +153,18 @@ func SetLyricsFetchOptions(opts LyricsFetchOptions) {
lyricsFetchOptionsMu.Lock()
defer lyricsFetchOptionsMu.Unlock()
changed := lyricsFetchOptions != normalized
lyricsFetchOptions = normalized
GoLog("[Lyrics] Fetch options set: translation=%v romanization=%v multi_person=%v musixmatch_lang=%q\n",
if changed {
globalLyricsCache.ClearAll()
}
GoLog("[Lyrics] Fetch options set: translation=%v romanization=%v multi_person=%v apple_elrc=%v musixmatch_lang=%q\n",
normalized.IncludeTranslationNetease,
normalized.IncludeRomanizationNetease,
normalized.MultiPersonWordByWord,
normalized.AppleElrcWordSync,
normalized.MusixmatchLanguage,
)
}
@@ -530,9 +538,9 @@ func (c *LyricsClient) FetchLyricsAllSources(spotifyID, trackName, artistName st
case LyricsProviderAppleMusic:
appleClient := NewAppleMusicClient()
lyrics, err = appleClient.FetchLyrics(trackName, primaryArtist, durationSec, fetchOptions.MultiPersonWordByWord)
lyrics, err = appleClient.FetchLyrics(trackName, primaryArtist, durationSec, fetchOptions.MultiPersonWordByWord, fetchOptions.AppleElrcWordSync)
if err != nil && primaryArtist != artistName {
lyrics, err = appleClient.FetchLyrics(trackName, artistName, durationSec, fetchOptions.MultiPersonWordByWord)
lyrics, err = appleClient.FetchLyrics(trackName, artistName, durationSec, fetchOptions.MultiPersonWordByWord, fetchOptions.AppleElrcWordSync)
}
case LyricsProviderQQMusic:
+11 -10
View File
@@ -173,25 +173,25 @@ func (c *AppleMusicClient) FetchLyricsByID(songID string) (string, error) {
return bodyStr, nil
}
func formatPaxLyricsToLRC(rawJSON string, multiPersonWordByWord bool) (string, error) {
func formatPaxLyricsToLRC(rawJSON string, multiPersonWordByWord bool, preserveWordTiming bool) (string, error) {
var paxResp paxResponse
if err := json.Unmarshal([]byte(rawJSON), &paxResp); err == nil && paxResp.Content != nil {
return formatPaxContent(paxResp.Type, paxResp.Content, multiPersonWordByWord), nil
return formatPaxContent(paxResp.Type, paxResp.Content, multiPersonWordByWord, preserveWordTiming), nil
}
var directLyrics []paxLyrics
if err := json.Unmarshal([]byte(rawJSON), &directLyrics); err == nil && len(directLyrics) > 0 {
return formatPaxContent("Syllable", directLyrics, multiPersonWordByWord), nil
return formatPaxContent("Syllable", directLyrics, multiPersonWordByWord, preserveWordTiming), nil
}
return "", fmt.Errorf("failed to parse pax lyrics response")
}
func appendPaxLyricDetail(builder *strings.Builder, details []paxLyricDetail) {
func appendPaxLyricDetail(builder *strings.Builder, details []paxLyricDetail, preserveWordTiming bool) {
lastStart := ""
for _, syllable := range details {
if syllable.Timestamp != nil {
if preserveWordTiming && syllable.Timestamp != nil {
start := fmt.Sprintf("<%s>", msToLRCTimestampInline(int64(*syllable.Timestamp)))
if start != lastStart {
builder.WriteString(start)
@@ -204,13 +204,13 @@ func appendPaxLyricDetail(builder *strings.Builder, details []paxLyricDetail) {
builder.WriteString(" ")
}
if syllable.EndTime != nil {
if preserveWordTiming && syllable.EndTime != nil {
builder.WriteString(fmt.Sprintf("<%s>", msToLRCTimestampInline(int64(*syllable.EndTime))))
}
}
}
func formatPaxContent(lyricsType string, content []paxLyrics, multiPersonWordByWord bool) string {
func formatPaxContent(lyricsType string, content []paxLyrics, multiPersonWordByWord bool, preserveWordTiming bool) string {
var sb strings.Builder
for i, line := range content {
@@ -230,11 +230,11 @@ func formatPaxContent(lyricsType string, content []paxLyrics, multiPersonWordByW
}
}
appendPaxLyricDetail(&sb, line.Text)
appendPaxLyricDetail(&sb, line.Text, preserveWordTiming)
if line.Background && multiPersonWordByWord && len(line.BackgroundText) > 0 {
sb.WriteString("\n[bg:")
appendPaxLyricDetail(&sb, line.BackgroundText)
appendPaxLyricDetail(&sb, line.BackgroundText, preserveWordTiming)
sb.WriteString("]")
}
} else {
@@ -253,6 +253,7 @@ func (c *AppleMusicClient) FetchLyrics(
artistName string,
durationSec float64,
multiPersonWordByWord bool,
preserveWordTiming bool,
) (*LyricsResponse, error) {
songID, err := c.SearchSong(trackName, artistName, durationSec)
if err != nil {
@@ -267,7 +268,7 @@ func (c *AppleMusicClient) FetchLyrics(
return nil, fmt.Errorf("apple music proxy returned non-lyric payload: %s", errMsg)
}
lrcText, err := formatPaxLyricsToLRC(rawLyrics, multiPersonWordByWord)
lrcText, err := formatPaxLyricsToLRC(rawLyrics, multiPersonWordByWord, preserveWordTiming)
if err != nil {
lrcText = rawLyrics
}
+2 -2
View File
@@ -87,7 +87,7 @@ func formatQQLyricsMetadataToLRC(rawJSON string, multiPersonWordByWord bool) (st
if len(response.Lyrics) == 0 {
return "", fmt.Errorf("qq metadata lyrics response was empty")
}
return formatPaxContent("Syllable", response.Lyrics, multiPersonWordByWord), nil
return formatPaxContent("Syllable", response.Lyrics, multiPersonWordByWord, true), nil
}
func (c *QQMusicClient) FetchLyrics(
@@ -106,7 +106,7 @@ func (c *QQMusicClient) FetchLyrics(
lrcText, err := formatQQLyricsMetadataToLRC(rawLyrics, multiPersonWordByWord)
if err != nil {
if fallback, fallbackErr := formatPaxLyricsToLRC(rawLyrics, multiPersonWordByWord); fallbackErr == nil {
if fallback, fallbackErr := formatPaxLyricsToLRC(rawLyrics, multiPersonWordByWord, true); fallbackErr == nil {
lrcText = fallback
} else {
lrcText = rawLyrics
+16 -2
View File
@@ -156,13 +156,27 @@ func TestExternalLyricsProvidersWithFakeHTTP(t *testing.T) {
if err != nil || !strings.Contains(rawApple, "Syllable") {
t.Fatalf("apple raw = %q/%v", rawApple, err)
}
appleLyrics, err := apple.FetchLyrics("Song", "Artist", 180, true)
appleLyrics, err := apple.FetchLyrics("Song", "Artist", 180, true, true)
if err != nil || appleLyrics.SyncType != "LINE_SYNCED" || appleLyrics.Provider != "Apple Music" {
t.Fatalf("apple lyrics = %#v/%v", appleLyrics, err)
}
if plain, err := formatPaxLyricsToLRC(`[{"timestamp":2000,"text":[{"text":"Plain","part":false}]}]`, false); err != nil || !strings.Contains(plain, "Plain") {
if plain, err := formatPaxLyricsToLRC(`[{"timestamp":2000,"text":[{"text":"Plain","part":false}]}]`, false, false); err != nil || !strings.Contains(plain, "Plain") {
t.Fatalf("direct pax = %q/%v", plain, err)
}
lineOnly, err := formatPaxLyricsToLRC(paxJSON, true, false)
if err != nil {
t.Fatalf("line-only pax = %v", err)
}
if strings.Contains(lineOnly, "<00:") {
t.Fatalf("line-only pax should not include inline word timing: %q", lineOnly)
}
elrc, err := formatPaxLyricsToLRC(paxJSON, true, true)
if err != nil {
t.Fatalf("elrc pax = %v", err)
}
if !strings.Contains(elrc, "<00:") {
t.Fatalf("elrc pax should include inline word timing: %q", elrc)
}
if _, err := apple.SearchSong("", "", 0); err == nil {
t.Fatal("expected empty apple search error")
}
+501 -1
View File
@@ -2286,6 +2286,12 @@ abstract class AppLocalizations {
/// **'Copy lyrics'**
String get trackCopyLyrics;
/// Label showing the lyrics source/provider
///
/// In en, this message translates to:
/// **'Source: {source}'**
String trackLyricsSource(String source);
/// Message when lyrics not found
///
/// In en, this message translates to:
@@ -2838,6 +2844,18 @@ abstract class AppLocalizations {
/// **'Best compatibility, ~10MB per track'**
String get downloadLossyMp3Subtitle;
/// Tidal lossy format option - AAC in M4A container at 320kbps
///
/// In en, this message translates to:
/// **'AAC/M4A 320kbps'**
String get downloadLossyAac;
/// Subtitle for AAC/M4A 320kbps Tidal lossy option
///
/// In en, this message translates to:
/// **'Best mobile compatibility, M4A container'**
String get downloadLossyAacSubtitle;
/// Tidal lossy format option - Opus 256kbps
///
/// In en, this message translates to:
@@ -4299,7 +4317,7 @@ abstract class AppLocalizations {
/// Subtitle for convert format menu item
///
/// In en, this message translates to:
/// **'Convert to MP3, Opus, ALAC, or FLAC'**
/// **'Convert to AAC/M4A, MP3, Opus, ALAC, or FLAC'**
String get trackConvertFormatSubtitle;
/// Title of convert bottom sheet
@@ -5209,6 +5227,24 @@ abstract class AppLocalizations {
/// **'Standard lyrics without speaker labels'**
String get downloadAppleQqMultiPersonDisabled;
/// Setting for preserving Apple Music word-by-word eLRC timestamps
///
/// In en, this message translates to:
/// **'Apple Music eLRC Word Sync'**
String get downloadAppleElrcWordSync;
/// Subtitle when Apple Music eLRC word sync is enabled
///
/// In en, this message translates to:
/// **'Raw word-by-word timestamps preserved'**
String get downloadAppleElrcWordSyncEnabled;
/// Subtitle when Apple Music eLRC word sync is disabled
///
/// In en, this message translates to:
/// **'Safer line-by-line Apple Music lyrics'**
String get downloadAppleElrcWordSyncDisabled;
/// Setting for Musixmatch lyrics translation language
///
/// In en, this message translates to:
@@ -6428,6 +6464,470 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Choose which extensions can be used as fallback'**
String get downloadFallbackExtensionsSubtitle;
/// Hint text for the edit metadata date field
///
/// In en, this message translates to:
/// **'YYYY-MM-DD or YYYY'**
String get editMetadataFieldDateHint;
/// Label for total tracks field in the edit metadata sheet
///
/// In en, this message translates to:
/// **'Track Total'**
String get editMetadataFieldTrackTotal;
/// Label for total discs field in the edit metadata sheet
///
/// In en, this message translates to:
/// **'Disc Total'**
String get editMetadataFieldDiscTotal;
/// Label for composer field in the edit metadata sheet
///
/// In en, this message translates to:
/// **'Composer'**
String get editMetadataFieldComposer;
/// Label for comment field in the edit metadata sheet
///
/// In en, this message translates to:
/// **'Comment'**
String get editMetadataFieldComment;
/// Expandable section label for advanced metadata fields
///
/// In en, this message translates to:
/// **'Advanced'**
String get editMetadataAdvanced;
/// Filter option - items missing track number
///
/// In en, this message translates to:
/// **'Missing track number'**
String get libraryFilterMetadataMissingTrackNumber;
/// Filter option - items missing disc number
///
/// In en, this message translates to:
/// **'Missing disc number'**
String get libraryFilterMetadataMissingDiscNumber;
/// Filter option - items missing artist
///
/// In en, this message translates to:
/// **'Missing artist'**
String get libraryFilterMetadataMissingArtist;
/// Filter option - items with an invalid ISRC format
///
/// In en, this message translates to:
/// **'Incorrect ISRC format'**
String get libraryFilterMetadataIncorrectIsrcFormat;
/// Filter option - items missing record label
///
/// In en, this message translates to:
/// **'Missing label'**
String get libraryFilterMetadataMissingLabel;
/// Confirmation message for deleting selected playlists
///
/// In en, this message translates to:
/// **'Delete {count} {count, plural, =1{playlist} other{playlists}}?'**
String collectionDeletePlaylistsMessage(int count);
/// Snackbar after deleting selected playlists
///
/// In en, this message translates to:
/// **'{count} {count, plural, =1{playlist} other{playlists}} deleted'**
String collectionPlaylistsDeleted(int count);
/// Snackbar after adding multiple tracks to a playlist
///
/// In en, this message translates to:
/// **'Added {count} {count, plural, =1{track} other{tracks}} to {playlistName}'**
String collectionAddedTracksToPlaylist(int count, String playlistName);
/// Snackbar after adding multiple tracks to a playlist when some were already present
///
/// In en, this message translates to:
/// **'Added {count} {count, plural, =1{track} other{tracks}} to {playlistName} ({alreadyCount} already in playlist)'**
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
);
/// Generic item count label
///
/// In en, this message translates to:
/// **'{count} {count, plural, =1{item} other{items}}'**
String itemCount(int count);
/// Snackbar summary after batch metadata re-enrichment finishes with failures
///
/// In en, this message translates to:
/// **'Metadata re-enriched successfully ({successCount}/{total}) - Failed: {failedCount}'**
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
);
/// Button label for deleting selected tracks
///
/// In en, this message translates to:
/// **'Delete {count} {count, plural, =1{track} other{tracks}}'**
String selectionDeleteTracksCount(int count);
/// Queue status while downloading with speed
///
/// In en, this message translates to:
/// **'Downloading - {speed} MB/s'**
String queueDownloadSpeedStatus(String speed);
/// Queue status before download progress is available
///
/// In en, this message translates to:
/// **'Starting...'**
String get queueDownloadStarting;
/// Accessibility label for selecting a track
///
/// In en, this message translates to:
/// **'Select track'**
String get a11ySelectTrack;
/// Accessibility label for deselecting a track
///
/// In en, this message translates to:
/// **'Deselect track'**
String get a11yDeselectTrack;
/// Accessibility label for playing a local library track
///
/// In en, this message translates to:
/// **'Play {trackName} by {artistName}'**
String a11yPlayTrackByArtist(String trackName, String artistName);
/// Store extension result count
///
/// In en, this message translates to:
/// **'{count} {count, plural, =1{extension} other{extensions}}'**
String storeExtensionsCount(int count);
/// Store compatibility badge for minimum app version
///
/// In en, this message translates to:
/// **'Requires v{version}+'**
String storeRequiresVersion(String version);
/// Generic action button label
///
/// In en, this message translates to:
/// **'Go'**
String get actionGo;
/// Header for log issue analysis summary
///
/// In en, this message translates to:
/// **'Issue Summary'**
String get logIssueSummary;
/// Total error count in log issue analysis
///
/// In en, this message translates to:
/// **'Total errors: {count}'**
String logTotalErrors(int count);
/// Affected domains in log issue analysis
///
/// In en, this message translates to:
/// **'Affected: {domains}'**
String logAffectedDomains(String domains);
/// Library scan status when a scan was cancelled
///
/// In en, this message translates to:
/// **'Scan cancelled'**
String get libraryScanCancelled;
/// Library scan status subtitle after cancellation
///
/// In en, this message translates to:
/// **'You can retry the scan when ready.'**
String get libraryScanCancelledSubtitle;
/// Library count note for downloaded history items excluded from the local list
///
/// In en, this message translates to:
/// **'{count} from Downloads history (excluded from list)'**
String libraryDownloadsHistoryExcluded(int count);
/// Setting title for Android native download worker
///
/// In en, this message translates to:
/// **'Native download worker'**
String get downloadNativeWorker;
/// Setting subtitle for Android native download worker
///
/// In en, this message translates to:
/// **'Beta Android service worker for extension downloads'**
String get downloadNativeWorkerSubtitle;
/// Badge label for beta features
///
/// In en, this message translates to:
/// **'BETA'**
String get badgeBeta;
/// Extension detail section header for service status
///
/// In en, this message translates to:
/// **'Service Status'**
String get extensionServiceStatus;
/// Extension capability label for service health checks
///
/// In en, this message translates to:
/// **'Service health'**
String get extensionServiceHealth;
/// Extension service health check count
///
/// In en, this message translates to:
/// **'{count} {count, plural, =1{check} other{checks}} configured'**
String extensionHealthChecksConfigured(int count);
/// Hint for an OAuth login link field before connecting Spotify
///
/// In en, this message translates to:
/// **'Tap Connect to Spotify to fill this field.'**
String get extensionOauthConnectHint;
/// Timestamp for the latest extension service health check
///
/// In en, this message translates to:
/// **'Last checked {time}'**
String extensionLastChecked(String time);
/// Tooltip for refreshing extension service health status
///
/// In en, this message translates to:
/// **'Refresh status'**
String get extensionRefreshStatus;
/// Extension detail section title for custom URL handling
///
/// In en, this message translates to:
/// **'Custom URL Handling'**
String get extensionCustomUrlHandling;
/// Extension detail subtitle for custom URL handling
///
/// In en, this message translates to:
/// **'This extension can handle links from these sites'**
String get extensionCustomUrlHandlingSubtitle;
/// Extension detail hint explaining share-to-app URL handling
///
/// In en, this message translates to:
/// **'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.'**
String get extensionCustomUrlHandlingShareHint;
/// Count of settings exposed by an extension quality option
///
/// In en, this message translates to:
/// **'{count} {count, plural, =1{setting} other{settings}}'**
String extensionSettingsCount(int count);
/// Extension service health status - online
///
/// In en, this message translates to:
/// **'Online'**
String get extensionHealthOnline;
/// Extension service health status - degraded
///
/// In en, this message translates to:
/// **'Degraded'**
String get extensionHealthDegraded;
/// Extension service health status - offline
///
/// In en, this message translates to:
/// **'Offline'**
String get extensionHealthOffline;
/// Extension service health status - not configured
///
/// In en, this message translates to:
/// **'Not configured'**
String get extensionHealthNotConfigured;
/// Extension service health status - unknown
///
/// In en, this message translates to:
/// **'Unknown'**
String get extensionHealthUnknown;
/// Label for a required extension service health check
///
/// In en, this message translates to:
/// **'required'**
String get extensionHealthRequired;
/// Value shown when an extension setting has no value
///
/// In en, this message translates to:
/// **'Not set'**
String get extensionSettingNotSet;
/// Fallback error when an extension action fails without details
///
/// In en, this message translates to:
/// **'Action failed'**
String get extensionActionFailed;
/// Hint for editing an extension setting value
///
/// In en, this message translates to:
/// **'Enter value'**
String get extensionEnterValue;
/// Tooltip for online extension service
///
/// In en, this message translates to:
/// **'Service online'**
String get extensionHealthServiceOnline;
/// Tooltip for degraded extension service
///
/// In en, this message translates to:
/// **'Service degraded'**
String get extensionHealthServiceDegraded;
/// Tooltip for offline extension service
///
/// In en, this message translates to:
/// **'Service offline'**
String get extensionHealthServiceOffline;
/// Tooltip for unknown extension service health
///
/// In en, this message translates to:
/// **'Service status unknown'**
String get extensionHealthServiceUnknown;
/// Audio channel layout label - stereo
///
/// In en, this message translates to:
/// **'Stereo'**
String get audioAnalysisStereo;
/// Audio channel layout label - mono
///
/// In en, this message translates to:
/// **'Mono'**
String get audioAnalysisMono;
/// Button label to open a track in a named music service
///
/// In en, this message translates to:
/// **'Open in {serviceName}'**
String trackOpenInService(String serviceName);
/// Lyrics source label for embedded lyrics
///
/// In en, this message translates to:
/// **'Embedded'**
String get trackLyricsEmbeddedSource;
/// Fallback album name when metadata is missing
///
/// In en, this message translates to:
/// **'Unknown Album'**
String get unknownAlbum;
/// Fallback artist name when metadata is missing
///
/// In en, this message translates to:
/// **'Unknown Artist'**
String get unknownArtist;
/// Audio permission type label
///
/// In en, this message translates to:
/// **'Audio'**
String get permissionAudio;
/// Storage permission type label
///
/// In en, this message translates to:
/// **'Storage'**
String get permissionStorage;
/// Notification permission type label
///
/// In en, this message translates to:
/// **'Notification'**
String get permissionNotification;
/// Error when the selected folder is invalid
///
/// In en, this message translates to:
/// **'Invalid folder selected'**
String get errorInvalidFolderSelected;
/// Error when persistent folder access cannot be saved
///
/// In en, this message translates to:
/// **'Could not keep access to the selected folder'**
String get errorCouldNotKeepFolderAccess;
/// Store detail value when any app version is accepted
///
/// In en, this message translates to:
/// **'Any'**
String get storeAnyVersion;
/// Store extension category - metadata
///
/// In en, this message translates to:
/// **'Metadata'**
String get storeCategoryMetadata;
/// Store extension category - download
///
/// In en, this message translates to:
/// **'Download'**
String get storeCategoryDownload;
/// Store extension category - utility
///
/// In en, this message translates to:
/// **'Utility'**
String get storeCategoryUtility;
/// Store extension category - lyrics
///
/// In en, this message translates to:
/// **'Lyrics'**
String get storeCategoryLyrics;
/// Store extension category - integration
///
/// In en, this message translates to:
/// **'Integration'**
String get storeCategoryIntegration;
/// Section header for all artist releases
///
/// In en, this message translates to:
/// **'Releases'**
String get artistReleases;
}
class _AppLocalizationsDelegate
+356
View File
@@ -1237,6 +1237,11 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get trackCopyLyrics => 'Lyrics kopieren';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable =>
'Lyrics sind für diesen Titel nicht verfügbar';
@@ -1547,6 +1552,13 @@ class AppLocalizationsDe extends AppLocalizations {
String get downloadLossyMp3Subtitle =>
'Beste Kompatibilität, ~10MB pro Titel';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256kbps';
@@ -3049,6 +3061,17 @@ class AppLocalizationsDe extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Simplified word-by-word formatting';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@@ -3839,4 +3862,337 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
+357 -1
View File
@@ -1220,6 +1220,11 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get trackCopyLyrics => 'Copy lyrics';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable => 'Lyrics not available for this track';
@@ -1523,6 +1528,13 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get downloadLossyMp3Subtitle => 'Best compatibility, ~10MB per track';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256kbps';
@@ -2408,7 +2420,7 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get trackConvertFormatSubtitle =>
'Convert to MP3, Opus, ALAC, or FLAC';
'Convert to AAC/M4A, MP3, Opus, ALAC, or FLAC';
@override
String get trackConvertTitle => 'Convert Audio';
@@ -3015,6 +3027,17 @@ class AppLocalizationsEn extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Standard lyrics without speaker labels';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@@ -3810,4 +3833,337 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
+356
View File
@@ -1220,6 +1220,11 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get trackCopyLyrics => 'Copy lyrics';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable => 'Lyrics not available for this track';
@@ -1523,6 +1528,13 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get downloadLossyMp3Subtitle => 'Best compatibility, ~10MB per track';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256kbps';
@@ -3015,6 +3027,17 @@ class AppLocalizationsEs extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Standard lyrics without speaker labels';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@@ -3804,6 +3827,339 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
/// The translations for Spanish Castilian, as used in Spain (`es_ES`).
+356
View File
@@ -1223,6 +1223,11 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get trackCopyLyrics => 'Copy lyrics';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable => 'Lyrics not available for this track';
@@ -1526,6 +1531,13 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get downloadLossyMp3Subtitle => 'Best compatibility, ~10MB per track';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256kbps';
@@ -3018,6 +3030,17 @@ class AppLocalizationsFr extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Simplified word-by-word formatting';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@@ -3808,4 +3831,337 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
+356
View File
@@ -1220,6 +1220,11 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get trackCopyLyrics => 'Copy lyrics';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable => 'Lyrics not available for this track';
@@ -1523,6 +1528,13 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get downloadLossyMp3Subtitle => 'Best compatibility, ~10MB per track';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256kbps';
@@ -3015,6 +3027,17 @@ class AppLocalizationsHi extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Simplified word-by-word formatting';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@@ -3805,4 +3828,337 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
+356
View File
@@ -1226,6 +1226,11 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get trackCopyLyrics => 'Salin lirik';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable => 'Lirik tidak tersedia untuk lagu ini';
@@ -1531,6 +1536,13 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get downloadLossyMp3Subtitle => 'Best compatibility, ~10MB per track';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256kbps';
@@ -3024,6 +3036,17 @@ class AppLocalizationsId extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Simplified word-by-word formatting';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@@ -3796,4 +3819,337 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
+356
View File
@@ -1214,6 +1214,11 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get trackCopyLyrics => '歌詞をコピー';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable => 'このトラックの歌詞は利用できません';
@@ -1513,6 +1518,13 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get downloadLossyMp3Subtitle => 'Best compatibility, ~10MB per track';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256kbps';
@@ -3002,6 +3014,17 @@ class AppLocalizationsJa extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Simplified word-by-word formatting';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@@ -3792,4 +3815,337 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
+356
View File
@@ -1200,6 +1200,11 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get trackCopyLyrics => 'Copy lyrics';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable => 'Lyrics not available for this track';
@@ -1503,6 +1508,13 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get downloadLossyMp3Subtitle => 'Best compatibility, ~10MB per track';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256kbps';
@@ -2995,6 +3007,17 @@ class AppLocalizationsKo extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Simplified word-by-word formatting';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@@ -3785,4 +3808,337 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
+356
View File
@@ -1220,6 +1220,11 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get trackCopyLyrics => 'Copy lyrics';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable => 'Lyrics not available for this track';
@@ -1523,6 +1528,13 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get downloadLossyMp3Subtitle => 'Best compatibility, ~10MB per track';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256kbps';
@@ -3015,6 +3027,17 @@ class AppLocalizationsNl extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Simplified word-by-word formatting';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@@ -3805,4 +3828,337 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
+356
View File
@@ -1220,6 +1220,11 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get trackCopyLyrics => 'Copy lyrics';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable => 'Lyrics not available for this track';
@@ -1523,6 +1528,13 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get downloadLossyMp3Subtitle => 'Best compatibility, ~10MB per track';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256kbps';
@@ -3015,6 +3027,17 @@ class AppLocalizationsPt extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Standard lyrics without speaker labels';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@@ -3804,6 +3827,339 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
/// The translations for Portuguese, as used in Portugal (`pt_PT`).
+356
View File
@@ -1238,6 +1238,11 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get trackCopyLyrics => 'Копировать текст';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable =>
'Текст песни недоступен для этого трека';
@@ -1547,6 +1552,13 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get downloadLossyMp3Subtitle => 'Best compatibility, ~10MB per track';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256kbps';
@@ -3074,6 +3086,17 @@ class AppLocalizationsRu extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Simplified word-by-word formatting';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@@ -3864,4 +3887,337 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
+356
View File
@@ -1234,6 +1234,11 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get trackCopyLyrics => 'Şarkı sözlerini kopyala';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable => 'Bu parça için şarkı sözü mevcut değil';
@@ -1540,6 +1545,13 @@ class AppLocalizationsTr extends AppLocalizations {
String get downloadLossyMp3Subtitle =>
'En iyi uyumluluk, parça başına ~10 Mb';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256kbps';
@@ -3041,6 +3053,17 @@ class AppLocalizationsTr extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Simplified word-by-word formatting';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@@ -3831,4 +3854,337 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
+356
View File
@@ -1240,6 +1240,11 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get trackCopyLyrics => 'Скопіювати тексти пісень';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable =>
'Текст пісні для цього треку недоступний';
@@ -1548,6 +1553,13 @@ class AppLocalizationsUk extends AppLocalizations {
String get downloadLossyMp3Subtitle =>
'Найкраща сумісність, ~10 МБ на доріжку';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256 кбіт/с';
@@ -3067,6 +3079,17 @@ class AppLocalizationsUk extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Спрощене послівне форматування';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Мова Musixmatch';
@@ -3864,4 +3887,337 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
+356
View File
@@ -1220,6 +1220,11 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get trackCopyLyrics => 'Copy lyrics';
@override
String trackLyricsSource(String source) {
return 'Source: $source';
}
@override
String get trackLyricsNotAvailable => 'Lyrics not available for this track';
@@ -1523,6 +1528,13 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get downloadLossyMp3Subtitle => 'Best compatibility, ~10MB per track';
@override
String get downloadLossyAac => 'AAC/M4A 320kbps';
@override
String get downloadLossyAacSubtitle =>
'Best mobile compatibility, M4A container';
@override
String get downloadLossyOpus256 => 'Opus 256kbps';
@@ -3015,6 +3027,17 @@ class AppLocalizationsZh extends AppLocalizations {
String get downloadAppleQqMultiPersonDisabled =>
'Standard lyrics without speaker labels';
@override
String get downloadAppleElrcWordSync => 'Apple Music eLRC Word Sync';
@override
String get downloadAppleElrcWordSyncEnabled =>
'Raw word-by-word timestamps preserved';
@override
String get downloadAppleElrcWordSyncDisabled =>
'Safer line-by-line Apple Music lyrics';
@override
String get downloadMusixmatchLanguage => 'Musixmatch Language';
@@ -3804,6 +3827,339 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get downloadFallbackExtensionsSubtitle =>
'Choose which extensions can be used as fallback';
@override
String get editMetadataFieldDateHint => 'YYYY-MM-DD or YYYY';
@override
String get editMetadataFieldTrackTotal => 'Track Total';
@override
String get editMetadataFieldDiscTotal => 'Disc Total';
@override
String get editMetadataFieldComposer => 'Composer';
@override
String get editMetadataFieldComment => 'Comment';
@override
String get editMetadataAdvanced => 'Advanced';
@override
String get libraryFilterMetadataMissingTrackNumber => 'Missing track number';
@override
String get libraryFilterMetadataMissingDiscNumber => 'Missing disc number';
@override
String get libraryFilterMetadataMissingArtist => 'Missing artist';
@override
String get libraryFilterMetadataIncorrectIsrcFormat =>
'Incorrect ISRC format';
@override
String get libraryFilterMetadataMissingLabel => 'Missing label';
@override
String collectionDeletePlaylistsMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return 'Delete $count $_temp0?';
}
@override
String collectionPlaylistsDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'playlists',
one: 'playlist',
);
return '$count $_temp0 deleted';
}
@override
String collectionAddedTracksToPlaylist(int count, String playlistName) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName';
}
@override
String collectionAddedTracksToPlaylistWithExisting(
int count,
String playlistName,
int alreadyCount,
) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Added $count $_temp0 to $playlistName ($alreadyCount already in playlist)';
}
@override
String itemCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'items',
one: 'item',
);
return '$count $_temp0';
}
@override
String trackReEnrichSuccessWithFailures(
int successCount,
int total,
int failedCount,
) {
return 'Metadata re-enriched successfully ($successCount/$total) - Failed: $failedCount';
}
@override
String selectionDeleteTracksCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Delete $count $_temp0';
}
@override
String queueDownloadSpeedStatus(String speed) {
return 'Downloading - $speed MB/s';
}
@override
String get queueDownloadStarting => 'Starting...';
@override
String get a11ySelectTrack => 'Select track';
@override
String get a11yDeselectTrack => 'Deselect track';
@override
String a11yPlayTrackByArtist(String trackName, String artistName) {
return 'Play $trackName by $artistName';
}
@override
String storeExtensionsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'extensions',
one: 'extension',
);
return '$count $_temp0';
}
@override
String storeRequiresVersion(String version) {
return 'Requires v$version+';
}
@override
String get actionGo => 'Go';
@override
String get logIssueSummary => 'Issue Summary';
@override
String logTotalErrors(int count) {
return 'Total errors: $count';
}
@override
String logAffectedDomains(String domains) {
return 'Affected: $domains';
}
@override
String get libraryScanCancelled => 'Scan cancelled';
@override
String get libraryScanCancelledSubtitle =>
'You can retry the scan when ready.';
@override
String libraryDownloadsHistoryExcluded(int count) {
return '$count from Downloads history (excluded from list)';
}
@override
String get downloadNativeWorker => 'Native download worker';
@override
String get downloadNativeWorkerSubtitle =>
'Beta Android service worker for extension downloads';
@override
String get badgeBeta => 'BETA';
@override
String get extensionServiceStatus => 'Service Status';
@override
String get extensionServiceHealth => 'Service health';
@override
String extensionHealthChecksConfigured(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'checks',
one: 'check',
);
return '$count $_temp0 configured';
}
@override
String get extensionOauthConnectHint =>
'Tap Connect to Spotify to fill this field.';
@override
String extensionLastChecked(String time) {
return 'Last checked $time';
}
@override
String get extensionRefreshStatus => 'Refresh status';
@override
String get extensionCustomUrlHandling => 'Custom URL Handling';
@override
String get extensionCustomUrlHandlingSubtitle =>
'This extension can handle links from these sites';
@override
String get extensionCustomUrlHandlingShareHint =>
'Share links from these sites to SpotiFLAC Mobile and this extension will handle them.';
@override
String extensionSettingsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'settings',
one: 'setting',
);
return '$count $_temp0';
}
@override
String get extensionHealthOnline => 'Online';
@override
String get extensionHealthDegraded => 'Degraded';
@override
String get extensionHealthOffline => 'Offline';
@override
String get extensionHealthNotConfigured => 'Not configured';
@override
String get extensionHealthUnknown => 'Unknown';
@override
String get extensionHealthRequired => 'required';
@override
String get extensionSettingNotSet => 'Not set';
@override
String get extensionActionFailed => 'Action failed';
@override
String get extensionEnterValue => 'Enter value';
@override
String get extensionHealthServiceOnline => 'Service online';
@override
String get extensionHealthServiceDegraded => 'Service degraded';
@override
String get extensionHealthServiceOffline => 'Service offline';
@override
String get extensionHealthServiceUnknown => 'Service status unknown';
@override
String get audioAnalysisStereo => 'Stereo';
@override
String get audioAnalysisMono => 'Mono';
@override
String trackOpenInService(String serviceName) {
return 'Open in $serviceName';
}
@override
String get trackLyricsEmbeddedSource => 'Embedded';
@override
String get unknownAlbum => 'Unknown Album';
@override
String get unknownArtist => 'Unknown Artist';
@override
String get permissionAudio => 'Audio';
@override
String get permissionStorage => 'Storage';
@override
String get permissionNotification => 'Notification';
@override
String get errorInvalidFolderSelected => 'Invalid folder selected';
@override
String get errorCouldNotKeepFolderAccess =>
'Could not keep access to the selected folder';
@override
String get storeAnyVersion => 'Any';
@override
String get storeCategoryMetadata => 'Metadata';
@override
String get storeCategoryDownload => 'Download';
@override
String get storeCategoryUtility => 'Utility';
@override
String get storeCategoryLyrics => 'Lyrics';
@override
String get storeCategoryIntegration => 'Integration';
@override
String get artistReleases => 'Releases';
}
/// The translations for Chinese, as used in China (`zh_CN`).
+442 -1
View File
@@ -1587,6 +1587,15 @@
"@trackCopyLyrics": {
"description": "Action - copy lyrics to clipboard"
},
"trackLyricsSource": "Source: {source}",
"@trackLyricsSource": {
"description": "Label showing the lyrics source/provider",
"placeholders": {
"source": {
"type": "String"
}
}
},
"trackLyricsNotAvailable": "Lyrics not available for this track",
"@trackLyricsNotAvailable": {
"description": "Message when lyrics not found"
@@ -2001,6 +2010,14 @@
"@downloadLossyMp3Subtitle": {
"description": "Subtitle for MP3 320kbps Tidal lossy option"
},
"downloadLossyAac": "AAC/M4A 320kbps",
"@downloadLossyAac": {
"description": "Tidal lossy format option - AAC in M4A container at 320kbps"
},
"downloadLossyAacSubtitle": "Best mobile compatibility, M4A container",
"@downloadLossyAacSubtitle": {
"description": "Subtitle for AAC/M4A 320kbps Tidal lossy option"
},
"downloadLossyOpus256": "Opus 256kbps",
"@downloadLossyOpus256": {
"description": "Tidal lossy format option - Opus 256kbps"
@@ -3170,7 +3187,7 @@
"@trackConvertFormat": {
"description": "Menu item - convert audio format"
},
"trackConvertFormatSubtitle": "Convert to MP3, Opus, ALAC, or FLAC",
"trackConvertFormatSubtitle": "Convert to AAC/M4A, MP3, Opus, ALAC, or FLAC",
"@trackConvertFormatSubtitle": {
"description": "Subtitle for convert format menu item"
},
@@ -3969,6 +3986,18 @@
"@downloadAppleQqMultiPersonDisabled": {
"description": "Subtitle when multi-person lyrics is off"
},
"downloadAppleElrcWordSync": "Apple Music eLRC Word Sync",
"@downloadAppleElrcWordSync": {
"description": "Setting for preserving Apple Music word-by-word eLRC timestamps"
},
"downloadAppleElrcWordSyncEnabled": "Raw word-by-word timestamps preserved",
"@downloadAppleElrcWordSyncEnabled": {
"description": "Subtitle when Apple Music eLRC word sync is enabled"
},
"downloadAppleElrcWordSyncDisabled": "Safer line-by-line Apple Music lyrics",
"@downloadAppleElrcWordSyncDisabled": {
"description": "Subtitle when Apple Music eLRC word sync is disabled"
},
"downloadMusixmatchLanguage": "Musixmatch Language",
"@downloadMusixmatchLanguage": {
"description": "Setting for Musixmatch lyrics translation language"
@@ -5009,5 +5038,417 @@
"downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback",
"@downloadFallbackExtensionsSubtitle": {
"description": "Subtitle for fallback extensions item"
},
"editMetadataFieldDateHint": "YYYY-MM-DD or YYYY",
"@editMetadataFieldDateHint": {
"description": "Hint text for the edit metadata date field"
},
"editMetadataFieldTrackTotal": "Track Total",
"@editMetadataFieldTrackTotal": {
"description": "Label for total tracks field in the edit metadata sheet"
},
"editMetadataFieldDiscTotal": "Disc Total",
"@editMetadataFieldDiscTotal": {
"description": "Label for total discs field in the edit metadata sheet"
},
"editMetadataFieldComposer": "Composer",
"@editMetadataFieldComposer": {
"description": "Label for composer field in the edit metadata sheet"
},
"editMetadataFieldComment": "Comment",
"@editMetadataFieldComment": {
"description": "Label for comment field in the edit metadata sheet"
},
"editMetadataAdvanced": "Advanced",
"@editMetadataAdvanced": {
"description": "Expandable section label for advanced metadata fields"
},
"libraryFilterMetadataMissingTrackNumber": "Missing track number",
"@libraryFilterMetadataMissingTrackNumber": {
"description": "Filter option - items missing track number"
},
"libraryFilterMetadataMissingDiscNumber": "Missing disc number",
"@libraryFilterMetadataMissingDiscNumber": {
"description": "Filter option - items missing disc number"
},
"libraryFilterMetadataMissingArtist": "Missing artist",
"@libraryFilterMetadataMissingArtist": {
"description": "Filter option - items missing artist"
},
"libraryFilterMetadataIncorrectIsrcFormat": "Incorrect ISRC format",
"@libraryFilterMetadataIncorrectIsrcFormat": {
"description": "Filter option - items with an invalid ISRC format"
},
"libraryFilterMetadataMissingLabel": "Missing label",
"@libraryFilterMetadataMissingLabel": {
"description": "Filter option - items missing record label"
},
"collectionDeletePlaylistsMessage": "Delete {count} {count, plural, =1{playlist} other{playlists}}?",
"@collectionDeletePlaylistsMessage": {
"description": "Confirmation message for deleting selected playlists",
"placeholders": {
"count": {
"type": "int"
}
}
},
"collectionPlaylistsDeleted": "{count} {count, plural, =1{playlist} other{playlists}} deleted",
"@collectionPlaylistsDeleted": {
"description": "Snackbar after deleting selected playlists",
"placeholders": {
"count": {
"type": "int"
}
}
},
"collectionAddedTracksToPlaylist": "Added {count} {count, plural, =1{track} other{tracks}} to {playlistName}",
"@collectionAddedTracksToPlaylist": {
"description": "Snackbar after adding multiple tracks to a playlist",
"placeholders": {
"count": {
"type": "int"
},
"playlistName": {
"type": "String"
}
}
},
"collectionAddedTracksToPlaylistWithExisting": "Added {count} {count, plural, =1{track} other{tracks}} to {playlistName} ({alreadyCount} already in playlist)",
"@collectionAddedTracksToPlaylistWithExisting": {
"description": "Snackbar after adding multiple tracks to a playlist when some were already present",
"placeholders": {
"count": {
"type": "int"
},
"playlistName": {
"type": "String"
},
"alreadyCount": {
"type": "int"
}
}
},
"itemCount": "{count} {count, plural, =1{item} other{items}}",
"@itemCount": {
"description": "Generic item count label",
"placeholders": {
"count": {
"type": "int"
}
}
},
"trackReEnrichSuccessWithFailures": "Metadata re-enriched successfully ({successCount}/{total}) - Failed: {failedCount}",
"@trackReEnrichSuccessWithFailures": {
"description": "Snackbar summary after batch metadata re-enrichment finishes with failures",
"placeholders": {
"successCount": {
"type": "int"
},
"total": {
"type": "int"
},
"failedCount": {
"type": "int"
}
}
},
"selectionDeleteTracksCount": "Delete {count} {count, plural, =1{track} other{tracks}}",
"@selectionDeleteTracksCount": {
"description": "Button label for deleting selected tracks",
"placeholders": {
"count": {
"type": "int"
}
}
},
"queueDownloadSpeedStatus": "Downloading - {speed} MB/s",
"@queueDownloadSpeedStatus": {
"description": "Queue status while downloading with speed",
"placeholders": {
"speed": {
"type": "String"
}
}
},
"queueDownloadStarting": "Starting...",
"@queueDownloadStarting": {
"description": "Queue status before download progress is available"
},
"a11ySelectTrack": "Select track",
"@a11ySelectTrack": {
"description": "Accessibility label for selecting a track"
},
"a11yDeselectTrack": "Deselect track",
"@a11yDeselectTrack": {
"description": "Accessibility label for deselecting a track"
},
"a11yPlayTrackByArtist": "Play {trackName} by {artistName}",
"@a11yPlayTrackByArtist": {
"description": "Accessibility label for playing a local library track",
"placeholders": {
"trackName": {
"type": "String"
},
"artistName": {
"type": "String"
}
}
},
"storeExtensionsCount": "{count} {count, plural, =1{extension} other{extensions}}",
"@storeExtensionsCount": {
"description": "Store extension result count",
"placeholders": {
"count": {
"type": "int"
}
}
},
"storeRequiresVersion": "Requires v{version}+",
"@storeRequiresVersion": {
"description": "Store compatibility badge for minimum app version",
"placeholders": {
"version": {
"type": "String"
}
}
},
"actionGo": "Go",
"@actionGo": {
"description": "Generic action button label"
},
"logIssueSummary": "Issue Summary",
"@logIssueSummary": {
"description": "Header for log issue analysis summary"
},
"logTotalErrors": "Total errors: {count}",
"@logTotalErrors": {
"description": "Total error count in log issue analysis",
"placeholders": {
"count": {
"type": "int"
}
}
},
"logAffectedDomains": "Affected: {domains}",
"@logAffectedDomains": {
"description": "Affected domains in log issue analysis",
"placeholders": {
"domains": {
"type": "String"
}
}
},
"libraryScanCancelled": "Scan cancelled",
"@libraryScanCancelled": {
"description": "Library scan status when a scan was cancelled"
},
"libraryScanCancelledSubtitle": "You can retry the scan when ready.",
"@libraryScanCancelledSubtitle": {
"description": "Library scan status subtitle after cancellation"
},
"libraryDownloadsHistoryExcluded": "{count} from Downloads history (excluded from list)",
"@libraryDownloadsHistoryExcluded": {
"description": "Library count note for downloaded history items excluded from the local list",
"placeholders": {
"count": {
"type": "int"
}
}
},
"downloadNativeWorker": "Native download worker",
"@downloadNativeWorker": {
"description": "Setting title for Android native download worker"
},
"downloadNativeWorkerSubtitle": "Beta Android service worker for extension downloads",
"@downloadNativeWorkerSubtitle": {
"description": "Setting subtitle for Android native download worker"
},
"badgeBeta": "BETA",
"@badgeBeta": {
"description": "Badge label for beta features"
},
"extensionServiceStatus": "Service Status",
"@extensionServiceStatus": {
"description": "Extension detail section header for service status"
},
"extensionServiceHealth": "Service health",
"@extensionServiceHealth": {
"description": "Extension capability label for service health checks"
},
"extensionHealthChecksConfigured": "{count} {count, plural, =1{check} other{checks}} configured",
"@extensionHealthChecksConfigured": {
"description": "Extension service health check count",
"placeholders": {
"count": {
"type": "int"
}
}
},
"extensionOauthConnectHint": "Tap Connect to Spotify to fill this field.",
"@extensionOauthConnectHint": {
"description": "Hint for an OAuth login link field before connecting Spotify"
},
"extensionLastChecked": "Last checked {time}",
"@extensionLastChecked": {
"description": "Timestamp for the latest extension service health check",
"placeholders": {
"time": {
"type": "String"
}
}
},
"extensionRefreshStatus": "Refresh status",
"@extensionRefreshStatus": {
"description": "Tooltip for refreshing extension service health status"
},
"extensionCustomUrlHandling": "Custom URL Handling",
"@extensionCustomUrlHandling": {
"description": "Extension detail section title for custom URL handling"
},
"extensionCustomUrlHandlingSubtitle": "This extension can handle links from these sites",
"@extensionCustomUrlHandlingSubtitle": {
"description": "Extension detail subtitle for custom URL handling"
},
"extensionCustomUrlHandlingShareHint": "Share links from these sites to SpotiFLAC Mobile and this extension will handle them.",
"@extensionCustomUrlHandlingShareHint": {
"description": "Extension detail hint explaining share-to-app URL handling"
},
"extensionSettingsCount": "{count} {count, plural, =1{setting} other{settings}}",
"@extensionSettingsCount": {
"description": "Count of settings exposed by an extension quality option",
"placeholders": {
"count": {
"type": "int"
}
}
},
"extensionHealthOnline": "Online",
"@extensionHealthOnline": {
"description": "Extension service health status - online"
},
"extensionHealthDegraded": "Degraded",
"@extensionHealthDegraded": {
"description": "Extension service health status - degraded"
},
"extensionHealthOffline": "Offline",
"@extensionHealthOffline": {
"description": "Extension service health status - offline"
},
"extensionHealthNotConfigured": "Not configured",
"@extensionHealthNotConfigured": {
"description": "Extension service health status - not configured"
},
"extensionHealthUnknown": "Unknown",
"@extensionHealthUnknown": {
"description": "Extension service health status - unknown"
},
"extensionHealthRequired": "required",
"@extensionHealthRequired": {
"description": "Label for a required extension service health check"
},
"extensionSettingNotSet": "Not set",
"@extensionSettingNotSet": {
"description": "Value shown when an extension setting has no value"
},
"extensionActionFailed": "Action failed",
"@extensionActionFailed": {
"description": "Fallback error when an extension action fails without details"
},
"extensionEnterValue": "Enter value",
"@extensionEnterValue": {
"description": "Hint for editing an extension setting value"
},
"extensionHealthServiceOnline": "Service online",
"@extensionHealthServiceOnline": {
"description": "Tooltip for online extension service"
},
"extensionHealthServiceDegraded": "Service degraded",
"@extensionHealthServiceDegraded": {
"description": "Tooltip for degraded extension service"
},
"extensionHealthServiceOffline": "Service offline",
"@extensionHealthServiceOffline": {
"description": "Tooltip for offline extension service"
},
"extensionHealthServiceUnknown": "Service status unknown",
"@extensionHealthServiceUnknown": {
"description": "Tooltip for unknown extension service health"
},
"audioAnalysisStereo": "Stereo",
"@audioAnalysisStereo": {
"description": "Audio channel layout label - stereo"
},
"audioAnalysisMono": "Mono",
"@audioAnalysisMono": {
"description": "Audio channel layout label - mono"
},
"trackOpenInService": "Open in {serviceName}",
"@trackOpenInService": {
"description": "Button label to open a track in a named music service",
"placeholders": {
"serviceName": {
"type": "String"
}
}
},
"trackLyricsEmbeddedSource": "Embedded",
"@trackLyricsEmbeddedSource": {
"description": "Lyrics source label for embedded lyrics"
},
"unknownAlbum": "Unknown Album",
"@unknownAlbum": {
"description": "Fallback album name when metadata is missing"
},
"unknownArtist": "Unknown Artist",
"@unknownArtist": {
"description": "Fallback artist name when metadata is missing"
},
"permissionAudio": "Audio",
"@permissionAudio": {
"description": "Audio permission type label"
},
"permissionStorage": "Storage",
"@permissionStorage": {
"description": "Storage permission type label"
},
"permissionNotification": "Notification",
"@permissionNotification": {
"description": "Notification permission type label"
},
"errorInvalidFolderSelected": "Invalid folder selected",
"@errorInvalidFolderSelected": {
"description": "Error when the selected folder is invalid"
},
"errorCouldNotKeepFolderAccess": "Could not keep access to the selected folder",
"@errorCouldNotKeepFolderAccess": {
"description": "Error when persistent folder access cannot be saved"
},
"storeAnyVersion": "Any",
"@storeAnyVersion": {
"description": "Store detail value when any app version is accepted"
},
"storeCategoryMetadata": "Metadata",
"@storeCategoryMetadata": {
"description": "Store extension category - metadata"
},
"storeCategoryDownload": "Download",
"@storeCategoryDownload": {
"description": "Store extension category - download"
},
"storeCategoryUtility": "Utility",
"@storeCategoryUtility": {
"description": "Store extension category - utility"
},
"storeCategoryLyrics": "Lyrics",
"@storeCategoryLyrics": {
"description": "Store extension category - lyrics"
},
"storeCategoryIntegration": "Integration",
"@storeCategoryIntegration": {
"description": "Store extension category - integration"
},
"artistReleases": "Releases",
"@artistReleases": {
"description": "Section header for all artist releases"
}
}
+7 -1
View File
@@ -47,7 +47,7 @@ class AppSettings {
final String locale;
final String lyricsMode;
final String
tidalHighFormat; // Format for Tidal HIGH quality: 'mp3_320', 'opus_256', or 'opus_128'
tidalHighFormat; // Format for Tidal HIGH quality: 'mp3_320', 'aac_320', 'opus_256', or 'opus_128'
final bool
useAllFilesAccess; // Android 13+ only: enable MANAGE_EXTERNAL_STORAGE
final bool
@@ -81,6 +81,8 @@ class AppSettings {
lyricsIncludeRomanizationNetease; // Append romanized lyrics (Netease)
final bool
lyricsMultiPersonWordByWord; // Enable v1/v2 + [bg:] tags for Apple/QQ syllable lyrics
final bool
lyricsAppleElrcWordSync; // Preserve Apple Music inline word timestamps for eLRC-capable players
final String
musixmatchLanguage; // Optional ISO language code for Musixmatch localized lyrics
@@ -146,6 +148,7 @@ class AppSettings {
this.lyricsIncludeTranslationNetease = false,
this.lyricsIncludeRomanizationNetease = false,
this.lyricsMultiPersonWordByWord = false,
this.lyricsAppleElrcWordSync = false,
this.musixmatchLanguage = '',
this.lastSeenVersion = '',
this.deduplicateDownloads = true,
@@ -210,6 +213,7 @@ class AppSettings {
bool? lyricsIncludeTranslationNetease,
bool? lyricsIncludeRomanizationNetease,
bool? lyricsMultiPersonWordByWord,
bool? lyricsAppleElrcWordSync,
String? musixmatchLanguage,
String? lastSeenVersion,
bool? deduplicateDownloads,
@@ -291,6 +295,8 @@ class AppSettings {
this.lyricsIncludeRomanizationNetease,
lyricsMultiPersonWordByWord:
lyricsMultiPersonWordByWord ?? this.lyricsMultiPersonWordByWord,
lyricsAppleElrcWordSync:
lyricsAppleElrcWordSync ?? this.lyricsAppleElrcWordSync,
musixmatchLanguage: musixmatchLanguage ?? this.musixmatchLanguage,
lastSeenVersion: lastSeenVersion ?? this.lastSeenVersion,
deduplicateDownloads: deduplicateDownloads ?? this.deduplicateDownloads,
+2
View File
@@ -78,6 +78,7 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
json['lyricsIncludeRomanizationNetease'] as bool? ?? false,
lyricsMultiPersonWordByWord:
json['lyricsMultiPersonWordByWord'] as bool? ?? false,
lyricsAppleElrcWordSync: json['lyricsAppleElrcWordSync'] as bool? ?? false,
musixmatchLanguage: json['musixmatchLanguage'] as String? ?? '',
lastSeenVersion: json['lastSeenVersion'] as String? ?? '',
deduplicateDownloads: json['deduplicateDownloads'] as bool? ?? true,
@@ -142,6 +143,7 @@ Map<String, dynamic> _$AppSettingsToJson(
'lyricsIncludeTranslationNetease': instance.lyricsIncludeTranslationNetease,
'lyricsIncludeRomanizationNetease': instance.lyricsIncludeRomanizationNetease,
'lyricsMultiPersonWordByWord': instance.lyricsMultiPersonWordByWord,
'lyricsAppleElrcWordSync': instance.lyricsAppleElrcWordSync,
'musixmatchLanguage': instance.musixmatchLanguage,
'lastSeenVersion': instance.lastSeenVersion,
'deduplicateDownloads': instance.deduplicateDownloads,
+56 -58
View File
@@ -109,6 +109,31 @@ bool _isLossyAudioFormat(String? value) {
}.contains(_normalizeAudioFormatValue(value));
}
String _lossyFormatForSetting(String value) {
final normalized = value.trim().toLowerCase();
if (normalized.startsWith('opus')) return 'opus';
if (normalized.startsWith('aac') || normalized.startsWith('m4a')) {
return 'aac';
}
return 'mp3';
}
String _lossyExtensionForFormat(String format) {
return switch (format) {
'opus' => '.opus',
'aac' => '.m4a',
_ => '.mp3',
};
}
String _metadataFormatForLossyFormat(String format) {
return format == 'aac' ? 'm4a' : format;
}
String _displayFormatForLossyFormat(String format) {
return format == 'aac' ? 'AAC' : format.toUpperCase();
}
String? _resolveDisplayQuality({
required String? filePath,
String? fileName,
@@ -6123,8 +6148,9 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
}
final tidalHighFormat = settings.tidalHighFormat;
final format = tidalHighFormat.startsWith('opus') ? 'opus' : 'mp3';
final newExt = format == 'opus' ? '.opus' : '.mp3';
final format = _lossyFormatForSetting(tidalHighFormat);
final newExt = _lossyExtensionForFormat(format);
final displayFormat = _displayFormatForLossyFormat(format);
final bitrateDisplay = tidalHighFormat.contains('_')
? '${tidalHighFormat.split('_').last}kbps'
: '320kbps';
@@ -6134,7 +6160,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
await _embedMetadataToFile(
convertedPath,
track,
format: format,
format: _metadataFormatForLossyFormat(format),
genre: result['genre'] as String?,
label: result['label'] as String?,
copyright: result['copyright'] as String?,
@@ -6182,8 +6208,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
await _deleteSafFile(filePath);
}
result['file_name'] = newFileName;
result['_native_actual_quality'] =
'${format.toUpperCase()} $bitrateDisplay';
result['_native_actual_quality'] = '$displayFormat $bitrateDisplay';
return newUri;
} finally {
try {
@@ -6207,8 +6232,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
return null;
}
await embedConvertedMetadata(convertedPath);
result['_native_actual_quality'] =
'${format.toUpperCase()} $bitrateDisplay';
result['_native_actual_quality'] = '$displayFormat $bitrateDisplay';
return convertedPath;
}
@@ -7535,9 +7559,8 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
progress: 0.95,
);
final format = tidalHighFormat.startsWith('opus')
? 'opus'
: 'mp3';
final format = _lossyFormatForSetting(tidalHighFormat);
final displayFormat = _displayFormatForLossyFormat(format);
convertedPath = await FFmpegService.convertM4aToLossy(
tempPath,
format: format,
@@ -7560,29 +7583,17 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
final backendLabel = result['label'] as String?;
final backendCopyright = result['copyright'] as String?;
if (format == 'mp3') {
await _embedMetadataToFile(
convertedPath,
trackToDownload,
format: 'mp3',
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
} else {
await _embedMetadataToFile(
convertedPath,
trackToDownload,
format: 'opus',
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
}
await _embedMetadataToFile(
convertedPath,
trackToDownload,
format: _metadataFormatForLossyFormat(format),
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
final newExt = format == 'opus' ? '.opus' : '.mp3';
final newExt = _lossyExtensionForFormat(format);
final newFileName = '${safBaseName ?? 'track'}$newExt';
final newUri = await _writeTempToSaf(
treeUri: settings.downloadTreeUri,
@@ -7601,7 +7612,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
final bitrateDisplay = tidalHighFormat.contains('_')
? '${tidalHighFormat.split('_').last}kbps'
: '320kbps';
actualQuality = '${format.toUpperCase()} $bitrateDisplay';
actualQuality = '$displayFormat $bitrateDisplay';
} else {
_log.w(
'Failed to write converted $format to SAF, keeping M4A',
@@ -7822,9 +7833,8 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
progress: 0.95,
);
final format = tidalHighFormat.startsWith('opus')
? 'opus'
: 'mp3';
final format = _lossyFormatForSetting(tidalHighFormat);
final displayFormat = _displayFormatForLossyFormat(format);
final convertedPath = await FFmpegService.convertM4aToLossy(
currentFilePath,
format: format,
@@ -7837,7 +7847,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
final bitrateDisplay = tidalHighFormat.contains('_')
? '${tidalHighFormat.split('_').last}kbps'
: '320kbps';
actualQuality = '${format.toUpperCase()} $bitrateDisplay';
actualQuality = '$displayFormat $bitrateDisplay';
_log.i(
'Successfully converted M4A to $format: $convertedPath',
);
@@ -7853,27 +7863,15 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
final backendLabel = result['label'] as String?;
final backendCopyright = result['copyright'] as String?;
if (format == 'mp3') {
await _embedMetadataToFile(
convertedPath,
trackToDownload,
format: 'mp3',
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
} else {
await _embedMetadataToFile(
convertedPath,
trackToDownload,
format: 'opus',
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
}
await _embedMetadataToFile(
convertedPath,
trackToDownload,
format: _metadataFormatForLossyFormat(format),
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
_log.d('Metadata embedded successfully');
} else {
_log.w('M4A to $format conversion failed, keeping M4A file');
+7
View File
@@ -108,6 +108,7 @@ class SettingsNotifier extends Notifier<AppSettings> {
'include_translation_netease': state.lyricsIncludeTranslationNetease,
'include_romanization_netease': state.lyricsIncludeRomanizationNetease,
'multi_person_word_by_word': state.lyricsMultiPersonWordByWord,
'apple_elrc_word_sync': state.lyricsAppleElrcWordSync,
'musixmatch_language': state.musixmatchLanguage,
}).catchError((Object e) {
_log.w('Failed to sync lyrics fetch options to backend: $e');
@@ -367,6 +368,12 @@ class SettingsNotifier extends Notifier<AppSettings> {
_syncLyricsSettingsToBackend();
}
void setLyricsAppleElrcWordSync(bool enabled) {
state = state.copyWith(lyricsAppleElrcWordSync: enabled);
_saveSettings();
_syncLyricsSettingsToBackend();
}
void setMusixmatchLanguage(String languageCode) {
state = state.copyWith(
musixmatchLanguage: languageCode.trim().toLowerCase(),
+1 -1
View File
@@ -507,7 +507,7 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen> {
if (releases.isNotEmpty)
SliverToBoxAdapter(
child: _buildAlbumSection(
'Releases',
context.l10n.artistReleases,
releases,
colorScheme,
),
+9 -5
View File
@@ -508,8 +508,10 @@ class _CollectionItemWidget extends StatelessWidget {
item.artistName.isNotEmpty
? item.artistName
: (isPlaylist
? 'Playlist'
: (isArtist ? 'Artist' : 'Album')),
? context.l10n.recentTypePlaylist
: (isArtist
? context.l10n.recentTypeArtist
: context.l10n.recentTypeAlbum)),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
@@ -607,7 +609,7 @@ class _SearchArtistItemWidget extends StatelessWidget {
),
const SizedBox(height: 2),
Text(
'Artist',
context.l10n.recentTypeArtist,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
@@ -707,7 +709,7 @@ class _SearchAlbumItemWidget extends StatelessWidget {
ClickableArtistName(
artistName: album.artists.isNotEmpty
? album.artists
: 'Album',
: context.l10n.recentTypeAlbum,
coverUrl: album.imageUrl,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
@@ -806,7 +808,9 @@ class _SearchPlaylistItemWidget extends StatelessWidget {
),
const SizedBox(height: 2),
Text(
playlist.owner.isNotEmpty ? playlist.owner : 'Playlist',
playlist.owner.isNotEmpty
? playlist.owner
: context.l10n.recentTypePlaylist,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
+10 -4
View File
@@ -440,8 +440,8 @@ class _LocalAlbumScreenState extends ConsumerState<LocalAlbumScreen> {
color: Colors.white,
),
const SizedBox(width: 4),
const Text(
'Local',
Text(
context.l10n.librarySourceLocal,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
@@ -470,7 +470,9 @@ class _LocalAlbumScreenState extends ConsumerState<LocalAlbumScreen> {
),
const SizedBox(width: 4),
Text(
'${_sortedTracksCache.length} tracks',
context.l10n.queueTrackCount(
_sortedTracksCache.length,
),
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
@@ -1155,7 +1157,11 @@ class _LocalAlbumScreenState extends ConsumerState<LocalAlbumScreen> {
final failedCount = total - successCount;
final summary = failedCount <= 0
? '${context.l10n.trackReEnrichSuccess} ($successCount/$total)'
: '${context.l10n.trackReEnrichSuccess} ($successCount/$total) • Failed: $failedCount';
: context.l10n.trackReEnrichSuccessWithFailures(
successCount,
total,
failedCount,
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(summary)));
+61 -23
View File
@@ -1124,9 +1124,7 @@ class _QueueTabState extends ConsumerState<QueueTab> {
context: context,
builder: (ctx) => AlertDialog(
title: Text(ctx.l10n.collectionDeletePlaylist),
content: Text(
'Delete $count ${count == 1 ? 'playlist' : 'playlists'}?',
),
content: Text(ctx.l10n.collectionDeletePlaylistsMessage(count)),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
@@ -1153,11 +1151,7 @@ class _QueueTabState extends ConsumerState<QueueTab> {
if (!context.mounted) return;
_exitPlaylistSelectionMode();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'$count ${count == 1 ? 'playlist' : 'playlists'} deleted',
),
),
SnackBar(content: Text(context.l10n.collectionPlaylistsDeleted(count))),
);
}
@@ -1933,7 +1927,11 @@ class _QueueTabState extends ConsumerState<QueueTab> {
),
),
FilterChip(
label: const Text('Missing track number'),
label: Text(
context
.l10n
.libraryFilterMetadataMissingTrackNumber,
),
selected:
tempMetadata == 'missing-track-number',
onSelected: (_) => setSheetState(
@@ -1941,21 +1939,33 @@ class _QueueTabState extends ConsumerState<QueueTab> {
),
),
FilterChip(
label: const Text('Missing disc number'),
label: Text(
context
.l10n
.libraryFilterMetadataMissingDiscNumber,
),
selected: tempMetadata == 'missing-disc-number',
onSelected: (_) => setSheetState(
() => tempMetadata = 'missing-disc-number',
),
),
FilterChip(
label: const Text('Missing artist'),
label: Text(
context
.l10n
.libraryFilterMetadataMissingArtist,
),
selected: tempMetadata == 'missing-artist',
onSelected: (_) => setSheetState(
() => tempMetadata = 'missing-artist',
),
),
FilterChip(
label: const Text('Incorrect ISRC format'),
label: Text(
context
.l10n
.libraryFilterMetadataIncorrectIsrcFormat,
),
selected:
tempMetadata == 'incorrect-isrc-format',
onSelected: (_) => setSheetState(
@@ -1963,7 +1973,11 @@ class _QueueTabState extends ConsumerState<QueueTab> {
),
),
FilterChip(
label: const Text('Missing label'),
label: Text(
context
.l10n
.libraryFilterMetadataMissingLabel,
),
selected: tempMetadata == 'missing-label',
onSelected: (_) => setSheetState(
() => tempMetadata = 'missing-label',
@@ -2549,8 +2563,16 @@ class _QueueTabState extends ConsumerState<QueueTab> {
if (!context.mounted) return;
final message = addedCount > 0
? 'Added $addedCount ${addedCount == 1 ? 'track' : 'tracks'} to $playlistName'
'${alreadyCount > 0 ? ' ($alreadyCount already in playlist)' : ''}'
? alreadyCount > 0
? context.l10n.collectionAddedTracksToPlaylistWithExisting(
addedCount,
playlistName,
alreadyCount,
)
: context.l10n.collectionAddedTracksToPlaylist(
addedCount,
playlistName,
)
: context.l10n.collectionAlreadyInPlaylist(playlistName);
ScaffoldMessenger.of(
context,
@@ -3256,7 +3278,7 @@ class _QueueTabState extends ConsumerState<QueueTab> {
).textTheme.bodySmall?.copyWith(fontWeight: FontWeight.w500),
),
Text(
'$count ${count == 1 ? 'item' : 'items'}',
context.l10n.itemCount(count),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.labelSmall?.copyWith(
@@ -4757,7 +4779,11 @@ class _QueueTabState extends ConsumerState<QueueTab> {
final failedCount = total - successCount;
final summary = failedCount <= 0
? '${context.l10n.trackReEnrichSuccess} ($successCount/$total)'
: '${context.l10n.trackReEnrichSuccess} ($successCount/$total) • Failed: $failedCount';
: context.l10n.trackReEnrichSuccessWithFailures(
successCount,
total,
failedCount,
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(summary)));
@@ -5582,7 +5608,7 @@ class _QueueTabState extends ConsumerState<QueueTab> {
icon: const Icon(Icons.delete_outline),
label: Text(
selectedCount > 0
? 'Delete $selectedCount ${selectedCount == 1 ? 'track' : 'tracks'}'
? context.l10n.selectionDeleteTracksCount(selectedCount)
: context.l10n.selectionSelectToDelete,
),
style: FilledButton.styleFrom(
@@ -5725,8 +5751,16 @@ class _QueueTabState extends ConsumerState<QueueTab> {
? '${(item.progress * 100).toStringAsFixed(0)}% • ${item.speedMBps.toStringAsFixed(1)} MB/s'
: '${(item.progress * 100).toStringAsFixed(0)}%')
: (item.speedMBps > 0
? 'Downloading • ${item.speedMBps.toStringAsFixed(1)} MB/s'
: 'Starting...'))),
? context.l10n
.queueDownloadSpeedStatus(
item.speedMBps
.toStringAsFixed(
1,
),
)
: context
.l10n
.queueDownloadStarting))),
style: Theme.of(context).textTheme.labelSmall
?.copyWith(
color: colorScheme.primary,
@@ -6154,7 +6188,9 @@ class _QueueTabState extends ConsumerState<QueueTab> {
if (_isSelectionMode) ...[
Semantics(
checked: isSelected,
label: isSelected ? 'Deselect track' : 'Select track',
label: isSelected
? context.l10n.a11yDeselectTrack
: context.l10n.a11ySelectTrack,
child: AnimatedSelectionCheckbox(
visible: true,
selected: isSelected,
@@ -6417,8 +6453,10 @@ class _QueueTabState extends ConsumerState<QueueTab> {
return fileExists
? Semantics(
button: true,
label:
'Play ${item.trackName} by ${item.artistName}',
label: context.l10n.a11yPlayTrackByArtist(
item.trackName,
item.artistName,
),
child: GestureDetector(
onTap: () => _openFile(
item.filePath,
+7 -3
View File
@@ -281,7 +281,9 @@ class _RepoTabState extends ConsumerState<RepoTab> {
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
child: Text(
'${filteredExtensions.length} ${filteredExtensions.length == 1 ? 'extension' : 'extensions'}',
context.l10n.storeExtensionsCount(
filteredExtensions.length,
),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurfaceVariant,
),
@@ -810,7 +812,9 @@ class _ExtensionItem extends StatelessWidget {
),
const SizedBox(width: 4),
Text(
'Requires v${extension.minAppVersion}+',
context.l10n.storeRequiresVersion(
extension.minAppVersion ?? '',
),
style: Theme.of(context).textTheme.labelSmall
?.copyWith(
color: colorScheme.onErrorContainer,
@@ -862,7 +866,7 @@ class _ExtensionItem extends StatelessWidget {
Icon(Icons.check, size: 16, color: colorScheme.outline),
const SizedBox(width: 4),
Text(
'Installed',
context.l10n.storeInstalled,
style: TextStyle(color: colorScheme.outline),
),
],
@@ -199,10 +199,10 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
if (Platform.isAndroid)
SettingsSwitchItem(
icon: Icons.downloading_outlined,
title: 'Native download worker',
title: context.l10n.downloadNativeWorker,
titleTrailing: const _BetaBadge(),
subtitle: hasDownloadExtensions
? 'Beta Android service worker for extension downloads'
? context.l10n.downloadNativeWorkerSubtitle
: context.l10n.extensionsNoDownloadProvider,
value:
settings.nativeDownloadWorkerEnabled &&
@@ -382,6 +382,8 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
switch (format) {
case 'mp3_320':
return context.l10n.downloadLossyMp3;
case 'aac_320':
return context.l10n.downloadLossyAac;
case 'opus_256':
return context.l10n.downloadLossyOpus256;
case 'opus_128':
@@ -441,6 +443,20 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
Navigator.pop(context);
},
),
ListTile(
leading: const Icon(Icons.album_outlined),
title: Text(context.l10n.downloadLossyAac),
subtitle: Text(context.l10n.downloadLossyAacSubtitle),
trailing: current == 'aac_320'
? Icon(Icons.check, color: colorScheme.primary)
: null,
onTap: () {
ref
.read(settingsProvider.notifier)
.setTidalHighFormat('aac_320');
Navigator.pop(context);
},
),
ListTile(
leading: const Icon(Icons.graphic_eq),
title: Text(context.l10n.downloadLossyOpus256),
@@ -829,7 +845,7 @@ class _BetaBadge extends StatelessWidget {
borderRadius: BorderRadius.circular(6),
),
child: Text(
'BETA',
context.l10n.badgeBeta,
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: colorScheme.onTertiaryContainer,
fontWeight: FontWeight.w700,
+36 -24
View File
@@ -253,8 +253,10 @@ class _ExtensionDetailPageState extends ConsumerState<ExtensionDetailPage> {
),
if (extension.hasServiceHealth) ...[
const SliverToBoxAdapter(
child: SettingsSectionHeader(title: 'Service Status'),
SliverToBoxAdapter(
child: SettingsSectionHeader(
title: context.l10n.extensionServiceStatus,
),
),
SliverToBoxAdapter(
child: SettingsGroup(
@@ -339,10 +341,12 @@ class _ExtensionDetailPageState extends ConsumerState<ExtensionDetailPage> {
),
_CapabilityItem(
icon: Icons.monitor_heart_outlined,
title: 'Service health',
title: context.l10n.extensionServiceHealth,
enabled: extension.hasServiceHealth,
subtitle: extension.hasServiceHealth
? '${extension.serviceHealth.length} check${extension.serviceHealth.length == 1 ? '' : 's'} configured'
? context.l10n.extensionHealthChecksConfigured(
extension.serviceHealth.length,
)
: null,
showDivider: false,
),
@@ -570,7 +574,7 @@ class _OauthLoginLinkPreview extends StatelessWidget {
final text = value?.trim() ?? '';
if (text.isEmpty) {
return Text(
'Tap Connect to Spotify to fill this field.',
context.l10n.extensionOauthConnectHint,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
fontStyle: FontStyle.italic,
@@ -725,18 +729,18 @@ IconData _healthStatusIcon(String status) {
}
}
String _healthStatusLabel(String status) {
String _healthStatusLabel(BuildContext context, String status) {
switch (status) {
case 'online':
return 'Online';
return context.l10n.extensionHealthOnline;
case 'degraded':
return 'Degraded';
return context.l10n.extensionHealthDegraded;
case 'offline':
return 'Offline';
return context.l10n.extensionHealthOffline;
case 'unsupported':
return 'Not configured';
return context.l10n.extensionHealthNotConfigured;
default:
return 'Unknown';
return context.l10n.extensionHealthUnknown;
}
}
@@ -771,7 +775,7 @@ class _HealthSummaryItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_healthStatusLabel(statusValue),
_healthStatusLabel(context, statusValue),
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: color,
fontWeight: FontWeight.w600,
@@ -780,7 +784,11 @@ class _HealthSummaryItem extends StatelessWidget {
if (status?.checkedAt != null) ...[
const SizedBox(height: 2),
Text(
'Last checked ${TimeOfDay.fromDateTime(status!.checkedAt!.toLocal()).format(context)}',
context.l10n.extensionLastChecked(
TimeOfDay.fromDateTime(
status!.checkedAt!.toLocal(),
).format(context),
),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
@@ -790,7 +798,7 @@ class _HealthSummaryItem extends StatelessWidget {
),
),
IconButton(
tooltip: 'Refresh status',
tooltip: context.l10n.extensionRefreshStatus,
onPressed: isRefreshing ? null : onRefresh,
icon: isRefreshing
? SizedBox(
@@ -829,11 +837,11 @@ class _HealthCheckItem extends StatelessWidget {
final colorScheme = Theme.of(context).colorScheme;
final color = _healthStatusColor(colorScheme, check.status);
final detailParts = <String>[
_healthStatusLabel(check.status),
_healthStatusLabel(context, check.status),
if (check.httpStatus != null) 'HTTP ${check.httpStatus}',
if (check.serviceKey?.isNotEmpty == true) check.serviceKey!,
if (check.latencyMs > 0) '${check.latencyMs} ms',
if (check.required) 'required',
if (check.required) context.l10n.extensionHealthRequired,
];
final message = check.error?.trim().isNotEmpty == true
? check.error!
@@ -1090,7 +1098,8 @@ class _SettingItemState extends State<_SettingItem> {
)
else
Text(
widget.value?.toString() ?? 'Not set',
widget.value?.toString() ??
context.l10n.extensionSettingNotSet,
style: Theme.of(context).textTheme.bodySmall
?.copyWith(color: colorScheme.primary),
),
@@ -1144,7 +1153,7 @@ class _SettingItemState extends State<_SettingItem> {
final error =
payload['error'] as String? ??
result['error'] as String? ??
'Action failed';
context.l10n.extensionActionFailed;
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(error)));
@@ -1206,7 +1215,8 @@ class _SettingItemState extends State<_SettingItem> {
? TextInputType.number
: TextInputType.text,
decoration: InputDecoration(
hintText: widget.setting.description ?? 'Enter value',
hintText:
widget.setting.description ?? context.l10n.extensionEnterValue,
filled: true,
fillColor: colorScheme.surfaceContainerHighest.withValues(
alpha: 0.3,
@@ -1327,7 +1337,7 @@ class _PostProcessingHookItem extends StatelessWidget {
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Auto',
context.l10n.extensionsHomeFeedAuto,
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: colorScheme.onPrimaryContainer,
),
@@ -1384,14 +1394,14 @@ class _URLHandlerInfo extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Custom URL Handling',
context.l10n.extensionCustomUrlHandling,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 2),
Text(
'This extension can handle links from these sites',
context.l10n.extensionCustomUrlHandlingSubtitle,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
@@ -1445,7 +1455,7 @@ class _URLHandlerInfo extends StatelessWidget {
const SizedBox(width: 12),
Expanded(
child: Text(
'Share links from these sites to SpotiFLAC and this extension will handle them.',
context.l10n.extensionCustomUrlHandlingShareHint,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
@@ -1533,7 +1543,9 @@ class _QualityOptionItem extends StatelessWidget {
borderRadius: BorderRadius.circular(8),
),
child: Text(
'${quality.settings.length} setting${quality.settings.length > 1 ? 's' : ''}',
context.l10n.extensionSettingsCount(
quality.settings.length,
),
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
+6 -6
View File
@@ -458,7 +458,7 @@ class _ExtensionItem extends StatelessWidget {
context.l10n.extensionsErrorLoading
: serviceHealthStatus == null
? 'v${extension.version}'
: 'v${extension.version} · ${_extensionHealthLabel(serviceHealthStatus)}',
: 'v${extension.version} · ${_extensionHealthLabel(context, serviceHealthStatus)}',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: hasError
? colorScheme.error
@@ -503,16 +503,16 @@ Color _extensionHealthColor(ColorScheme colorScheme, String status) {
}
}
String _extensionHealthLabel(String status) {
String _extensionHealthLabel(BuildContext context, String status) {
switch (status) {
case 'online':
return 'Online';
return context.l10n.extensionHealthOnline;
case 'degraded':
return 'Degraded';
return context.l10n.extensionHealthDegraded;
case 'offline':
return 'Offline';
return context.l10n.extensionHealthOffline;
default:
return 'Unknown';
return context.l10n.extensionHealthUnknown;
}
}
+16 -14
View File
@@ -221,6 +221,7 @@ class _FilesSettingsPageState extends ConsumerState<FilesSettingsPage> {
icon: Icons.folder_outlined,
title: context.l10n.downloadAlbumFolderStructure,
subtitle: _getAlbumFolderStructureLabel(
context,
settings.albumFolderStructure,
),
onTap: () => _showAlbumFolderStructurePicker(
@@ -234,6 +235,7 @@ class _FilesSettingsPageState extends ConsumerState<FilesSettingsPage> {
icon: Icons.create_new_folder_outlined,
title: context.l10n.downloadFolderOrganization,
subtitle: _getFolderOrganizationLabel(
context,
settings.folderOrganization,
),
onTap: () => _showFolderOrganizationPicker(
@@ -375,35 +377,35 @@ class _FilesSettingsPageState extends ConsumerState<FilesSettingsPage> {
);
}
String _getAlbumFolderStructureLabel(String structure) {
String _getAlbumFolderStructureLabel(BuildContext context, String structure) {
switch (structure) {
case 'album_only':
return 'Albums/Album Name/';
return context.l10n.albumFolderAlbumOnlySubtitle;
case 'artist_year_album':
return 'Albums/Artist/[Year] Album/';
return context.l10n.albumFolderArtistYearAlbumSubtitle;
case 'year_album':
return 'Albums/[Year] Album/';
return context.l10n.albumFolderYearAlbumSubtitle;
case 'artist_album_singles':
return 'Artist/Album/ + Artist/Singles/';
return context.l10n.albumFolderArtistAlbumSinglesSubtitle;
case 'artist_album_flat':
return 'Artist/Album/ + Artist/song.flac';
return context.l10n.albumFolderArtistAlbumFlatSubtitle;
default:
return 'Albums/Artist/Album Name/';
return context.l10n.albumFolderArtistAlbumSubtitle;
}
}
String _getFolderOrganizationLabel(String value) {
String _getFolderOrganizationLabel(BuildContext context, String value) {
switch (value) {
case 'playlist':
return 'By Playlist';
return context.l10n.folderOrganizationByPlaylist;
case 'artist':
return 'By Artist';
return context.l10n.folderOrganizationByArtist;
case 'album':
return 'By Album';
return context.l10n.folderOrganizationByAlbum;
case 'artist_album':
return 'Artist/Album';
return context.l10n.folderOrganizationByArtistAlbum;
default:
return 'None';
return context.l10n.folderOrganizationNone;
}
}
@@ -641,7 +643,7 @@ class _FilesSettingsPageState extends ConsumerState<FilesSettingsPage> {
SnackBar(
content: Text(
ctx.l10n.snackbarFolderPickerFailed(
'Could not keep access to the selected folder',
ctx.l10n.errorCouldNotKeepFolderAccess,
),
),
backgroundColor: Theme.of(ctx).colorScheme.error,
@@ -480,7 +480,7 @@ class _LibrarySettingsPageState extends ConsumerState<LibrarySettingsPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Scan cancelled',
context.l10n.libraryScanCancelled,
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(
fontWeight: FontWeight.w600,
@@ -489,7 +489,7 @@ class _LibrarySettingsPageState extends ConsumerState<LibrarySettingsPage> {
),
const SizedBox(height: 2),
Text(
'You can retry the scan when ready.',
context.l10n.libraryScanCancelledSubtitle,
style: Theme.of(context).textTheme.bodySmall
?.copyWith(
color: colorScheme.onTertiaryContainer
@@ -760,7 +760,7 @@ class _LibraryHeroCard extends StatelessWidget {
),
const SizedBox(width: 8),
Text(
'Scanning...',
context.l10n.libraryScanning,
style: TextStyle(
color: colorScheme.onPrimary,
fontSize: 12,
@@ -801,8 +801,9 @@ class _LibraryHeroCard extends StatelessWidget {
if (!isScanning && excludedDownloadedCount > 0) ...[
const SizedBox(height: 4),
Text(
'$excludedDownloadedCount from Downloads history '
'(excluded from list)',
context.l10n.libraryDownloadsHistoryExcluded(
excludedDownloadedCount,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
+6 -6
View File
@@ -495,9 +495,9 @@ class _LogEntryTile extends StatelessWidget {
color: Colors.teal.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'Go',
style: TextStyle(
child: Text(
context.l10n.actionGo,
style: const TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: Colors.teal,
@@ -597,7 +597,7 @@ class _LogSummaryCard extends StatelessWidget {
),
const SizedBox(width: 8),
Text(
'Issue Summary',
context.l10n.logIssueSummary,
style: Theme.of(context).textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
@@ -653,7 +653,7 @@ class _LogSummaryCard extends StatelessWidget {
const SizedBox(height: 12),
Text(
'Total errors: ${analysis.errorCount}',
context.l10n.logTotalErrors(analysis.errorCount),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
@@ -807,7 +807,7 @@ class _IssueBadge extends StatelessWidget {
if (domains != null && domains!.isNotEmpty) ...[
const SizedBox(height: 4),
Text(
'Affected: ${domains!.join(", ")}',
context.l10n.logAffectedDomains(domains!.join(', ')),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
fontFamily: 'monospace',
+36 -17
View File
@@ -77,8 +77,7 @@ class LyricsSettingsPage extends ConsumerWidget {
onChanged: (value) => ref
.read(settingsProvider.notifier)
.setEmbedLyrics(value),
showDivider:
settings.embedMetadata && settings.embedLyrics,
showDivider: settings.embedMetadata && settings.embedLyrics,
),
if (settings.embedMetadata && settings.embedLyrics) ...[
SettingsItem(
@@ -88,8 +87,11 @@ class LyricsSettingsPage extends ConsumerWidget {
context,
settings.lyricsMode,
),
onTap: () =>
_showLyricsModePicker(context, ref, settings.lyricsMode),
onTap: () => _showLyricsModePicker(
context,
ref,
settings.lyricsMode,
),
),
SettingsItem(
icon: Icons.source_outlined,
@@ -124,8 +126,12 @@ class LyricsSettingsPage extends ConsumerWidget {
icon: Icons.translate_outlined,
title: context.l10n.downloadNeteaseIncludeTranslation,
subtitle: settings.lyricsIncludeTranslationNetease
? context.l10n.downloadNeteaseIncludeTranslationEnabled
: context.l10n.downloadNeteaseIncludeTranslationDisabled,
? context
.l10n
.downloadNeteaseIncludeTranslationEnabled
: context
.l10n
.downloadNeteaseIncludeTranslationDisabled,
value: settings.lyricsIncludeTranslationNetease,
onChanged: (value) => ref
.read(settingsProvider.notifier)
@@ -157,6 +163,17 @@ class LyricsSettingsPage extends ConsumerWidget {
.read(settingsProvider.notifier)
.setLyricsMultiPersonWordByWord(value),
),
SettingsSwitchItem(
icon: Icons.graphic_eq_outlined,
title: context.l10n.downloadAppleElrcWordSync,
subtitle: settings.lyricsAppleElrcWordSync
? context.l10n.downloadAppleElrcWordSyncEnabled
: context.l10n.downloadAppleElrcWordSyncDisabled,
value: settings.lyricsAppleElrcWordSync,
onChanged: (value) => ref
.read(settingsProvider.notifier)
.setLyricsAppleElrcWordSync(value),
),
SettingsItem(
icon: Icons.language_outlined,
title: context.l10n.downloadMusixmatchLanguage,
@@ -206,9 +223,7 @@ class LyricsSettingsPage extends ConsumerWidget {
List<String> providers,
) {
if (providers.isEmpty) return context.l10n.downloadProvidersNoneEnabled;
return providers
.map((p) => _providerDisplayNames[p] ?? p)
.join(' > ');
return providers.map((p) => _providerDisplayNames[p] ?? p).join(' > ');
}
void _showLyricsModePicker(
@@ -233,16 +248,18 @@ class LyricsSettingsPage extends ConsumerWidget {
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
child: Text(
context.l10n.lyricsMode,
style: Theme.of(context).textTheme.titleLarge
?.copyWith(fontWeight: FontWeight.bold),
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),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
),
ListTile(
@@ -311,14 +328,16 @@ class LyricsSettingsPage extends ConsumerWidget {
children: [
Text(
context.l10n.downloadMusixmatchLanguage,
style: Theme.of(context).textTheme.titleLarge
?.copyWith(fontWeight: FontWeight.bold),
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),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 16),
TextField(
+10 -6
View File
@@ -106,6 +106,8 @@ class _SetupScreenState extends ConsumerState<SetupScreen> {
}
Future<void> _requestStoragePermission() async {
final permissionAudio = context.l10n.permissionAudio;
final permissionStorage = context.l10n.permissionStorage;
setState(() => _isLoading = true);
try {
if (Platform.isIOS) {
@@ -121,7 +123,7 @@ class _SetupScreenState extends ConsumerState<SetupScreen> {
allGranted = audioStatus.isGranted;
if (audioStatus.isPermanentlyDenied) {
await _showPermissionDeniedDialog('Audio');
await _showPermissionDeniedDialog(permissionAudio);
return;
}
} else if (_androidSdkVersion >= 30) {
@@ -139,7 +141,7 @@ class _SetupScreenState extends ConsumerState<SetupScreen> {
final status = await Permission.storage.request();
allGranted = status.isGranted;
if (status.isPermanentlyDenied) {
await _showPermissionDeniedDialog('Storage');
await _showPermissionDeniedDialog(permissionStorage);
return;
}
}
@@ -184,6 +186,7 @@ class _SetupScreenState extends ConsumerState<SetupScreen> {
}
Future<void> _requestNotificationPermission() async {
final permissionNotification = context.l10n.permissionNotification;
setState(() => _isLoading = true);
try {
if (Platform.isIOS) {
@@ -191,14 +194,14 @@ class _SetupScreenState extends ConsumerState<SetupScreen> {
if (status.isGranted || status.isProvisional) {
setState(() => _notificationPermissionGranted = true);
} else if (status.isPermanentlyDenied) {
await _showPermissionDeniedDialog('Notification');
await _showPermissionDeniedDialog(permissionNotification);
}
} else if (_androidSdkVersion >= 33) {
final status = await Permission.notification.request();
if (status.isGranted) {
setState(() => _notificationPermissionGranted = true);
} else if (status.isPermanentlyDenied) {
await _showPermissionDeniedDialog('Notification');
await _showPermissionDeniedDialog(permissionNotification);
}
} else {
setState(() => _notificationPermissionGranted = true);
@@ -392,7 +395,8 @@ class _SetupScreenState extends ConsumerState<SetupScreen> {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
validation.errorReason ?? 'Invalid folder selected',
validation.errorReason ??
context.l10n.errorInvalidFolderSelected,
),
backgroundColor: Theme.of(context).colorScheme.error,
duration: const Duration(seconds: 4),
@@ -410,7 +414,7 @@ class _SetupScreenState extends ConsumerState<SetupScreen> {
SnackBar(
content: Text(
context.l10n.snackbarFolderPickerFailed(
'Could not keep access to the selected folder',
context.l10n.errorCouldNotKeepFolderAccess,
),
),
backgroundColor: Theme.of(context).colorScheme.error,
@@ -194,7 +194,7 @@ class _ExtensionDetailsScreenState
textColor: colorScheme.onSecondaryContainer,
),
_Badge(
label: _getCategoryName(ext.category),
label: _getCategoryName(context, ext.category),
color: colorScheme.tertiaryContainer,
textColor: colorScheme.onTertiaryContainer,
),
@@ -390,7 +390,7 @@ class _ExtensionDetailsScreenState
),
_MetadataRow(
label: context.l10n.extensionMinAppVersion,
value: ext.minAppVersion ?? 'Any',
value: ext.minAppVersion ?? context.l10n.storeAnyVersion,
colorScheme: colorScheme,
isLast: true,
),
@@ -496,18 +496,18 @@ class _ExtensionDetailsScreenState
}
}
String _getCategoryName(String category) {
String _getCategoryName(BuildContext context, String category) {
switch (category) {
case 'metadata':
return 'Metadata';
return context.l10n.storeCategoryMetadata;
case 'download':
return 'Download';
return context.l10n.storeCategoryDownload;
case 'utility':
return 'Utility';
return context.l10n.storeCategoryUtility;
case 'lyrics':
return 'Lyrics';
return context.l10n.storeCategoryLyrics;
case 'integration':
return 'Integration';
return context.l10n.storeCategoryIntegration;
default:
return category;
}
+37 -20
View File
@@ -265,11 +265,11 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
case 'track_number':
return l10n.editMetadataFieldTrackNum;
case 'total_tracks':
return 'Track Total';
return l10n.editMetadataFieldTrackTotal;
case 'disc_number':
return l10n.editMetadataFieldDiscNum;
case 'total_discs':
return 'Disc Total';
return l10n.editMetadataFieldDiscTotal;
case 'genre':
return l10n.editMetadataFieldGenre;
case 'isrc':
@@ -281,7 +281,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
case 'copyright':
return l10n.editMetadataFieldCopyright;
case 'composer':
return 'Composer';
return l10n.editMetadataFieldComposer;
case 'cover':
return l10n.editMetadataFieldCover;
default:
@@ -1224,16 +1224,23 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
const SizedBox(height: 6),
_buildCoverEditor(cs),
_buildAutoFillSection(cs),
_field('Title', _titleCtrl),
_field('Artist', _artistCtrl),
_field('Album', _albumCtrl),
_field('Album Artist', _albumArtistCtrl),
_field('Date', _dateCtrl, hint: 'YYYY-MM-DD or YYYY'),
_field(context.l10n.editMetadataFieldTitle, _titleCtrl),
_field(context.l10n.editMetadataFieldArtist, _artistCtrl),
_field(context.l10n.editMetadataFieldAlbum, _albumCtrl),
_field(
context.l10n.editMetadataFieldAlbumArtist,
_albumArtistCtrl,
),
_field(
context.l10n.editMetadataFieldDate,
_dateCtrl,
hint: context.l10n.editMetadataFieldDateHint,
),
Row(
children: [
Expanded(
child: _field(
'Track #',
context.l10n.editMetadataFieldTrackNum,
_trackNumCtrl,
keyboard: TextInputType.number,
),
@@ -1241,7 +1248,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
const SizedBox(width: 12),
Expanded(
child: _field(
'Track Total',
context.l10n.editMetadataFieldTrackTotal,
_trackTotalCtrl,
keyboard: TextInputType.number,
),
@@ -1253,7 +1260,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
children: [
Expanded(
child: _field(
'Disc #',
context.l10n.editMetadataFieldDiscNum,
_discNumCtrl,
keyboard: TextInputType.number,
),
@@ -1261,15 +1268,15 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
const SizedBox(width: 12),
Expanded(
child: _field(
'Disc Total',
context.l10n.editMetadataFieldDiscTotal,
_discTotalCtrl,
keyboard: TextInputType.number,
),
),
],
),
_field('Genre', _genreCtrl),
_field('ISRC', _isrcCtrl),
_field(context.l10n.editMetadataFieldGenre, _genreCtrl),
_field(context.l10n.editMetadataFieldIsrc, _isrcCtrl),
_field(
context.l10n.trackLyrics,
_lyricsCtrl,
@@ -1295,7 +1302,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
),
const SizedBox(width: 8),
Text(
'Advanced',
context.l10n.editMetadataAdvanced,
style: Theme.of(context).textTheme.labelLarge
?.copyWith(color: cs.onSurfaceVariant),
),
@@ -1305,10 +1312,20 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
),
),
if (_showAdvanced) ...[
_field('Label', _labelCtrl),
_field('Copyright', _copyrightCtrl),
_field('Composer', _composerCtrl),
_field('Comment', _commentCtrl, maxLines: 3),
_field(context.l10n.editMetadataFieldLabel, _labelCtrl),
_field(
context.l10n.editMetadataFieldCopyright,
_copyrightCtrl,
),
_field(
context.l10n.editMetadataFieldComposer,
_composerCtrl,
),
_field(
context.l10n.editMetadataFieldComment,
_commentCtrl,
maxLines: 3,
),
],
const SizedBox(height: 24),
],
@@ -1501,7 +1518,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Cover Art',
context.l10n.editMetadataFieldCover,
style: Theme.of(
context,
).textTheme.labelLarge?.copyWith(color: cs.onSurface),
+43 -23
View File
@@ -1414,8 +1414,8 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
color: Colors.white,
),
const SizedBox(width: 4),
const Text(
'Local',
Text(
context.l10n.librarySourceLocal,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
@@ -1508,11 +1508,13 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
if (openService == 'deezer') {
buttonLabel = context.l10n.trackOpenInDeezer;
} else if (openService == 'amazon') {
buttonLabel = 'Open in Amazon Music';
buttonLabel = context.l10n.trackOpenInService(
'Amazon Music',
);
} else if (openService == 'tidal') {
buttonLabel = 'Open in Tidal';
buttonLabel = context.l10n.trackOpenInService('Tidal');
} else if (openService == 'qobuz') {
buttonLabel = 'Open in Qobuz';
buttonLabel = context.l10n.trackOpenInService('Qobuz');
} else {
buttonLabel = context.l10n.trackOpenInSpotify;
}
@@ -1617,11 +1619,17 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
if (trackNumber != null && trackNumber! > 0)
_MetadataItem(context.l10n.trackTrackNumber, trackNumber.toString()),
if (totalTracks != null && totalTracks! > 0)
_MetadataItem('Track Total', totalTracks.toString()),
_MetadataItem(
context.l10n.editMetadataFieldTrackTotal,
totalTracks.toString(),
),
if (discNumber != null && discNumber! > 0)
_MetadataItem(context.l10n.trackDiscNumber, discNumber.toString()),
if (totalDiscs != null && totalDiscs! > 0)
_MetadataItem('Disc Total', totalDiscs.toString()),
_MetadataItem(
context.l10n.editMetadataFieldDiscTotal,
totalDiscs.toString(),
),
if (duration != null)
_MetadataItem(context.l10n.trackDuration, _formatDuration(duration!)),
if (audioQualityStr != null)
@@ -1635,7 +1643,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
if (copyright != null && copyright!.isNotEmpty)
_MetadataItem(context.l10n.trackCopyright, copyright!),
if (composer != null && composer!.isNotEmpty)
_MetadataItem('Composer', composer!),
_MetadataItem(context.l10n.editMetadataFieldComposer, composer!),
if (isrc != null && isrc!.isNotEmpty) _MetadataItem('ISRC', isrc!),
];
@@ -2019,7 +2027,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
Padding(
padding: const EdgeInsets.only(top: 4),
child: Text(
'Source: ${_lyricsSource!}',
context.l10n.trackLyricsSource(_lyricsSource!),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
@@ -2219,7 +2227,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
_rawLyrics = embeddedLyrics;
_lyricsSource = embeddedSource.isNotEmpty
? embeddedSource
: 'Embedded';
: context.l10n.trackLyricsEmbeddedSource;
_lyricsEmbedded = true;
_lyricsLoading = false;
_embeddedLyricsChecked = true;
@@ -2350,7 +2358,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
_rawLyrics = embeddedLyrics;
_lyricsSource = embeddedSource.isNotEmpty
? embeddedSource
: 'Embedded';
: context.l10n.trackLyricsEmbeddedSource;
_lyricsEmbedded = true;
_lyricsLoading = false;
_embeddedLyricsChecked = true;
@@ -3545,19 +3553,25 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
final formats = <String>[];
if (currentFormat == 'FLAC') {
formats.addAll(['ALAC', 'MP3', 'Opus']);
formats.addAll(['ALAC', 'AAC', 'MP3', 'Opus']);
} else if (currentFormat == 'M4A') {
formats.addAll(['FLAC', 'MP3', 'Opus']);
formats.addAll(['FLAC', 'AAC', 'MP3', 'Opus']);
} else if (currentFormat == 'MP3') {
formats.add('Opus');
formats.addAll(['AAC', 'Opus']);
} else if (currentFormat == 'Opus') {
formats.add('MP3');
formats.addAll(['AAC', 'MP3']);
} else {
formats.addAll(['MP3', 'Opus']);
formats.addAll(['AAC', 'MP3', 'Opus']);
}
String selectedFormat = formats.first;
String selectedBitrate = selectedFormat == 'Opus' ? '128k' : '320k';
String defaultBitrateForFormat(String format) {
if (format == 'Opus') return '128k';
if (format == 'AAC') return '256k';
return '320k';
}
String selectedBitrate = defaultBitrateForFormat(selectedFormat);
bool isLosslessTarget =
selectedFormat == 'ALAC' || selectedFormat == 'FLAC';
@@ -3622,9 +3636,9 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
isLosslessTarget =
format == 'ALAC' || format == 'FLAC';
if (!isLosslessTarget) {
selectedBitrate = format == 'Opus'
? '128k'
: '320k';
selectedBitrate = defaultBitrateForFormat(
format,
);
}
});
}
@@ -3717,6 +3731,8 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
void _showCueSplitSheet(BuildContext context) async {
var cuePath = cleanFilePath;
final unknownAlbum = context.l10n.unknownAlbum;
final unknownArtist = context.l10n.unknownArtist;
final trackSuffix = RegExp(r'#track\d+$');
if (trackSuffix.hasMatch(cuePath)) {
cuePath = cuePath.replaceFirst(trackSuffix, '');
@@ -3737,8 +3753,8 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
return;
}
final album = cueInfo['album'] as String? ?? 'Unknown Album';
final artist = cueInfo['artist'] as String? ?? 'Unknown Artist';
final album = cueInfo['album'] as String? ?? unknownAlbum;
final artist = cueInfo['artist'] as String? ?? unknownArtist;
final audioPath = cueInfo['audio_path'] as String? ?? '';
final genre = cueInfo['genre'] as String? ?? '';
final date = cueInfo['date'] as String? ?? '';
@@ -4387,6 +4403,10 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
newExt = '.opus';
mimeType = 'audio/opus';
break;
case 'aac':
newExt = '.m4a';
mimeType = 'audio/mp4';
break;
case 'alac':
newExt = '.m4a';
mimeType = 'audio/mp4';
@@ -4584,7 +4604,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
if (refreshedLyrics.isNotEmpty) {
_lyrics = _cleanLrcForDisplay(refreshedLyrics);
_rawLyrics = refreshedLyrics;
_lyricsSource = 'Embedded';
_lyricsSource = context.l10n.trackLyricsEmbeddedSource;
_lyricsEmbedded = true;
} else {
_lyrics = null;
+30 -8
View File
@@ -295,7 +295,7 @@ class FFmpegService {
header[0] == 0x66 && // 'f'
header[1] == 0x4C && // 'L'
header[2] == 0x61 && // 'a'
header[3] == 0x43; // 'C'
header[3] == 0x43; // 'C'
} finally {
await raf.close();
}
@@ -330,7 +330,8 @@ class FFmpegService {
String? bitrate,
bool deleteOriginal = true,
}) async {
String bitrateValue = format == 'opus' ? '128k' : '320k';
final normalizedFormat = format.toLowerCase();
String bitrateValue = normalizedFormat == 'opus' ? '128k' : '320k';
if (bitrate != null && bitrate.contains('_')) {
final parts = bitrate.split('_');
if (parts.length == 2) {
@@ -338,13 +339,20 @@ class FFmpegService {
}
}
final extension = format == 'opus' ? '.opus' : '.mp3';
final extension = switch (normalizedFormat) {
'opus' => '.opus',
'aac' || 'm4a' => '.m4a',
_ => '.mp3',
};
final outputPath = _buildOutputPath(inputPath, extension);
String command;
if (format == 'opus') {
if (normalizedFormat == 'opus') {
command =
'-v error -hide_banner -i "$inputPath" -codec:a libopus -b:a $bitrateValue -vbr on -compression_level 10 -map 0:a "$outputPath" -y';
} else if (normalizedFormat == 'aac' || normalizedFormat == 'm4a') {
command =
'-v error -hide_banner -i "$inputPath" -codec:a aac -b:a $bitrateValue -map 0:a -f mp4 "$outputPath" -y';
} else {
command =
'-v error -hide_banner -i "$inputPath" -codec:a libmp3lame -b:a $bitrateValue -map 0:a -id3v2_version 3 "$outputPath" -y';
@@ -361,7 +369,7 @@ class FFmpegService {
return outputPath;
}
_log.e('M4A to $format conversion failed: ${result.output}');
_log.e('M4A to $normalizedFormat conversion failed: ${result.output}');
return null;
}
@@ -1839,7 +1847,7 @@ class FFmpegService {
}
/// Unified audio format conversion with full metadata + cover preservation.
/// Supports: FLAC/M4A/MP3/Opus -> MP3/Opus/ALAC/FLAC.
/// Supports: FLAC/M4A/MP3/Opus -> AAC/M4A/MP3/Opus/ALAC/FLAC.
/// ALAC and FLAC targets are lossless (bitrate parameter is ignored).
static Future<String?> convertAudioFormat({
required String inputPath,
@@ -1851,7 +1859,7 @@ class FFmpegService {
bool deleteOriginal = true,
}) async {
final format = targetFormat.toLowerCase();
if (!const {'mp3', 'opus', 'alac', 'flac'}.contains(format)) {
if (!const {'mp3', 'opus', 'aac', 'alac', 'flac'}.contains(format)) {
_log.e('Unsupported target format: $targetFormat');
return null;
}
@@ -1874,13 +1882,20 @@ class FFmpegService {
);
}
final extension = format == 'opus' ? '.opus' : '.mp3';
final extension = switch (format) {
'opus' => '.opus',
'aac' => '.m4a',
_ => '.mp3',
};
final outputPath = _buildOutputPath(inputPath, extension);
String command;
if (format == 'opus') {
command =
'-v error -hide_banner -i "$inputPath" -codec:a libopus -b:a $bitrate -vbr on -compression_level 10 -map 0:a "$outputPath" -y';
} else if (format == 'aac') {
command =
'-v error -hide_banner -i "$inputPath" -codec:a aac -b:a $bitrate -map 0:a -f mp4 "$outputPath" -y';
} else {
command =
'-v error -hide_banner -i "$inputPath" -codec:a libmp3lame -b:a $bitrate -map 0:a -id3v2_version 3 "$outputPath" -y';
@@ -1907,6 +1922,13 @@ class FFmpegService {
coverPath: coverPath,
metadata: metadata,
);
} else if (format == 'aac') {
embedResult = await embedMetadataToM4a(
m4aPath: outputPath,
coverPath: coverPath,
metadata: metadata,
preserveMetadata: true,
);
} else {
embedResult = await embedMetadataToOpus(
opusPath: outputPath,
+6 -1
View File
@@ -1753,7 +1753,9 @@ class LibraryDatabase {
bitrate: bitrate,
);
if (normalizedFormat == 'mp3' || normalizedFormat == 'opus') {
if (normalizedFormat == 'mp3' ||
normalizedFormat == 'opus' ||
normalizedFormat == 'aac') {
updated['bitDepth'] = null;
}
@@ -2014,6 +2016,8 @@ class LibraryDatabase {
switch (targetFormat.trim().toLowerCase()) {
case 'alac':
return 'm4a';
case 'aac':
return 'aac';
case 'flac':
return 'flac';
case 'opus':
@@ -2030,6 +2034,7 @@ class LibraryDatabase {
switch (targetFormat.trim().toLowerCase()) {
case 'mp3':
case 'opus':
case 'aac':
final match = RegExp(r'(\d+)').firstMatch(bitrate);
return match != null ? int.tryParse(match.group(1)!) : null;
default:
+6 -7
View File
@@ -182,7 +182,9 @@ class _AudioAnalysisCardState extends State<AudioAnalysisCard> {
await _clearCache(widget.filePath);
}
final cached = forceRefresh ? null : await _loadFromCache(widget.filePath);
final cached = forceRefresh
? null
: await _loadFromCache(widget.filePath);
AudioAnalysisData data;
bool fromCache = false;
@@ -571,10 +573,7 @@ class _AudioAnalysisCardState extends State<AudioAnalysisCard> {
tooltip: l10n.audioAnalysisRescan,
visualDensity: VisualDensity.compact,
padding: EdgeInsets.zero,
constraints: const BoxConstraints(
minWidth: 32,
minHeight: 32,
),
constraints: const BoxConstraints(minWidth: 32, minHeight: 32),
color: cs.onErrorContainer,
onPressed: () => _analyze(forceRefresh: true),
),
@@ -904,9 +903,9 @@ class _AudioInfoCard extends StatelessWidget {
icon: Icons.surround_sound,
label: context.l10n.audioAnalysisChannels,
value: data.channels == 2
? 'Stereo'
? context.l10n.audioAnalysisStereo
: data.channels == 1
? 'Mono'
? context.l10n.audioAnalysisMono
: '${data.channels}',
cs: cs,
),
+6 -6
View File
@@ -380,7 +380,7 @@ class _ServiceHealthDot extends StatelessWidget {
Widget build(BuildContext context) {
final color = _serviceHealthColor(status);
return Tooltip(
message: _serviceHealthTooltip(status),
message: _serviceHealthTooltip(context, status),
child: Container(
width: 8,
height: 8,
@@ -404,16 +404,16 @@ Color _serviceHealthColor(String status) {
}
}
String _serviceHealthTooltip(String status) {
String _serviceHealthTooltip(BuildContext context, String status) {
switch (status) {
case 'online':
return 'Service online';
return context.l10n.extensionHealthServiceOnline;
case 'degraded':
return 'Service degraded';
return context.l10n.extensionHealthServiceDegraded;
case 'offline':
return 'Service offline';
return context.l10n.extensionHealthServiceOffline;
default:
return 'Service status unknown';
return context.l10n.extensionHealthServiceUnknown;
}
}
+7 -3
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/utils/app_bar_layout.dart';
class PrioritySettingsScaffold extends StatelessWidget {
@@ -6,7 +7,7 @@ class PrioritySettingsScaffold extends StatelessWidget {
final String title;
final String description;
final String infoText;
final String saveLabel;
final String? saveLabel;
final EdgeInsetsGeometry descriptionPadding;
final List<Widget> slivers;
final Future<void> Function() onSave;
@@ -21,7 +22,7 @@ class PrioritySettingsScaffold extends StatelessWidget {
required this.slivers,
required this.onSave,
required this.onConfirmDiscard,
this.saveLabel = 'Save',
this.saveLabel,
this.descriptionPadding = const EdgeInsets.fromLTRB(16, 4, 16, 8),
});
@@ -67,7 +68,10 @@ class PrioritySettingsScaffold extends StatelessWidget {
),
actions: [
if (hasChanges)
TextButton(onPressed: onSave, child: Text(saveLabel)),
TextButton(
onPressed: onSave,
child: Text(saveLabel ?? context.l10n.dialogSave),
),
],
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
+5
View File
@@ -188,6 +188,7 @@ void main() {
expect(settings.artistTagMode, artistTagModeJoined);
expect(settings.autoFallback, isTrue);
expect(settings.lyricsProviders, ['lrclib', 'apple_music']);
expect(settings.lyricsAppleElrcWordSync, isFalse);
expect(settings.deduplicateDownloads, isTrue);
});
@@ -203,6 +204,7 @@ void main() {
concurrentDownloads: 4,
embedReplayGain: true,
lyricsProviders: ['apple_music'],
lyricsAppleElrcWordSync: true,
deduplicateDownloads: false,
clearDownloadFallbackExtensionIds: true,
clearSearchProvider: true,
@@ -213,6 +215,7 @@ void main() {
expect(updated.concurrentDownloads, 4);
expect(updated.embedReplayGain, isTrue);
expect(updated.lyricsProviders, ['apple_music']);
expect(updated.lyricsAppleElrcWordSync, isTrue);
expect(updated.deduplicateDownloads, isFalse);
expect(updated.downloadFallbackExtensionIds, isNull);
expect(updated.searchProvider, isNull);
@@ -235,6 +238,7 @@ void main() {
localLibraryPath: '/music',
hasCompletedTutorial: true,
musixmatchLanguage: 'id',
lyricsAppleElrcWordSync: true,
lastSeenVersion: '4.5.0',
deduplicateDownloads: false,
nativeDownloadWorkerEnabled: true,
@@ -255,6 +259,7 @@ void main() {
expect(decoded.localLibraryPath, '/music');
expect(decoded.hasCompletedTutorial, isTrue);
expect(decoded.musixmatchLanguage, 'id');
expect(decoded.lyricsAppleElrcWordSync, isTrue);
expect(decoded.lastSeenVersion, '4.5.0');
expect(decoded.deduplicateDownloads, isFalse);
expect(decoded.nativeDownloadWorkerEnabled, isTrue);