refactor(settings): remove unused features, implement working i18n

- Remove Compact Mode toggle (feature was not implemented)
- Remove Storage Limit dropdown from Downloads (always 2GB, not functional)
- Connect language dropdown to svelte-i18n system
- Language setting now properly persists and changes app locale
- Add comprehensive translations for en and es locales
- Settings view now uses $t() for all translatable strings
- Support for Auto language detection based on browser locale
This commit is contained in:
vicrodh committed 2026-01-14 09:51:12 -06:00
1 parent 2568862aa6
commit 5c869eeae9
3 files changed
+560 -193

No files matched your search

+89 -107
View File
@@ -11,7 +11,6 @@
getDownloadCacheStats,
clearDownloadCache,
openDownloadCacheFolder,
setDownloadCacheLimit,
type DownloadCacheStats
} from '$lib/stores/downloadState';
import { clearCache as clearLyricsCache } from '$lib/stores/lyricsStore';
@@ -26,6 +25,8 @@
setSystemNotificationsEnabled,
loadSystemNotificationsPreference
} from '$lib/services/playbackService';
import { setLocale, locale, t } from '$lib/i18n';
import { get } from 'svelte/store';
interface Props {
onBack?: () => void;
@@ -63,7 +64,6 @@
// Download cache state (offline storage)
let downloadStats = $state<DownloadCacheStats | null>(null);
let isClearingDownloads = $state(false);
let downloadCacheLimit = $state('2 GB'); // Default 2GB
// Lyrics cache state
let isClearingLyrics = $state(false);
@@ -119,6 +119,21 @@
'warm': 'Warm'
};
// Language mapping: display name -> locale code
const languageToLocale: Record<string, string | null> = {
'Auto': null,
'English': 'en',
'Español': 'es'
};
const localeToLanguage: Record<string, string> = {
'en': 'English',
'es': 'Español'
};
// Available languages (only those with translations)
const availableLanguages = ['Auto', 'English', 'Español'];
// Audio settings
let streamingQuality = $state('Hi-Res');
let preferHighest = $state(true);
@@ -147,7 +162,6 @@
// Appearance settings
let theme = $state('Dark');
let compactMode = $state(false);
let toastsEnabled = $state(true);
let systemNotificationsEnabled = $state(true);
let language = $state('Auto');
@@ -196,10 +210,12 @@
preferHighest = savedPreferHighest === 'true';
}
// Load language setting
const savedLanguage = localStorage.getItem('qbz-language');
if (savedLanguage) {
language = savedLanguage;
// Load language setting from i18n locale
const currentLocale = get(locale);
if (currentLocale && localeToLanguage[currentLocale]) {
language = localeToLanguage[currentLocale];
} else {
language = 'Auto';
}
// Load UI zoom level
@@ -519,7 +535,18 @@
function handleLanguageChange(lang: string) {
language = lang;
localStorage.setItem('qbz-language', lang);
const localeCode = languageToLocale[lang];
if (localeCode) {
// Set specific locale
setLocale(localeCode);
} else {
// 'Auto' - use browser locale, defaulting to 'en'
const browserLocale = navigator.language.split('-')[0];
const supportedLocale = ['en', 'es'].includes(browserLocale) ? browserLocale : 'en';
setLocale(supportedLocale);
// Clear the stored locale so it uses browser detection on next load
localStorage.removeItem('qbz-locale');
}
}
interface AudioSettings {
@@ -624,18 +651,6 @@
async function loadDownloadStats() {
try {
downloadStats = await getDownloadCacheStats();
// Set the limit dropdown to match current limit
if (downloadStats.limitBytes) {
const limitGb = downloadStats.limitBytes / (1024 * 1024 * 1024);
if (limitGb <= 0.5) downloadCacheLimit = '500 MB';
else if (limitGb <= 1) downloadCacheLimit = '1 GB';
else if (limitGb <= 2) downloadCacheLimit = '2 GB';
else if (limitGb <= 5) downloadCacheLimit = '5 GB';
else if (limitGb <= 10) downloadCacheLimit = '10 GB';
else downloadCacheLimit = 'Unlimited';
} else {
downloadCacheLimit = 'Unlimited';
}
} catch (err) {
console.error('Failed to load download stats:', err);
}
@@ -662,27 +677,6 @@
}
}
async function handleDownloadLimitChange(limit: string) {
downloadCacheLimit = limit;
let limitMb: number | null = null;
switch (limit) {
case '500 MB': limitMb = 500; break;
case '1 GB': limitMb = 1024; break;
case '2 GB': limitMb = 2048; break;
case '5 GB': limitMb = 5120; break;
case '10 GB': limitMb = 10240; break;
case 'Unlimited': limitMb = null; break;
}
try {
await setDownloadCacheLimit(limitMb);
await loadDownloadStats();
} catch (err) {
console.error('Failed to set download limit:', err);
}
}
async function handleClearCache() {
if (isClearing) return;
isClearing = true;
@@ -750,15 +744,15 @@
{#if onBack}
<button class="back-btn" onclick={onBack}>
<ArrowLeft size={16} />
<span>Back</span>
<span>{$t('actions.back')}</span>
</button>
{/if}
<h1 class="title">Settings</h1>
<h1 class="title">{$t('settings.title')}</h1>
</div>
<!-- Account Section -->
<section class="section">
<h3 class="section-title">Account</h3>
<h3 class="section-title">{$t('settings.account.title')}</h3>
<div class="account-card">
<div class="avatar">{userName.charAt(0).toUpperCase()}</div>
<div class="account-info">
@@ -768,15 +762,15 @@
{/if}
<div class="subscription">{subscription}</div>
</div>
<button class="logout-btn" onclick={onLogout}>Logout</button>
<button class="logout-btn" onclick={onLogout}>{$t('settings.account.logout')}</button>
</div>
</section>
<!-- Audio Section -->
<section class="section">
<h3 class="section-title">Audio</h3>
<h3 class="section-title">{$t('settings.audio.title')}</h3>
<div class="setting-row">
<span class="setting-label">Streaming Quality</span>
<span class="setting-label">{$t('settings.audio.streamingQuality')}</span>
<Dropdown
value={streamingQuality}
options={['MP3', 'CD Quality', 'Hi-Res', 'Hi-Res+']}
@@ -784,12 +778,12 @@
/>
</div>
<div class="setting-row">
<span class="setting-label">Prefer highest available</span>
<span class="setting-label">{$t('settings.audio.preferHighest')}</span>
<Toggle enabled={preferHighest} onchange={handlePreferHighestChange} />
</div>
<div class="setting-row">
<div class="label-with-tooltip">
<span class="setting-label">Output Device</span>
<span class="setting-label">{$t('settings.audio.outputDevice')}</span>
<Tooltip text="Select your preferred audio output device. Changes take effect on app restart." />
</div>
<Dropdown
@@ -803,48 +797,48 @@
</div>
<div class="setting-row">
<div class="label-with-tooltip">
<span class="setting-label">Exclusive Mode</span>
<Tooltip text="Locks the audio device for exclusive use by QBZ for better quality" />
<span class="setting-label">{$t('settings.audio.exclusiveMode')}</span>
<Tooltip text={$t('settings.audio.exclusiveModeDesc')} />
</div>
<Toggle enabled={exclusiveMode} onchange={handleExclusiveModeChange} />
</div>
<div class="setting-row">
<div class="label-with-tooltip">
<span class="setting-label">DAC Passthrough</span>
<Tooltip text="Bypass the system audio mixer to send audio directly to your DAC at its native sample rate. Recommended for external DACs." />
<span class="setting-label">{$t('settings.audio.dacPassthrough')}</span>
<Tooltip text={$t('settings.audio.dacPassthroughDesc')} />
</div>
<Toggle enabled={dacPassthrough} onchange={handleDacPassthroughChange} />
</div>
<div class="setting-row last">
<span class="setting-label">Sample Rate</span>
<span class="setting-label">{$t('settings.audio.currentSampleRate')}</span>
<span class="setting-value">192 kHz</span>
</div>
</section>
<!-- Playback Section -->
<section class="section">
<h3 class="section-title">Playback</h3>
<h3 class="section-title">{$t('settings.playback.title')}</h3>
<div class="setting-row">
<span class="setting-label">Gapless Playback</span>
<span class="setting-label">{$t('settings.playback.gapless')}</span>
<Toggle enabled={gaplessPlayback} onchange={(v) => (gaplessPlayback = v)} />
</div>
<div class="setting-row">
<span class="setting-label">Crossfade</span>
<span class="setting-label">{$t('settings.playback.crossfade')}</span>
<div class="slider-container">
<VolumeSlider value={crossfade} onchange={(v) => (crossfade = v)} max={12} showValue />
</div>
</div>
<div class="setting-row last">
<span class="setting-label">Normalize Volume</span>
<span class="setting-label">{$t('settings.playback.normalizeVolume')}</span>
<Toggle enabled={normalizeVolume} onchange={(v) => (normalizeVolume = v)} />
</div>
</section>
<!-- Appearance Section -->
<section class="section">
<h3 class="section-title">Appearance</h3>
<h3 class="section-title">{$t('settings.appearance.title')}</h3>
<div class="setting-row">
<span class="setting-label">Theme</span>
<span class="setting-label">{$t('settings.appearance.theme')}</span>
<Dropdown
value={theme}
options={['Dark', 'Light', 'OLED Black', 'Warm']}
@@ -852,15 +846,15 @@
/>
</div>
<div class="setting-row">
<span class="setting-label">Language</span>
<span class="setting-label">{$t('settings.appearance.language')}</span>
<Dropdown
value={language}
options={['Auto', 'English', 'Español', 'Français', 'Deutsch', 'Italiano', 'Português']}
options={availableLanguages}
onchange={handleLanguageChange}
/>
</div>
<div class="setting-row">
<span class="setting-label">UI Scale</span>
<span class="setting-label">{$t('settings.appearance.uiScale')}</span>
<Dropdown
value={zoomLevel}
options={zoomOptions}
@@ -868,26 +862,22 @@
/>
</div>
<div class="setting-row">
<span class="setting-label">Compact Mode</span>
<Toggle enabled={compactMode} onchange={(v) => (compactMode = v)} />
</div>
<div class="setting-row">
<span class="setting-label">In-app Toasts</span>
<span class="setting-label">{$t('settings.appearance.inAppToasts')}</span>
<Toggle enabled={toastsEnabled} onchange={(v) => { toastsEnabled = v; setToastsEnabled(v); }} />
</div>
<div class="setting-row last">
<span class="setting-label">System Notifications</span>
<span class="setting-label">{$t('settings.appearance.systemNotifications')}</span>
<Toggle enabled={systemNotificationsEnabled} onchange={(v) => { systemNotificationsEnabled = v; setSystemNotificationsEnabled(v); }} />
</div>
</section>
<!-- Library Section -->
<section class="section">
<h3 class="section-title">Library</h3>
<h3 class="section-title">{$t('settings.library.title')}</h3>
<div class="setting-row last">
<div class="setting-with-description">
<span class="setting-label">Fetch Qobuz Artist Images</span>
<span class="setting-description">Show artist photos from Qobuz in local library</span>
<span class="setting-label">{$t('settings.library.fetchArtistImages')}</span>
<span class="setting-description">{$t('settings.library.fetchArtistImagesDesc')}</span>
</div>
<Toggle enabled={fetchQobuzArtistImages} onchange={(v) => {
fetchQobuzArtistImages = v;
@@ -898,34 +888,34 @@
<!-- Integrations Section -->
<section class="section">
<h3 class="section-title">Integrations</h3>
<h3 class="section-title">{$t('settings.integrations.title')}</h3>
{#if lastfmConnected}
<div class="setting-row">
<div class="lastfm-connected">
<span class="setting-label">Last.fm</span>
<span class="lastfm-username">Connected as {lastfmUsername}</span>
<span class="setting-label">{$t('settings.integrations.lastfm')}</span>
<span class="lastfm-username">{$t('settings.integrations.connectedAs', { values: { username: lastfmUsername }})}</span>
</div>
<button
class="connect-btn connected"
onclick={handleLastfmDisconnect}
>
Disconnect
{$t('settings.integrations.disconnect')}
</button>
</div>
<div class="setting-row last">
<span class="setting-label">Scrobbling</span>
<span class="setting-label">{$t('settings.integrations.scrobbling')}</span>
<Toggle enabled={scrobbling} onchange={handleScrobblingChange} />
</div>
{:else}
<div class="setting-row" class:last={!showLastfmConfig && !lastfmAuthToken}>
<span class="setting-label">Last.fm</span>
<span class="setting-label">{$t('settings.integrations.lastfm')}</span>
<button
class="connect-btn"
onclick={handleLastfmConnect}
disabled={lastfmConnecting}
>
{lastfmConnecting ? 'Connecting...' : 'Connect'}
{lastfmConnecting ? 'Connecting...' : $t('settings.integrations.connect')}
</button>
</div>
@@ -990,98 +980,90 @@
<!-- Storage Section (Memory Cache) -->
<section class="section">
<h3 class="section-title">Storage</h3>
<h3 class="section-title">{$t('settings.storage.title')}</h3>
<div class="setting-row">
<span class="setting-label">Cache Size</span>
<span class="setting-label">{$t('settings.storage.cacheSize')}</span>
<span class="setting-value">
{#if cacheStats}
{formatBytes(cacheStats.current_size_bytes)} / {formatBytes(cacheStats.max_size_bytes)}
{:else}
Loading...
{$t('actions.loading')}
{/if}
</span>
</div>
<div class="setting-row">
<span class="setting-label">Cached Tracks</span>
<span class="setting-label">{$t('settings.storage.cachedTracks')}</span>
<span class="setting-value">
{#if cacheStats}
{cacheStats.cached_tracks} tracks
{cacheStats.cached_tracks} {$t('album.tracks')}
{:else}
-
{/if}
</span>
</div>
<div class="setting-row last">
<span class="setting-label">Clear Cache</span>
<span class="setting-label">{$t('settings.storage.clearCache')}</span>
<button
class="clear-btn"
onclick={handleClearCache}
disabled={isClearing || !cacheStats || cacheStats.current_size_bytes === 0}
>
{isClearing ? 'Clearing...' : 'Clear'}
{isClearing ? $t('settings.storage.clearing') : $t('actions.clear')}
</button>
</div>
</section>
<!-- Downloads Section (Offline Storage) -->
<section class="section">
<h3 class="section-title">Downloads</h3>
<h3 class="section-title">{$t('settings.downloads.title')}</h3>
<div class="setting-row">
<span class="setting-label">Downloaded Tracks</span>
<span class="setting-label">{$t('settings.downloads.downloadedTracks')}</span>
<span class="setting-value">
{#if downloadStats}
{downloadStats.readyTracks} tracks ({formatBytes(downloadStats.totalSizeBytes)})
{downloadStats.readyTracks} {$t('album.tracks')} ({formatBytes(downloadStats.totalSizeBytes)})
{:else}
Loading...
{$t('actions.loading')}
{/if}
</span>
</div>
<div class="setting-row">
<span class="setting-label">Storage Limit</span>
<Dropdown
value={downloadCacheLimit}
options={['500 MB', '1 GB', '2 GB', '5 GB', '10 GB', 'Unlimited']}
onchange={handleDownloadLimitChange}
/>
</div>
<div class="setting-row">
<span class="setting-label">Clear Downloads</span>
<span class="setting-label">{$t('settings.downloads.clearDownloads')}</span>
<button
class="clear-btn"
onclick={handleClearDownloads}
disabled={isClearingDownloads || !downloadStats || downloadStats.readyTracks === 0}
>
{isClearingDownloads ? 'Clearing...' : 'Clear All'}
{isClearingDownloads ? $t('settings.storage.clearing') : $t('actions.clearAll')}
</button>
</div>
<div class="setting-row last">
<span class="setting-label">Open Folder</span>
<span class="setting-label">{$t('settings.downloads.openFolder')}</span>
<button
class="folder-btn"
onclick={handleOpenDownloadFolder}
title="Open download cache folder"
title={$t('settings.downloads.openFolder')}
>
<FolderOpen size={16} />
<span>Open</span>
<span>{$t('actions.open')}</span>
</button>
</div>
</section>
<!-- Lyrics Section -->
<section class="section">
<h3 class="section-title">Lyrics</h3>
<h3 class="section-title">{$t('settings.lyrics.title')}</h3>
<div class="setting-row">
<span class="setting-label">Provider</span>
<span class="setting-value">LRCLIB / lyrics.ovh</span>
</div>
<div class="setting-row last">
<span class="setting-label">Clear Lyrics Cache</span>
<span class="setting-label">{$t('settings.lyrics.clearLyrics')}</span>
<button
class="clear-btn"
onclick={handleClearLyricsCache}
disabled={isClearingLyrics}
>
{isClearingLyrics ? 'Clearing...' : 'Clear'}
{isClearingLyrics ? $t('settings.storage.clearing') : $t('actions.clear')}
</button>
</div>
</section>
@@ -1097,7 +1079,7 @@
{:else}
<ChevronRight size={18} />
{/if}
<span>API Keys</span>
<span>{$t('settings.integrations.apiKeys')}</span>
{#if hasAnyUserCredentials()}
<span class="keys-badge">Custom</span>
{/if}
+212 -20
View File
@@ -11,73 +11,179 @@
"artists": "Artists",
"tracks": "Tracks",
"playlists": "Playlists",
"favorites": "Favorites",
"settings": "Settings",
"newReleases": "New Releases",
"pressAwards": "Press Awards",
"radio": "Radio",
"recentlyAdded": "Recently Added"
"recentlyAdded": "Recently Added",
"backTo": "Back to"
},
"home": {
"title": "Home",
"featuredForYou": "Featured for you",
"recentlyPlayed": "Recently Played",
"newReleases": "New Releases",
"pressAwards": "Press Awards",
"madeForYou": "Made for You",
"topPicks": "Top Picks"
"topPicks": "Top Picks",
"yourPlaylists": "Your Playlists",
"seeAll": "See All"
},
"search": {
"title": "Search",
"placeholder": "Search albums, artists, tracks...",
"all": "All",
"albums": "Albums",
"tracks": "Tracks",
"artists": "Artists",
"noResults": "No results found",
"searching": "Searching..."
"searching": "Searching...",
"recentSearches": "Recent Searches",
"clearHistory": "Clear History"
},
"library": {
"title": "Local Library",
"albums": "Albums",
"artists": "Artists",
"tracks": "Tracks",
"folders": "Folders",
"noAlbums": "No albums in library",
"noArtists": "No artists in library",
"noTracks": "No tracks in library",
"noFolders": "No folders added",
"addFolder": "Add Folder",
"removeFolder": "Remove",
"scan": "Scan",
"scanning": "Scanning...",
"stopScan": "Stop",
"scanComplete": "Scan complete",
"scanCancelled": "Scan cancelled",
"scanError": "Scan error",
"searchPlaceholder": "Search...",
"searchAlbums": "Search albums...",
"searchArtists": "Search artists...",
"searchTracks": "Search tracks, albums, artists...",
"trackCount": "{count} tracks",
"albumCount": "{count} albums",
"artistCount": "{count} artists",
"groupBy": "Group by",
"sortBy": "Sort by",
"viewAs": "View as",
"grid": "Grid",
"list": "List",
"fetchingArtwork": "Fetching artwork..."
},
"album": {
"tracks": "tracks",
"track": "track",
"by": "by",
"released": "Released",
"releaseDate": "Release Date",
"label": "Label",
"genre": "Genre"
"genre": "Genre",
"duration": "Duration",
"quality": "Quality",
"play": "Play",
"shuffle": "Shuffle",
"addToQueue": "Add to Queue",
"addToPlaylist": "Add to Playlist",
"download": "Download",
"downloadAll": "Download All",
"disc": "Disc"
},
"artist": {
"albums": "Albums",
"discography": "Discography",
"eps": "EPs & Singles",
"epsSingles": "EPs & Singles",
"live": "Live Albums",
"liveAlbums": "Live Albums",
"compilations": "Compilations",
"appearsOn": "Appears On",
"popularTracks": "Popular Tracks",
"similarArtists": "Similar Artists",
"followers": "followers"
"biography": "Biography",
"readMore": "Read More",
"readLess": "Read Less",
"followers": "followers",
"loadMore": "Load More",
"noAlbums": "No albums",
"noBiography": "No biography available"
},
"playlist": {
"tracks": "tracks",
"track": "track",
"createNew": "Create New...",
"createPlaylist": "Create Playlist",
"deletePlaylist": "Delete Playlist",
"renamePlaylist": "Rename Playlist",
"allPlaylists": "All Playlists",
"myPlaylists": "My Playlists"
"myPlaylists": "My Playlists",
"qobuzPlaylists": "Qobuz Playlists",
"emptyPlaylist": "This playlist is empty",
"addTracks": "Add Tracks",
"removeTracks": "Remove",
"playlistName": "Playlist Name",
"description": "Description",
"public": "Public",
"private": "Private"
},
"favorites": {
"title": "Favorites",
"albums": "Albums",
"tracks": "Tracks",
"artists": "Artists",
"noFavorites": "No favorites yet",
"addedToFavorites": "Added to favorites",
"removedFromFavorites": "Removed from favorites"
},
"player": {
"nowPlaying": "Now Playing",
"queue": "Queue",
"upNext": "Up Next",
"history": "History",
"lyrics": "Lyrics",
"noLyrics": "No lyrics available",
"fetchingLyrics": "Fetching lyrics...",
"shuffle": "Shuffle",
"repeat": "Repeat",
"repeatOne": "Repeat One",
"repeatOff": "Repeat Off",
"previous": "Previous",
"next": "Next",
"play": "Play",
"pause": "Pause",
"volume": "Volume",
"mute": "Mute"
"mute": "Mute",
"unmute": "Unmute",
"clearQueue": "Clear Queue",
"queueEmpty": "Queue is empty",
"playNext": "Play Next",
"addToQueue": "Add to Queue",
"removeFromQueue": "Remove from Queue",
"saveQueue": "Save as Playlist"
},
"quality": {
"mp3": "MP3",
"mp3": "MP3 320kbps",
"cdQuality": "CD Quality",
"cdQualityDesc": "16-bit / 44.1kHz FLAC",
"hiRes": "Hi-Res",
"ultraHiRes": "Ultra Hi-Res",
"hiResDesc": "24-bit up to 96kHz FLAC",
"ultraHiRes": "Hi-Res 24-bit",
"ultraHiResDesc": "24-bit up to 192kHz FLAC",
"flac": "FLAC",
"bitDepth": "{depth}-bit",
"sampleRate": "{rate} kHz",
"lossless": "Lossless"
"lossless": "Lossless",
"max": "Max"
},
"actions": {
"play": "Play",
"playAll": "Play All",
"playNow": "Play Now",
"shuffle": "Shuffle",
"shuffleAll": "Shuffle All",
"addToQueue": "Add to Queue",
"addToPlaylist": "Add to Playlist",
"addToFavorites": "Add to Favorites",
@@ -86,13 +192,26 @@
"share": "Share",
"viewAlbum": "View Album",
"viewArtist": "View Artist",
"goToArtist": "Go to Artist",
"goToAlbum": "Go to Album",
"more": "More",
"back": "Back",
"seeAll": "See All",
"cancel": "Cancel",
"save": "Save",
"delete": "Delete",
"edit": "Edit"
"edit": "Edit",
"close": "Close",
"open": "Open",
"refresh": "Refresh",
"retry": "Retry",
"clear": "Clear",
"clearAll": "Clear All",
"confirm": "Confirm",
"yes": "Yes",
"no": "No",
"ok": "OK",
"loading": "Loading..."
},
"settings": {
"title": "Settings",
@@ -106,8 +225,10 @@
"audio": {
"title": "Audio",
"streamingQuality": "Streaming Quality",
"preferHighest": "Prefer highest available",
"preferHighest": "Prefer Highest Available",
"preferHighestDesc": "Automatically select highest quality for your subscription",
"outputDevice": "Output Device",
"audioDevice": "Audio Device",
"exclusiveMode": "Exclusive Mode",
"exclusiveModeDesc": "Lock audio device for exclusive use",
"dacPassthrough": "DAC Passthrough",
@@ -131,22 +252,59 @@
"warm": "Warm"
},
"accentColor": "Accent Color",
"compactMode": "Compact Mode",
"language": "Language"
"language": "Language",
"uiScale": "UI Scale",
"inAppToasts": "In-app Toasts",
"systemNotifications": "System Notifications"
},
"library": {
"title": "Library",
"fetchArtistImages": "Fetch Qobuz Artist Images",
"fetchArtistImagesDesc": "Show artist photos from Qobuz in local library"
},
"integrations": {
"title": "Integrations",
"lastfm": "Last.fm",
"spotify": "Spotify",
"tidal": "Tidal",
"discogs": "Discogs",
"connected": "Connected",
"connectedAs": "Connected as {username}",
"notConnected": "Not connected",
"connect": "Connect",
"disconnect": "Disconnect",
"scrobbling": "Scrobbling"
"scrobbling": "Scrobbling",
"configureApi": "Configure API",
"apiKeys": "API Keys",
"clientId": "Client ID",
"clientSecret": "Client Secret",
"consumerKey": "Consumer Key",
"consumerSecret": "Consumer Secret"
},
"storage": {
"title": "Storage",
"cachedTracks": "Cached Tracks",
"cacheSize": "Cache Size",
"clearCache": "Clear Cache"
"clearCache": "Clear Cache",
"clearing": "Clearing..."
},
"downloads": {
"title": "Downloads",
"downloadedTracks": "Downloaded Tracks",
"clearDownloads": "Clear All",
"openFolder": "Open Folder"
},
"lyrics": {
"title": "Lyrics",
"lyricsCache": "Lyrics Cache",
"clearLyrics": "Clear Lyrics"
},
"about": {
"title": "About",
"version": "Version",
"sourceCode": "Source Code",
"reportIssue": "Report Issue",
"checkUpdates": "Check for Updates"
}
},
"auth": {
@@ -156,7 +314,25 @@
"loginButton": "Sign In",
"loggingIn": "Signing in...",
"loginError": "Login failed. Please check your credentials.",
"welcomeBack": "Welcome back"
"welcomeBack": "Welcome back",
"rememberMe": "Remember me"
},
"notifications": {
"trackAdded": "Track added to queue",
"tracksAdded": "{count} tracks added to queue",
"albumAdded": "Album added to queue",
"addedToPlaylist": "Added to playlist",
"removedFromPlaylist": "Removed from playlist",
"playlistCreated": "Playlist created",
"playlistDeleted": "Playlist deleted",
"downloadStarted": "Download started",
"downloadComplete": "Download complete",
"downloadFailed": "Download failed",
"scanComplete": "Library scan complete",
"scanCancelled": "Library scan cancelled",
"scanFailed": "Library scan failed",
"settingsSaved": "Settings saved",
"copied": "Copied to clipboard"
},
"errors": {
"generic": "Something went wrong",
@@ -164,12 +340,20 @@
"notFound": "Not found",
"unauthorized": "Please log in to continue",
"trackNotAvailable": "This track is not available for streaming",
"qualityNotAvailable": "Requested quality is not available"
"qualityNotAvailable": "Requested quality is not available",
"loadFailed": "Failed to load",
"playbackFailed": "Playback failed",
"downloadFailed": "Download failed",
"saveFailed": "Failed to save"
},
"time": {
"hours": "h",
"minutes": "min",
"seconds": "s"
"seconds": "s",
"justNow": "Just now",
"minutesAgo": "{count} min ago",
"hoursAgo": "{count}h ago",
"daysAgo": "{count}d ago"
},
"empty": {
"noAlbums": "No albums yet",
@@ -177,6 +361,14 @@
"noTracks": "No tracks yet",
"noPlaylists": "No playlists yet",
"noFavorites": "No favorites yet",
"queueEmpty": "Queue is empty"
"queueEmpty": "Queue is empty",
"noResults": "No results found",
"noContent": "No content available"
},
"confirm": {
"deletePlaylist": "Are you sure you want to delete this playlist?",
"clearQueue": "Clear all tracks from queue?",
"clearCache": "Clear all cached data?",
"logout": "Are you sure you want to log out?"
}
}
+259 -66
View File
@@ -9,116 +9,238 @@
"library": "Biblioteca",
"albums": "Álbumes",
"artists": "Artistas",
"tracks": "Canciones",
"playlists": "Playlists",
"tracks": "Pistas",
"playlists": "Listas",
"favorites": "Favoritos",
"settings": "Ajustes",
"newReleases": "Novedades",
"pressAwards": "Premios",
"radio": "Radio",
"recentlyAdded": "Añadido recientemente"
"recentlyAdded": "Agregado Recientemente",
"backTo": "Volver a"
},
"home": {
"title": "Inicio",
"featuredForYou": "Sugerencias destacadas para ti",
"recentlyPlayed": "Escuchado recientemente",
"newReleases": "Nuevos lanzamientos",
"madeForYou": "Hecho para ti",
"topPicks": "Selecciones destacadas"
"featuredForYou": "Destacados para ti",
"recentlyPlayed": "Escuchado Recientemente",
"newReleases": "Novedades",
"pressAwards": "Premios de Prensa",
"madeForYou": "Hecho para Ti",
"topPicks": "Selecciones Destacadas",
"yourPlaylists": "Tus Listas",
"seeAll": "Ver Todo"
},
"search": {
"title": "Buscar",
"placeholder": "Buscar álbumes, artistas, canciones...",
"placeholder": "Buscar álbumes, artistas, pistas...",
"all": "Todo",
"noResults": "No se encontraron resultados",
"searching": "Buscando..."
"albums": "Álbumes",
"tracks": "Pistas",
"artists": "Artistas",
"noResults": "Sin resultados",
"searching": "Buscando...",
"recentSearches": "Búsquedas Recientes",
"clearHistory": "Limpiar Historial"
},
"library": {
"title": "Biblioteca Local",
"albums": "Álbumes",
"artists": "Artistas",
"tracks": "Pistas",
"folders": "Carpetas",
"noAlbums": "No hay álbumes en la biblioteca",
"noArtists": "No hay artistas en la biblioteca",
"noTracks": "No hay pistas en la biblioteca",
"noFolders": "No hay carpetas agregadas",
"addFolder": "Agregar Carpeta",
"removeFolder": "Eliminar",
"scan": "Escanear",
"scanning": "Escaneando...",
"stopScan": "Detener",
"scanComplete": "Escaneo completo",
"scanCancelled": "Escaneo cancelado",
"scanError": "Error de escaneo",
"searchPlaceholder": "Buscar...",
"searchAlbums": "Buscar álbumes...",
"searchArtists": "Buscar artistas...",
"searchTracks": "Buscar pistas, álbumes, artistas...",
"trackCount": "{count} pistas",
"albumCount": "{count} álbumes",
"artistCount": "{count} artistas",
"groupBy": "Agrupar por",
"sortBy": "Ordenar por",
"viewAs": "Ver como",
"grid": "Cuadrícula",
"list": "Lista",
"fetchingArtwork": "Obteniendo portada..."
},
"album": {
"tracks": "canciones",
"track": "canción",
"tracks": "pistas",
"track": "pista",
"by": "de",
"released": "Publicado",
"released": "Lanzado",
"releaseDate": "Fecha de Lanzamiento",
"label": "Sello",
"genre": "Género"
"genre": "Género",
"duration": "Duración",
"quality": "Calidad",
"play": "Reproducir",
"shuffle": "Aleatorio",
"addToQueue": "Agregar a la Cola",
"addToPlaylist": "Agregar a Lista",
"download": "Descargar",
"downloadAll": "Descargar Todo",
"disc": "Disco"
},
"artist": {
"albums": "Álbumes",
"popularTracks": "Canciones populares",
"similarArtists": "Artistas similares",
"followers": "seguidores"
"discography": "Discografía",
"eps": "EPs y Sencillos",
"epsSingles": "EPs y Sencillos",
"live": "Álbumes en Vivo",
"liveAlbums": "Álbumes en Vivo",
"compilations": "Compilaciones",
"appearsOn": "Aparece En",
"popularTracks": "Pistas Populares",
"similarArtists": "Artistas Similares",
"biography": "Biografía",
"readMore": "Leer Más",
"readLess": "Leer Menos",
"followers": "seguidores",
"loadMore": "Cargar Más",
"noAlbums": "Sin álbumes",
"noBiography": "No hay biografía disponible"
},
"playlist": {
"tracks": "canciones",
"createNew": "Crear nueva...",
"allPlaylists": "Todas las playlists",
"myPlaylists": "Mis playlists"
"tracks": "pistas",
"track": "pista",
"createNew": "Crear Nueva...",
"createPlaylist": "Crear Lista",
"deletePlaylist": "Eliminar Lista",
"renamePlaylist": "Renombrar Lista",
"allPlaylists": "Todas las Listas",
"myPlaylists": "Mis Listas",
"qobuzPlaylists": "Listas de Qobuz",
"emptyPlaylist": "Esta lista está vacía",
"addTracks": "Agregar Pistas",
"removeTracks": "Eliminar",
"playlistName": "Nombre de la Lista",
"description": "Descripción",
"public": "Pública",
"private": "Privada"
},
"favorites": {
"title": "Favoritos",
"albums": "Álbumes",
"tracks": "Pistas",
"artists": "Artistas",
"noFavorites": "Aún no hay favoritos",
"addedToFavorites": "Agregado a favoritos",
"removedFromFavorites": "Eliminado de favoritos"
},
"player": {
"nowPlaying": "Reproduciendo ahora",
"nowPlaying": "Reproduciendo",
"queue": "Cola",
"lyrics": "Letra",
"upNext": "Siguiente",
"history": "Historial",
"lyrics": "Letras",
"noLyrics": "No hay letras disponibles",
"fetchingLyrics": "Buscando letras...",
"shuffle": "Aleatorio",
"repeat": "Repetir",
"repeatOne": "Repetir una",
"repeatOne": "Repetir Una",
"repeatOff": "No Repetir",
"previous": "Anterior",
"next": "Siguiente",
"play": "Reproducir",
"pause": "Pausar",
"volume": "Volumen",
"mute": "Silenciar"
"mute": "Silenciar",
"unmute": "Activar Sonido",
"clearQueue": "Limpiar Cola",
"queueEmpty": "La cola está vacía",
"playNext": "Reproducir Siguiente",
"addToQueue": "Agregar a la Cola",
"removeFromQueue": "Quitar de la Cola",
"saveQueue": "Guardar como Lista"
},
"quality": {
"mp3": "MP3",
"mp3": "MP3 320kbps",
"cdQuality": "Calidad CD",
"cdQualityDesc": "16-bit / 44.1kHz FLAC",
"hiRes": "Hi-Res",
"ultraHiRes": "Ultra Hi-Res",
"hiResDesc": "24-bit hasta 96kHz FLAC",
"ultraHiRes": "Hi-Res 24-bit",
"ultraHiResDesc": "24-bit hasta 192kHz FLAC",
"flac": "FLAC",
"bitDepth": "{depth}-bit",
"sampleRate": "{rate} kHz",
"lossless": "Sin pérdida"
"lossless": "Sin Pérdida",
"max": "Máx"
},
"actions": {
"play": "Reproducir",
"playAll": "Reproducir todo",
"playAll": "Reproducir Todo",
"playNow": "Reproducir Ahora",
"shuffle": "Aleatorio",
"addToQueue": "Añadir a la cola",
"addToPlaylist": "Añadir a playlist",
"addToFavorites": "Añadir a favoritos",
"removeFromFavorites": "Quitar de favoritos",
"shuffleAll": "Aleatorio Todo",
"addToQueue": "Agregar a la Cola",
"addToPlaylist": "Agregar a Lista",
"addToFavorites": "Agregar a Favoritos",
"removeFromFavorites": "Quitar de Favoritos",
"download": "Descargar",
"share": "Compartir",
"viewAlbum": "Ver álbum",
"viewArtist": "Ver artista",
"viewAlbum": "Ver Álbum",
"viewArtist": "Ver Artista",
"goToArtist": "Ir al Artista",
"goToAlbum": "Ir al Álbum",
"more": "Más",
"back": "Atrás",
"seeAll": "Ver todo",
"seeAll": "Ver Todo",
"cancel": "Cancelar",
"save": "Guardar",
"delete": "Eliminar",
"edit": "Editar"
"edit": "Editar",
"close": "Cerrar",
"open": "Abrir",
"refresh": "Actualizar",
"retry": "Reintentar",
"clear": "Limpiar",
"clearAll": "Limpiar Todo",
"confirm": "Confirmar",
"yes": "Sí",
"no": "No",
"ok": "OK",
"loading": "Cargando..."
},
"settings": {
"title": "Configuración",
"title": "Ajustes",
"account": {
"title": "Cuenta",
"loggedInAs": "Conectado como",
"subscription": "Suscripción",
"logout": "Cerrar sesión",
"login": "Iniciar sesión"
"logout": "Cerrar Sesión",
"login": "Iniciar Sesión"
},
"audio": {
"title": "Audio",
"streamingQuality": "Calidad de streaming",
"preferHighest": "Preferir la más alta disponible",
"outputDevice": "Dispositivo de salida",
"exclusiveMode": "Modo exclusivo",
"streamingQuality": "Calidad de Streaming",
"preferHighest": "Preferir Máxima Calidad",
"preferHighestDesc": "Seleccionar automáticamente la mejor calidad para tu suscripción",
"outputDevice": "Dispositivo de Salida",
"audioDevice": "Dispositivo de Audio",
"exclusiveMode": "Modo Exclusivo",
"exclusiveModeDesc": "Bloquear dispositivo de audio para uso exclusivo",
"dacPassthrough": "Passthrough DAC",
"dacPassthroughDesc": "Bypass del mixer del sistema para salida bit-perfect"
"dacPassthroughDesc": "Bypass del mixer del sistema para salida bit-perfect",
"currentSampleRate": "Tasa de Muestreo Actual"
},
"playback": {
"title": "Reproducción",
"gapless": "Reproducción sin pausas",
"gapless": "Reproducción Sin Pausas",
"crossfade": "Crossfade",
"crossfadeSeconds": "{seconds}s",
"normalizeVolume": "Normalizar volumen"
"normalizeVolume": "Normalizar Volumen"
},
"appearance": {
"title": "Apariencia",
@@ -129,53 +251,124 @@
"oled": "OLED Negro",
"warm": "Cálido"
},
"accentColor": "Color de acento",
"compactMode": "Modo compacto",
"language": "Idioma"
"accentColor": "Color de Acento",
"language": "Idioma",
"uiScale": "Escala de UI",
"inAppToasts": "Notificaciones en App",
"systemNotifications": "Notificaciones del Sistema"
},
"library": {
"title": "Biblioteca",
"fetchArtistImages": "Obtener Imágenes de Artistas de Qobuz",
"fetchArtistImagesDesc": "Mostrar fotos de artistas de Qobuz en biblioteca local"
},
"integrations": {
"title": "Integraciones",
"lastfm": "Last.fm",
"spotify": "Spotify",
"tidal": "Tidal",
"discogs": "Discogs",
"connected": "Conectado",
"connectedAs": "Conectado como {username}",
"notConnected": "No conectado",
"connect": "Conectar",
"disconnect": "Desconectar",
"scrobbling": "Scrobbling"
"scrobbling": "Scrobbling",
"configureApi": "Configurar API",
"apiKeys": "Claves API",
"clientId": "Client ID",
"clientSecret": "Client Secret",
"consumerKey": "Consumer Key",
"consumerSecret": "Consumer Secret"
},
"storage": {
"title": "Almacenamiento",
"cacheSize": "Tamaño de caché",
"clearCache": "Limpiar caché"
"cachedTracks": "Pistas en Caché",
"cacheSize": "Tamaño de Caché",
"clearCache": "Limpiar Caché",
"clearing": "Limpiando..."
},
"downloads": {
"title": "Descargas",
"downloadedTracks": "Pistas Descargadas",
"clearDownloads": "Limpiar Todo",
"openFolder": "Abrir Carpeta"
},
"lyrics": {
"title": "Letras",
"lyricsCache": "Caché de Letras",
"clearLyrics": "Limpiar Letras"
},
"about": {
"title": "Acerca de",
"version": "Versión",
"sourceCode": "Código Fuente",
"reportIssue": "Reportar Problema",
"checkUpdates": "Buscar Actualizaciones"
}
},
"auth": {
"login": "Iniciar sesión",
"email": "Correo electrónico",
"login": "Iniciar Sesión",
"email": "Correo Electrónico",
"password": "Contraseña",
"loginButton": "Entrar",
"loggingIn": "Iniciando sesión...",
"loginError": "Error al iniciar sesión. Verifica tus credenciales.",
"welcomeBack": "Bienvenido de nuevo"
"welcomeBack": "Bienvenido de nuevo",
"rememberMe": "Recordarme"
},
"notifications": {
"trackAdded": "Pista agregada a la cola",
"tracksAdded": "{count} pistas agregadas a la cola",
"albumAdded": "Álbum agregado a la cola",
"addedToPlaylist": "Agregado a la lista",
"removedFromPlaylist": "Eliminado de la lista",
"playlistCreated": "Lista creada",
"playlistDeleted": "Lista eliminada",
"downloadStarted": "Descarga iniciada",
"downloadComplete": "Descarga completa",
"downloadFailed": "Descarga fallida",
"scanComplete": "Escaneo de biblioteca completo",
"scanCancelled": "Escaneo de biblioteca cancelado",
"scanFailed": "Escaneo de biblioteca fallido",
"settingsSaved": "Ajustes guardados",
"copied": "Copiado al portapapeles"
},
"errors": {
"generic": "Algo salió mal",
"networkError": "Error de red. Verifica tu conexión.",
"notFound": "No encontrado",
"unauthorized": "Inicia sesión para continuar",
"trackNotAvailable": "Esta canción no está disponible para streaming",
"qualityNotAvailable": "La calidad solicitada no está disponible"
"trackNotAvailable": "Esta pista no está disponible para streaming",
"qualityNotAvailable": "La calidad solicitada no está disponible",
"loadFailed": "Error al cargar",
"playbackFailed": "Error de reproducción",
"downloadFailed": "Error de descarga",
"saveFailed": "Error al guardar"
},
"time": {
"hours": "h",
"minutes": "min",
"seconds": "s"
"seconds": "s",
"justNow": "Ahora mismo",
"minutesAgo": "hace {count} min",
"hoursAgo": "hace {count}h",
"daysAgo": "hace {count}d"
},
"empty": {
"noAlbums": "Sin álbumes aún",
"noArtists": "Sin artistas aún",
"noTracks": "Sin canciones aún",
"noPlaylists": "Sin playlists aún",
"noFavorites": "Sin favoritos aún",
"queueEmpty": "La cola está vacía"
"noAlbums": "Aún no hay álbumes",
"noArtists": "Aún no hay artistas",
"noTracks": "Aún no hay pistas",
"noPlaylists": "Aún no hay listas",
"noFavorites": "Aún no hay favoritos",
"queueEmpty": "La cola está vacía",
"noResults": "Sin resultados",
"noContent": "No hay contenido disponible"
},
"confirm": {
"deletePlaylist": "¿Estás seguro de que quieres eliminar esta lista?",
"clearQueue": "¿Limpiar todas las pistas de la cola?",
"clearCache": "¿Limpiar todos los datos en caché?",
"logout": "¿Estás seguro de que quieres cerrar sesión?"
}
}