Implemented multilingual support, the default language is English, Italian, Spanish, French and German have been added.

Other languages can be added in the future.
This commit is contained in:
Rino Russo 2023-08-20 19:11:59 +02:00
parent 964fa42a0f
commit 942962fd1f
37 changed files with 950 additions and 195 deletions

View file

@ -41,6 +41,7 @@ import androidx.compose.ui.geometry.center
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
@ -48,6 +49,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.center import it.vfsfitvnm.vimusic.utils.center
import it.vfsfitvnm.vimusic.utils.drawCircle import it.vfsfitvnm.vimusic.utils.drawCircle
@ -62,8 +64,8 @@ fun TextFieldDialog(
onDismiss: () -> Unit, onDismiss: () -> Unit,
onDone: (String) -> Unit, onDone: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
cancelText: String = "Cancel", cancelText: String = stringResource(R.string.cancel),
doneText: String = "Done", doneText: String = stringResource(R.string.done),
initialTextInput: String = "", initialTextInput: String = "",
singleLine: Boolean = true, singleLine: Boolean = true,
maxLines: Int = 1, maxLines: Int = 1,
@ -167,8 +169,8 @@ fun ConfirmationDialog(
onDismiss: () -> Unit, onDismiss: () -> Unit,
onConfirm: () -> Unit, onConfirm: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
cancelText: String = "Cancel", cancelText: String = stringResource(R.string.cancel),
confirmText: String = "Confirm", confirmText: String = stringResource(R.string.confirm),
onCancel: () -> Unit = onDismiss onCancel: () -> Unit = onDismiss
) { ) {
val (_, typography) = LocalAppearance.current val (_, typography) = LocalAppearance.current

View file

@ -40,6 +40,7 @@ import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
@ -70,7 +71,6 @@ import it.vfsfitvnm.vimusic.utils.formatAsDuration
import it.vfsfitvnm.vimusic.utils.medium import it.vfsfitvnm.vimusic.utils.medium
import it.vfsfitvnm.vimusic.utils.semiBold import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.vimusic.utils.thumbnail import it.vfsfitvnm.vimusic.utils.thumbnail
import kotlin.system.measureTimeMillis
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -90,7 +90,7 @@ fun InHistoryMediaItemMenu(
if (isHiding) { if (isHiding) {
ConfirmationDialog( ConfirmationDialog(
text = "Do you really want to hide this song? Its playback time and cache will be wiped.\nThis action is irreversible.", text = stringResource(R.string.hidesong),
onDismiss = { isHiding = false }, onDismiss = { isHiding = false },
onConfirm = { onConfirm = {
onDismiss() onDismiss()
@ -366,7 +366,7 @@ fun MediaItemMenu(
if (onAddToPlaylist != null) { if (onAddToPlaylist != null) {
SecondaryTextButton( SecondaryTextButton(
text = "New playlist", text = stringResource(R.string.new_playlist),
onClick = { isCreatingNewPlaylist = true }, onClick = { isCreatingNewPlaylist = true },
alternative = true alternative = true
) )
@ -378,7 +378,7 @@ fun MediaItemMenu(
MenuEntry( MenuEntry(
icon = R.drawable.playlist, icon = R.drawable.playlist,
text = playlistPreview.playlist.name, text = playlistPreview.playlist.name,
secondaryText = "${playlistPreview.songCount} songs", secondaryText = "${playlistPreview.songCount} " + stringResource(R.string.songs),
onClick = { onClick = {
onDismiss() onDismiss()
onAddToPlaylist(playlistPreview.playlist, playlistPreview.songCount) onAddToPlaylist(playlistPreview.playlist, playlistPreview.songCount)
@ -464,7 +464,7 @@ fun MediaItemMenu(
onStartRadio?.let { onStartRadio -> onStartRadio?.let { onStartRadio ->
MenuEntry( MenuEntry(
icon = R.drawable.radio, icon = R.drawable.radio,
text = "Start radio", text = stringResource(R.string.start_radio),
onClick = { onClick = {
onDismiss() onDismiss()
onStartRadio() onStartRadio()
@ -475,7 +475,7 @@ fun MediaItemMenu(
onPlayNext?.let { onPlayNext -> onPlayNext?.let { onPlayNext ->
MenuEntry( MenuEntry(
icon = R.drawable.play_skip_forward, icon = R.drawable.play_skip_forward,
text = "Play next", text = stringResource(R.string.play_next),
onClick = { onClick = {
onDismiss() onDismiss()
onPlayNext() onPlayNext()
@ -486,7 +486,7 @@ fun MediaItemMenu(
onEnqueue?.let { onEnqueue -> onEnqueue?.let { onEnqueue ->
MenuEntry( MenuEntry(
icon = R.drawable.enqueue, icon = R.drawable.enqueue,
text = "Enqueue", text = stringResource(R.string.enqueue),
onClick = { onClick = {
onDismiss() onDismiss()
onEnqueue() onEnqueue()
@ -497,7 +497,7 @@ fun MediaItemMenu(
onGoToEqualizer?.let { onGoToEqualizer -> onGoToEqualizer?.let { onGoToEqualizer ->
MenuEntry( MenuEntry(
icon = R.drawable.equalizer, icon = R.drawable.equalizer,
text = "Equalizer", text = stringResource(R.string.equalizer),
onClick = { onClick = {
onDismiss() onDismiss()
onGoToEqualizer() onGoToEqualizer()
@ -521,7 +521,7 @@ fun MediaItemMenu(
if (isShowingSleepTimerDialog) { if (isShowingSleepTimerDialog) {
if (sleepTimerMillisLeft != null) { if (sleepTimerMillisLeft != null) {
ConfirmationDialog( ConfirmationDialog(
text = "Do you want to stop the sleep timer?", text = stringResource(R.string.stop_sleep_timer),
cancelText = "No", cancelText = "No",
confirmText = "Stop", confirmText = "Stop",
onDismiss = { isShowingSleepTimerDialog = false }, onDismiss = { isShowingSleepTimerDialog = false },
@ -539,7 +539,7 @@ fun MediaItemMenu(
} }
BasicText( BasicText(
text = "Set sleep timer", text = stringResource(R.string.set_sleep_timer),
style = typography.s.semiBold, style = typography.s.semiBold,
modifier = Modifier modifier = Modifier
.padding(vertical = 8.dp, horizontal = 24.dp) .padding(vertical = 8.dp, horizontal = 24.dp)
@ -604,12 +604,12 @@ fun MediaItemMenu(
.fillMaxWidth() .fillMaxWidth()
) { ) {
DialogTextButton( DialogTextButton(
text = "Cancel", text = stringResource(R.string.cancel),
onClick = { isShowingSleepTimerDialog = false } onClick = { isShowingSleepTimerDialog = false }
) )
DialogTextButton( DialogTextButton(
text = "Set", text = stringResource(R.string.set),
enabled = amount > 0, enabled = amount > 0,
primary = true, primary = true,
onClick = { onClick = {
@ -624,12 +624,15 @@ fun MediaItemMenu(
MenuEntry( MenuEntry(
icon = R.drawable.alarm, icon = R.drawable.alarm,
text = "Sleep timer", text = stringResource(R.string.sleep_timer),
onClick = { isShowingSleepTimerDialog = true }, onClick = { isShowingSleepTimerDialog = true },
trailingContent = sleepTimerMillisLeft?.let { trailingContent = sleepTimerMillisLeft?.let {
{ {
BasicText( BasicText(
text = "${formatAsDuration(it)} left", text = stringResource(
R.string.left,
formatAsDuration(it)
),
style = typography.xxs.medium, style = typography.xxs.medium,
modifier = modifier modifier = modifier
.background( .background(
@ -647,7 +650,7 @@ fun MediaItemMenu(
if (onAddToPlaylist != null) { if (onAddToPlaylist != null) {
MenuEntry( MenuEntry(
icon = R.drawable.playlist, icon = R.drawable.playlist,
text = "Add to playlist", text = stringResource(R.string.add_to_playlist),
onClick = { isViewingPlaylists = true }, onClick = { isViewingPlaylists = true },
trailingContent = { trailingContent = {
Image( Image(
@ -667,7 +670,7 @@ fun MediaItemMenu(
albumInfo?.let { (albumId) -> albumInfo?.let { (albumId) ->
MenuEntry( MenuEntry(
icon = R.drawable.disc, icon = R.drawable.disc,
text = "Go to album", text = stringResource(R.string.go_to_album),
onClick = { onClick = {
onDismiss() onDismiss()
onGoToAlbum(albumId) onGoToAlbum(albumId)
@ -680,7 +683,7 @@ fun MediaItemMenu(
artistsInfo?.forEach { (authorId, authorName) -> artistsInfo?.forEach { (authorId, authorName) ->
MenuEntry( MenuEntry(
icon = R.drawable.person, icon = R.drawable.person,
text = "More of $authorName", text = stringResource(R.string.more_of) + " $authorName",
onClick = { onClick = {
onDismiss() onDismiss()
onGoToArtist(authorId) onGoToArtist(authorId)
@ -692,7 +695,7 @@ fun MediaItemMenu(
onRemoveFromQueue?.let { onRemoveFromQueue -> onRemoveFromQueue?.let { onRemoveFromQueue ->
MenuEntry( MenuEntry(
icon = R.drawable.trash, icon = R.drawable.trash,
text = "Remove from queue", text = stringResource(R.string.remove_from_queue),
onClick = { onClick = {
onDismiss() onDismiss()
onRemoveFromQueue() onRemoveFromQueue()
@ -703,7 +706,7 @@ fun MediaItemMenu(
onRemoveFromPlaylist?.let { onRemoveFromPlaylist -> onRemoveFromPlaylist?.let { onRemoveFromPlaylist ->
MenuEntry( MenuEntry(
icon = R.drawable.trash, icon = R.drawable.trash,
text = "Remove from playlist", text = stringResource(R.string.remove_from_playlist),
onClick = { onClick = {
onDismiss() onDismiss()
onRemoveFromPlaylist() onRemoveFromPlaylist()
@ -714,7 +717,7 @@ fun MediaItemMenu(
onHideFromDatabase?.let { onHideFromDatabase -> onHideFromDatabase?.let { onHideFromDatabase ->
MenuEntry( MenuEntry(
icon = R.drawable.trash, icon = R.drawable.trash,
text = "Hide", text = stringResource(R.string.hide),
onClick = onHideFromDatabase onClick = onHideFromDatabase
) )
} }
@ -722,7 +725,7 @@ fun MediaItemMenu(
onRemoveFromQuickPicks?.let { onRemoveFromQuickPicks?.let {
MenuEntry( MenuEntry(
icon = R.drawable.trash, icon = R.drawable.trash,
text = "Hide from \"Quick picks\"", text = stringResource(R.string.hide_from_quick_picks),
onClick = { onClick = {
onDismiss() onDismiss()
onRemoveFromQuickPicks() onRemoveFromQuickPicks()

View file

@ -15,6 +15,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.valentinilk.shimmer.shimmer import com.valentinilk.shimmer.shimmer
import it.vfsfitvnm.compose.persist.PersistMapCleanup import it.vfsfitvnm.compose.persist.PersistMapCleanup
@ -182,8 +183,8 @@ fun AlbumScreen(browseId: String) {
tabIndex = tabIndex, tabIndex = tabIndex,
onTabChanged = { tabIndex = it }, onTabChanged = { tabIndex = it },
tabColumnContent = { Item -> tabColumnContent = { Item ->
Item(0, "Songs", R.drawable.musical_notes) Item(0, stringResource(R.string.songs), R.drawable.musical_notes)
Item(1, "Other versions", R.drawable.disc) Item(1, stringResource(R.string.other_versions), R.drawable.disc)
} }
) { currentTabIndex -> ) { currentTabIndex ->
saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { saveableStateHolder.SaveableStateProvider(key = currentTabIndex) {
@ -203,7 +204,7 @@ fun AlbumScreen(browseId: String) {
headerContent = headerContent, headerContent = headerContent,
initialPlaceholderCount = 1, initialPlaceholderCount = 1,
continuationPlaceholderCount = 1, continuationPlaceholderCount = 1,
emptyItemsText = "This album doesn't have any alternative version", emptyItemsText = stringResource(R.string.album_no_alternative_version),
itemsPageProvider = albumPage?.let { itemsPageProvider = albumPage?.let {
({ ({
Result.success( Result.success(

View file

@ -21,6 +21,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import it.vfsfitvnm.compose.persist.persistList import it.vfsfitvnm.compose.persist.persistList
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
@ -86,7 +87,7 @@ fun AlbumSongs(
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
headerContent { headerContent {
SecondaryTextButton( SecondaryTextButton(
text = "Enqueue", text = stringResource(R.string.enqueue),
enabled = songs.isNotEmpty(), enabled = songs.isNotEmpty(),
onClick = { onClick = {
binder?.player?.enqueue(songs.map(Song::asMediaItem)) binder?.player?.enqueue(songs.map(Song::asMediaItem))

View file

@ -19,6 +19,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import it.vfsfitvnm.compose.persist.persist import it.vfsfitvnm.compose.persist.persist
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
@ -81,7 +82,7 @@ fun ArtistLocalSongs(
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
headerContent { headerContent {
SecondaryTextButton( SecondaryTextButton(
text = "Enqueue", text = stringResource(R.string.enqueue),
enabled = !songs.isNullOrEmpty(), enabled = !songs.isNullOrEmpty(),
onClick = { onClick = {
binder?.player?.enqueue(songs!!.map(Song::asMediaItem)) binder?.player?.enqueue(songs!!.map(Song::asMediaItem))

View file

@ -24,6 +24,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.innertube.Innertube import it.vfsfitvnm.innertube.Innertube
@ -103,7 +104,7 @@ fun ArtistOverview(
headerContent { headerContent {
youtubeArtistPage?.shuffleEndpoint?.let { endpoint -> youtubeArtistPage?.shuffleEndpoint?.let { endpoint ->
SecondaryTextButton( SecondaryTextButton(
text = "Shuffle", text = stringResource(R.string.shuffle),
onClick = { onClick = {
binder?.stopRadio() binder?.stopRadio()
binder?.playRadio(endpoint) binder?.playRadio(endpoint)
@ -125,14 +126,14 @@ fun ArtistOverview(
.padding(endPaddingValues) .padding(endPaddingValues)
) { ) {
BasicText( BasicText(
text = "Songs", text = stringResource(R.string.songs),
style = typography.m.semiBold, style = typography.m.semiBold,
modifier = sectionTextModifier modifier = sectionTextModifier
) )
youtubeArtistPage.songsEndpoint?.let { youtubeArtistPage.songsEndpoint?.let {
BasicText( BasicText(
text = "View all", text = stringResource(R.string.view_all),
style = typography.xs.secondary, style = typography.xs.secondary,
modifier = sectionTextModifier modifier = sectionTextModifier
.clickable(onClick = onViewAllSongsClick), .clickable(onClick = onViewAllSongsClick),
@ -178,14 +179,14 @@ fun ArtistOverview(
.padding(endPaddingValues) .padding(endPaddingValues)
) { ) {
BasicText( BasicText(
text = "Albums", text = stringResource(R.string.albums),
style = typography.m.semiBold, style = typography.m.semiBold,
modifier = sectionTextModifier modifier = sectionTextModifier
) )
youtubeArtistPage.albumsEndpoint?.let { youtubeArtistPage.albumsEndpoint?.let {
BasicText( BasicText(
text = "View all", text = stringResource(R.string.view_all),
style = typography.xs.secondary, style = typography.xs.secondary,
modifier = sectionTextModifier modifier = sectionTextModifier
.clickable(onClick = onViewAllAlbumsClick), .clickable(onClick = onViewAllAlbumsClick),
@ -299,7 +300,7 @@ fun ArtistOverview(
if (attributionsIndex != -1) { if (attributionsIndex != -1) {
BasicText( BasicText(
text = "From Wikipedia under Creative Commons Attribution CC-BY-SA 3.0", text = stringResource(R.string.from_wikipedia_cca),
style = typography.xxs.color(colorPalette.textDisabled).align(TextAlign.End), style = typography.xxs.color(colorPalette.textDisabled).align(TextAlign.End),
modifier = Modifier modifier = Modifier
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)

View file

@ -15,6 +15,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.valentinilk.shimmer.shimmer import com.valentinilk.shimmer.shimmer
import it.vfsfitvnm.compose.persist.PersistMapCleanup import it.vfsfitvnm.compose.persist.PersistMapCleanup
@ -176,16 +177,17 @@ fun ArtistScreen(browseId: String) {
tabIndex = tabIndex, tabIndex = tabIndex,
onTabChanged = { tabIndex = it }, onTabChanged = { tabIndex = it },
tabColumnContent = { Item -> tabColumnContent = { Item ->
Item(0, "Overview", R.drawable.sparkles) Item(0, stringResource(R.string.overview), R.drawable.sparkles)
Item(1, "Songs", R.drawable.musical_notes) Item(1, stringResource(R.string.songs), R.drawable.musical_notes)
Item(2, "Albums", R.drawable.disc) Item(2, stringResource(R.string.albums), R.drawable.disc)
Item(3, "Singles", R.drawable.disc) Item(3, stringResource(R.string.singles), R.drawable.disc)
Item(4, "Library", R.drawable.library) Item(4, stringResource(R.string.library), R.drawable.library)
}, },
) { currentTabIndex -> ) { currentTabIndex ->
saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { saveableStateHolder.SaveableStateProvider(key = currentTabIndex) {
when (currentTabIndex) { when (currentTabIndex) {
0 -> ArtistOverview( 0 -> {
ArtistOverview(
youtubeArtistPage = artistPage, youtubeArtistPage = artistPage,
thumbnailContent = thumbnailContent, thumbnailContent = thumbnailContent,
headerContent = headerContent, headerContent = headerContent,
@ -194,6 +196,7 @@ fun ArtistScreen(browseId: String) {
onViewAllAlbumsClick = { tabIndex = 2 }, onViewAllAlbumsClick = { tabIndex = 2 },
onViewAllSinglesClick = { tabIndex = 3 }, onViewAllSinglesClick = { tabIndex = 3 },
) )
}
1 -> { 1 -> {
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
@ -267,7 +270,7 @@ fun ArtistScreen(browseId: String) {
ItemsPage( ItemsPage(
tag = "artist/$browseId/albums", tag = "artist/$browseId/albums",
headerContent = headerContent, headerContent = headerContent,
emptyItemsText = "This artist didn't release any album", emptyItemsText = stringResource(R.string.artist_no_release_album),
itemsPageProvider = artistPage?.let { itemsPageProvider = artistPage?.let {
({ continuation -> ({ continuation ->
continuation?.let { continuation?.let {
@ -317,7 +320,7 @@ fun ArtistScreen(browseId: String) {
ItemsPage( ItemsPage(
tag = "artist/$browseId/singles", tag = "artist/$browseId/singles",
headerContent = headerContent, headerContent = headerContent,
emptyItemsText = "This artist didn't release any single", emptyItemsText = stringResource(R.string.artist_no_release_single),
itemsPageProvider = artistPage?.let { itemsPageProvider = artistPage?.let {
({ continuation -> ({ continuation ->
continuation?.let { continuation?.let {
@ -360,7 +363,8 @@ fun ArtistScreen(browseId: String) {
) )
} }
4 -> ArtistLocalSongs( 4 -> {
ArtistLocalSongs(
browseId = browseId, browseId = browseId,
headerContent = headerContent, headerContent = headerContent,
thumbnailContent = thumbnailContent, thumbnailContent = thumbnailContent,
@ -371,3 +375,4 @@ fun ArtistScreen(browseId: String) {
} }
} }
} }
}

View file

@ -6,6 +6,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.saveable.rememberSaveableStateHolder import androidx.compose.runtime.saveable.rememberSaveableStateHolder
import androidx.compose.ui.res.stringResource
import it.vfsfitvnm.compose.persist.PersistMapCleanup import it.vfsfitvnm.compose.persist.PersistMapCleanup
import it.vfsfitvnm.compose.routing.RouteHandler import it.vfsfitvnm.compose.routing.RouteHandler
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
@ -38,8 +39,8 @@ fun BuiltInPlaylistScreen(builtInPlaylist: BuiltInPlaylist) {
tabIndex = tabIndex, tabIndex = tabIndex,
onTabChanged = onTabIndexChanged, onTabChanged = onTabIndexChanged,
tabColumnContent = { Item -> tabColumnContent = { Item ->
Item(0, "Favorites", R.drawable.heart) Item(0, stringResource(R.string.favorites), R.drawable.heart)
Item(1, "Offline", R.drawable.airplane) Item(1, stringResource(R.string.offline), R.drawable.airplane)
} }
) { currentTabIndex -> ) { currentTabIndex ->
saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { saveableStateHolder.SaveableStateProvider(key = currentTabIndex) {

View file

@ -19,6 +19,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.compose.persist.persistList import it.vfsfitvnm.compose.persist.persistList
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
@ -94,14 +95,14 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) {
) { ) {
Header( Header(
title = when (builtInPlaylist) { title = when (builtInPlaylist) {
BuiltInPlaylist.Favorites -> "Favorites" BuiltInPlaylist.Favorites -> stringResource(R.string.favorites)
BuiltInPlaylist.Offline -> "Offline" BuiltInPlaylist.Offline -> stringResource(R.string.offline)
}, },
modifier = Modifier modifier = Modifier
.padding(bottom = 8.dp) .padding(bottom = 8.dp)
) { ) {
SecondaryTextButton( SecondaryTextButton(
text = "Enqueue", text = stringResource(R.string.enqueue),
enabled = songs.isNotEmpty(), enabled = songs.isNotEmpty(),
onClick = { onClick = {
binder?.player?.enqueue(songs.map(Song::asMediaItem)) binder?.player?.enqueue(songs.map(Song::asMediaItem))

View file

@ -15,16 +15,15 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.compose.persist.persist import it.vfsfitvnm.compose.persist.persist
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
@ -85,7 +84,7 @@ fun HomeAlbums(
key = "header", key = "header",
contentType = 0 contentType = 0
) { ) {
Header(title = "Albums") { Header(title = stringResource(R.string.albums)) {
HeaderIconButton( HeaderIconButton(
icon = R.drawable.calendar, icon = R.drawable.calendar,
color = if (sortBy == AlbumSortBy.Year) colorPalette.text else colorPalette.textDisabled, color = if (sortBy == AlbumSortBy.Year) colorPalette.text else colorPalette.textDisabled,

View file

@ -27,6 +27,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.compose.persist.persistList import it.vfsfitvnm.compose.persist.persistList
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
@ -94,7 +95,7 @@ fun HomeArtistList(
contentType = 0, contentType = 0,
span = { GridItemSpan(maxLineSpan) } span = { GridItemSpan(maxLineSpan) }
) { ) {
Header(title = "Artists") { Header(title = stringResource(R.string.artists)) {
HeaderIconButton( HeaderIconButton(
icon = R.drawable.text, icon = R.drawable.text,
color = if (sortBy == ArtistSortBy.Name) colorPalette.text else colorPalette.textDisabled, color = if (sortBy == ArtistSortBy.Name) colorPalette.text else colorPalette.textDisabled,

View file

@ -29,6 +29,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.compose.persist.persistList import it.vfsfitvnm.compose.persist.persistList
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
@ -69,7 +70,7 @@ fun HomePlaylists(
if (isCreatingANewPlaylist) { if (isCreatingANewPlaylist) {
TextFieldDialog( TextFieldDialog(
hintText = "Enter the playlist name", hintText = stringResource(R.string.enter_the_playlist_name),
onDismiss = { onDismiss = {
isCreatingANewPlaylist = false isCreatingANewPlaylist = false
}, },
@ -116,9 +117,9 @@ fun HomePlaylists(
.background(colorPalette.background0) .background(colorPalette.background0)
) { ) {
item(key = "header", contentType = 0, span = { GridItemSpan(maxLineSpan) }) { item(key = "header", contentType = 0, span = { GridItemSpan(maxLineSpan) }) {
Header(title = "Playlists") { Header(title = stringResource(R.string.playlists)) {
SecondaryTextButton( SecondaryTextButton(
text = "New playlist", text = stringResource(R.string.new_playlist),
onClick = { isCreatingANewPlaylist = true } onClick = { isCreatingANewPlaylist = true }
) )
@ -164,7 +165,7 @@ fun HomePlaylists(
PlaylistItem( PlaylistItem(
icon = R.drawable.heart, icon = R.drawable.heart,
colorTint = colorPalette.red, colorTint = colorPalette.red,
name = "Favorites", name = stringResource(R.string.favorites),
songCount = null, songCount = null,
thumbnailSizeDp = thumbnailSizeDp, thumbnailSizeDp = thumbnailSizeDp,
alternative = true, alternative = true,
@ -178,7 +179,7 @@ fun HomePlaylists(
PlaylistItem( PlaylistItem(
icon = R.drawable.airplane, icon = R.drawable.airplane,
colorTint = colorPalette.blue, colorTint = colorPalette.blue,
name = "Offline", name = stringResource(R.string.offline),
songCount = null, songCount = null,
thumbnailSizeDp = thumbnailSizeDp, thumbnailSizeDp = thumbnailSizeDp,
alternative = true, alternative = true,

View file

@ -5,6 +5,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.saveable.rememberSaveableStateHolder import androidx.compose.runtime.saveable.rememberSaveableStateHolder
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import it.vfsfitvnm.compose.persist.PersistMapCleanup import it.vfsfitvnm.compose.persist.PersistMapCleanup
import it.vfsfitvnm.compose.routing.RouteHandler import it.vfsfitvnm.compose.routing.RouteHandler
import it.vfsfitvnm.compose.routing.defaultStacking import it.vfsfitvnm.compose.routing.defaultStacking
@ -119,11 +120,11 @@ fun HomeScreen(onPlaylistUrl: (String) -> Unit) {
tabIndex = tabIndex, tabIndex = tabIndex,
onTabChanged = onTabChanged, onTabChanged = onTabChanged,
tabColumnContent = { Item -> tabColumnContent = { Item ->
Item(0, "Quick picks", R.drawable.sparkles) Item(0, stringResource(R.string.quick_picks), R.drawable.sparkles)
Item(1, "Songs", R.drawable.musical_notes) Item(1, stringResource(R.string.songs), R.drawable.musical_notes)
Item(2, "Playlists", R.drawable.playlist) Item(2, stringResource(R.string.playlists), R.drawable.playlist)
Item(3, "Artists", R.drawable.person) Item(3, stringResource(R.string.artists), R.drawable.person)
Item(4, "Albums", R.drawable.disc) Item(4, stringResource(R.string.albums), R.drawable.disc)
} }
) { currentTabIndex -> ) { currentTabIndex ->
saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { saveableStateHolder.SaveableStateProvider(key = currentTabIndex) {

View file

@ -29,6 +29,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.compose.persist.persistList import it.vfsfitvnm.compose.persist.persistList
@ -102,7 +103,7 @@ fun HomeSongs(
key = "header", key = "header",
contentType = 0 contentType = 0
) { ) {
Header(title = "Songs") { Header(title = stringResource(R.string.songs)) {
HeaderIconButton( HeaderIconButton(
icon = R.drawable.trending, icon = R.drawable.trending,
color = if (sortBy == SongSortBy.PlayTime) colorPalette.text else colorPalette.textDisabled, color = if (sortBy == SongSortBy.PlayTime) colorPalette.text else colorPalette.textDisabled,

View file

@ -37,6 +37,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.compose.persist.persist import it.vfsfitvnm.compose.persist.persist
import it.vfsfitvnm.innertube.Innertube import it.vfsfitvnm.innertube.Innertube
@ -152,7 +153,7 @@ fun QuickPicks(
) )
) { ) {
Header( Header(
title = "Quick picks", title = stringResource(R.string.quick_picks),
modifier = Modifier modifier = Modifier
.padding(endPaddingValues) .padding(endPaddingValues)
) )
@ -248,7 +249,7 @@ fun QuickPicks(
related.albums?.let { albums -> related.albums?.let { albums ->
BasicText( BasicText(
text = "Related albums", text = stringResource(R.string.related_albums),
style = typography.m.semiBold, style = typography.m.semiBold,
modifier = sectionTextModifier modifier = sectionTextModifier
) )
@ -272,7 +273,7 @@ fun QuickPicks(
related.artists?.let { artists -> related.artists?.let { artists ->
BasicText( BasicText(
text = "Similar artists", text = stringResource(R.string.similar_artists),
style = typography.m.semiBold, style = typography.m.semiBold,
modifier = sectionTextModifier modifier = sectionTextModifier
) )
@ -296,7 +297,7 @@ fun QuickPicks(
related.playlists?.let { playlists -> related.playlists?.let { playlists ->
BasicText( BasicText(
text = "Playlists you might like", text = stringResource(R.string.playlists_you_might_like),
style = typography.m.semiBold, style = typography.m.semiBold,
modifier = Modifier modifier = Modifier
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
@ -323,7 +324,7 @@ fun QuickPicks(
Unit Unit
} ?: relatedPageResult?.exceptionOrNull()?.let { } ?: relatedPageResult?.exceptionOrNull()?.let {
BasicText( BasicText(
text = "An error has occurred", text = stringResource(R.string.an_error_has_occurred),
style = typography.s.secondary.center, style = typography.s.secondary.center,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)

View file

@ -4,6 +4,7 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.saveable.rememberSaveableStateHolder import androidx.compose.runtime.saveable.rememberSaveableStateHolder
import androidx.compose.ui.res.stringResource
import it.vfsfitvnm.compose.persist.PersistMapCleanup import it.vfsfitvnm.compose.persist.PersistMapCleanup
import it.vfsfitvnm.compose.routing.RouteHandler import it.vfsfitvnm.compose.routing.RouteHandler
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
@ -28,7 +29,7 @@ fun LocalPlaylistScreen(playlistId: Long) {
tabIndex = 0, tabIndex = 0,
onTabChanged = { }, onTabChanged = { },
tabColumnContent = { Item -> tabColumnContent = { Item ->
Item(0, "Songs", R.drawable.musical_notes) Item(0, stringResource(R.string.songs), R.drawable.musical_notes)
} }
) { currentTabIndex -> ) { currentTabIndex ->
saveableStateHolder.SaveableStateProvider(currentTabIndex) { saveableStateHolder.SaveableStateProvider(currentTabIndex) {

View file

@ -22,6 +22,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.compose.persist.persist import it.vfsfitvnm.compose.persist.persist
import it.vfsfitvnm.innertube.Innertube import it.vfsfitvnm.innertube.Innertube
@ -102,7 +103,7 @@ fun LocalPlaylistSongs(
if (isRenaming) { if (isRenaming) {
TextFieldDialog( TextFieldDialog(
hintText = "Enter the playlist name", hintText = stringResource(R.string.enter_the_playlist_name),
initialTextInput = playlistWithSongs?.playlist?.name ?: "", initialTextInput = playlistWithSongs?.playlist?.name ?: "",
onDismiss = { isRenaming = false }, onDismiss = { isRenaming = false },
onDone = { text -> onDone = { text ->
@ -119,7 +120,7 @@ fun LocalPlaylistSongs(
if (isDeleting) { if (isDeleting) {
ConfirmationDialog( ConfirmationDialog(
text = "Do you really want to delete this playlist?", text = stringResource(R.string.delete_playlist),
onDismiss = { isDeleting = false }, onDismiss = { isDeleting = false },
onConfirm = { onConfirm = {
query { query {
@ -154,7 +155,7 @@ fun LocalPlaylistSongs(
.padding(bottom = 8.dp) .padding(bottom = 8.dp)
) { ) {
SecondaryTextButton( SecondaryTextButton(
text = "Enqueue", text = stringResource(R.string.enqueue),
enabled = playlistWithSongs?.songs?.isNotEmpty() == true, enabled = playlistWithSongs?.songs?.isNotEmpty() == true,
onClick = { onClick = {
playlistWithSongs?.songs playlistWithSongs?.songs
@ -179,7 +180,7 @@ fun LocalPlaylistSongs(
playlistWithSongs?.playlist?.browseId?.let { browseId -> playlistWithSongs?.playlist?.browseId?.let { browseId ->
MenuEntry( MenuEntry(
icon = R.drawable.sync, icon = R.drawable.sync,
text = "Sync", text = stringResource(R.string.sync),
onClick = { onClick = {
menuState.hide() menuState.hide()
transaction { transaction {
@ -210,7 +211,7 @@ fun LocalPlaylistSongs(
MenuEntry( MenuEntry(
icon = R.drawable.pencil, icon = R.drawable.pencil,
text = "Rename", text = stringResource(R.string.rename),
onClick = { onClick = {
menuState.hide() menuState.hide()
isRenaming = true isRenaming = true
@ -219,7 +220,7 @@ fun LocalPlaylistSongs(
MenuEntry( MenuEntry(
icon = R.drawable.trash, icon = R.drawable.trash,
text = "Delete", text = stringResource(R.string.delete),
onClick = { onClick = {
menuState.hide() menuState.hide()
isDeleting = true isDeleting = true

View file

@ -44,6 +44,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.media3.common.C import androidx.media3.common.C
@ -169,7 +170,7 @@ fun Lyrics(
if (isEditing) { if (isEditing) {
TextFieldDialog( TextFieldDialog(
hintText = "Enter the lyrics", hintText = stringResource(R.string.enter_the_lyrics),
initialTextInput = text ?: "", initialTextInput = text ?: "",
singleLine = false, singleLine = false,
maxLines = 10, maxLines = 10,
@ -334,8 +335,14 @@ fun Lyrics(
Menu { Menu {
MenuEntry( MenuEntry(
icon = R.drawable.time, icon = R.drawable.time,
text = "Show ${if (isShowingSynchronizedLyrics) "un" else ""}synchronized lyrics", text = stringResource(R.string.show) + " ${
secondaryText = if (isShowingSynchronizedLyrics) null else "Provided by kugou.com", if (isShowingSynchronizedLyrics) stringResource(
R.string.unsynchronized_lyrics
) else stringResource(R.string.synchronized_lyrics)
}",
secondaryText = if (isShowingSynchronizedLyrics) null else stringResource(
R.string.provided_by
) + " kugou.com",
onClick = { onClick = {
menuState.hide() menuState.hide()
isShowingSynchronizedLyrics = isShowingSynchronizedLyrics =
@ -345,7 +352,7 @@ fun Lyrics(
MenuEntry( MenuEntry(
icon = R.drawable.pencil, icon = R.drawable.pencil,
text = "Edit lyrics", text = stringResource(R.string.edit_lyrics),
onClick = { onClick = {
menuState.hide() menuState.hide()
isEditing = true isEditing = true
@ -354,7 +361,7 @@ fun Lyrics(
MenuEntry( MenuEntry(
icon = R.drawable.search, icon = R.drawable.search,
text = "Search lyrics online", text = stringResource(R.string.search_lyrics_online),
onClick = { onClick = {
menuState.hide() menuState.hide()
val mediaMetadata = mediaMetadataProvider() val mediaMetadata = mediaMetadataProvider()
@ -376,7 +383,7 @@ fun Lyrics(
MenuEntry( MenuEntry(
icon = R.drawable.download, icon = R.drawable.download,
text = "Fetch lyrics again", text = stringResource(R.string.fetch_lyrics_again),
enabled = lyrics != null, enabled = lyrics != null,
onClick = { onClick = {
menuState.hide() menuState.hide()

View file

@ -48,6 +48,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import androidx.media3.common.Player import androidx.media3.common.Player
@ -331,7 +332,7 @@ fun Queue(
.height(64.dp) .height(64.dp)
) { ) {
BasicText( BasicText(
text = "${windows.size} songs", text = "${windows.size} " + stringResource(R.string.songs),
style = typography.xxs.medium, style = typography.xxs.medium,
modifier = Modifier modifier = Modifier
.background( .background(
@ -361,7 +362,7 @@ fun Queue(
.animateContentSize() .animateContentSize()
) { ) {
BasicText( BasicText(
text = "Queue loop ", text = stringResource(R.string.queue_loop),
style = typography.xxs.medium, style = typography.xxs.medium,
) )

View file

@ -24,6 +24,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.media3.datasource.cache.Cache import androidx.media3.datasource.cache.Cache
import androidx.media3.datasource.cache.CacheSpan import androidx.media3.datasource.cache.CacheSpan
@ -32,6 +33,7 @@ import it.vfsfitvnm.innertube.models.bodies.PlayerBody
import it.vfsfitvnm.innertube.requests.player import it.vfsfitvnm.innertube.requests.player
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.models.Format import it.vfsfitvnm.vimusic.models.Format
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.onOverlay import it.vfsfitvnm.vimusic.ui.styling.onOverlay
@ -140,27 +142,27 @@ fun StatsForNerds(
) { ) {
Column(horizontalAlignment = Alignment.End) { Column(horizontalAlignment = Alignment.End) {
BasicText( BasicText(
text = "Id", text = stringResource(R.string.id),
style = typography.xs.medium.color(colorPalette.onOverlay) style = typography.xs.medium.color(colorPalette.onOverlay)
) )
BasicText( BasicText(
text = "Itag", text = stringResource(R.string.itag),
style = typography.xs.medium.color(colorPalette.onOverlay) style = typography.xs.medium.color(colorPalette.onOverlay)
) )
BasicText( BasicText(
text = "Bitrate", text = stringResource(R.string.bitrate),
style = typography.xs.medium.color(colorPalette.onOverlay) style = typography.xs.medium.color(colorPalette.onOverlay)
) )
BasicText( BasicText(
text = "Size", text = stringResource(R.string.size),
style = typography.xs.medium.color(colorPalette.onOverlay) style = typography.xs.medium.color(colorPalette.onOverlay)
) )
BasicText( BasicText(
text = "Cached", text = stringResource(R.string.cached),
style = typography.xs.medium.color(colorPalette.onOverlay) style = typography.xs.medium.color(colorPalette.onOverlay)
) )
BasicText( BasicText(
text = "Loudness", text = stringResource(R.string.loudness),
style = typography.xs.medium.color(colorPalette.onOverlay) style = typography.xs.medium.color(colorPalette.onOverlay)
) )
} }

View file

@ -4,6 +4,7 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.saveable.rememberSaveableStateHolder import androidx.compose.runtime.saveable.rememberSaveableStateHolder
import androidx.compose.ui.res.stringResource
import it.vfsfitvnm.compose.persist.PersistMapCleanup import it.vfsfitvnm.compose.persist.PersistMapCleanup
import it.vfsfitvnm.compose.routing.RouteHandler import it.vfsfitvnm.compose.routing.RouteHandler
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
@ -27,7 +28,7 @@ fun PlaylistScreen(browseId: String) {
tabIndex = 0, tabIndex = 0,
onTabChanged = { }, onTabChanged = { },
tabColumnContent = { Item -> tabColumnContent = { Item ->
Item(0, "Songs", R.drawable.musical_notes) Item(0, stringResource(R.string.songs), R.drawable.musical_notes)
} }
) { currentTabIndex -> ) { currentTabIndex ->
saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { saveableStateHolder.SaveableStateProvider(key = currentTabIndex) {

View file

@ -24,6 +24,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.valentinilk.shimmer.shimmer import com.valentinilk.shimmer.shimmer
import it.vfsfitvnm.compose.persist.persist import it.vfsfitvnm.compose.persist.persist
import it.vfsfitvnm.innertube.Innertube import it.vfsfitvnm.innertube.Innertube
@ -92,7 +93,7 @@ fun PlaylistSongList(
if (isImportingPlaylist) { if (isImportingPlaylist) {
TextFieldDialog( TextFieldDialog(
hintText = "Enter the playlist name", hintText = stringResource(R.string.enter_the_playlist_name),
initialTextInput = playlistPage?.title ?: "", initialTextInput = playlistPage?.title ?: "",
onDismiss = { isImportingPlaylist = false }, onDismiss = { isImportingPlaylist = false },
onDone = { text -> onDone = { text ->
@ -125,7 +126,7 @@ fun PlaylistSongList(
} else { } else {
Header(title = playlistPage?.title ?: "Unknown") { Header(title = playlistPage?.title ?: "Unknown") {
SecondaryTextButton( SecondaryTextButton(
text = "Enqueue", text = stringResource(R.string.enqueue),
enabled = playlistPage?.songsPage?.items?.isNotEmpty() == true, enabled = playlistPage?.songsPage?.items?.isNotEmpty() == true,
onClick = { onClick = {
playlistPage?.songsPage?.items?.map(Innertube.SongItem::asMediaItem)?.let { mediaItems -> playlistPage?.songsPage?.items?.map(Innertube.SongItem::asMediaItem)?.let { mediaItems ->

View file

@ -37,6 +37,7 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
@ -172,7 +173,7 @@ fun OnlineSearch(
if (textFieldValue.text.isNotEmpty()) { if (textFieldValue.text.isNotEmpty()) {
SecondaryTextButton( SecondaryTextButton(
text = "Clear", text = stringResource(R.string.clear),
onClick = { onTextFieldValueChanged(TextFieldValue()) } onClick = { onTextFieldValueChanged(TextFieldValue()) }
) )
} }
@ -304,7 +305,7 @@ fun OnlineSearch(
.fillMaxSize() .fillMaxSize()
) { ) {
BasicText( BasicText(
text = "An error has occurred.", text = stringResource(R.string.error),
style = typography.s.secondary.center, style = typography.s.secondary.center,
modifier = Modifier modifier = Modifier
.align(Alignment.Center) .align(Alignment.Center)

View file

@ -14,6 +14,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.saveable.rememberSaveableStateHolder import androidx.compose.runtime.saveable.rememberSaveableStateHolder
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import it.vfsfitvnm.compose.persist.PersistMapCleanup import it.vfsfitvnm.compose.persist.PersistMapCleanup
@ -66,7 +67,7 @@ fun SearchScreen(
.align(Alignment.CenterEnd) .align(Alignment.CenterEnd)
) { ) {
BasicText( BasicText(
text = "Enter a name", text = stringResource(R.string.enter_a_name),
maxLines = 1, maxLines = 1,
style = LocalAppearance.current.typography.xxl.secondary style = LocalAppearance.current.typography.xxl.secondary
) )
@ -82,8 +83,8 @@ fun SearchScreen(
tabIndex = tabIndex, tabIndex = tabIndex,
onTabChanged = onTabChanged, onTabChanged = onTabChanged,
tabColumnContent = { Item -> tabColumnContent = { Item ->
Item(0, "Online", R.drawable.globe) Item(0, stringResource(R.string.online), R.drawable.globe)
Item(1, "Library", R.drawable.library) Item(1, stringResource(R.string.library), R.drawable.library)
} }
) { currentTabIndex -> ) { currentTabIndex ->
saveableStateHolder.SaveableStateProvider(currentTabIndex) { saveableStateHolder.SaveableStateProvider(currentTabIndex) {

View file

@ -10,6 +10,7 @@ import androidx.compose.runtime.saveable.rememberSaveableStateHolder
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.compose.persist.PersistMapCleanup import it.vfsfitvnm.compose.persist.PersistMapCleanup
import it.vfsfitvnm.compose.persist.persistMap import it.vfsfitvnm.compose.persist.persistMap
@ -75,7 +76,7 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
) )
} }
val emptyItemsText = "No results found. Please try a different query or category" val emptyItemsText = stringResource(R.string.no_results_found)
Scaffold( Scaffold(
topIconButtonId = R.drawable.chevron_back, topIconButtonId = R.drawable.chevron_back,
@ -83,12 +84,12 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
tabIndex = tabIndex, tabIndex = tabIndex,
onTabChanged = onTabIndexChanges, onTabChanged = onTabIndexChanges,
tabColumnContent = { Item -> tabColumnContent = { Item ->
Item(0, "Songs", R.drawable.musical_notes) Item(0, stringResource(R.string.songs), R.drawable.musical_notes)
Item(1, "Albums", R.drawable.disc) Item(1, stringResource(R.string.albums), R.drawable.disc)
Item(2, "Artists", R.drawable.person) Item(2, stringResource(R.string.artists), R.drawable.person)
Item(3, "Videos", R.drawable.film) Item(3, stringResource(R.string.videos), R.drawable.film)
Item(4, "Playlists", R.drawable.playlist) Item(4, stringResource(R.string.playlists), R.drawable.playlist)
Item(5, "Featured", R.drawable.playlist) Item(5, stringResource(R.string.featured), R.drawable.playlist)
} }
) { tabIndex -> ) { tabIndex ->
saveableStateHolder.SaveableStateProvider(tabIndex) { saveableStateHolder.SaveableStateProvider(tabIndex) {

View file

@ -14,8 +14,10 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import it.vfsfitvnm.vimusic.BuildConfig import it.vfsfitvnm.vimusic.BuildConfig
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.components.themed.Header import it.vfsfitvnm.vimusic.ui.components.themed.Header
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.secondary import it.vfsfitvnm.vimusic.utils.secondary
@ -48,7 +50,7 @@ fun About() {
SettingsEntry( SettingsEntry(
title = "GitHub", title = "GitHub",
text = "View the source code", text = stringResource(R.string.view_the_source_code),
onClick = { onClick = {
uriHandler.openUri("https://github.com/vfsfitvnm/ViMusic") uriHandler.openUri("https://github.com/vfsfitvnm/ViMusic")
} }
@ -59,16 +61,16 @@ fun About() {
SettingsEntryGroupText(title = "TROUBLESHOOTING") SettingsEntryGroupText(title = "TROUBLESHOOTING")
SettingsEntry( SettingsEntry(
title = "Report an issue", title = stringResource(R.string.report_an_issue),
text = "You will be redirected to GitHub", text = stringResource(R.string.you_will_be_redirected_to_github),
onClick = { onClick = {
uriHandler.openUri("https://github.com/vfsfitvnm/ViMusic/issues/new?assignees=&labels=bug&template=bug_report.yaml") uriHandler.openUri("https://github.com/vfsfitvnm/ViMusic/issues/new?assignees=&labels=bug&template=bug_report.yaml")
} }
) )
SettingsEntry( SettingsEntry(
title = "Request a feature or suggest an idea", title = stringResource(R.string.request_a_feature_or_suggest_an_idea),
text = "You will be redirected to GitHub", text = stringResource(R.string.you_will_be_redirected_to_github),
onClick = { onClick = {
uriHandler.openUri("https://github.com/vfsfitvnm/ViMusic/issues/new?assignees=&labels=enhancement&template=feature_request.yaml") uriHandler.openUri("https://github.com/vfsfitvnm/ViMusic/issues/new?assignees=&labels=enhancement&template=feature_request.yaml")
} }

View file

@ -17,8 +17,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.ColorPaletteMode import it.vfsfitvnm.vimusic.enums.ColorPaletteMode
import it.vfsfitvnm.vimusic.enums.ColorPaletteName import it.vfsfitvnm.vimusic.enums.ColorPaletteName
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
@ -62,18 +64,18 @@ fun AppearanceSettings() {
.asPaddingValues() .asPaddingValues()
) )
) { ) {
Header(title = "Appearance") Header(title = stringResource(R.string.appearance))
SettingsEntryGroupText(title = "COLORS") SettingsEntryGroupText(title = stringResource(R.string.colors))
EnumValueSelectorSettingsEntry( EnumValueSelectorSettingsEntry(
title = "Theme", title = stringResource(R.string.theme),
selectedValue = colorPaletteName, selectedValue = colorPaletteName,
onValueSelected = { colorPaletteName = it } onValueSelected = { colorPaletteName = it }
) )
EnumValueSelectorSettingsEntry( EnumValueSelectorSettingsEntry(
title = "Theme mode", title = stringResource(R.string.theme_mode),
selectedValue = colorPaletteMode, selectedValue = colorPaletteMode,
isEnabled = colorPaletteName != ColorPaletteName.PureBlack, isEnabled = colorPaletteName != ColorPaletteName.PureBlack,
onValueSelected = { colorPaletteMode = it } onValueSelected = { colorPaletteMode = it }
@ -81,17 +83,24 @@ fun AppearanceSettings() {
SettingsGroupSpacer() SettingsGroupSpacer()
SettingsEntryGroupText(title = "SHAPES") SettingsEntryGroupText(title = stringResource(R.string.shapes))
EnumValueSelectorSettingsEntry( EnumValueSelectorSettingsEntry(
title = "Thumbnail roundness", title = stringResource(R.string.thumbnail_roundness),
selectedValue = thumbnailRoundness, selectedValue = thumbnailRoundness,
onValueSelected = { thumbnailRoundness = it }, onValueSelected = { thumbnailRoundness = it },
trailingContent = { trailingContent = {
Spacer( Spacer(
modifier = Modifier modifier = Modifier
.border(width = 1.dp, color = colorPalette.accent, shape = thumbnailRoundness.shape()) .border(
.background(color = colorPalette.background1, shape = thumbnailRoundness.shape()) width = 1.dp,
color = colorPalette.accent,
shape = thumbnailRoundness.shape()
)
.background(
color = colorPalette.background1,
shape = thumbnailRoundness.shape()
)
.size(36.dp) .size(36.dp)
) )
} }
@ -99,18 +108,18 @@ fun AppearanceSettings() {
SettingsGroupSpacer() SettingsGroupSpacer()
SettingsEntryGroupText(title = "TEXT") SettingsEntryGroupText(title = stringResource(R.string.text))
SwitchSettingEntry( SwitchSettingEntry(
title = "Use system font", title = stringResource(R.string.use_system_font),
text = "Use the font applied by the system", text = stringResource(R.string.use_font_by_the_system),
isChecked = useSystemFont, isChecked = useSystemFont,
onCheckedChange = { useSystemFont = it } onCheckedChange = { useSystemFont = it }
) )
SwitchSettingEntry( SwitchSettingEntry(
title = "Apply font padding", title = stringResource(R.string.apply_font_padding),
text = "Add spacing around texts", text = stringResource(R.string.add_spacing_around_texts),
isChecked = applyFontPadding, isChecked = applyFontPadding,
onCheckedChange = { applyFontPadding = it } onCheckedChange = { applyFontPadding = it }
) )
@ -118,11 +127,11 @@ fun AppearanceSettings() {
if (!isAtLeastAndroid13) { if (!isAtLeastAndroid13) {
SettingsGroupSpacer() SettingsGroupSpacer()
SettingsEntryGroupText(title = "LOCKSCREEN") SettingsEntryGroupText(title = stringResource(R.string.lockscreen))
SwitchSettingEntry( SwitchSettingEntry(
title = "Show song cover", title = stringResource(R.string.show_song_cover),
text = "Use the playing song cover as the lockscreen wallpaper", text = stringResource(R.string.use_song_cover_on_lockscreen),
isChecked = isShowingThumbnailInLockscreen, isChecked = isShowingThumbnailInLockscreen,
onCheckedChange = { isShowingThumbnailInLockscreen = it } onCheckedChange = { isShowingThumbnailInLockscreen = it }
) )

View file

@ -18,10 +18,12 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import coil.Coil import coil.Coil
import coil.annotation.ExperimentalCoilApi import coil.annotation.ExperimentalCoilApi
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.CoilDiskCacheMaxSize import it.vfsfitvnm.vimusic.enums.CoilDiskCacheMaxSize
import it.vfsfitvnm.vimusic.enums.ExoPlayerDiskCacheMaxSize import it.vfsfitvnm.vimusic.enums.ExoPlayerDiskCacheMaxSize
import it.vfsfitvnm.vimusic.ui.components.themed.Header import it.vfsfitvnm.vimusic.ui.components.themed.Header
@ -58,9 +60,9 @@ fun CacheSettings() {
.asPaddingValues() .asPaddingValues()
) )
) { ) {
Header(title = "Cache") Header(title = stringResource(R.string.cache))
SettingsDescription(text = "When the cache runs out of space, the resources that haven't been accessed for the longest time are cleared") SettingsDescription(text = stringResource(R.string.cache_cleared))
Coil.imageLoader(context).diskCache?.let { diskCache -> Coil.imageLoader(context).diskCache?.let { diskCache ->
val diskCacheSize = remember(diskCache) { val diskCacheSize = remember(diskCache) {
@ -69,7 +71,7 @@ fun CacheSettings() {
SettingsGroupSpacer() SettingsGroupSpacer()
SettingsEntryGroupText(title = "IMAGE CACHE") SettingsEntryGroupText(title = stringResource(R.string.image_cache))
SettingsDescription( SettingsDescription(
text = "${ text = "${
@ -81,7 +83,7 @@ fun CacheSettings() {
) )
EnumValueSelectorSettingsEntry( EnumValueSelectorSettingsEntry(
title = "Max size", title = stringResource(R.string.max_size),
selectedValue = coilDiskCacheMaxSize, selectedValue = coilDiskCacheMaxSize,
onValueSelected = { coilDiskCacheMaxSize = it } onValueSelected = { coilDiskCacheMaxSize = it }
) )
@ -96,7 +98,7 @@ fun CacheSettings() {
SettingsGroupSpacer() SettingsGroupSpacer()
SettingsEntryGroupText(title = "SONG CACHE") SettingsEntryGroupText(title = stringResource(R.string.song_cache))
SettingsDescription( SettingsDescription(
text = buildString { text = buildString {
@ -110,7 +112,7 @@ fun CacheSettings() {
) )
EnumValueSelectorSettingsEntry( EnumValueSelectorSettingsEntry(
title = "Max size", title = stringResource(R.string.max_size),
selectedValue = exoPlayerDiskCacheMaxSize, selectedValue = exoPlayerDiskCacheMaxSize,
onValueSelected = { exoPlayerDiskCacheMaxSize = it } onValueSelected = { exoPlayerDiskCacheMaxSize = it }
) )

View file

@ -20,8 +20,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.internal import it.vfsfitvnm.vimusic.internal
import it.vfsfitvnm.vimusic.path import it.vfsfitvnm.vimusic.path
import it.vfsfitvnm.vimusic.query import it.vfsfitvnm.vimusic.query
@ -94,16 +96,16 @@ fun DatabaseSettings() {
.asPaddingValues() .asPaddingValues()
) )
) { ) {
Header(title = "Database") Header(title = stringResource(R.string.database))
SettingsEntryGroupText(title = "CLEANUP") SettingsEntryGroupText(title = stringResource(R.string.cleanup))
SettingsEntry( SettingsEntry(
title = "Reset quick picks", title = stringResource(R.string.reset_quick_picks),
text = if (eventsCount > 0) { text = if (eventsCount > 0) {
"Delete $eventsCount playback events" stringResource(R.string.delete_playback_events, eventsCount)
} else { } else {
"Quick picks are cleared" stringResource(R.string.quick_picks_are_cleared)
}, },
isEnabled = eventsCount > 0, isEnabled = eventsCount > 0,
onClick = { query(Database::clearEvents) } onClick = { query(Database::clearEvents) }
@ -111,13 +113,13 @@ fun DatabaseSettings() {
SettingsGroupSpacer() SettingsGroupSpacer()
SettingsEntryGroupText(title = "BACKUP") SettingsEntryGroupText(title = stringResource(R.string.backup))
SettingsDescription(text = "Personal preferences (i.e. the theme mode) and the cache are excluded.") SettingsDescription(text = stringResource(R.string.personal_preference))
SettingsEntry( SettingsEntry(
title = "Backup", title = stringResource(R.string.backup_1),
text = "Export the database to the external storage", text = stringResource(R.string.export_the_database),
onClick = { onClick = {
@SuppressLint("SimpleDateFormat") @SuppressLint("SimpleDateFormat")
val dateFormat = SimpleDateFormat("yyyyMMddHHmmss") val dateFormat = SimpleDateFormat("yyyyMMddHHmmss")
@ -132,13 +134,16 @@ fun DatabaseSettings() {
SettingsGroupSpacer() SettingsGroupSpacer()
SettingsEntryGroupText(title = "RESTORE") SettingsEntryGroupText(title = stringResource(R.string.restore))
ImportantSettingsDescription(text = "Existing data will be overwritten.\n${context.applicationInfo.nonLocalizedLabel} will automatically close itself after restoring the database.") ImportantSettingsDescription(text = stringResource(
R.string.existing_data_will_be_overwritten,
context.applicationInfo.nonLocalizedLabel
))
SettingsEntry( SettingsEntry(
title = "Restore", title = stringResource(R.string.restore_1),
text = "Import the database from the external storage", text = stringResource(R.string.import_the_database),
onClick = { onClick = {
try { try {
restoreLauncher.launch( restoreLauncher.launch(

View file

@ -28,8 +28,10 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.query import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.service.PlayerMediaBrowserService import it.vfsfitvnm.vimusic.service.PlayerMediaBrowserService
import it.vfsfitvnm.vimusic.ui.components.themed.Header import it.vfsfitvnm.vimusic.ui.components.themed.Header
@ -98,36 +100,36 @@ fun OtherSettings() {
.asPaddingValues() .asPaddingValues()
) )
) { ) {
Header(title = "Other") Header(title = stringResource(R.string.other))
SettingsEntryGroupText(title = "ANDROID AUTO") SettingsEntryGroupText(title = stringResource(R.string.android_auto))
SettingsDescription(text = "Remember to enable \"Unknown sources\" in the Developer Settings of Android Auto.") SettingsDescription(text = stringResource(R.string.enable_unknown_sources))
SwitchSettingEntry( SwitchSettingEntry(
title = "Android Auto", title = stringResource(R.string.android_auto_1),
text = "Enable Android Auto support", text = stringResource(R.string.enable_android_auto_support),
isChecked = isAndroidAutoEnabled, isChecked = isAndroidAutoEnabled,
onCheckedChange = { isAndroidAutoEnabled = it } onCheckedChange = { isAndroidAutoEnabled = it }
) )
SettingsGroupSpacer() SettingsGroupSpacer()
SettingsEntryGroupText(title = "SEARCH HISTORY") SettingsEntryGroupText(title = stringResource(R.string.search_history))
SwitchSettingEntry( SwitchSettingEntry(
title = "Pause search history", title = stringResource(R.string.pause_search_history),
text = "Neither save new searched queries nor show history", text = stringResource(R.string.neither_save_new_searched_query),
isChecked = pauseSearchHistory, isChecked = pauseSearchHistory,
onCheckedChange = { pauseSearchHistory = it } onCheckedChange = { pauseSearchHistory = it }
) )
SettingsEntry( SettingsEntry(
title = "Clear search history", title = stringResource(R.string.clear_search_history),
text = if (queriesCount > 0) { text = if (queriesCount > 0) {
"Delete $queriesCount search queries" "Delete $queriesCount search queries"
} else { } else {
"History is empty" stringResource(R.string.history_is_empty)
}, },
isEnabled = queriesCount > 0, isEnabled = queriesCount > 0,
onClick = { query(Database::clearQueries) } onClick = { query(Database::clearQueries) }
@ -135,21 +137,21 @@ fun OtherSettings() {
SettingsGroupSpacer() SettingsGroupSpacer()
SettingsEntryGroupText(title = "SERVICE LIFETIME") SettingsEntryGroupText(title = stringResource(R.string.service_lifetime))
ImportantSettingsDescription(text = "If battery optimizations are applied, the playback notification can suddenly disappear when paused.") ImportantSettingsDescription(text = stringResource(R.string.battery_optimizations_applied))
if (isAtLeastAndroid12) { if (isAtLeastAndroid12) {
SettingsDescription(text = "Since Android 12, disabling battery optimizations is required for the \"Invincible service\" option to take effect.") SettingsDescription(text = stringResource(R.string.is_android12))
} }
SettingsEntry( SettingsEntry(
title = "Ignore battery optimizations", title = stringResource(R.string.ignore_battery_optimizations),
isEnabled = !isIgnoringBatteryOptimizations, isEnabled = !isIgnoringBatteryOptimizations,
text = if (isIgnoringBatteryOptimizations) { text = if (isIgnoringBatteryOptimizations) {
"Already unrestricted" stringResource(R.string.already_unrestricted)
} else { } else {
"Disable background restrictions" stringResource(R.string.disable_background_restrictions)
}, },
onClick = { onClick = {
if (!isAtLeastAndroid6) return@SettingsEntry if (!isAtLeastAndroid6) return@SettingsEntry
@ -173,8 +175,8 @@ fun OtherSettings() {
) )
SwitchSettingEntry( SwitchSettingEntry(
title = "Invincible service", title = stringResource(R.string.invincible_service),
text = "When turning off battery optimizations is not enough", text = stringResource(R.string.turning_off_battery_optimizations_is_not_enough),
isChecked = isInvincibilityEnabled, isChecked = isInvincibilityEnabled,
onCheckedChange = { isInvincibilityEnabled = it } onCheckedChange = { isInvincibilityEnabled = it }
) )

View file

@ -20,8 +20,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.components.themed.Header import it.vfsfitvnm.vimusic.ui.components.themed.Header
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.isAtLeastAndroid6 import it.vfsfitvnm.vimusic.utils.isAtLeastAndroid6
@ -61,13 +63,13 @@ fun PlayerSettings() {
.asPaddingValues() .asPaddingValues()
) )
) { ) {
Header(title = "Player & Audio") Header(title = stringResource(R.string.player_audio))
SettingsEntryGroupText(title = "PLAYER") SettingsEntryGroupText(title = stringResource(R.string.player))
SwitchSettingEntry( SwitchSettingEntry(
title = "Persistent queue", title = stringResource(R.string.persistent_queue),
text = "Save and restore playing songs", text = stringResource(R.string.save_and_restore_playing_songs),
isChecked = persistentQueue, isChecked = persistentQueue,
onCheckedChange = { onCheckedChange = {
persistentQueue = it persistentQueue = it
@ -76,8 +78,8 @@ fun PlayerSettings() {
if (isAtLeastAndroid6) { if (isAtLeastAndroid6) {
SwitchSettingEntry( SwitchSettingEntry(
title = "Resume playback", title = stringResource(R.string.resume_playback),
text = "When a wired or bluetooth device is connected", text = stringResource(R.string.when_device_is_connected),
isChecked = resumePlaybackWhenDeviceConnected, isChecked = resumePlaybackWhenDeviceConnected,
onCheckedChange = { onCheckedChange = {
resumePlaybackWhenDeviceConnected = it resumePlaybackWhenDeviceConnected = it
@ -87,11 +89,11 @@ fun PlayerSettings() {
SettingsGroupSpacer() SettingsGroupSpacer()
SettingsEntryGroupText(title = "AUDIO") SettingsEntryGroupText(title = stringResource(R.string.audio))
SwitchSettingEntry( SwitchSettingEntry(
title = "Skip silence", title = stringResource(R.string.skip_silence),
text = "Skip silent parts during playback", text = stringResource(R.string.skip_silent_parts_during_playback),
isChecked = skipSilence, isChecked = skipSilence,
onCheckedChange = { onCheckedChange = {
skipSilence = it skipSilence = it
@ -99,8 +101,8 @@ fun PlayerSettings() {
) )
SwitchSettingEntry( SwitchSettingEntry(
title = "Loudness normalization", title = stringResource(R.string.loudness_normalization),
text = "Adjust the volume to a fixed level", text = stringResource(R.string.autoadjust_the_volume),
isChecked = volumeNormalization, isChecked = volumeNormalization,
onCheckedChange = { onCheckedChange = {
volumeNormalization = it volumeNormalization = it
@ -108,8 +110,8 @@ fun PlayerSettings() {
) )
SettingsEntry( SettingsEntry(
title = "Equalizer", title = stringResource(R.string.equalizer),
text = "Interact with the system equalizer", text = stringResource(R.string.interact_with_the_system_equalizer),
onClick = { onClick = {
val intent = Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL).apply { val intent = Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL).apply {
putExtra(AudioEffect.EXTRA_AUDIO_SESSION, binder?.player?.audioSessionId) putExtra(AudioEffect.EXTRA_AUDIO_SESSION, binder?.player?.audioSessionId)

View file

@ -21,6 +21,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.compose.routing.RouteHandler import it.vfsfitvnm.compose.routing.RouteHandler
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
@ -53,12 +54,12 @@ fun SettingsScreen() {
tabIndex = tabIndex, tabIndex = tabIndex,
onTabChanged = onTabChanged, onTabChanged = onTabChanged,
tabColumnContent = { Item -> tabColumnContent = { Item ->
Item(0, "Appearance", R.drawable.color_palette) Item(0, stringResource(R.string.appearance), R.drawable.color_palette)
Item(1, "Player", R.drawable.play) Item(1, stringResource(R.string.player), R.drawable.play)
Item(2, "Cache", R.drawable.server) Item(2, stringResource(R.string.cache), R.drawable.server)
Item(3, "Database", R.drawable.server) Item(3, stringResource(R.string.database), R.drawable.server)
Item(4, "Other", R.drawable.shapes) Item(4, stringResource(R.string.other), R.drawable.shapes)
Item(5, "About", R.drawable.information) Item(5, stringResource(R.string.about), R.drawable.information)
} }
) { currentTabIndex -> ) { currentTabIndex ->
saveableStateHolder.SaveableStateProvider(currentTabIndex) { saveableStateHolder.SaveableStateProvider(currentTabIndex) {

View file

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="cancel">Stornieren</string>
<string name="done">Fertig</string>
<string name="hidesong">Möchten Sie diesen Song wirklich verstecken? Die Wiedergabezeit und der \nCache werden gelöscht.</string>
<string name="new_playlist">Neue playlist</string>
<string name="start_radio">Radio hören</string>
<string name="equalizer">Equalizer</string>
<string name="stop_sleep_timer">Möchten Sie den Schlaf-Timer stoppen?</string>
<string name="set_sleep_timer">Einstellen des Sleep Timers</string>
<string name="add_to_playlist">Zu Playlist hinzufügen</string>
<string name="go_to_album">Zum Album gehen</string>
<string name="remove_from_queue">Aus der playlist</string>
<string name="remove_from_playlist">Aus der Playlist entfernen</string>
<string name="hide">Verstecken</string>
<string name="hide_from_quick_picks">Ausblenden von \"Quick picks\"</string>
<string name="related_albums">Ähnliche Alben</string>
<string name="similar_artists">Ähnliche Künstler</string>
<string name="playlists_you_might_like">Playlists, die dir gefallen könnten</string>
<string name="an_error_has_occurred">Es ist ein Fehler aufgetreten</string>
<string name="albums">Alben</string>
<string name="artists">Künstlern</string>
<string name="enter_the_playlist_name">Geben Namen playlist</string>
<string name="playlists">Playlists</string>
<string name="favorites">Favoriten</string>
<string name="offline">Offline</string>
<string name="quick_picks">Quick picks</string>
<string name="songs">Lieder</string>
<string name="other_versions">Anderen Versionen</string>
<string name="album_no_alternative_version">Dieses Album hat keine alternative Version</string>
<string name="enqueue">Enqueue</string>
<string name="shuffle">Shuffle</string>
<string name="view_all">Anzeigen aller</string>
<string name="from_wikipedia_cca">Von Wikipedia unter Creative Commons Attribution CC-BY-SA 3.0</string>
<string name="overview">Übersicht</string>
<string name="singles">Einzel</string>
<string name="library">Library</string>
<string name="artist_no_release_album">Dieser Künstler hat kein Album veröffentlicht</string>
<string name="artist_no_release_single">Dieser Künstler hat keine Single veröffentlicht</string>
<string name="delete_playlist">Möchtest du diese Playlist wirklich löschen?</string>
<string name="sync">Synchronisieren</string>
<string name="rename">Umbenennen</string>
<string name="delete">löschen</string>
<string name="enter_the_lyrics">Geben Sie die lyrics</string>
<string name="edit_lyrics">Bearbeiten lyrics</string>
<string name="search_lyrics_online">Suche lyrics online</string>
<string name="fetch_lyrics_again">Wieder holen lyrics</string>
<string name="queue_loop">"Reihe Looping "</string>
<string name="id">Id</string>
<string name="itag">Itag</string>
<string name="bitrate">Bitrate</string>
<string name="size">Size</string>
<string name="cached">Cached</string>
<string name="loudness">Volumen</string>
<string name="clear">Klar</string>
<string name="error">Ein Fehler aufgetreten.</string>
<string name="enter_a_name">Suche</string>
<string name="online">Online</string>
<string name="no_results_found">Keine Ergebnisse gefunden. Bitte versuchen Sie eine andere Suchanfrage oder Kategorie</string>
<string name="videos">Videos</string>
<string name="featured">Vorgestellt</string>
<string name="view_the_source_code">Den Quellcode anzeigen</string>
<string name="report_an_issue">Ein Problem melden</string>
<string name="you_will_be_redirected_to_github">Sie werden weitergeleitet zu GitHub</string>
<string name="request_a_feature_or_suggest_an_idea">Fordern Sie ein Feature an oder schlagen Sie eine Idee vor</string>
<string name="appearance">Aussehen</string>
<string name="colors">FARBEN</string>
<string name="theme">Theme</string>
<string name="theme_mode">Themenmodus</string>
<string name="shapes">FORMEN</string>
<string name="thumbnail_roundness">Thumbnail Rundheit</string>
<string name="text">TEXT</string>
<string name="use_system_font">Systemschrift verwenden</string>
<string name="use_font_by_the_system">Verwenden Sie die vom System angewendete Schriftart</string>
<string name="apply_font_padding">Anwenden von Font Padding</string>
<string name="add_spacing_around_texts">Hinzufügen von Abständen um Texte</string>
<string name="lockscreen">SPERRBILDSCHIRM</string>
<string name="show_song_cover">Show song cover</string>
<string name="use_song_cover_on_lockscreen">Verwenden Sie die Titelabdeckung als Sperrbildschirmhintergrund</string>
<string name="cache">Cache</string>
<string name="cache_cleared">Wenn der Cache keinen Speicherplatz mehr hat, werden die Ressourcen, auf die am längsten nicht zugegriffen wurde, gelöscht</string>
<string name="image_cache">IMAGE CACHE</string>
<string name="max_size">Max Größe</string>
<string name="song_cache">SONG CACHE</string>
<string name="database">Database</string>
<string name="reset_quick_picks">Zurücksetzen quick picks</string>
<string name="quick_picks_are_cleared">Quick picks werden gelöscht</string>
<string name="backup">BACKUP</string>
<string name="personal_preference">Persönliche Einstellungen (z.B. der Theme-Modus) und der Cache sind ausgeschlossen.</string>
<string name="backup_1">Backup</string>
<string name="export_the_database">Exportieren der Datenbank in den externen Speicher</string>
<string name="restore">RESTORE</string>
<string name="restore_1">Restore</string>
<string name="import_the_database">Importieren der Datenbank aus dem externen Speicher</string>
<string name="other">Ander</string>
<string name="android_auto">ANDROID AUTO</string>
<string name="enable_unknown_sources">Denken Sie daran, \"Unbekannte Quellen\" in den Entwicklereinstellungen von Android Auto zu aktivieren.</string>
<string name="android_auto_1">Android Auto</string>
<string name="enable_android_auto_support">Unterstützung für Android Auto aktivieren</string>
<string name="search_history">SUCHVERLAUF</string>
<string name="pause_search_history">Anhalten des Suchverlaufs</string>
<string name="neither_save_new_searched_query">Keine neuen Suchabfragen speichern oder Verlauf anzeigen</string>
<string name="clear_search_history">Suchverlauf löschen</string>
<string name="history_is_empty">Geschichte ist leer</string>
<string name="service_lifetime">SERVICE LIFETIME</string>
<string name="battery_optimizations_applied">Wenn Batterieoptimierungen angewendet werden, kann die Wiedergabebenachrichtigung plötzlich verschwinden, wenn sie angehalten wird.</string>
<string name="is_android12">Seit Android 12 ist das Deaktivieren von Batterieoptimierungen erforderlich, damit die Option \"Unbesiegbarer Dienst\" wirksam wird.</string>
<string name="ignore_battery_optimizations">Ignorieren von Batterieoptimierungen</string>
<string name="already_unrestricted">Schon uneingeschränkt</string>
<string name="disable_background_restrictions">Deaktivieren von Einschränkungen im Hintergrund</string>
<string name="invincible_service">Invincible service</string>
<string name="turning_off_battery_optimizations_is_not_enough">Beim Ausschalten der Batterie Optimierungen ist nicht genug</string>
<string name="player_audio"><![CDATA[Player & Audio]]></string>
<string name="player">PLAYER</string>
<string name="persistent_queue">Persistente Warteschlange</string>
<string name="save_and_restore_playing_songs">Speichern und Wiederherstellen der Wiedergabe von Songs</string>
<string name="resume_playback">Wiedergabe fortzusetzen</string>
<string name="when_device_is_connected">Wenn ein kabelgebundenes oder Bluetooth-Gerät verbunden ist</string>
<string name="audio">AUDIO</string>
<string name="skip_silence">Stille auslassen</string>
<string name="skip_silent_parts_during_playback">Stumme Teile während der Wiedergabe überspringen</string>
<string name="loudness_normalization">Lautheitsanpassung</string>
<string name="autoadjust_the_volume">Stellen Sie die Lautstärke auf ein festes Niveau ein</string>
<string name="interact_with_the_system_equalizer">Interagieren Sie mit dem System-Equalizer</string>
<string name="about">About</string>
<string name="more_of">Mehr von</string>
<string name="sleep_timer">Sleep timer</string>
<string name="left">%1$s übrigen</string>
<string name="set">Legen Sie</string>
<string name="play_next">Play next</string>
<string name="confirm">Bestätigen</string>
<string name="show">Zeigen</string>
<string name="unsynchronized_lyrics">unsynchronisierten lyrics</string>
<string name="synchronized_lyrics">synchronisiert lyrics</string>
<string name="provided_by">Bildmaterial von</string>
<string name="cleanup">CLEANUP</string>
<string name="delete_playback_events">Löschen %1$s Wiedergabeereignisse</string>
<string name="existing_data_will_be_overwritten">Vorhandene Daten werden überschrieben.\n%1$s schließt sich automatisch nach dem Wiederherstellen der Datenbank.</string>
</resources>

View file

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="cancel">Cancela</string>
<string name="done">Ok</string>
<string name="hidesong">¿Realmente desea ocultar esta canción? El tiempo de reproducción y la caché se borrarán. Esta acción es irreversible.</string>
<string name="new_playlist">Nueva playlist</string>
<string name="start_radio">Escuchar radio</string>
<string name="equalizer">Ecualizador</string>
<string name="stop_sleep_timer">¿Apaga el temporizador de apagado?</string>
<string name="set_sleep_timer">Temporizador apagado</string>
<string name="add_to_playlist">Añadir a la playlist</string>
<string name="go_to_album">Ver álbum</string>
<string name="remove_from_queue">Remover de la cola</string>
<string name="remove_from_playlist">Remover de la playlist</string>
<string name="hide">Esconde</string>
<string name="hide_from_quick_picks">Ocultar de \"Métodos abreviados\"</string>
<string name="related_albums">Álbumes relacionados</string>
<string name="similar_artists">Artistas similares</string>
<string name="playlists_you_might_like">Playlist que podrían gustarte</string>
<string name="an_error_has_occurred">Ha habido un error</string>
<string name="albums">Álbum</string>
<string name="artists">Artistas</string>
<string name="enter_the_playlist_name">Introduzca el nombre de la playlist</string>
<string name="playlists">Playlist</string>
<string name="favorites">Favoritos</string>
<string name="offline">Offline</string>
<string name="quick_picks">Métodos abreviados</string>
<string name="songs">Canciones</string>
<string name="other_versions">Otras versiones</string>
<string name="album_no_alternative_version">El álbum no tiene versiones alternativas</string>
<string name="enqueue">Añadir a la cola</string>
<string name="shuffle">Aleatorio</string>
<string name="view_all">Mostrar todo</string>
<string name="from_wikipedia_cca">Da Wikipedia bajo Creative Commons Atribución CC-BY-SA 3.0</string>
<string name="overview">Panorámica</string>
<string name="singles">Individuales</string>
<string name="library">Librería</string>
<string name="artist_no_release_album">El artista no lanzó ningún álbum</string>
<string name="artist_no_release_single">El artista no ha lanzado ningún individuo</string>
<string name="delete_playlist">¿Realmente quieres eliminar la playlist?</string>
<string name="sync">Sincroniza</string>
<string name="rename">Cambia el nombre</string>
<string name="delete">Elimina</string>
<string name="enter_the_lyrics">Introduzca lyrics</string>
<string name="edit_lyrics">Modifica lyrics</string>
<string name="search_lyrics_online">Busca lyrics online</string>
<string name="fetch_lyrics_again">Recupera nuevamente lyrics</string>
<string name="queue_loop">"Reproducción continua "</string>
<string name="id">Id</string>
<string name="itag">Itag</string>
<string name="bitrate">Bitrate</string>
<string name="size">Tamaño</string>
<string name="cached">Descargado</string>
<string name="loudness">Volumen</string>
<string name="clear">Limpia</string>
<string name="error">Ha habido un error.</string>
<string name="enter_a_name">Busca</string>
<string name="online">Online</string>
<string name="no_results_found">Sin resultados. Cambiar la clave de búsqueda o la categoría</string>
<string name="videos">Video</string>
<string name="featured">En primer plano</string>
<string name="view_the_source_code">Ver el código fuente</string>
<string name="report_an_issue">Reportar un error</string>
<string name="you_will_be_redirected_to_github">Serás redirigido a GitHub</string>
<string name="request_a_feature_or_suggest_an_idea">Solicitar una función o sugerir una idea</string>
<string name="appearance">Apariencia</string>
<string name="colors">COLORES</string>
<string name="theme">Tema</string>
<string name="theme_mode">Modo tema</string>
<string name="shapes">FORMAS</string>
<string name="thumbnail_roundness">Miniaturas redondeadas</string>
<string name="text">TEXTO</string>
<string name="use_system_font">Usar fuentes del sistema</string>
<string name="use_font_by_the_system">Utilizar fuente aplicada por el sistema</string>
<string name="apply_font_padding">Aplicar relleno de carácter</string>
<string name="add_spacing_around_texts">Añadir espaciado en los textos</string>
<string name="lockscreen">BLOQUEO DE PANTALLA</string>
<string name="show_song_cover">Mostrar la portada de la canción</string>
<string name="use_song_cover_on_lockscreen">Use la portada de la canción que se está reproduciendo como fondo de la pantalla de bloqueo</string>
<string name="cache">Caché</string>
<string name="cache_cleared">Cuando la caché se queda sin espacio, los recursos a los que no se ha accedido durante el mayor tiempo se borran</string>
<string name="image_cache">CACHÉ IMÁGENES</string>
<string name="max_size">Tamaño máximo</string>
<string name="song_cache">CACHÉ CANCIONES</string>
<string name="database">Base de datos</string>
<string name="reset_quick_picks">Reset Métodos abreviados</string>
<string name="quick_picks_are_cleared">Métodos abreviados eliminados</string>
<string name="backup">BACKUP</string>
<string name="personal_preference">Se excluyen las preferencias personales (es decir, el modo de tema) y la caché.</string>
<string name="backup_1">Backup</string>
<string name="export_the_database">Exportar la base de datos a un medio externo</string>
<string name="restore">RESTORE</string>
<string name="restore_1">Restore</string>
<string name="import_the_database">Importar bases de datos desde un medio externo</string>
<string name="other">Otro</string>
<string name="android_auto">ANDROID AUTO</string>
<string name="enable_unknown_sources">Recuerda habilitar \"Orígenes desconocidos\" en la configuración del desarrollador de Android Auto.</string>
<string name="android_auto_1">Android Auto</string>
<string name="enable_android_auto_support">Habilitar soporte para Android Auto</string>
<string name="search_history">HISTORIAL DE BÚSQUEDA</string>
<string name="pause_search_history">Suspender el historial de búsqueda</string>
<string name="neither_save_new_searched_query">No guardar nuevas búsquedas ni mostrar el historial</string>
<string name="clear_search_history">Limpiar historial de búsqueda</string>
<string name="history_is_empty">El historial está en blanco</string>
<string name="service_lifetime">SERVICIO LIFETIME</string>
<string name="battery_optimizations_applied">Si se aplican las optimizaciones de la batería, la notificación de reproducción puede desaparecer repentinamente cuando se detiene.</string>
<string name="is_android12">A partir de Android 12, debe deshabilitar las optimizaciones de la batería para que la opción \"Servicio invencible\" surta efecto.</string>
<string name="ignore_battery_optimizations">Ignorar optimización de la batería</string>
<string name="already_unrestricted">Ya sin restricciones</string>
<string name="disable_background_restrictions">Desactivar las restricciones en segundo plano</string>
<string name="invincible_service">Invincible servicio</string>
<string name="turning_off_battery_optimizations_is_not_enough">Cuando desactivar las optimizaciones de la batería no es suficiente</string>
<string name="player_audio"><![CDATA[Player & Audio]]></string>
<string name="player">PLAYER</string>
<string name="persistent_queue">Cola persistente</string>
<string name="save_and_restore_playing_songs">Guardar y restaurar la reproducción de canciones</string>
<string name="resume_playback">Reanudar la reproducción</string>
<string name="when_device_is_connected">Cuando es collegato un dispositivo cablato o bluetooth</string>
<string name="audio">AUDIO</string>
<string name="skip_silence">Saltar el silencio</string>
<string name="skip_silent_parts_during_playback">Omitir las partes silenciosas durante la reproducción</string>
<string name="loudness_normalization">Normalización del volumen</string>
<string name="autoadjust_the_volume">Ajustar el volumen a un nivel fijo</string>
<string name="interact_with_the_system_equalizer">Interactúa con el ecualizador del sistema</string>
<string name="about">About</string>
<string name="more_of">Más sobre</string>
<string name="sleep_timer">Temporizador apagado</string>
<string name="left">%1$s restantes</string>
<string name="set">Establece</string>
<string name="play_next">Reproducir siguiente</string>
<string name="confirm">Confirma</string>
<string name="show">Muestra</string>
<string name="unsynchronized_lyrics">lyrics no sincronizadas</string>
<string name="synchronized_lyrics">lyrics sincronizadas</string>
<string name="provided_by">Proporcionado por</string>
<string name="cleanup">LIMPIEZA</string>
<string name="delete_playback_events">Eliminar %1$s eventos de reproducción</string>
<string name="existing_data_will_be_overwritten">Los datos existentes se sobrescribirán.\n%1$s se cerrará automáticamente después de restaurar la base de datos.</string>
</resources>

View file

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="cancel">Annuler</string>
<string name="done">Fait</string>
<string name="hidesong">Voulez-vous vraiment cacher cette chanson ? Son temps de lecture et son cache seront effacés.\nCette action est irréversible.</string>
<string name="new_playlist">Nouvelle playlist</string>
<string name="start_radio">Écoute la radio</string>
<string name="equalizer">Égaliseur</string>
<string name="stop_sleep_timer">Désactiver la minuterie de mise en veille?</string>
<string name="set_sleep_timer">Minuterie d\'arrêt</string>
<string name="add_to_playlist">Ajouter à la playlist</string>
<string name="go_to_album">Aller à l\'album</string>
<string name="remove_from_queue">Supprimer de la file d\'attente</string>
<string name="remove_from_playlist">Supprimer de la liste de lecture</string>
<string name="hide">Cacher</string>
<string name="hide_from_quick_picks">Cacher de \"Sélections rapides\"</string>
<string name="related_albums">Albums associés</string>
<string name="similar_artists">Artistes similaires</string>
<string name="playlists_you_might_like">Listes de lecture que vous pourriez aimer</string>
<string name="an_error_has_occurred">Une erreur est survenue</string>
<string name="albums">Albums</string>
<string name="artists">Artistes</string>
<string name="enter_the_playlist_name">Entrez le nom de la playlist</string>
<string name="playlists">Playlists</string>
<string name="favorites">Favoris</string>
<string name="offline">Offline</string>
<string name="quick_picks">Sélections rapides</string>
<string name="songs">Chansons</string>
<string name="other_versions">Autres versions</string>
<string name="album_no_alternative_version">Cet album n\'a pas de version alternative</string>
<string name="enqueue">Mettre en file d\'attente</string>
<string name="shuffle">aléatoire</string>
<string name="view_all">Voir tout</string>
<string name="from_wikipedia_cca">De Wikipedia sous Creative Commons Attribution CC-BY-SA 3.0</string>
<string name="overview">Panoramique</string>
<string name="singles">Singles</string>
<string name="library">Librairie</string>
<string name="artist_no_release_album">Cet artiste na sorti aucun album</string>
<string name="artist_no_release_single">Cet artiste na sorti aucun single</string>
<string name="delete_playlist">Voulez-vous vraiment supprimer cette playlist?</string>
<string name="sync">Synchronise</string>
<string name="rename">Renommer</string>
<string name="delete">Supprimer</string>
<string name="enter_the_lyrics">Entrer les lyrics</string>
<string name="edit_lyrics">Modifier les lyrics</string>
<string name="search_lyrics_online">Rechercher lyrics online</string>
<string name="fetch_lyrics_again">Récupérer lyrics</string>
<string name="queue_loop">"Lecture continue "</string>
<string name="id">Id</string>
<string name="itag">Itag</string>
<string name="bitrate">Bitrate</string>
<string name="size">Taille</string>
<string name="cached">Téléchargé</string>
<string name="loudness">Volume</string>
<string name="clear">Clair</string>
<string name="error">Une erreur est survenue.</string>
<string name="enter_a_name">Cherche</string>
<string name="online">Online</string>
<string name="no_results_found">Aucun résultat trouvé. Veuillez essayer une autre requête ou catégorie</string>
<string name="videos">Vidéos</string>
<string name="featured">Présenté</string>
<string name="view_the_source_code">Afficher le code source</string>
<string name="report_an_issue">Signaler un problème</string>
<string name="you_will_be_redirected_to_github">Vous allez être redirigé vers GitHub</string>
<string name="request_a_feature_or_suggest_an_idea">Demander une fonctionnalité ou suggérer une idée</string>
<string name="appearance">Appearance</string>
<string name="colors">COLORS</string>
<string name="theme">Theme</string>
<string name="theme_mode">Theme mode</string>
<string name="shapes">SHAPES</string>
<string name="thumbnail_roundness">Aspect</string>
<string name="text">TEXTE</string>
<string name="use_system_font">Utiliser la police du système</string>
<string name="use_font_by_the_system">Utilisez la police appliquée par le système</string>
<string name="apply_font_padding">Appliquer un remplissage de police</string>
<string name="add_spacing_around_texts">Add spacing around texts</string>
<string name="lockscreen">LOCKSCREEN</string>
<string name="show_song_cover">Show song cover</string>
<string name="use_song_cover_on_lockscreen">Use the playing song cover as the lockscreen wallpaper</string>
<string name="cache">Cache</string>
<string name="cache_cleared">Lorsque le cache manque despace, les ressources qui nont pas été consultées depuis le plus longtemps sont effacées</string>
<string name="image_cache">CACHE D\'IMAGE</string>
<string name="max_size">Taille max</string>
<string name="song_cache">CACHE DE CHANSONS</string>
<string name="database">Database</string>
<string name="reset_quick_picks">Réinitialiser sélections rapides</string>
<string name="quick_picks_are_cleared">Les sélections rapides sont effacés</string>
<string name="backup">BACKUP</string>
<string name="personal_preference">Les préférences personnelles (c.-à-d. le mode de thème) et le cache sont exclus.</string>
<string name="backup_1">Backup</string>
<string name="export_the_database">Exporter la base de données vers le stockage externe</string>
<string name="restore">RESTAURER</string>
<string name="restore_1">Restaurer</string>
<string name="import_the_database">Importer la base de données à partir du stockage externe</string>
<string name="other">Autre</string>
<string name="android_auto">ANDROID AUTO</string>
<string name="enable_unknown_sources">Noubliez pas dactiver \"Sources inconnues\" dans les paramètres du développeur dAndroid Auto.</string>
<string name="android_auto_1">Android Auto</string>
<string name="enable_android_auto_support">Activer le support pour Android Auto</string>
<string name="search_history">HISTORIQUE DE RECHERCHES</string>
<string name="pause_search_history">Suspendre lhistorique des recherches</string>
<string name="neither_save_new_searched_query">Ne pas enregistrer de nouvelles recherches ni afficher lhistorique</string>
<string name="clear_search_history">Effacer lhistorique de recherche</string>
<string name="history_is_empty">Lhistorique est vide</string>
<string name="service_lifetime">SERVICE LIFETIME</string>
<string name="battery_optimizations_applied">Si des optimisations de la batterie sont appliquées, la notification de lecture peut soudainement disparaître en cas de pause.</string>
<string name="is_android12">Depuis Android 12, la désactivation des optimisations de la batterie est nécessaire pour que loption \"Invincible service\" prenne effet.</string>
<string name="ignore_battery_optimizations">IIgnorer les optimisations de la batterie</string>
<string name="already_unrestricted">Déjà sans restriction</string>
<string name="disable_background_restrictions">Désactiver les restrictions en arrière-plan</string>
<string name="invincible_service">Invincible service</string>
<string name="turning_off_battery_optimizations_is_not_enough">When turning off battery optimizations is not enough</string>
<string name="player_audio"><![CDATA[Player & Audio]]></string>
<string name="player">PLAYER</string>
<string name="persistent_queue">Queue persistante</string>
<string name="save_and_restore_playing_songs">Enregistrer et restaurer la lecture des chansons</string>
<string name="resume_playback">Reprendre la lecture</string>
<string name="when_device_is_connected">Quand è collegato un dispositif câblé le bluetooth</string>
<string name="audio">AUDIO</string>
<string name="skip_silence">Ignorer le silence</string>
<string name="skip_silent_parts_during_playback">Ignorer les parties silencieuses pendant la lecture</string>
<string name="loudness_normalization">Normalisation de volume</string>
<string name="autoadjust_the_volume">Régler le volume à un niveau fixe</string>
<string name="interact_with_the_system_equalizer">Interagir avec légaliseur du système</string>
<string name="about">About</string>
<string name="more_of">Plus de</string>
<string name="sleep_timer">Minuterie d\'arrêt</string>
<string name="left">%1$s restants</string>
<string name="set">Impôt</string>
<string name="play_next">Lecture suivante</string>
<string name="confirm">Confirmer</string>
<string name="show">Montre</string>
<string name="unsynchronized_lyrics">lyrics non synchronisées</string>
<string name="synchronized_lyrics">lyrics synchronisées</string>
<string name="provided_by">Fournies par</string>
<string name="cleanup">NETTOYAGE</string>
<string name="delete_playback_events">Supprimer %1$s événements de lecture</string>
<string name="existing_data_will_be_overwritten">Les données existantes seront écrasées.\n%1$s se fermera automatiquement après la restauration de la base de données.</string>
</resources>

View file

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="cancel">Annulla</string>
<string name="done">Ok</string>
<string name="hidesong">Vuoi davvero nascondere questa canzone? Il tempo di riproduzione e la cache verranno cancellati. Questa azione è irreversibile.</string>
<string name="new_playlist">Nuova playlist</string>
<string name="start_radio">Ascolta radio</string>
<string name="equalizer">Equalizzatore</string>
<string name="stop_sleep_timer">Disattiva timer di spegnimento?</string>
<string name="set_sleep_timer">Timer spegnimento</string>
<string name="add_to_playlist">Aggiungi alla playlist</string>
<string name="go_to_album">Visualizza album</string>
<string name="remove_from_queue">Rimuovi dalla coda</string>
<string name="remove_from_playlist">Rimuovi dalla playlist</string>
<string name="hide">Nascondi</string>
<string name="hide_from_quick_picks">Nascondi da \"Scelte rapide\"</string>
<string name="related_albums">Album correlati</string>
<string name="similar_artists">Artisti simili</string>
<string name="playlists_you_might_like">Playlist che potrebbero piacerti</string>
<string name="an_error_has_occurred">C\'è stato un errore</string>
<string name="albums">Album</string>
<string name="artists">Artisti</string>
<string name="enter_the_playlist_name">Inserisci il nome della playlist</string>
<string name="playlists">Playlist</string>
<string name="favorites">Preferiti</string>
<string name="offline">Offline</string>
<string name="quick_picks">Scelte rapide</string>
<string name="songs">Brani</string>
<string name="other_versions">Altre versioni</string>
<string name="album_no_alternative_version">L\'album non ha versioni alternative</string>
<string name="enqueue">Aggiungi alla coda</string>
<string name="shuffle">Casuale</string>
<string name="view_all">Visualizza tutto</string>
<string name="from_wikipedia_cca">Da Wikipedia under Creative Commons Attribution CC-BY-SA 3.0</string>
<string name="overview">Panoramica</string>
<string name="singles">Singoli</string>
<string name="library">Libreria</string>
<string name="artist_no_release_album">L\'artista non ha rilasciato nessun album</string>
<string name="artist_no_release_single">L\'artista non ha rilasciato nessun singolo</string>
<string name="delete_playlist">Vuoi veramente eliminare la playlist?</string>
<string name="sync">Sincronizza</string>
<string name="rename">Rinomina</string>
<string name="delete">Elimina</string>
<string name="enter_the_lyrics">Inserisci lyrics</string>
<string name="edit_lyrics">Modifica lyrics</string>
<string name="search_lyrics_online">Cerca lyrics online</string>
<string name="fetch_lyrics_again">Recupera nuovamente lyrics</string>
<string name="queue_loop">"Riproduzione continua "</string>
<string name="id">Id</string>
<string name="itag">Itag</string>
<string name="bitrate">Bitrate</string>
<string name="size">Grandezza</string>
<string name="cached">Scaricato</string>
<string name="loudness">Volume</string>
<string name="clear">Pulisci</string>
<string name="error">C\'è stato un errore.</string>
<string name="enter_a_name">Cerca</string>
<string name="online">Online</string>
<string name="no_results_found">Nessun risultato. Cambia la chiave di ricerca o la categoria</string>
<string name="videos">Video</string>
<string name="featured">In primo piano</string>
<string name="view_the_source_code">Visualizza il codice sorgente</string>
<string name="report_an_issue">Segnala un bug</string>
<string name="you_will_be_redirected_to_github">Sarai reindirizzato su GitHub</string>
<string name="request_a_feature_or_suggest_an_idea">Richiedi una funzionalità o suggerisci un\'idea</string>
<string name="appearance">Aspetto</string>
<string name="colors">COLORI</string>
<string name="theme">Tema</string>
<string name="theme_mode">Modalità tema</string>
<string name="shapes">FORME</string>
<string name="thumbnail_roundness">Miniature arrotondate</string>
<string name="text">TESTO</string>
<string name="use_system_font">Usa font di sistema</string>
<string name="use_font_by_the_system">Usa font applicato dal sistema</string>
<string name="apply_font_padding">Applicare riempimento del carattere</string>
<string name="add_spacing_around_texts">Aggiungi spaziatura nei testi</string>
<string name="lockscreen">BLOCCO SCHERMO</string>
<string name="show_song_cover">Mostra copertina della canzone</string>
<string name="use_song_cover_on_lockscreen">Usa la copertina del brano in riproduzione come sfondo della schermata di blocco</string>
<string name="cache">Cache</string>
<string name="cache_cleared">Quando la cache esaurisce lo spazio, le risorse a cui non è stato effettuato l\'accesso per il tempo più lungo vengono cancellate</string>
<string name="image_cache">CACHE IMMAGINI</string>
<string name="max_size">Grandezza massima</string>
<string name="song_cache">CACHE BRANI</string>
<string name="database">Database</string>
<string name="reset_quick_picks">Reset Scelte rapide</string>
<string name="quick_picks_are_cleared">Scelte rapide cancellate</string>
<string name="backup">BACKUP</string>
<string name="personal_preference">Le preferenze personali (ovvero la modalità tema) e la cache sono escluse.</string>
<string name="backup_1">Backup</string>
<string name="export_the_database">Esporta il database su un supporto esterno</string>
<string name="restore">RIPRISTINO</string>
<string name="restore_1">Ripristino</string>
<string name="import_the_database">Importa database da un supporto esterno</string>
<string name="other">Altro</string>
<string name="android_auto">ANDROID AUTO</string>
<string name="enable_unknown_sources">Ricorda di abilitare \"Origini sconosciute\" nelle Impostazioni sviluppatore di Android Auto.</string>
<string name="android_auto_1">Android Auto</string>
<string name="enable_android_auto_support">Abilita supporto per Android Auto</string>
<string name="search_history">CRONOLOGIA RICERCHE</string>
<string name="pause_search_history">Sospendi cronologia ricerche</string>
<string name="neither_save_new_searched_query">Non salvare nuove ricerche né mostrare la cronologia</string>
<string name="clear_search_history">Pulisci cronologia ricerche</string>
<string name="history_is_empty">La cronologia è vuota</string>
<string name="service_lifetime">SERVIZIO LIFETIME</string>
<string name="battery_optimizations_applied">Se vengono applicate le ottimizzazioni della batteria, la notifica di riproduzione può scomparire improvvisamente quando viene messa in pausa.</string>
<string name="is_android12">A partire da Android 12, è necessario disabilitare le ottimizzazioni della batteria affinché l\'opzione \"Servizio invincibile\" abbia effetto.</string>
<string name="ignore_battery_optimizations">Ignora ottimizzazione batteria</string>
<string name="already_unrestricted">Già senza restrizioni</string>
<string name="disable_background_restrictions">Disabilita le restrizioni sullo sfondo</string>
<string name="invincible_service">Invincible service</string>
<string name="turning_off_battery_optimizations_is_not_enough">Quando disattivare le ottimizzazioni della batteria non è sufficiente</string>
<string name="player_audio"><![CDATA[Player & Audio]]></string>
<string name="player">PLAYER</string>
<string name="persistent_queue">Coda persistente</string>
<string name="save_and_restore_playing_songs">Salva e ripristina la riproduzione di brani</string>
<string name="resume_playback">Riprendi la riproduzione</string>
<string name="when_device_is_connected">Quando è collegato un dispositivo cablato o bluetooth</string>
<string name="audio">AUDIO</string>
<string name="skip_silence">Salta il silenzio</string>
<string name="skip_silent_parts_during_playback">Salta le parti silenziose durante la riproduzione</string>
<string name="loudness_normalization">Normalizzazione del volume</string>
<string name="autoadjust_the_volume">Regola il volume a un livello fisso</string>
<string name="interact_with_the_system_equalizer">Interagisci con l\'equalizzatore di sistema</string>
<string name="about">About</string>
<string name="more_of">Altro su</string>
<string name="sleep_timer">Timer spegnimento</string>
<string name="left">%1$s rimanenti</string>
<string name="set">Imposta</string>
<string name="play_next">Riproduci successivo</string>
<string name="confirm">Conferma</string>
<string name="show">Mostra</string>
<string name="unsynchronized_lyrics">lyrics non sincronizzate</string>
<string name="synchronized_lyrics">lyrics sincronizzate</string>
<string name="provided_by">Fornito da</string>
<string name="cleanup">PULIZIA</string>
<string name="delete_playback_events">Elimina %1$s eventi di riproduzione</string>
<string name="existing_data_will_be_overwritten">I dati esistenti verranno sovrascritti.\n%1$s si chiuderà automaticamente dopo aver ripristinato il database.</string>
</resources>

View file

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="cancel">Cancel</string>
<string name="done">Done</string>
<string name="hidesong">Do you really want to hide this song? Its playback time and cache will be wiped.\nThis action is irreversible.</string>
<string name="new_playlist">New playlist</string>
<string name="start_radio">Start radio</string>
<string name="equalizer">Equalizer</string>
<string name="stop_sleep_timer">Do you want to stop the sleep timer?</string>
<string name="set_sleep_timer">Set sleep timer</string>
<string name="add_to_playlist">Add to playlist</string>
<string name="go_to_album">Go to album</string>
<string name="remove_from_queue">Remove from queue</string>
<string name="remove_from_playlist">Remove from playlist</string>
<string name="hide">Hide</string>
<string name="hide_from_quick_picks">Hide from \"Quick picks\"</string>
<string name="related_albums">Related albums</string>
<string name="similar_artists">Similar artists</string>
<string name="playlists_you_might_like">Playlists you might like</string>
<string name="an_error_has_occurred">An error has occurred</string>
<string name="albums">Albums</string>
<string name="artists">Artists</string>
<string name="enter_the_playlist_name">Enter the playlist name</string>
<string name="playlists">Playlists</string>
<string name="favorites">Favorites</string>
<string name="offline">Offline</string>
<string name="quick_picks">Quick picks</string>
<string name="songs">Songs</string>
<string name="other_versions">Other versions</string>
<string name="album_no_alternative_version">This album doesn\'t have any alternative version</string>
<string name="enqueue">Enqueue</string>
<string name="shuffle">Shuffle</string>
<string name="view_all">View all</string>
<string name="from_wikipedia_cca">From Wikipedia under Creative Commons Attribution CC-BY-SA 3.0</string>
<string name="overview">Overview</string>
<string name="singles">Singles</string>
<string name="library">Library</string>
<string name="artist_no_release_album">This artist didn\'t release any album</string>
<string name="artist_no_release_single">This artist didn\'t release any single</string>
<string name="delete_playlist">Do you really want to delete this playlist?</string>
<string name="sync">Sync</string>
<string name="rename">Rename</string>
<string name="delete">Delete</string>
<string name="enter_the_lyrics">Enter the lyrics</string>
<string name="edit_lyrics">Edit lyrics</string>
<string name="search_lyrics_online">Search lyrics online</string>
<string name="fetch_lyrics_again">Fetch lyrics again</string>
<string name="queue_loop">"Queue loop "</string>
<string name="id">Id</string>
<string name="itag">Itag</string>
<string name="bitrate">Bitrate</string>
<string name="size">Size</string>
<string name="cached">Cached</string>
<string name="loudness">Loudness</string>
<string name="clear">Clear</string>
<string name="error">An error has occurred.</string>
<string name="enter_a_name">Enter a name</string>
<string name="online">Online</string>
<string name="no_results_found">No results found. Please try a different query or category</string>
<string name="videos">Videos</string>
<string name="featured">Featured</string>
<string name="view_the_source_code">View the source code</string>
<string name="report_an_issue">Report an issue</string>
<string name="you_will_be_redirected_to_github">You will be redirected to GitHub</string>
<string name="request_a_feature_or_suggest_an_idea">Request a feature or suggest an idea</string>
<string name="appearance">Appearance</string>
<string name="colors">COLORS</string>
<string name="theme">Theme</string>
<string name="theme_mode">Theme mode</string>
<string name="shapes">SHAPES</string>
<string name="thumbnail_roundness">Thumbnail roundness</string>
<string name="text">TEXT</string>
<string name="use_system_font">Use system font</string>
<string name="use_font_by_the_system">Use the font applied by the system</string>
<string name="apply_font_padding">Apply font padding</string>
<string name="add_spacing_around_texts">Add spacing around texts</string>
<string name="lockscreen">LOCKSCREEN</string>
<string name="show_song_cover">Show song cover</string>
<string name="use_song_cover_on_lockscreen">Use the playing song cover as the lockscreen wallpaper</string>
<string name="cache">Cache</string>
<string name="cache_cleared">When the cache runs out of space, the resources that haven\'t been accessed for the longest time are cleared</string>
<string name="image_cache">IMAGE CACHE</string>
<string name="max_size">Max size</string>
<string name="song_cache">SONG CACHE</string>
<string name="database">Database</string>
<string name="reset_quick_picks">Reset quick picks</string>
<string name="quick_picks_are_cleared">Quick picks are cleared</string>
<string name="backup">BACKUP</string>
<string name="personal_preference">Personal preferences (i.e. the theme mode) and the cache are excluded.</string>
<string name="backup_1">Backup</string>
<string name="export_the_database">Export the database to the external storage</string>
<string name="restore">RESTORE</string>
<string name="restore_1">Restore</string>
<string name="import_the_database">Import the database from the external storage</string>
<string name="other">Other</string>
<string name="android_auto">ANDROID AUTO</string>
<string name="enable_unknown_sources">Remember to enable \"Unknown sources\" in the Developer Settings of Android Auto.</string>
<string name="android_auto_1">Android Auto</string>
<string name="enable_android_auto_support">Enable Android Auto support</string>
<string name="search_history">SEARCH HISTORY</string>
<string name="pause_search_history">Pause search history</string>
<string name="neither_save_new_searched_query">Neither save new searched queries nor show history</string>
<string name="clear_search_history">Clear search history</string>
<string name="history_is_empty">History is empty</string>
<string name="service_lifetime">SERVICE LIFETIME</string>
<string name="battery_optimizations_applied">If battery optimizations are applied, the playback notification can suddenly disappear when paused.</string>
<string name="is_android12">Since Android 12, disabling battery optimizations is required for the \"Invincible service\" option to take effect.</string>
<string name="ignore_battery_optimizations">Ignore battery optimizations</string>
<string name="already_unrestricted">Already unrestricted</string>
<string name="disable_background_restrictions">Disable background restrictions</string>
<string name="invincible_service">Invincible service</string>
<string name="turning_off_battery_optimizations_is_not_enough">When turning off battery optimizations is not enough</string>
<string name="player_audio"><![CDATA[Player & Audio]]></string>
<string name="player">PLAYER</string>
<string name="persistent_queue">Persistent queue</string>
<string name="save_and_restore_playing_songs">Save and restore playing songs</string>
<string name="resume_playback">Resume playback</string>
<string name="when_device_is_connected">When a wired or bluetooth device is connected</string>
<string name="audio">AUDIO</string>
<string name="skip_silence">Skip silence</string>
<string name="skip_silent_parts_during_playback">Skip silent parts during playback</string>
<string name="loudness_normalization">Loudness normalization</string>
<string name="autoadjust_the_volume">Adjust the volume to a fixed level</string>
<string name="interact_with_the_system_equalizer">Interact with the system equalizer</string>
<string name="about">About</string>
<string name="more_of">More of</string>
<string name="sleep_timer">Sleep timer</string>
<string name="left">%1$s left</string>
<string name="set">Set</string>
<string name="play_next">Play next</string>
<string name="confirm">Confirm</string>
<string name="show">Show</string>
<string name="unsynchronized_lyrics">unsynchronized lyrics</string>
<string name="synchronized_lyrics">synchronized lyrics</string>
<string name="provided_by">Provided by</string>
<string name="cleanup">CLEANUP</string>
<string name="delete_playback_events">Delete %1$s playback events</string>
<string name="existing_data_will_be_overwritten">Existing data will be overwritten.\n%1$s will automatically close itself after restoring the database.</string>
</resources>