Added localization
This commit is contained in:
parent
40ca9b7090
commit
9eb2bc7fda
8 changed files with 217 additions and 44 deletions
|
|
@ -26,9 +26,11 @@ data class Recording(
|
||||||
) {
|
) {
|
||||||
val hasMidi: Boolean get() = midiFileName != null
|
val hasMidi: Boolean get() = midiFileName != null
|
||||||
|
|
||||||
/** "22 Jun 2026" style for display. */
|
/** "22 Jun 2026" style for display, in the device's current locale. */
|
||||||
val formattedDate: String
|
val formattedDate: String
|
||||||
get() = date.format(DateTimeFormatter.ofPattern("d MMM yyyy", Locale.ENGLISH))
|
get() = date.format(
|
||||||
|
DateTimeFormatter.ofPattern("d MMM yyyy", Locale.getDefault())
|
||||||
|
)
|
||||||
|
|
||||||
val groupingArtist: String
|
val groupingArtist: String
|
||||||
get() = if (artist.contains("soundtrack", ignoreCase = true)) "Soundtracks" else artist
|
get() = if (artist.contains("soundtrack", ignoreCase = true)) "Soundtracks" else artist
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,11 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
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 com.keylightpiano.pianotimeline.R
|
||||||
import com.keylightpiano.pianotimeline.domain.Recording
|
import com.keylightpiano.pianotimeline.domain.Recording
|
||||||
import com.keylightpiano.pianotimeline.player.PlaybackState
|
import com.keylightpiano.pianotimeline.player.PlaybackState
|
||||||
|
|
||||||
|
|
@ -154,7 +156,7 @@ fun MiniPlayer(
|
||||||
IconButton(onClick = onPrevious, modifier = Modifier.size(40.dp)) {
|
IconButton(onClick = onPrevious, modifier = Modifier.size(40.dp)) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(android.R.drawable.ic_media_previous),
|
painter = painterResource(android.R.drawable.ic_media_previous),
|
||||||
contentDescription = "Previous",
|
contentDescription = stringResource(R.string.cd_previous),
|
||||||
modifier = Modifier.size(22.dp)
|
modifier = Modifier.size(22.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -165,7 +167,7 @@ fun MiniPlayer(
|
||||||
if (playback.isPlaying) android.R.drawable.ic_media_pause
|
if (playback.isPlaying) android.R.drawable.ic_media_pause
|
||||||
else android.R.drawable.ic_media_play
|
else android.R.drawable.ic_media_play
|
||||||
),
|
),
|
||||||
contentDescription = if (playback.isPlaying) "Pause" else "Play",
|
contentDescription = stringResource(if (playback.isPlaying) R.string.cd_pause else R.string.cd_play),
|
||||||
modifier = Modifier.size(24.dp)
|
modifier = Modifier.size(24.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -173,7 +175,7 @@ fun MiniPlayer(
|
||||||
IconButton(onClick = onNext, modifier = Modifier.size(40.dp)) {
|
IconButton(onClick = onNext, modifier = Modifier.size(40.dp)) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(android.R.drawable.ic_media_next),
|
painter = painterResource(android.R.drawable.ic_media_next),
|
||||||
contentDescription = "Next",
|
contentDescription = stringResource(R.string.cd_next),
|
||||||
modifier = Modifier.size(22.dp)
|
modifier = Modifier.size(22.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -207,7 +209,7 @@ fun MiniPlayer(
|
||||||
IconButton(onClick = onDismiss, modifier = Modifier.size(40.dp)) {
|
IconButton(onClick = onDismiss, modifier = Modifier.size(40.dp)) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Close,
|
imageVector = Icons.Default.Close,
|
||||||
contentDescription = "Stop",
|
contentDescription = stringResource(R.string.cd_stop),
|
||||||
modifier = Modifier.size(20.dp)
|
modifier = Modifier.size(20.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,11 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
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 com.keylightpiano.pianotimeline.R
|
||||||
import com.keylightpiano.pianotimeline.domain.Recording
|
import com.keylightpiano.pianotimeline.domain.Recording
|
||||||
|
|
||||||
private val NotesGreen = Color(0xFF25B300)
|
private val NotesGreen = Color(0xFF25B300)
|
||||||
|
|
@ -88,7 +90,7 @@ fun RecordingItem(
|
||||||
if (isPlaying) android.R.drawable.ic_media_pause
|
if (isPlaying) android.R.drawable.ic_media_pause
|
||||||
else android.R.drawable.ic_media_play
|
else android.R.drawable.ic_media_play
|
||||||
),
|
),
|
||||||
contentDescription = if (isPlaying) "Pause" else "Play",
|
contentDescription = stringResource(if (isPlaying) R.string.cd_pause else R.string.cd_play),
|
||||||
modifier = Modifier.size(22.dp)
|
modifier = Modifier.size(22.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +150,7 @@ fun RecordingItem(
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = if (recording.isFavorite) Icons.Filled.Star
|
imageVector = if (recording.isFavorite) Icons.Filled.Star
|
||||||
else Icons.Outlined.StarBorder,
|
else Icons.Outlined.StarBorder,
|
||||||
contentDescription = if (recording.isFavorite) "Unfavorite" else "Favorite",
|
contentDescription = stringResource(if (recording.isFavorite) R.string.cd_unfavorite else R.string.cd_favorite),
|
||||||
tint = if (recording.isFavorite) Color(0xFFFFC107)
|
tint = if (recording.isFavorite) Color(0xFFFFC107)
|
||||||
else MaterialTheme.colorScheme.onSurfaceVariant,
|
else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
modifier = Modifier.size(20.dp)
|
modifier = Modifier.size(20.dp)
|
||||||
|
|
@ -164,7 +166,7 @@ fun RecordingItem(
|
||||||
border = BorderStroke(1.5.dp, NotesGreen)
|
border = BorderStroke(1.5.dp, NotesGreen)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
"Notes",
|
stringResource(R.string.notes),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = NotesGreen
|
color = NotesGreen
|
||||||
)
|
)
|
||||||
|
|
@ -191,7 +193,7 @@ fun RecordingItem(
|
||||||
onDismissRequest = { menuExpanded = false }
|
onDismissRequest = { menuExpanded = false }
|
||||||
) {
|
) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("Share audio") },
|
text = { Text(stringResource(R.string.menu_share_audio)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
menuExpanded = false
|
menuExpanded = false
|
||||||
onShareAudio()
|
onShareAudio()
|
||||||
|
|
@ -199,7 +201,7 @@ fun RecordingItem(
|
||||||
)
|
)
|
||||||
if (onShareVisualization != null) {
|
if (onShareVisualization != null) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("Share visualization") },
|
text = { Text(stringResource(R.string.menu_share_visualization)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
menuExpanded = false
|
menuExpanded = false
|
||||||
onShareVisualization()
|
onShareVisualization()
|
||||||
|
|
@ -207,7 +209,7 @@ fun RecordingItem(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("Copy link") },
|
text = { Text(stringResource(R.string.menu_copy_link)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
menuExpanded = false
|
menuExpanded = false
|
||||||
onCopyLink()
|
onCopyLink()
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
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.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
|
@ -172,7 +173,7 @@ fun RecordingListScreen(
|
||||||
.size(28.dp)
|
.size(28.dp)
|
||||||
.padding(end = 8.dp)
|
.padding(end = 8.dp)
|
||||||
)
|
)
|
||||||
Text("Piano Timeline", fontWeight = FontWeight.Bold)
|
Text(stringResource(R.string.title_app), fontWeight = FontWeight.Bold)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
|
|
@ -180,7 +181,7 @@ fun RecordingListScreen(
|
||||||
IconButton(onClick = { vm.toggleShuffle() }) {
|
IconButton(onClick = { vm.toggleShuffle() }) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Shuffle,
|
imageVector = Icons.Default.Shuffle,
|
||||||
contentDescription = "Shuffle",
|
contentDescription = stringResource(R.string.cd_shuffle),
|
||||||
tint = if (shuffle) MaterialTheme.colorScheme.primary
|
tint = if (shuffle) MaterialTheme.colorScheme.primary
|
||||||
else MaterialTheme.colorScheme.onSurfaceVariant
|
else MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
|
|
@ -190,7 +191,7 @@ fun RecordingListScreen(
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = if (repeatMode == RepeatMode.ONE)
|
imageVector = if (repeatMode == RepeatMode.ONE)
|
||||||
Icons.Default.RepeatOne else Icons.Default.Repeat,
|
Icons.Default.RepeatOne else Icons.Default.Repeat,
|
||||||
contentDescription = "Repeat",
|
contentDescription = stringResource(R.string.cd_repeat),
|
||||||
tint = if (repeatMode == RepeatMode.OFF)
|
tint = if (repeatMode == RepeatMode.OFF)
|
||||||
MaterialTheme.colorScheme.onSurfaceVariant
|
MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
else MaterialTheme.colorScheme.primary
|
else MaterialTheme.colorScheme.primary
|
||||||
|
|
@ -200,7 +201,7 @@ fun RecordingListScreen(
|
||||||
IconButton(onClick = { showControls = !showControls }) {
|
IconButton(onClick = { showControls = !showControls }) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Search,
|
imageVector = Icons.Default.Search,
|
||||||
contentDescription = "Search & Filter",
|
contentDescription = stringResource(R.string.cd_search_filter),
|
||||||
tint = if (hasActiveFilter) MaterialTheme.colorScheme.primary
|
tint = if (hasActiveFilter) MaterialTheme.colorScheme.primary
|
||||||
else MaterialTheme.colorScheme.onSurfaceVariant
|
else MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
|
|
@ -229,7 +230,7 @@ fun RecordingListScreen(
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
Spacer(Modifier.height(12.dp))
|
Spacer(Modifier.height(12.dp))
|
||||||
Text("Syncing...", style = MaterialTheme.typography.bodyMedium)
|
Text(stringResource(R.string.syncing), style = MaterialTheme.typography.bodyMedium)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -241,9 +242,9 @@ fun RecordingListScreen(
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
modifier = Modifier.padding(24.dp)
|
modifier = Modifier.padding(24.dp)
|
||||||
) {
|
) {
|
||||||
Text("Couldn't load recordings", style = MaterialTheme.typography.titleMedium)
|
Text(stringResource(R.string.error_load_title), style = MaterialTheme.typography.titleMedium)
|
||||||
Text(s.message, style = MaterialTheme.typography.bodySmall)
|
Text(s.message, style = MaterialTheme.typography.bodySmall)
|
||||||
Button(onClick = { vm.retry() }) { Text("Retry") }
|
Button(onClick = { vm.retry() }) { Text(stringResource(R.string.retry)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -260,12 +261,12 @@ fun RecordingListScreen(
|
||||||
value = searchQuery,
|
value = searchQuery,
|
||||||
onValueChange = { vm.setSearchQuery(it) },
|
onValueChange = { vm.setSearchQuery(it) },
|
||||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 4.dp),
|
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 4.dp),
|
||||||
placeholder = { Text("Search artist, title...") },
|
placeholder = { Text(stringResource(R.string.search_placeholder)) },
|
||||||
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
|
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
if (searchQuery.isNotEmpty()) {
|
if (searchQuery.isNotEmpty()) {
|
||||||
IconButton(onClick = { vm.setSearchQuery("") }) {
|
IconButton(onClick = { vm.setSearchQuery("") }) {
|
||||||
Icon(Icons.Default.Clear, contentDescription = "Clear")
|
Icon(Icons.Default.Clear, contentDescription = stringResource(R.string.cd_clear))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -280,14 +281,14 @@ fun RecordingListScreen(
|
||||||
) {
|
) {
|
||||||
Box {
|
Box {
|
||||||
TextButton(onClick = { showSortMenu = true }) {
|
TextButton(onClick = { showSortMenu = true }) {
|
||||||
Text(sortMode.label, style = MaterialTheme.typography.labelMedium)
|
Text(stringResource(sortMode.labelRes), style = MaterialTheme.typography.labelMedium)
|
||||||
}
|
}
|
||||||
DropdownMenu(expanded = showSortMenu, onDismissRequest = { showSortMenu = false }) {
|
DropdownMenu(expanded = showSortMenu, onDismissRequest = { showSortMenu = false }) {
|
||||||
SortMode.entries.forEach { mode ->
|
SortMode.entries.forEach { mode ->
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = {
|
text = {
|
||||||
Text(
|
Text(
|
||||||
mode.label,
|
stringResource(mode.labelRes),
|
||||||
fontWeight = if (mode == sortMode) FontWeight.Bold else FontWeight.Normal
|
fontWeight = if (mode == sortMode) FontWeight.Bold else FontWeight.Normal
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -302,16 +303,16 @@ fun RecordingListScreen(
|
||||||
|
|
||||||
TextButton(onClick = { showArtistFilter = !showArtistFilter }) {
|
TextButton(onClick = { showArtistFilter = !showArtistFilter }) {
|
||||||
Text(
|
Text(
|
||||||
if (hiddenArtists.isEmpty()) "Artists"
|
if (hiddenArtists.isEmpty()) stringResource(R.string.filter_artists)
|
||||||
else "Artists (${hiddenArtists.size} hidden)",
|
else stringResource(R.string.filter_artists_hidden, hiddenArtists.size),
|
||||||
style = MaterialTheme.typography.labelMedium
|
style = MaterialTheme.typography.labelMedium
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
TextButton(onClick = { showYearFilter = !showYearFilter }) {
|
TextButton(onClick = { showYearFilter = !showYearFilter }) {
|
||||||
Text(
|
Text(
|
||||||
if (hiddenYears.isEmpty()) "Years"
|
if (hiddenYears.isEmpty()) stringResource(R.string.filter_years)
|
||||||
else "Years (${hiddenYears.size} hidden)",
|
else stringResource(R.string.filter_years_hidden, hiddenYears.size),
|
||||||
style = MaterialTheme.typography.labelMedium
|
style = MaterialTheme.typography.labelMedium
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -325,10 +326,10 @@ fun RecordingListScreen(
|
||||||
Column(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)) {
|
Column(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)) {
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
TextButton(onClick = { vm.showAllArtists() }) {
|
TextButton(onClick = { vm.showAllArtists() }) {
|
||||||
Text("All", style = MaterialTheme.typography.labelSmall)
|
Text(stringResource(R.string.filter_all), style = MaterialTheme.typography.labelSmall)
|
||||||
}
|
}
|
||||||
TextButton(onClick = { vm.hideAllArtists() }) {
|
TextButton(onClick = { vm.hideAllArtists() }) {
|
||||||
Text("None", style = MaterialTheme.typography.labelSmall)
|
Text(stringResource(R.string.filter_none), style = MaterialTheme.typography.labelSmall)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FlowRow(
|
FlowRow(
|
||||||
|
|
@ -354,10 +355,10 @@ fun RecordingListScreen(
|
||||||
Column(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)) {
|
Column(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)) {
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
TextButton(onClick = { vm.showAllYears() }) {
|
TextButton(onClick = { vm.showAllYears() }) {
|
||||||
Text("All", style = MaterialTheme.typography.labelSmall)
|
Text(stringResource(R.string.filter_all), style = MaterialTheme.typography.labelSmall)
|
||||||
}
|
}
|
||||||
TextButton(onClick = { vm.hideAllYears() }) {
|
TextButton(onClick = { vm.hideAllYears() }) {
|
||||||
Text("None", style = MaterialTheme.typography.labelSmall)
|
Text(stringResource(R.string.filter_none), style = MaterialTheme.typography.labelSmall)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FlowRow(
|
FlowRow(
|
||||||
|
|
@ -380,7 +381,7 @@ fun RecordingListScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "${recordings.size} recordings",
|
text = stringResource(R.string.recordings_count, recordings.size),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp)
|
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp)
|
||||||
|
|
@ -450,6 +451,6 @@ private fun copyLink(context: android.content.Context, url: String) {
|
||||||
// On Android 13+ the system shows its own "copied" confirmation; on older
|
// On Android 13+ the system shows its own "copied" confirmation; on older
|
||||||
// versions a Toast is the norm.
|
// versions a Toast is the norm.
|
||||||
if (android.os.Build.VERSION.SDK_INT < 33) {
|
if (android.os.Build.VERSION.SDK_INT < 33) {
|
||||||
android.widget.Toast.makeText(context, "Link copied", android.widget.Toast.LENGTH_SHORT).show()
|
android.widget.Toast.makeText(context, context.getString(R.string.toast_link_copied), android.widget.Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
package com.keylightpiano.pianotimeline.ui
|
package com.keylightpiano.pianotimeline.ui
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import com.keylightpiano.pianotimeline.R
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Available sort modes for the recording list.
|
* Available sort modes for the recording list.
|
||||||
* Each knows how to produce a Comparator for Recording.
|
* Each carries a string-resource id for its label (resolved at display time via
|
||||||
|
* stringResource), so the labels localize automatically.
|
||||||
*/
|
*/
|
||||||
enum class SortMode(val label: String) {
|
enum class SortMode(@StringRes val labelRes: Int) {
|
||||||
DATE_DESC("Newest first"),
|
DATE_DESC(R.string.sort_newest),
|
||||||
DATE_ASC("Oldest first"),
|
DATE_ASC(R.string.sort_oldest),
|
||||||
ARTIST_AZ("Artist A → Z"),
|
ARTIST_AZ(R.string.sort_artist_az),
|
||||||
ARTIST_ZA("Artist Z → A"),
|
ARTIST_ZA(R.string.sort_artist_za),
|
||||||
TITLE_AZ("Title A → Z"),
|
TITLE_AZ(R.string.sort_title_az),
|
||||||
DURATION_DESC("Longest first"),
|
DURATION_DESC(R.string.sort_longest),
|
||||||
DURATION_ASC("Shortest first");
|
DURATION_ASC(R.string.sort_shortest);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
55
app/src/main/res/values-es/strings.xml
Normal file
55
app/src/main/res/values-es/strings.xml
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- app_name intentionally NOT translated: keep the brand name "Piano Timeline" -->
|
||||||
|
|
||||||
|
<!-- Top bar -->
|
||||||
|
<string name="title_app">Piano Timeline</string>
|
||||||
|
<string name="cd_shuffle">Aleatorio</string>
|
||||||
|
<string name="cd_repeat">Repetir</string>
|
||||||
|
<string name="cd_search_filter">Buscar y filtrar</string>
|
||||||
|
|
||||||
|
<!-- Sync states -->
|
||||||
|
<string name="syncing">Sincronizando\u2026</string>
|
||||||
|
<string name="error_load_title">No se pudieron cargar las grabaciones</string>
|
||||||
|
<string name="retry">Reintentar</string>
|
||||||
|
|
||||||
|
<!-- Search & filters -->
|
||||||
|
<string name="search_placeholder">Buscar artista, t\u00EDtulo\u2026</string>
|
||||||
|
<string name="cd_clear">Borrar</string>
|
||||||
|
<string name="filter_artists">Artistas</string>
|
||||||
|
<string name="filter_artists_hidden">Artistas (%1$d ocultos)</string>
|
||||||
|
<string name="filter_years">A\u00F1os</string>
|
||||||
|
<string name="filter_years_hidden">A\u00F1os (%1$d ocultos)</string>
|
||||||
|
<string name="filter_all">Todos</string>
|
||||||
|
<string name="filter_none">Ninguno</string>
|
||||||
|
|
||||||
|
<!-- Recording count -->
|
||||||
|
<string name="recordings_count">%1$d grabaciones</string>
|
||||||
|
|
||||||
|
<!-- Card -->
|
||||||
|
<string name="cd_play">Reproducir</string>
|
||||||
|
<string name="cd_pause">Pausar</string>
|
||||||
|
<string name="cd_favorite">A\u00F1adir a favoritos</string>
|
||||||
|
<string name="cd_unfavorite">Quitar de favoritos</string>
|
||||||
|
<string name="notes">Notas</string>
|
||||||
|
|
||||||
|
<!-- Long-press menu -->
|
||||||
|
<string name="menu_share_audio">Compartir audio</string>
|
||||||
|
<string name="menu_share_visualization">Compartir visualizaci\u00F3n</string>
|
||||||
|
<string name="menu_copy_link">Copiar enlace</string>
|
||||||
|
<string name="toast_link_copied">Enlace copiado</string>
|
||||||
|
|
||||||
|
<!-- Mini-player -->
|
||||||
|
<string name="cd_previous">Anterior</string>
|
||||||
|
<string name="cd_next">Siguiente</string>
|
||||||
|
<string name="cd_stop">Detener</string>
|
||||||
|
|
||||||
|
<!-- Sort modes -->
|
||||||
|
<string name="sort_newest">M\u00E1s recientes</string>
|
||||||
|
<string name="sort_oldest">M\u00E1s antiguas</string>
|
||||||
|
<string name="sort_artist_az">Artista A \u2192 Z</string>
|
||||||
|
<string name="sort_artist_za">Artista Z \u2192 A</string>
|
||||||
|
<string name="sort_title_az">T\u00EDtulo A \u2192 Z</string>
|
||||||
|
<string name="sort_longest">M\u00E1s largas</string>
|
||||||
|
<string name="sort_shortest">M\u00E1s cortas</string>
|
||||||
|
</resources>
|
||||||
55
app/src/main/res/values-it/strings.xml
Normal file
55
app/src/main/res/values-it/strings.xml
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- app_name intentionally NOT translated: keep the brand name "Piano Timeline" -->
|
||||||
|
|
||||||
|
<!-- Top bar -->
|
||||||
|
<string name="title_app">Piano Timeline</string>
|
||||||
|
<string name="cd_shuffle">Casuale</string>
|
||||||
|
<string name="cd_repeat">Ripeti</string>
|
||||||
|
<string name="cd_search_filter">Cerca e filtra</string>
|
||||||
|
|
||||||
|
<!-- Sync states -->
|
||||||
|
<string name="syncing">Sincronizzazione\u2026</string>
|
||||||
|
<string name="error_load_title">Impossibile caricare le registrazioni</string>
|
||||||
|
<string name="retry">Riprova</string>
|
||||||
|
|
||||||
|
<!-- Search & filters -->
|
||||||
|
<string name="search_placeholder">Cerca artista, titolo\u2026</string>
|
||||||
|
<string name="cd_clear">Cancella</string>
|
||||||
|
<string name="filter_artists">Artisti</string>
|
||||||
|
<string name="filter_artists_hidden">Artisti (%1$d nascosti)</string>
|
||||||
|
<string name="filter_years">Anni</string>
|
||||||
|
<string name="filter_years_hidden">Anni (%1$d nascosti)</string>
|
||||||
|
<string name="filter_all">Tutti</string>
|
||||||
|
<string name="filter_none">Nessuno</string>
|
||||||
|
|
||||||
|
<!-- Recording count -->
|
||||||
|
<string name="recordings_count">%1$d registrazioni</string>
|
||||||
|
|
||||||
|
<!-- Card -->
|
||||||
|
<string name="cd_play">Riproduci</string>
|
||||||
|
<string name="cd_pause">Pausa</string>
|
||||||
|
<string name="cd_favorite">Aggiungi ai preferiti</string>
|
||||||
|
<string name="cd_unfavorite">Rimuovi dai preferiti</string>
|
||||||
|
<string name="notes">Note</string>
|
||||||
|
|
||||||
|
<!-- Long-press menu -->
|
||||||
|
<string name="menu_share_audio">Condividi audio</string>
|
||||||
|
<string name="menu_share_visualization">Condividi visualizzazione</string>
|
||||||
|
<string name="menu_copy_link">Copia link</string>
|
||||||
|
<string name="toast_link_copied">Link copiato</string>
|
||||||
|
|
||||||
|
<!-- Mini-player -->
|
||||||
|
<string name="cd_previous">Precedente</string>
|
||||||
|
<string name="cd_next">Successivo</string>
|
||||||
|
<string name="cd_stop">Ferma</string>
|
||||||
|
|
||||||
|
<!-- Sort modes -->
|
||||||
|
<string name="sort_newest">Pi\u00F9 recenti</string>
|
||||||
|
<string name="sort_oldest">Meno recenti</string>
|
||||||
|
<string name="sort_artist_az">Artista A \u2192 Z</string>
|
||||||
|
<string name="sort_artist_za">Artista Z \u2192 A</string>
|
||||||
|
<string name="sort_title_az">Titolo A \u2192 Z</string>
|
||||||
|
<string name="sort_longest">Pi\u00F9 lunghi</string>
|
||||||
|
<string name="sort_shortest">Pi\u00F9 brevi</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -1,3 +1,55 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">PianoTimeline</string>
|
<string name="app_name">Piano Timeline</string>
|
||||||
|
|
||||||
|
<!-- Top bar -->
|
||||||
|
<string name="title_app">Piano Timeline</string>
|
||||||
|
<string name="cd_shuffle">Shuffle</string>
|
||||||
|
<string name="cd_repeat">Repeat</string>
|
||||||
|
<string name="cd_search_filter">Search & Filter</string>
|
||||||
|
|
||||||
|
<!-- Sync states -->
|
||||||
|
<string name="syncing">Syncing\u2026</string>
|
||||||
|
<string name="error_load_title">Couldn\'t load recordings</string>
|
||||||
|
<string name="retry">Retry</string>
|
||||||
|
|
||||||
|
<!-- Search & filters -->
|
||||||
|
<string name="search_placeholder">Search artist, title\u2026</string>
|
||||||
|
<string name="cd_clear">Clear</string>
|
||||||
|
<string name="filter_artists">Artists</string>
|
||||||
|
<string name="filter_artists_hidden">Artists (%1$d hidden)</string>
|
||||||
|
<string name="filter_years">Years</string>
|
||||||
|
<string name="filter_years_hidden">Years (%1$d hidden)</string>
|
||||||
|
<string name="filter_all">All</string>
|
||||||
|
<string name="filter_none">None</string>
|
||||||
|
|
||||||
|
<!-- Recording count -->
|
||||||
|
<string name="recordings_count">%1$d recordings</string>
|
||||||
|
|
||||||
|
<!-- Card -->
|
||||||
|
<string name="cd_play">Play</string>
|
||||||
|
<string name="cd_pause">Pause</string>
|
||||||
|
<string name="cd_favorite">Favorite</string>
|
||||||
|
<string name="cd_unfavorite">Unfavorite</string>
|
||||||
|
<string name="notes">Notes</string>
|
||||||
|
|
||||||
|
<!-- Long-press menu -->
|
||||||
|
<string name="menu_share_audio">Share audio</string>
|
||||||
|
<string name="menu_share_visualization">Share visualization</string>
|
||||||
|
<string name="menu_copy_link">Copy link</string>
|
||||||
|
<string name="toast_link_copied">Link copied</string>
|
||||||
|
|
||||||
|
<!-- Mini-player -->
|
||||||
|
<string name="cd_previous">Previous</string>
|
||||||
|
<string name="cd_next">Next</string>
|
||||||
|
<string name="cd_stop">Stop</string>
|
||||||
|
|
||||||
|
<!-- Sort modes -->
|
||||||
|
<string name="sort_newest">Newest first</string>
|
||||||
|
<string name="sort_oldest">Oldest first</string>
|
||||||
|
<string name="sort_artist_az">Artist A \u2192 Z</string>
|
||||||
|
<string name="sort_artist_za">Artist Z \u2192 A</string>
|
||||||
|
<string name="sort_title_az">Title A \u2192 Z</string>
|
||||||
|
<string name="sort_longest">Longest first</string>
|
||||||
|
<string name="sort_shortest">Shortest first</string>
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
Reference in a new issue