关于安卓切换语言这件事

365 阅读2分钟

前置操作

1. 在res文件下添加对应的values

values-xx-xx, 通用格式为values-<语言代号>-r<国家/地区代号>, 自己按需要查找添加。

image.png

2. 添加对应的locale-config文件

1. 手动添加

在res/xml下添加locales_config.xml,将涉及到的tag加入

<locale-config xmlns:android="http://schemas.android.com/apk/res/android" android:defaultLocale="en">
    <locale android:name="en"/>
    <locale android:name="cs"/>
    <locale android:name="da"/>
    <locale android:name="de"/>
    <locale android:name="es"/>
    <locale android:name="fr"/>
    <locale android:name="it"/>
    <locale android:name="ko"/>
    <locale android:name="nl"/>
    <locale android:name="pl"/>
    <locale android:name="pt-PT"/>
    <locale android:name="ru"/>
    <locale android:name="th"/>
    <locale android:name="uk"/>
    <locale android:name="vi"/>
    <locale android:name="zh-CN"/>
</locale-config>

接着在minifest.xml中应用该文件,可能会出现33+才能使用的提示,可以不管

<application
    android:localeConfig="@xml/locales_config"
/>
2. 通过gradle添加

在app模块下的build.gradle中添加

android {
    androidResources {
        generateLocaleConfig = true
    }
}

点击编译后,gradle会根据项目的res文件夹,在build/generated/res中生成对应的localeConfig文件,并且在manifest.xml文件中也会进行对应的android:localeConfig设置。

切换语言方法

官方推荐

1. 具体操作

导入谷歌appcompact的依赖。

implementation 'androidx.appcompat:appcompat:1.7.1'

调用AppCompatDelegate的方法即可

AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(languageTag));
2. 注意点(低于API33版本)

谷歌的方法虽然兼容了33以前的版本,但是需要添加一个服务在manifest.xml中

<application
  ...
  <service
    android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
    android:enabled="false"
    android:exported="false">
    <meta-data
      android:name="autoStoreLocales"
      android:value="true" />
  </service>
  ...
</application>

但是要注意,AppCompatDelegate.setApplicationLocales()的方法虽然做了兼容处理,但是对于API33以前的版本所做的操作,还是基于缓存,在Activity的attachContext方法中,替换Configuration的Locale。这个Service就是用来进行混存操作的,其中autoStoreLocales参数就是用来开启这个缓存的开关。

Tip,我在集成Compose时遇到的问题

切换语言后,Activity会自动重建,来进行语言的切换

可以在manifest.xml中添加以下代码,防止Activity重建。

android:configChanges="locale|layoutDirection"

API33+,没有问题,语言正常进行了切换。

但是 !!在API33之前的,没有办法切换。为啥??

因为在API33之前,AppCompatDelegate.setApplicationLocales(),只是进行了缓存的切换,具体应用的逻辑是在attchContext里面,当Activity没有重建的时候,就没有办法进行语言切换。当然了,下次启动APP,语言就换成了新的,但是显然这不是我们想要的。

好在我们是Compose,数据驱动UI的方式,很适合页面刷新。

本质上stringResource()的方法,也是通过context获取resource,然后获取对应的string。因此我们可以跟谷歌操作一样,对getResource方法进行修改。

private class LocalizedContextWrapper(
    base: Context,
    private val language: String
) : ContextWrapper(base) {

    private val localizedResources: Resources by lazy {
        val config = Configuration(baseContext.resources.configuration)
        config.setLocale(Locale.forLanguageTag(language))
        baseContext.createConfigurationContext(config).resources
    }

    override fun getResources(): Resources {
        return localizedResources
    }
}

@Composable
fun AppLanguage(
    language: String,
    content: @Composable () -> Unit
) {
    var context = LocalContext.current

    context =  remember(context, language) {
        LocalizedContextWrapper(context, language)
    }

    CompositionLocalProvider(
        LocalContext provides context,
        content = content
    )

}

依旧是我们自己本地缓存当前的语言,然后将context进行修改。由于是Compose,所以当我们切换语言后,页面会自动重构。

引用:

  1. 官方文档:developer.android.com/guide/topic…
  2. GSY的Compose项目 github.com/CarGuo/GSYG…