fix(android): prefer live AppCompat locales

This commit is contained in:
Vincent Koc
2026-07-12 13:02:25 +02:00
parent 8757142752
commit 1d4a401b0d
2 changed files with 27 additions and 19 deletions

View File

@@ -5,6 +5,7 @@ import android.content.Context
import android.content.res.Configuration
import android.os.Build
import android.util.Xml
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.core.app.LocaleManagerCompat
@@ -135,7 +136,13 @@ internal object NativeStringResources {
fun install(context: Context) {
val appContext = context.applicationContext
applicationContext = appContext
val requestedLocales = appContext.requestedApplicationLocales()
val liveLocales =
if (Build.VERSION.SDK_INT >= 33) {
LocaleManagerCompat.getApplicationLocales(appContext)
} else {
AppCompatDelegate.getApplicationLocales()
}
val requestedLocales = liveLocales.takeUnless { it.isEmpty } ?: appContext.readStoredAppLocales()
applicationLocaleMode =
if (requestedLocales.isEmpty) {
ApplicationLocaleMode.System(ConfigurationCompat.getLocales(appContext.resources.configuration))
@@ -162,14 +169,19 @@ internal object NativeStringResources {
@Synchronized
fun setConfigurationLocales(configuration: Configuration) {
val requestedLocales =
applicationContext?.requestedApplicationLocales()
?: LocaleListCompat.getEmptyLocaleList()
val previousMode = applicationLocaleMode
val context = applicationContext
val liveLocales =
when {
context == null -> LocaleListCompat.getEmptyLocaleList()
Build.VERSION.SDK_INT >= 33 -> LocaleManagerCompat.getApplicationLocales(context)
else -> AppCompatDelegate.getApplicationLocales()
}
applicationLocaleMode =
if (requestedLocales.isEmpty) {
ApplicationLocaleMode.System(ConfigurationCompat.getLocales(configuration))
} else {
ApplicationLocaleMode.Pinned(requestedLocales)
when {
!liveLocales.isEmpty -> ApplicationLocaleMode.Pinned(liveLocales)
Build.VERSION.SDK_INT < 33 && previousMode is ApplicationLocaleMode.Pinned -> previousMode
else -> ApplicationLocaleMode.System(ConfigurationCompat.getLocales(configuration))
}
localizedContext = null
}
@@ -211,12 +223,6 @@ private fun Context.localizedContext(locales: LocaleListCompat): Context =
createConfigurationContext(configuration)
}
private fun Context.requestedApplicationLocales(): LocaleListCompat =
LocaleManagerCompat
.getApplicationLocales(this)
.takeUnless { it.isEmpty }
?: readStoredAppLocales()
private fun Context.readStoredAppLocales(): LocaleListCompat {
if (Build.VERSION.SDK_INT >= 33) return LocaleListCompat.getEmptyLocaleList()
// AppCompat only hydrates auto-stored locales when a delegate attaches. A cold service

View File

@@ -2,6 +2,7 @@ package ai.openclaw.app.i18n
import android.content.Context
import android.content.res.Configuration
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.ConfigurationCompat
import androidx.core.os.LocaleListCompat
import kotlinx.coroutines.CompletableDeferred
@@ -70,20 +71,21 @@ class NativeStringsTest {
}
@Test
fun configurationLocaleRefreshesPersistedAppLocale() {
fun configurationLocaleUsesLiveAppCompatLocaleBeforeStorage() {
val app = RuntimeEnvironment.getApplication()
persistAppLocales(app, "fr")
persistAppLocales(app, "en")
try {
NativeStringResources.install(app)
assertEquals("Micro désactivé", nativeString("Mic off"))
assertEquals("Mic off", nativeString("Mic off"))
persistAppLocales(app, "de")
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags("fr"))
val configuration = Configuration(app.resources.configuration)
ConfigurationCompat.setLocales(configuration, LocaleListCompat.forLanguageTags("en"))
NativeStringResources.setConfigurationLocales(configuration)
assertEquals("Mikrofon aus", nativeString("Mic off"))
assertEquals("Micro désactivé", nativeString("Mic off"))
} finally {
AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList())
app.deleteFile(APP_LOCALES_FILE)
NativeStringResources.setApplicationLocales(LocaleListCompat.getEmptyLocaleList())
}