From 4c5b6aac1551428bc6c5cc61856b0cd0e588ed4e Mon Sep 17 00:00:00 2001 From: timklge <2026103+timklge@users.noreply.github.com> Date: Sat, 16 Aug 2025 19:56:34 +0200 Subject: [PATCH] Add german localization (#60) --- app/build.gradle.kts | 2 +- .../karoopowerbar/CustomProgressBarSize.kt | 26 ++-- .../karoopowerbar/ForegroundService.kt | 13 +- .../karoopowerbar/screens/MainScreen.kt | 139 +++++++++--------- app/src/main/res/values-de/strings.xml | 77 ++++++++++ app/src/main/res/values/strings.xml | 73 +++++++++ 6 files changed, 240 insertions(+), 90 deletions(-) create mode 100644 app/src/main/res/values-de/strings.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 594a42b..fbd330e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -72,7 +72,7 @@ tasks.register("generateManifest") { "latestVersionCode" to android.defaultConfig.versionCode, "developer" to "github.com/timklge", "description" to "Open-source extension that adds colored power or heart rate progress bars to the edges of the screen, similar to the LEDs on Wahoo computers", - "releaseNotes" to "* Add gear data sources\n* Show zero value on bars to indicate sensor availability\n* Fix pedal balance values\n* Add pedal balance data source\n* Add option to split bars", + "releaseNotes" to "* Add german localization\n* Add gear data sources\n* Show zero value on bars to indicate sensor availability\n* Fix pedal balance values\n* Add pedal balance data source\n* Add option to split bars", "screenshotUrls" to listOf( "$baseUrl/powerbar_min.gif", "$baseUrl/powerbar0.png", diff --git a/app/src/main/kotlin/de/timklge/karoopowerbar/CustomProgressBarSize.kt b/app/src/main/kotlin/de/timklge/karoopowerbar/CustomProgressBarSize.kt index c91f77d..879c3e6 100644 --- a/app/src/main/kotlin/de/timklge/karoopowerbar/CustomProgressBarSize.kt +++ b/app/src/main/kotlin/de/timklge/karoopowerbar/CustomProgressBarSize.kt @@ -3,17 +3,17 @@ package de.timklge.karoopowerbar import kotlinx.serialization.Serializable @Serializable -enum class CustomProgressBarSize(val id: String, val label: String, val fontSize: Float, val barHeight: Float) { - SMALL("small", "Small", 35f, 10f), - MEDIUM("medium", "Medium", 40f, 15f), - LARGE("large", "Large", 60f, 25f), +enum class CustomProgressBarSize(val id: String, val labelResId: Int, val fontSize: Float, val barHeight: Float) { + SMALL("small", R.string.size_small, 35f, 10f), + MEDIUM("medium", R.string.size_medium, 40f, 15f), + LARGE("large", R.string.size_large, 60f, 25f), } @Serializable -enum class CustomProgressBarFontSize(val id: String, val label: String, val fontSize: Float) { - SMALL("small", "Small", 35f), - MEDIUM("medium", "Medium", 40f), - LARGE("large", "Large", 60f); +enum class CustomProgressBarFontSize(val id: String, val labelResId: Int, val fontSize: Float) { + SMALL("small", R.string.size_small, 35f), + MEDIUM("medium", R.string.size_medium, 40f), + LARGE("large", R.string.size_large, 60f); companion object { fun fromSize(size: CustomProgressBarSize): CustomProgressBarFontSize { @@ -27,11 +27,11 @@ enum class CustomProgressBarFontSize(val id: String, val label: String, val font } @Serializable -enum class CustomProgressBarBarSize(val id: String, val label: String, val barHeight: Float) { - NONE("none", "None", 0f), - SMALL("small", "Small", 10f), - MEDIUM("medium", "Medium", 15f), - LARGE("large", "Large", 25f); +enum class CustomProgressBarBarSize(val id: String, val labelResId: Int, val barHeight: Float) { + NONE("none", R.string.size_none, 0f), + SMALL("small", R.string.size_small, 10f), + MEDIUM("medium", R.string.size_medium, 15f), + LARGE("large", R.string.size_large, 25f); companion object { fun fromSize(size: CustomProgressBarSize): CustomProgressBarBarSize { diff --git a/app/src/main/kotlin/de/timklge/karoopowerbar/ForegroundService.kt b/app/src/main/kotlin/de/timklge/karoopowerbar/ForegroundService.kt index a3470f8..ac4e2e0 100644 --- a/app/src/main/kotlin/de/timklge/karoopowerbar/ForegroundService.kt +++ b/app/src/main/kotlin/de/timklge/karoopowerbar/ForegroundService.kt @@ -79,7 +79,7 @@ class ForegroundService : Service() { private fun setupForeground() { val channelId = "de.timklge.karoopowerbar" - val channelName = "Background Service" + val channelName = getString(R.string.notification_channel_name) val chan = NotificationChannel( channelId, channelName, @@ -88,14 +88,13 @@ class ForegroundService : Service() { val manager = checkNotNull(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?) - manager.createNotificationChannel(chan) - val notificationBuilder: NotificationCompat.Builder = - NotificationCompat.Builder(this, channelId) - val notification: Notification = notificationBuilder.setOngoing(true) - .setContentTitle("Powerbar service running") - .setContentText("Displaying on top of other apps") + manager.createNotificationChannel(chan) + val notificationBuilder = NotificationCompat.Builder(this, channelId) + val notification = notificationBuilder.setOngoing(true) .setSmallIcon(R.drawable.bar) + .setContentTitle(getString(R.string.app_name)) + .setContentText(getString(R.string.notification_text)) .setPriority(NotificationManager.IMPORTANCE_MIN) .setCategory(Notification.CATEGORY_SERVICE) .build() diff --git a/app/src/main/kotlin/de/timklge/karoopowerbar/screens/MainScreen.kt b/app/src/main/kotlin/de/timklge/karoopowerbar/screens/MainScreen.kt index 91c1d66..c2ef410 100644 --- a/app/src/main/kotlin/de/timklge/karoopowerbar/screens/MainScreen.kt +++ b/app/src/main/kotlin/de/timklge/karoopowerbar/screens/MainScreen.kt @@ -50,6 +50,7 @@ import androidx.compose.ui.focus.FocusState import androidx.compose.ui.focus.onFocusEvent import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -77,22 +78,22 @@ import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlin.math.roundToInt -enum class SelectedSource(val id: String, val label: String) { - NONE("none", "None"), - HEART_RATE("hr", "Heart Rate"), - POWER("power", "Power"), - POWER_3S("power_3s", "Power (3 sec avg)"), - POWER_10S("power_10s", "Power (10 sec avg)"), - SPEED("speed", "Speed"), - SPEED_3S("speed_3s", "Speed (3 sec avg)"), - CADENCE("cadence", "Cadence"), - CADENCE_3S("cadence_3s", "Cadence (3 sec avg)"), - GRADE("grade", "Grade"), - POWER_BALANCE("power_balance", "Power Balance"), - ROUTE_PROGRESS("route_progress", "Route Progress"), - REMAINING_ROUTE("route_progress_remaining", "Route Remaining"), - FRONT_GEAR("front_gear", "Front Gear"), - REAR_GEAR("rear_gear", "Rear Gear"); +enum class SelectedSource(val id: String, val labelResId: Int) { + NONE("none", R.string.source_none), + HEART_RATE("hr", R.string.source_heart_rate), + POWER("power", R.string.source_power), + POWER_3S("power_3s", R.string.source_power_3s), + POWER_10S("power_10s", R.string.source_power_10s), + SPEED("speed", R.string.source_speed), + SPEED_3S("speed_3s", R.string.source_speed_3s), + CADENCE("cadence", R.string.source_cadence), + CADENCE_3S("cadence_3s", R.string.source_cadence_3s), + GRADE("grade", R.string.source_grade), + POWER_BALANCE("power_balance", R.string.source_power_balance), + ROUTE_PROGRESS("route_progress", R.string.source_route_progress), + REMAINING_ROUTE("route_progress_remaining", R.string.source_route_remaining), + FRONT_GEAR("front_gear", R.string.source_front_gear), + REAR_GEAR("rear_gear", R.string.source_rear_gear); fun isPower() = this == POWER || this == POWER_3S || this == POWER_10S } @@ -121,7 +122,7 @@ fun BarSelectDialog(currentSelectedSource: SelectedSource, onHide: () -> Unit, o onSelect(pattern) }) Text( - text = pattern.label, + text = stringResource(pattern.labelResId), modifier = Modifier.padding(start = 10.dp) ) } @@ -305,7 +306,7 @@ fun MainScreen(onFinish: () -> Unit) { Column(modifier = Modifier .fillMaxSize() .background(MaterialTheme.colorScheme.background)) { - TopAppBar(title = { Text("Powerbar") }) + TopAppBar(title = { Text(stringResource(R.string.powerbar_title)) }) Column(modifier = Modifier .padding(5.dp) .verticalScroll(rememberScrollState()) @@ -313,11 +314,11 @@ fun MainScreen(onFinish: () -> Unit) { if (showAlerts){ if(!karooConnected){ - Text(modifier = Modifier.padding(5.dp), text = "Could not read device status. Is your Karoo updated?") + Text(modifier = Modifier.padding(5.dp), text = stringResource(R.string.karoo_connection_error)) } if (!givenPermissions) { - Text(modifier = Modifier.padding(5.dp), text = "You have not granted the permission to show the power bar overlay. Please do so.") + Text(modifier = Modifier.padding(5.dp), text = stringResource(R.string.permission_not_granted)) FilledTonalButton(modifier = Modifier .fillMaxWidth() @@ -325,17 +326,17 @@ fun MainScreen(onFinish: () -> Unit) { val myIntent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) startActivity(ctx, myIntent, null) }) { - Icon(Icons.Default.Build, contentDescription = "Give permission") + Icon(Icons.Default.Build, contentDescription = stringResource(R.string.content_desc_give_permission)) Spacer(modifier = Modifier.width(5.dp)) - Text("Give permission") + Text(stringResource(R.string.give_permission)) } } } Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 10.dp)) { - Text("Top Bar", style = MaterialTheme.typography.titleMedium) + Text(stringResource(R.string.top_bar), style = MaterialTheme.typography.titleMedium) Spacer(modifier = Modifier.weight(1f)) - Text("Split") + Text(stringResource(R.string.split)) Spacer(modifier = Modifier.width(10.dp)) Switch(checked = splitTopBar, onCheckedChange = { splitTopBar = it @@ -350,9 +351,9 @@ fun MainScreen(onFinish: () -> Unit) { onClick = { topBarLeftDialogVisible = true }) { - Icon(Icons.Default.Build, contentDescription = "Select", modifier = Modifier.size(20.dp)) + Icon(Icons.Default.Build, contentDescription = stringResource(R.string.content_desc_select), modifier = Modifier.size(20.dp)) Spacer(modifier = Modifier.width(5.dp)) - Text("Top Bar (Left): ${topSelectedSourceLeft.label}", modifier = Modifier.weight(1.0f)) + Text(stringResource(R.string.top_bar_left, stringResource(topSelectedSourceLeft.labelResId)), modifier = Modifier.weight(1.0f)) } if (topBarLeftDialogVisible){ @@ -369,9 +370,9 @@ fun MainScreen(onFinish: () -> Unit) { onClick = { topBarRightDialogVisible = true }) { - Icon(Icons.Default.Build, contentDescription = "Select", modifier = Modifier.size(20.dp)) + Icon(Icons.Default.Build, contentDescription = stringResource(R.string.content_desc_select), modifier = Modifier.size(20.dp)) Spacer(modifier = Modifier.width(5.dp)) - Text("Top Bar (Right): ${topSelectedSourceRight.label}", modifier = Modifier.weight(1.0f)) + Text(stringResource(R.string.top_bar_right, stringResource(topSelectedSourceRight.labelResId)), modifier = Modifier.weight(1.0f)) } if (topBarRightDialogVisible){ @@ -388,9 +389,9 @@ fun MainScreen(onFinish: () -> Unit) { onClick = { topBarDialogVisible = true }) { - Icon(Icons.Default.Build, contentDescription = "Select", modifier = Modifier.size(20.dp)) + Icon(Icons.Default.Build, contentDescription = stringResource(R.string.content_desc_select), modifier = Modifier.size(20.dp)) Spacer(modifier = Modifier.width(5.dp)) - Text("Top Bar: ${topSelectedSource.label}", modifier = Modifier.weight(1.0f)) + Text(stringResource(R.string.top_bar_single, stringResource(topSelectedSource.labelResId)), modifier = Modifier.weight(1.0f)) } } @@ -403,9 +404,9 @@ fun MainScreen(onFinish: () -> Unit) { } Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 10.dp)) { - Text("Bottom Bar", style = MaterialTheme.typography.titleMedium) + Text(stringResource(R.string.bottom_bar), style = MaterialTheme.typography.titleMedium) Spacer(modifier = Modifier.weight(1f)) - Text("Split") + Text(stringResource(R.string.split)) Spacer(modifier = Modifier.width(10.dp)) Switch(checked = splitBottomBar, onCheckedChange = { splitBottomBar = it @@ -420,9 +421,9 @@ fun MainScreen(onFinish: () -> Unit) { onClick = { bottomBarLeftDialogVisible = true }) { - Icon(Icons.Default.Build, contentDescription = "Select", modifier = Modifier.size(20.dp)) + Icon(Icons.Default.Build, contentDescription = stringResource(R.string.content_desc_select), modifier = Modifier.size(20.dp)) Spacer(modifier = Modifier.width(5.dp)) - Text("Bottom Bar (Left): ${bottomSelectedSourceLeft.label}", modifier = Modifier.weight(1.0f)) + Text(stringResource(R.string.bottom_bar_left, stringResource(bottomSelectedSourceLeft.labelResId)), modifier = Modifier.weight(1.0f)) } if (bottomBarLeftDialogVisible){ @@ -439,9 +440,9 @@ fun MainScreen(onFinish: () -> Unit) { onClick = { bottomBarRightDialogVisible = true }) { - Icon(Icons.Default.Build, contentDescription = "Select", modifier = Modifier.size(20.dp)) + Icon(Icons.Default.Build, contentDescription = stringResource(R.string.content_desc_select), modifier = Modifier.size(20.dp)) Spacer(modifier = Modifier.width(5.dp)) - Text("Bottom Bar (Right): ${bottomSelectedSourceRight.label}", modifier = Modifier.weight(1.0f)) + Text(stringResource(R.string.bottom_bar_right, stringResource(bottomSelectedSourceRight.labelResId)), modifier = Modifier.weight(1.0f)) } if (bottomBarRightDialogVisible){ @@ -458,9 +459,9 @@ fun MainScreen(onFinish: () -> Unit) { onClick = { bottomBarDialogVisible = true }) { - Icon(Icons.Default.Build, contentDescription = "Select", modifier = Modifier.size(20.dp)) + Icon(Icons.Default.Build, contentDescription = stringResource(R.string.content_desc_select), modifier = Modifier.size(20.dp)) Spacer(modifier = Modifier.width(5.dp)) - Text("Bottom Bar: ${bottomSelectedSource.label}", modifier = Modifier.weight(1.0f)) + Text(stringResource(R.string.bottom_bar_single, stringResource(bottomSelectedSource.labelResId)), modifier = Modifier.weight(1.0f)) } } @@ -473,22 +474,22 @@ fun MainScreen(onFinish: () -> Unit) { } apply { - val dropdownOptions = CustomProgressBarBarSize.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } + val dropdownOptions = CustomProgressBarBarSize.entries.toList().map { unit -> DropdownOption(unit.id, stringResource(unit.labelResId)) } val dropdownInitialSelection by remember(barBarSize) { mutableStateOf(dropdownOptions.find { option -> option.id == barBarSize.id }!!) } - Dropdown(label = "Bar Size", options = dropdownOptions, selected = dropdownInitialSelection) { selectedOption -> + Dropdown(label = stringResource(R.string.bar_size), options = dropdownOptions, selected = dropdownInitialSelection) { selectedOption -> barBarSize = CustomProgressBarBarSize.entries.find { unit -> unit.id == selectedOption.id }!! coroutineScope.launch { updateSettings() } } } apply { - val dropdownOptions = CustomProgressBarFontSize.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } + val dropdownOptions = CustomProgressBarFontSize.entries.toList().map { unit -> DropdownOption(unit.id, stringResource(unit.labelResId)) } val dropdownInitialSelection by remember(barFontSize) { mutableStateOf(dropdownOptions.find { option -> option.id == barFontSize.id }!!) } - Dropdown(label = "Text Size", options = dropdownOptions, selected = dropdownInitialSelection) { selectedOption -> + Dropdown(label = stringResource(R.string.text_size), options = dropdownOptions, selected = dropdownInitialSelection) { selectedOption -> barFontSize = CustomProgressBarFontSize.entries.find { unit -> unit.id == selectedOption.id }!! coroutineScope.launch { updateSettings() } } @@ -506,8 +507,8 @@ fun MainScreen(onFinish: () -> Unit) { .absolutePadding(right = 2.dp) .onFocusEvent(::updateFocus), onValueChange = { minSpeed = it.filter { c -> c.isDigit() } }, - label = { Text("Min Speed") }, - suffix = { Text(if (isImperial) "mph" else "kph") }, + label = { Text(stringResource(R.string.min_speed)) }, + suffix = { Text(stringResource(if (isImperial) R.string.unit_mph else R.string.unit_kph)) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true ) @@ -517,8 +518,8 @@ fun MainScreen(onFinish: () -> Unit) { .absolutePadding(left = 2.dp) .onFocusEvent(::updateFocus), onValueChange = { maxSpeed = it.filter { c -> c.isDigit() } }, - label = { Text("Max Speed") }, - suffix = { Text(if (isImperial) "mph" else "kph") }, + label = { Text(stringResource(R.string.max_speed)) }, + suffix = { Text(stringResource(if (isImperial) R.string.unit_mph else R.string.unit_kph)) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true ) @@ -535,7 +536,7 @@ fun MainScreen(onFinish: () -> Unit) { coroutineScope.launch { updateSettings() } }) Spacer(modifier = Modifier.width(10.dp)) - Text("Use custom power range") + Text(stringResource(R.string.use_custom_power_range)) } if(useCustomPowerRange){ @@ -545,8 +546,8 @@ fun MainScreen(onFinish: () -> Unit) { .absolutePadding(right = 2.dp) .onFocusEvent(::updateFocus), onValueChange = { customMinPower = it.filter { c -> c.isDigit() } }, - label = { Text("Min Power", fontSize = 12.sp) }, - suffix = { Text("W") }, + label = { Text(stringResource(R.string.min_power), fontSize = 12.sp) }, + suffix = { Text(stringResource(R.string.unit_watts)) }, placeholder = { Text("$profileMinPower") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true @@ -557,8 +558,8 @@ fun MainScreen(onFinish: () -> Unit) { .absolutePadding(left = 2.dp) .onFocusEvent(::updateFocus), onValueChange = { customMaxPower = it.filter { c -> c.isDigit() } }, - label = { Text("Max Power", fontSize = 12.sp) }, - suffix = { Text("W") }, + label = { Text(stringResource(R.string.max_power), fontSize = 12.sp) }, + suffix = { Text(stringResource(R.string.unit_watts)) }, placeholder = { Text("$profileMaxPower") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true @@ -577,7 +578,7 @@ fun MainScreen(onFinish: () -> Unit) { coroutineScope.launch { updateSettings() } }) Spacer(modifier = Modifier.width(10.dp)) - Text("Use custom HR range") + Text(stringResource(R.string.use_custom_hr_range)) } if (useCustomHrRange){ @@ -587,8 +588,8 @@ fun MainScreen(onFinish: () -> Unit) { .absolutePadding(right = 2.dp) .onFocusEvent(::updateFocus), onValueChange = { customMinHr = it.filter { c -> c.isDigit() } }, - label = { Text("Min Hr") }, - suffix = { Text("bpm") }, + label = { Text(stringResource(R.string.min_hr)) }, + suffix = { Text(stringResource(R.string.unit_bpm)) }, placeholder = { if(profileRestHr > 0) Text("$profileRestHr") else Unit }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true @@ -599,8 +600,8 @@ fun MainScreen(onFinish: () -> Unit) { .absolutePadding(left = 2.dp) .onFocusEvent(::updateFocus), onValueChange = { customMaxHr = it.filter { c -> c.isDigit() } }, - label = { Text("Max Hr") }, - suffix = { Text("bpm") }, + label = { Text(stringResource(R.string.max_hr)) }, + suffix = { Text(stringResource(R.string.unit_bpm)) }, placeholder = { if(profileMaxHr > 0) Text("$profileMaxHr") else Unit }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true @@ -621,8 +622,8 @@ fun MainScreen(onFinish: () -> Unit) { .absolutePadding(right = 2.dp) .onFocusEvent(::updateFocus), onValueChange = { minCadence = it.filter { c -> c.isDigit() } }, - label = { Text("Min Cadence") }, - suffix = { Text("rpm") }, + label = { Text(stringResource(R.string.min_cadence)) }, + suffix = { Text(stringResource(R.string.unit_rpm)) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true ) @@ -632,8 +633,8 @@ fun MainScreen(onFinish: () -> Unit) { .absolutePadding(left = 2.dp) .onFocusEvent(::updateFocus), onValueChange = { maxCadence = it.filter { c -> c.isDigit() } }, - label = { Text("Min Cadence") }, - suffix = { Text("rpm") }, + label = { Text(stringResource(R.string.max_cadence)) }, + suffix = { Text(stringResource(R.string.unit_rpm)) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true ) @@ -650,8 +651,8 @@ fun MainScreen(onFinish: () -> Unit) { .absolutePadding(right = 2.dp) .onFocusEvent(::updateFocus), onValueChange = { minGrade = it.filterIndexed { index, c -> c.isDigit() || (c == '-' && index == 0) } }, - label = { Text("Min Grade") }, - suffix = { Text("%") }, + label = { Text(stringResource(R.string.min_grade)) }, + suffix = { Text(stringResource(R.string.unit_percent)) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true ) @@ -661,8 +662,8 @@ fun MainScreen(onFinish: () -> Unit) { .absolutePadding(left = 2.dp) .onFocusEvent(::updateFocus), onValueChange = { maxGrade = it.filterIndexed { index, c -> c.isDigit() || (c == '-' && index == 0) } }, - label = { Text("Max Grade") }, - suffix = { Text("%") }, + label = { Text(stringResource(R.string.max_grade)) }, + suffix = { Text(stringResource(R.string.unit_percent)) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true ) @@ -675,7 +676,7 @@ fun MainScreen(onFinish: () -> Unit) { coroutineScope.launch { updateSettings() } }) Spacer(modifier = Modifier.width(10.dp)) - Text("Color based on HR / power zones") + Text(stringResource(R.string.color_based_on_zones)) } Row(verticalAlignment = Alignment.CenterVertically) { @@ -684,7 +685,7 @@ fun MainScreen(onFinish: () -> Unit) { coroutineScope.launch { updateSettings() } }) Spacer(modifier = Modifier.width(10.dp)) - Text("Show value on bars") + Text(stringResource(R.string.show_value_on_bars)) } Row(verticalAlignment = Alignment.CenterVertically) { @@ -693,7 +694,7 @@ fun MainScreen(onFinish: () -> Unit) { coroutineScope.launch { updateSettings() } }) Spacer(modifier = Modifier.width(10.dp)) - Text("Solid background") + Text(stringResource(R.string.solid_background)) } Row(verticalAlignment = Alignment.CenterVertically) { @@ -702,7 +703,7 @@ fun MainScreen(onFinish: () -> Unit) { coroutineScope.launch { updateSettings() } }) Spacer(modifier = Modifier.width(10.dp)) - Text("Only show while riding") + Text(stringResource(R.string.only_show_while_riding)) } Spacer(modifier = Modifier.padding(30.dp)) @@ -711,7 +712,7 @@ fun MainScreen(onFinish: () -> Unit) { Image( painter = painterResource(id = R.drawable.back), - contentDescription = "Back", + contentDescription = stringResource(R.string.content_desc_back), modifier = Modifier .align(Alignment.BottomStart) .padding(bottom = 10.dp) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml new file mode 100644 index 0000000..12ee110 --- /dev/null +++ b/app/src/main/res/values-de/strings.xml @@ -0,0 +1,77 @@ + + Powerbar + Powerbar + + + Powerbar + Gerätestatus konnte nicht gelesen werden. Ist Ihr Karoo aktualisiert? + Sie haben die Berechtigung zum Anzeigen der Powerbar nicht erteilt. Bitte tun Sie dies. + Berechtigung erteilen + Oben + Unten + Teilen + Oben (Links): %s + Oben (Rechts): %s + Oben: %s + Unten (Links): %s + Unten (Rechts): %s + Unten: %s + Balkengröße + Textgröße + Min. Geschwindigkeit + Max. Geschwindigkeit + Eigenen Leistungsbereich verwenden + Min. Leistung + Max. Leistung + Eigenen HF-Bereich verwenden + Min. HF + Max. HF + Min. Trittfrequenz + Max. Trittfrequenz + Min. Steigung + Max. Steigung + Farbe basierend auf HF-/Leistungszonen + Werte auf Balken anzeigen + Fester Hintergrund + Nur während der Fahrt anzeigen + + + Keine + Herzfrequenz + Leistung + Leistung (3 Sek. Ø) + Leistung (10 Sek. Ø) + Geschwindigkeit + Geschwindigkeit (3 Sek. Ø) + Trittfrequenz + Trittfrequenz (3 Sek. Ø) + Steigung + Leistungsbalance + Routenfortschritt + Verbleibende Route + Vorderer Gang + Hinterer Gang + + + mph + km/h + W + S/min + U/min + % + + + Berechtigung erteilen + Auswählen + Zurück + + + Anzeige über anderen Apps + Hintergrunddienst + + + Keine + Klein + Mittel + Groß + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6fd0ff1..fc85420 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,77 @@ Powerbar Powerbar + + + Powerbar + Could not read device status. Is your Karoo updated? + You have not granted the permission to show the power bar overlay. Please do so. + Give permission + Top Bar + Bottom Bar + Split + Top Bar (Left): %s + Top Bar (Right): %s + Top Bar: %s + Bottom Bar (Left): %s + Bottom Bar (Right): %s + Bottom Bar: %s + Bar Size + Text Size + Min Speed + Max Speed + Use custom power range + Min Power + Max Power + Use custom HR range + Min Hr + Max Hr + Min Cadence + Max Cadence + Min Grade + Max Grade + Color based on HR / power zones + Show value on bars + Solid background + Only show while riding + + + None + Heart Rate + Power + Power (3 sec avg) + Power (10 sec avg) + Speed + Speed (3 sec avg) + Cadence + Cadence (3 sec avg) + Grade + Power Balance + Route Progress + Route Remaining + Front Gear + Rear Gear + + + mph + kph + W + bpm + rpm + % + + + Give permission + Select + Back + + + Displaying on top of other apps + Background Service + + + None + Small + Medium + Large \ No newline at end of file