mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-05-15 05:10:28 +02:00
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:
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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`).
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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`).
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -507,7 +507,7 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen> {
|
||||
if (releases.isNotEmpty)
|
||||
SliverToBoxAdapter(
|
||||
child: _buildAlbumSection(
|
||||
'Releases',
|
||||
context.l10n.artistReleases,
|
||||
releases,
|
||||
colorScheme,
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user