fix(android): localize settings and thinking presentation

This commit is contained in:
Vincent Koc
2026-07-12 11:02:07 +02:00
parent c984bf0e8b
commit c5f94adef4
5 changed files with 43 additions and 17 deletions
@@ -1311,12 +1311,17 @@ private fun SectionLabel(
title: String,
action: (@Composable () -> Unit)? = null,
) {
val localizedTitle = nativeString(title)
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(text = nativeString(title).uppercase(), style = ClawTheme.type.caption.copy(fontSize = 12.5.sp, lineHeight = 16.sp), color = ClawTheme.colors.textMuted)
Text(
text = localizedUppercase(localizedTitle, currentAppLanguage().languageTag),
style = ClawTheme.type.caption.copy(fontSize = 12.5.sp, lineHeight = 16.sp),
color = ClawTheme.colors.textMuted,
)
action?.invoke()
}
}
@@ -1326,6 +1331,7 @@ private fun ModuleListRow(
row: ModuleRow,
onClick: () -> Unit,
) {
val localizedTitle = nativeString(row.title)
Surface(color = Color.Transparent, contentColor = ClawTheme.colors.text) {
Row(
modifier =
@@ -1341,7 +1347,7 @@ private fun ModuleListRow(
Icon(imageVector = row.icon, contentDescription = null, modifier = Modifier.size(20.dp), tint = ClawTheme.colors.text)
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(1.dp)) {
Text(
text = nativeString(row.title),
text = localizedTitle,
style = ClawTheme.type.body,
color = ClawTheme.colors.text,
maxLines = 1,
@@ -1353,7 +1359,7 @@ private fun ModuleListRow(
}
Icon(
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight,
contentDescription = nativeString("Open \${row.title}", row.title),
contentDescription = settingsRowDisclosureDescription(localizedTitle, opensRoute = true),
modifier = Modifier.size(17.dp),
tint = ClawTheme.colors.textMuted,
)
@@ -1898,8 +1904,9 @@ internal fun settingsSectionTitleForRoute(route: SettingsRoute): String =
@Composable
private fun SettingsSectionTitle(title: String) {
val localizedTitle = nativeString(title)
Text(
text = nativeString(title).uppercase(),
text = localizedUppercase(localizedTitle, currentAppLanguage().languageTag),
style = ClawTheme.type.caption.copy(fontSize = 12.sp, lineHeight = 16.sp),
color = ClawTheme.colors.textMuted,
)
@@ -1984,6 +1991,7 @@ private fun SettingsListRow(
showDisclosure: Boolean,
onClick: () -> Unit,
) {
val localizedTitle = nativeString(row.title)
Row(
modifier =
Modifier
@@ -1996,7 +2004,7 @@ private fun SettingsListRow(
horizontalArrangement = Arrangement.spacedBy(10.dp),
) {
Icon(imageVector = row.icon, contentDescription = null, modifier = Modifier.size(20.dp), tint = ClawTheme.colors.text)
Text(text = nativeString(row.title), style = ClawTheme.type.body, color = ClawTheme.colors.text, modifier = Modifier.weight(1f), maxLines = 1)
Text(text = localizedTitle, style = ClawTheme.type.body, color = ClawTheme.colors.text, modifier = Modifier.weight(1f), maxLines = 1)
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(5.dp)) {
if (row.value.isNotBlank()) {
Text(text = row.value, style = ClawTheme.type.caption.copy(fontSize = 13.sp, lineHeight = 17.sp), color = ClawTheme.colors.textMuted, maxLines = 1, overflow = TextOverflow.Ellipsis)
@@ -2007,12 +2015,7 @@ private fun SettingsListRow(
if (showDisclosure) {
Icon(
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight,
contentDescription =
if (row.route != null) {
nativeString("Open \${row.title}", row.title)
} else {
nativeString(row.title)
},
contentDescription = settingsRowDisclosureDescription(localizedTitle, opensRoute = row.route != null),
modifier = Modifier.size(17.dp),
tint = ClawTheme.colors.text,
)
@@ -2021,6 +2024,11 @@ private fun SettingsListRow(
}
}
internal fun settingsRowDisclosureDescription(
localizedTitle: String,
opensRoute: Boolean,
): String = if (opensRoute) nativeString("Open \${row.title}", localizedTitle) else localizedTitle
private fun relativeSessionTime(updatedAtMs: Long): String {
val deltaMs = (System.currentTimeMillis() - updatedAtMs).coerceAtLeast(0L)
val minutes = deltaMs / 60_000L
@@ -1920,7 +1920,16 @@ internal fun contextMeterLabel(
return if (thinkingSupported) nativeString("\$contextLabel · \${contextMeterThinkingLabel(thinkingLevel)}", contextLabel, contextMeterThinkingLabel(thinkingLevel)) else contextLabel
}
internal fun contextMeterThinkingLabel(value: String): String = value.trim().lowercase(Locale.US).ifEmpty { "off" }
internal fun contextMeterThinkingLabel(value: String): String {
val normalized = value.trim().lowercase(Locale.US).ifEmpty { "off" }
return when (normalized) {
"off" -> nativeString("Off")
"low" -> nativeString("Low")
"medium" -> nativeString("Medium")
"high" -> nativeString("High")
else -> normalized
}
}
internal fun chatThinkingSupported(
selection: ChatThinkingLevelSelection,
@@ -5,6 +5,7 @@ import ai.openclaw.app.i18n.joinedNativeText
import ai.openclaw.app.i18n.nativeText
import ai.openclaw.app.i18n.resolveNativeText
import ai.openclaw.app.i18n.verbatimText
import ai.openclaw.app.ui.chat.contextMeterThinkingLabel
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.LocaleListCompat
import kotlinx.coroutines.flow.MutableStateFlow
@@ -137,6 +138,8 @@ class AppLanguageTest {
setAppLanguage(AppLanguage.French)
assertEquals("Micro désactivé", retained.value)
assertEquals("Micro désactivé · raw", retainedComposite.value)
assertEquals("Élevé", contextMeterThinkingLabel("high"))
assertEquals("adaptive", contextMeterThinkingLabel("adaptive"))
assertEquals("Connexion…", gatewayConnectionStatusForDisplay("Connecting…"))
assertEquals(
"Impossible de charger les approbations.",
@@ -37,6 +37,12 @@ class ShellScreenLogicTest {
assertEquals("İLETİŞİM", localizedUppercase("iletişim", languageTag = "tr", fallbackLocale = Locale.US))
}
@Test
fun settingsDisclosureUsesTheLocalizedTitle() {
assertEquals("Open Nœuds et appareils", settingsRowDisclosureDescription("Nœuds et appareils", opensRoute = true))
assertEquals("Nœuds et appareils", settingsRowDisclosureDescription("Nœuds et appareils", opensRoute = false))
}
@Test
fun appearanceThemeModeDefaultsToDarkForExistingInstalls() {
assertEquals(AppearanceThemeMode.Dark, AppearanceThemeMode.fromRawValue(null))
@@ -34,7 +34,7 @@ class ChatContextMeterTest {
assertEquals(ChatContextUsage(totalTokens = 1_250L, totalTokensFresh = true, contextTokens = 5_000L), usage)
assertEquals(0.25f, contextMeterWidth(usage))
assertEquals("Context 25% · high", contextMeterLabel(usage, "high"))
assertEquals("Context 25% · High", contextMeterLabel(usage, "high"))
}
@Test
@@ -59,7 +59,7 @@ class ChatContextMeterTest {
)
assertEquals(ChatContextUsage(totalTokens = 41_000L, totalTokensFresh = true, contextTokens = 100_000L), usage)
assertEquals("Context 41% · off", contextMeterLabel(usage, "off"))
assertEquals("Context 41% · Off", contextMeterLabel(usage, "off"))
}
@Test
@@ -67,7 +67,7 @@ class ChatContextMeterTest {
val usage = ChatContextUsage(totalTokens = 8_200L, totalTokensFresh = true, contextTokens = null)
assertNull(contextMeterWidth(usage))
assertEquals("Context -- · medium", contextMeterLabel(usage, "medium"))
assertEquals("Context -- · Medium", contextMeterLabel(usage, "medium"))
}
@Test
@@ -75,7 +75,7 @@ class ChatContextMeterTest {
val usage = ChatContextUsage(totalTokens = 150_000L, totalTokensFresh = true, contextTokens = 100_000L)
assertEquals(1.0f, contextMeterWidth(usage))
assertEquals("Context 100% · low", contextMeterLabel(usage, "low"))
assertEquals("Context 100% · Low", contextMeterLabel(usage, "low"))
}
@Test
@@ -83,7 +83,7 @@ class ChatContextMeterTest {
val usage = ChatContextUsage(totalTokens = 82_000L, totalTokensFresh = false, contextTokens = 100_000L)
assertNull(contextMeterWidth(usage))
assertEquals("Context -- · high", contextMeterLabel(usage, "high"))
assertEquals("Context -- · High", contextMeterLabel(usage, "high"))
}
@Test