在 Jetpack Compose 应用中更新语言需要结合传统的 Android 语言配置方法和 Compose 的重组机制。以下是完整的实现方案:
1. 创建语言管理类
object LocaleManager {
private var currentLocale: Locale = Locale.getDefault()
fun setLocale(context: Context, locale: Locale) {
currentLocale = locale
updateResources(context, locale)
persistLocale(context, locale)
}
fun getCurrentLocale(): Locale = currentLocale
private fun updateResources(context: Context, locale: Locale) {
Locale.setDefault(locale)
val resources = context.resources
val configuration = Configuration(resources.configuration)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
configuration.setLocale(locale)
context.createConfigurationContext(configuration)
} else {
@Suppress("DEPRECATION")
configuration.locale = locale
resources.updateConfiguration(configuration, resources.displayMetrics)
}
}
private fun persistLocale(context: Context, locale: Locale) {
context.getSharedPreferences("AppPreferences", Context.MODE_PRIVATE)
.edit()
.putString("app_language", locale.language)
.apply()
}
fun getPersistedLocale(context: Context): Locale {
val prefs = context.getSharedPreferences("AppPreferences", Context.MODE_PRIVATE)
val lang = prefs.getString("app_language", Locale.getDefault().language) ?: Locale.getDefault().language
return Locale(lang)
}
}
2. 创建可组合函数管理语言状态
@Composable
fun rememberLocaleState(context: Context): State<Locale> {
val localeState = remember { mutableStateOf(LocaleManager.getPersistedLocale(context)) }
DisposableEffect(Unit) {
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
if (key == "app_language") {
localeState.value = LocaleManager.getPersistedLocale(context)
}
}
val prefs = context.getSharedPreferences("AppPreferences", Context.MODE_PRIVATE)
prefs.registerOnSharedPreferenceChangeListener(listener)
onDispose {
prefs.unregisterOnSharedPreferenceChangeListener(listener)
}
}
return localeState
}
3. 创建自定义 Activity
abstract class LocaleAwareComponentActivity : ComponentActivity() {
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(
newBase.wrapWithLocale(LocaleManager.getPersistedLocale(newBase))
)
}
private fun Context.wrapWithLocale(locale: Locale): Context {
val configuration = Configuration(resources.configuration)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
configuration.setLocale(locale)
createConfigurationContext(configuration)
} else {
@Suppress("DEPRECATION")
configuration.locale = locale
resources.updateConfiguration(configuration, resources.displayMetrics)
this
}
}
}
4. 在应用中使用
MainActivity.kt
class MainActivity : LocaleAwareComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val localeState = rememberLocaleState(this)
// 使用DisposableEffect确保语言变更时更新Activity
DisposableEffect(localeState.value) {
onDispose { }
}
YourAppTheme(locale = localeState.value) {
AppContent(localeState)
}
}
}
}
主题设置
@Composable
fun YourAppTheme(
locale: Locale = LocaleManager.getCurrentLocale(),
content: @Composable () -> Unit
) {
val context = LocalContext.current
CompositionLocalProvider(
LocalContext provides context.wrapWithLocale(locale),
content = content
)
}
private fun Context.wrapWithLocale(locale: Locale): Context {
val configuration = Configuration(resources.configuration)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
configuration.setLocale(locale)
createConfigurationContext(configuration)
} else {
@Suppress("DEPRECATION")
configuration.locale = locale
resources.updateConfiguration(configuration, resources.displayMetrics)
this
}
}
语言切换界面
@Composable
fun LanguageSwitcher(localeState: State<Locale>) {
val context = LocalContext.current
Column {
val currentLocale = localeState.value
Text("Current language: ${currentLocale.displayLanguage}")
Button(onClick = {
LocaleManager.setLocale(context, Locale("en"))
(context as? Activity)?.recreate()
}) {
Text("English")
}
Button(onClick = {
LocaleManager.setLocale(context, Locale("es"))
(context as? Activity)?.recreate()
}) {
Text("Español")
}
Button(onClick = {
LocaleManager.setLocale(context, Locale("fr"))
(context as? Activity)?.recreate()
}) {
Text("Français")
}
}
}
5. 完整应用结构示例
@Composable
fun AppContent(localeState: State<Locale>) {
val context = LocalContext.current
Scaffold(
topBar = { TopAppBar(title = { Text(stringResource(R.string.app_name)) }) },
content = { padding ->
Column(modifier = Modifier.padding(padding)) {
LanguageSwitcher(localeState)
// 示例文本,会随语言变化
Text(stringResource(R.string.hello_world))
}
}
)
}
注意事项
-
Activity重启:在Compose中更改语言后,仍然需要调用
activity.recreate()
来完全刷新界面 -
资源文件:确保在
res/values-<language code>
目录下有对应的翻译资源 -
动态文本:对于动态生成的文本(如从API获取的),需要在代码中根据当前语言处理
-
测试:特别注意测试不同Android版本的兼容性
-
性能优化:频繁的语言切换可能会影响性能,考虑添加适当的防抖机制
这种方法结合了传统的Android语言配置和Compose的响应式特性,能够在保持良好用户体验的同时实现动态语言切换。
Android应用中设置非系统默认语言(java)-CSDN博客
Android应用中设置非系统默认语言(使用Kotlin)-CSDN博客