在许多情况下,多语言用户会将其系统语言设置为某一种语言(例如英语),但又想为特定应用选择其他语言(例如荷兰语、中文或印地语)。为了帮助应用为这些用户提供更好的体验,Android 13 针对支持多种语言的应用引入了以下功能:
系统设置:用户可以在这个集中位置为每个应用选择首选语言。
您可以将应用配置为自动生成文件,以便支持单个应用语言设置,并显示在系统设置中。如需了解详情,请参阅有关启用各应用自动设定语言支持的说明。
其他 API:借助这些公共 API(例如
LocaleManager中的setApplicationLocales和getApplicationLocales方法),应用可以在运行时设置不同于系统语言的其他语言。这些 API 会自动与系统设置同步;因此,使用这些 API 创建自定义应用内语言选择器的应用将确保用户获得一致的用户体验,无论他们在何处选择语言偏好设置。公共 API 还有助于减少样板代码量、支持拆分 APK,并且支持应用自动备份,以存储应用级的用户语言设置。
为了向后兼容以前的 Android 版本,AndroidX 中也提供了等效的 API。不过,对于 Android 12(API 级别 32)及更低版本,向后兼容的 API 可与 AppCompatActivity 上下文一起使用,而非应用上下文。请使用 Appcompat 1.6.0 或更高版本访问向后兼容的 API。
此功能的实现方式概览
下表根据不同的用例展示了推荐的实现方式。
| 用例 | 推荐的实现方式 |
|---|---|
| 您的应用没有应用内语言选择器 |
|
| 您的应用已有应用内语言选择器 |
|
面向用户的系统设置
从 Android 13 开始,Android 在系统设置中引入了一个集中位置,以用于设置单个应用语言。为了确保在搭载 Android 13 或更高版本的设备上,在系统设置中可以配置应用的语言,请启用各应用自动设定语言支持(推荐)或手动配置支持。
启用各应用自动设定语言支持
这是向应用添加应用级语言支持的推荐方式,因为它不需要修改 XML。
从 Android Studio Giraffe 和 AGP 8.1 开始,您可以将应用配置为自动支持单个应用语言设置。AGP 会根据您的项目资源生成 LocaleConfig 文件,并在最终清单文件中添加对该文件的引用,这样您就不再需要手动操作。AGP 使用应用模块的 res 文件夹中的资源以及任何库模块依赖项来确定要在 LocaleConfig 文件中添加的语言区域。这意味着,如果您向应用中添加了新语言的资源,则无需担心更新 LocaleConfig 文件。
请注意,各应用自动语言功能支持搭载 Android 13(API 级别 33)或更高版本的应用。如需使用此功能,您必须将 compileSdkVersion 设为 33 或更高版本。如需为之前版本的 Android 配置单个应用语言设置,您仍然需要使用 API 和应用内语言选择器。
如需启用各应用自动设定语言支持,请按以下步骤操作:
- 如需启用此功能,请使用模块级
build.gradle.kts文件(如果您使用的是 Groovy,则为build.gradle文件)的androidResources {}块中的generateLocaleConfig设置。该功能默认处于关闭状态。
Kotlin
android {
androidResources {
generateLocaleConfig = true
}
}
Groovy
android {
androidResources {
generateLocaleConfig true
}
}
- 指定默认语言区域:
- 在应用模块的
res文件夹中,创建一个名为resources.properties的新文件。 - 在
resources.properties文件中,使用unqualifiedResLocale标签设置默认语言区域。如需设置语言区域名称的格式,请参阅如何构成语言区域名称。
- 在应用模块的
AGP 会将此默认语言区域和您指定的所有其他语言区域(使用 res 文件夹中的 values-* 目录)添加到自动生成的 LocaleConfig 文件中。
如何构成语言区域名称
将语言代码与可选的脚本代码和区域代码(使用短划线将各部分隔开)组合在一起即可构成语言区域名称。
- 语言:使用由两个或三个字母组成的 ISO 639-2 代码。
- 文字(可选):使用 ISO 15924 代码。
- 区域(可选):使用由两个字母组成的 ISO 3166-1-alpha-2 代码或三位数的 UN_M.49 代码。
例如,如果您的默认语言区域是美式英语:
unqualifiedResLocale=en-US
使用 android:localeConfig 将受支持的语言添加到系统设置中
您可以手动设置应用,以确保在搭载 Android 13 或更高版本的设备上可以在系统设置中配置其语言。如需实现此目的,请创建一个 locale_config XML 文件,并使用 android:localeConfig 属性将其添加到应用的清单中。省略 android:localeConfig 清单条目表明用户不应该能够在他们的系统设置中独立于系统语言来设置您的应用语言。
若要将应用支持的语言手动添加到用户的系统设置中,请执行以下操作:
创建一个名为
res/xml/locale_config.xml的文件,并指定应用的语言,包括应用的最终回退语言区域(即在res/values/strings.xml中指定的语言区域)。如需了解格式要求,请参阅如何构成语言区域名称。另请参阅示例
locale_config.xml文件,了解最常用的语言区域列表。例如,对于支持以下语言的应用,可以为
locale_config.xml文件设置如下格式:- 使用“英语(美国)”作为最终回退语言区域
- 英语(英国)
- 法语
- 日语
- 中文(简体,澳门)
- 中文(繁体,澳门)
<?xml version="1.0" encoding="utf-8"?> <locale-config xmlns:android="http://schemas.android.com/apk/res/android"> <locale android:name="en-US"/> <locale android:name="en-GB"/> <locale android:name="fr"/> <locale android:name="ja"/> <locale android:name="zh-Hans-MO"/> <locale android:name="zh-Hant-MO"/> </locale-config>在清单中,添加一行指向这个新文件的代码:
<manifest> ... <application ... android:localeConfig="@xml/locale_config"> </application> </manifest>
您可以使用 LocaleManager.setOverrideLocaleConfig 动态更新应用的 localeConfig,以自定义 Android 设置中单个应用语言列表内显示的一组语言。这样一来,您就可以按区域自定义语言列表、运行 A/B 实验,以及提供更新后的语言区域(如果您的应用使用服务器端本地化推送,如下例所示):
//For setOverrideLocaleConfig
val localeManager = applicationContext
.getSystemService(LocaleManager::class.java)
localeManager.overrideLocaleConfig = LocaleConfig(
LocaleList.forLanguageTags("en-US,ja-JP,zh-Hans-SG")
)
//For getOverrideLocaleConfig
// The app calls the API to get the override LocaleConfig
val overrideLocaleConfig = localeManager.overrideLocaleConfig
// If the returned overrideLocaleConfig isn't equal to NULL, then the app calls the API to get the supported Locales
val supportedLocales = overrideLocaleConfig.supportedLocales()
此外,IME 现在可以使用 LocaleManager.getApplicationLocales 来了解当前应用的界面语言,以便更新键盘语言,如下所示:
val currentAppLocales: LocaleList = applicationContext.getSystemService(LocaleManager::class.java).getApplicationLocales(appPackageName)
在 Gradle 中指定受支持的语言
请使用应用的模块级 build.gradle 文件中的 resourceConfigurations 属性指定相同的语言(如果这些语言尚不存在):
android {
...
defaultConfig {
resourceConfigurations += ["en", "en-rGB", "fr", "ja", "b+zh+Hans+MO", "b+zh+Hant+MO"]
}
}
当存在 resourceConfigurations 属性时,构建系统仅在 APK 中包含这些指定语言的语言资源,以防止从可能支持相应应用所不支持的语言的其他库中添加经过翻译的字符串。如需了解详情,请参阅指定应用支持的语言。
用户在系统设置中如何选择应用语言
用户可以通过系统设置为每个应用选择首选语言。他们可以通过以下两种方式访问这些设置:
通过系统设置访问
设置 > 系统 > 语言和输入法 > 应用语言 >(选择一款应用)
通过应用设置访问
设置 > 应用 >(选择一款应用)> 语言
处理应用内语言选择器
对于具有或想要使用应用内语言选择器的应用,请使用这些公共 API(而非自定义应用逻辑)来处理相关设置和获取用户对应用的首选语言设置。如果您为应用内语言选择器使用这些公共 API,设备系统设置会自动更新,以匹配用户通过应用内体验选择的语言。
使用 Jetpack Compose 实现
对于完全使用 Jetpack Compose 构建的应用,当应用的语言区域发生变化时,系统会自动处理界面更新。调用 API 以设置新语言会触发配置更改。Compose 会通过重新组合界面来做出响应,并使用新的语言区域设置自动解析所有 stringResource 调用。
为了向后兼容 Android 12(API 级别 32)及更低版本,强烈建议在实现应用内语言选择器时使用 AndroidX 支持库 (AppCompatDelegate)。如果您使用此方法,托管 Compose 界面的 activity 必须扩展 AppCompatActivity。不过,如有需要,您也可以直接实现框架 API。
以下代码段展示了如何在可组合函数中读取当前应用语言区域并设置新的语言区域:
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.core.os.LocaleListCompat
@Composable
fun LanguageSelector() {
// Retrieve the currently configured app locale.
// If no app-specific locale is set, LocaleListCompat.get(0) returns null,
// so we safely fall back to a default (e.g., "en").
val appLocales = AppCompatDelegate.getApplicationLocales()
val currentLocaleTag = appLocales.get(0)?.toLanguageTag() ?: "en"
// Example UI: A button to toggle between English and Spanish
Button(
onClick = {
val newLanguageTag = if (currentLocaleTag == "en") "es" else "en"
val localeList = LocaleListCompat.forLanguageTags(newLanguageTag)
// Setting the locale re-creates the Activity by default,
// which automatically applies the new configuration to Compose.
AppCompatDelegate.setApplicationLocales(localeList)
}
) {
Text(
text = if (currentLocaleTag == "en") "Switch to Spanish" else "Switch to English"
)
}
}
如需设置用户的首选语言,您需要让用户在语言选择器中选择语言区域,然后在系统中设置该值:
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags("xx-YY")
// Call this on the main thread as it may require Activity.restart()
AppCompatDelegate.setApplicationLocales(appLocale)
请注意,除非应用自行处理语言区域配置更改,否则调用 setApplicationLocales 会重新创建 Activity。
使用 AppCompatDelegate.getApplicationLocales 检索用户的首选语言区域。用户可能是从系统设置或应用内语言选择器中选择的应用语言区域。
支持 Android 12 及更低版本
如需支持搭载 Android 12(API 级别 32)及更低版本的设备,请在应用的 AppLocalesMetadataHolderService 服务的清单条目中将 autoStoreLocales 值设置为 true 并将 android:enabled 设置为 false,以指示 AndroidX 处理语言区域存储空间,如以下代码段所示:
<application
...
<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
...
</application>
请注意,将 autoStoreLocales 值设置为 true 会导致主线程上出现阻塞读取,并可能会导致 StrictMode diskRead 和 diskWrite 违规行为(如果您记录线程违规行为)。如需了解详情,请参阅 AppCompatDelegate.setApplicationLocales。
自定义存储空间处理方式
省略清单条目或将 autoStoreLocales 设置为 false 表示您在处理自己的存储空间。在这种情况下,在 Android 12(API 级别 32)或更低版本中,您必须在 activity 生命周期的 onCreate 之前提供已存储的语言区域,并限制对 AppCompatDelegate.setApplicationLocales 的调用。
如果您的应用具有自定义语言区域存储位置,我们建议您在自定义语言区域存储解决方案和 autoStoreLocales 之间使用一次性切换功能,以便用户可以继续以他们首选的语言使用您的应用。当您的应用在设备升级到 Android 13 后首次运行时,此功能特别有用。在这种情况下,您可以从自定义存储空间检索语言区域并将其传递到 AppCompatDelegate.setApplicationLocales,从而提供用户请求的现有语言区域。
使用 Android 框架 API 来实现
虽然我们强烈建议您使用 AndroidX 支持库来实现应用内语言选择器,但对于搭载 Android 13 的设备,您也可以使用 Android 框架中的 setApplicationLocales 和 getApplicationLocales 方法。
以下代码段展示了如何使用 LocaleManager 系统服务设置和获取用户的首选语言:
import android.app.LocaleManager
import android.content.Context
import android.os.Build
import android.os.LocaleList
import androidx.annotation.RequiresApi
import java.util.Locale
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun setAppLanguage(context: Context, languageTag: String) {
// 1. Retrieve the system service
val localeManager = context.getSystemService(LocaleManager::class.java)
// 2. Create a LocaleList from the language tag (e.g., "es-ES" or "ja")
val localeList = LocaleList(Locale.forLanguageTag(languageTag))
// 3. Set the locale. The system automatically updates the locale and
// restarts the app, including any necessary configuration updates.
localeManager.applicationLocales = localeList
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun getAppLanguage(context: Context): String {
val localeManager = context.getSystemService(LocaleManager::class.java)
val currentLocales = localeManager.applicationLocales
// Return the primary app locale, or fall back to the system default
return if (!currentLocales.isEmpty) {
currentLocales.get(0).toLanguageTag()
} else {
Locale.getDefault().toLanguageTag()
}
}
其他最佳实践
请注意以下最佳实践。
在其他应用中调用 intent 时考虑语言
以语言为中心的 intent 可能允许您指定所调用的应用要使用的语言。例如,Speech Recognizer API 中的 EXTRA_LANGUAGE 功能就是如此。
考虑使用 Chrome 自定义标签页的 Accept-Language 标头
考虑通过 Browser.EXTRA_HEADERS 添加 Accept-Language 标头,以便在调用 Chrome 自定义标签页时使用您的应用语言打开网页。
如果您从系统设置中移除单个应用语言设置,请将您的应用语言区域重置为系统语言区域
如果您从系统设置中移除您的应用的语言偏好设定(通过从该应用的 AndroidManifest.xml 中移除 android:localeConfig),用户就无法轻松地将其应用语言重置为系统默认设置。
因此,如果您移除 android:localeConfig,不妨考虑使用 LocaleListCompat.getEmptyLocaleList 或 LocaleList.getEmptyLocaleList 将应用语言区域重置为系统语言区域,如以下代码段所示:
// Use the AndroidX APIs to reset to the system locale for backward and forward compatibility
AppCompatDelegate.setApplicationLocales(
LocaleListCompat.getEmptyLocaleList()
)
// Or use the Framework APIs for Android 13 and above to reset to the system locale
val context = LocalContext.current
context.getSystemService(LocaleManager::class.java)
.applicationLocales = LocaleList.getEmptyLocaleList()
locale_config.xml 文件示例
默认情况下,Android 会在 Android 开源项目 (AOSP) 中添加针对一组标准的常用语言区域的系统级翻译。本部分提供的示例 locale_config.xml 文件表明了其中每个语言区域的推荐格式。您可以参考此示例文件,针对您的应用支持的一组语言构建您自己的 locale_config.xml 文件。
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="af"/> <!-- Afrikaans -->
<locale android:name="am"/> <!-- Amharic -->
<locale android:name="ar"/> <!-- Arabic -->
<locale android:name="as"/> <!-- Assamese -->
<locale android:name="az"/> <!-- Azerbaijani -->
<locale android:name="be"/> <!-- Belarusian -->
<locale android:name="bg"/> <!-- Bulgarian -->
<locale android:name="bn"/> <!-- Bengali -->
<locale android:name="bs"/> <!-- Bosnian -->
<locale android:name="ca"/> <!-- Catalan -->
<locale android:name="cs"/> <!-- Czech -->
<locale android:name="da"/> <!-- Danish -->
<locale android:name="de"/> <!-- German -->
<locale android:name="el"/> <!-- Greek -->
<locale android:name="en-AU"/> <!-- English (Australia) -->
<locale android:name="en-CA"/> <!-- English (Canada) -->
<locale android:name="en-GB"/> <!-- English (United Kingdom) -->
<locale android:name="en-IN"/> <!-- English (India) -->
<locale android:name="en-US"/> <!-- English (United States) -->
<locale android:name="es"/> <!-- Spanish (Spain) -->
<locale android:name="es-US"/> <!-- Spanish (United States) -->
<locale android:name="et"/> <!-- Estonian -->
<locale android:name="eu"/> <!-- Basque -->
<locale android:name="fa"/> <!-- Farsi -->
<locale android:name="fi"/> <!-- Finnish -->
<locale android:name="fil"/> <!-- Filipino -->
<locale android:name="fr"/> <!-- French (France) -->
<locale android:name="fr-CA"/> <!-- French (Canada) -->
<locale android:name="gl"/> <!-- Galician -->
<locale android:name="gu"/> <!-- Gujarati -->
<locale android:name="hi"/> <!-- Hindi -->
<locale android:name="hr"/> <!-- Croatian -->
<locale android:name="hu"/> <!-- Hungarian -->
<locale android:name="hy"/> <!-- Armenian -->
<locale android:name="in"/> <!-- Indonesian -->
<locale android:name="is"/> <!-- Icelandic -->
<locale android:name="it"/> <!-- Italian -->
<locale android:name="iw"/> <!-- Hebrew -->
<locale android:name="ja"/> <!-- Japanese -->
<locale android:name="ka"/> <!-- Georgian -->
<locale android:name="kk"/> <!-- Kazakh -->
<locale android:name="km"/> <!-- Khmer -->
<locale android:name="kn"/> <!-- Kannada -->
<locale android:name="ko"/> <!-- Korean -->
<locale android:name="ky"/> <!-- Kyrgyz -->
<locale android:name="lo"/> <!-- Lao -->
<locale android:name="lt"/> <!-- Lithuanian -->
<locale android:name="lv"/> <!-- Latvian -->
<locale android:name="mk"/> <!-- Macedonian -->
<locale android:name="ml"/> <!-- Malayalam -->
<locale android:name="mn"/> <!-- Mongolian -->
<locale android:name="mr"/> <!-- Marathi -->
<locale android:name="ms"/> <!-- Malay -->
<locale android:name="my"/> <!-- Burmese -->
<locale android:name="nb"/> <!-- Norwegian -->
<locale android:name="ne"/> <!-- Nepali -->
<locale android:name="nl"/> <!-- Dutch -->
<locale android:name="or"/> <!-- Odia -->
<locale android:name="pa"/> <!-- Punjabi -->
<locale android:name="pl"/> <!-- Polish -->
<locale android:name="pt-BR"/> <!-- Portuguese (Brazil) -->
<locale android:name="pt-PT"/> <!-- Portuguese (Portugal) -->
<locale android:name="ro"/> <!-- Romanian -->
<locale android:name="ru"/> <!-- Russian -->
<locale android:name="si"/> <!-- Sinhala -->
<locale android:name="sk"/> <!-- Slovak -->
<locale android:name="sl"/> <!-- Slovenian -->
<locale android:name="sq"/> <!-- Albanian -->
<locale android:name="sr"/> <!-- Serbian (Cyrillic) -->
<locale android:name="sr-Latn"/> <!-- Serbian (Latin) -->
<locale android:name="sv"/> <!-- Swedish -->
<locale android:name="sw"/> <!-- Swahili -->
<locale android:name="ta"/> <!-- Tamil -->
<locale android:name="te"/> <!-- Telugu -->
<locale android:name="th"/> <!-- Thai -->
<locale android:name="tr"/> <!-- Turkish -->
<locale android:name="uk"/> <!-- Ukrainian -->
<locale android:name="ur"/> <!-- Urdu -->
<locale android:name="uz"/> <!-- Uzbek -->
<locale android:name="vi"/> <!-- Vietnamese -->
<locale android:name="zh-Hans"/> <!-- Chinese (Simplified) -->
<locale android:name="zh-Hant"/> <!-- Chinese (Traditional) -->
<locale android:name="zu"/> <!-- Zulu -->
</locale-config>
其他资源
如需了解更多信息,请参考我们的代码示例、博文和视频。