mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 21:07:01 -06:00
fix(android): localize settings and thinking presentation
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user