各应用语言偏好设定

在系统设置中设置各应用的语言

在许多情况下,多语言用户会将其系统语言设置为某一种语言(例如英语),但又想为特定应用选择其他语言(例如荷兰语、中文或印地语)。为了帮助应用为这些用户提供更好的体验,Android 13 针对支持多种语言的应用引入了以下功能:

  • 系统设置:用户可以在这个集中位置为每个应用选择首选语言。

    您的应用必须在应用的清单中声明 android:localeConfig 属性,以告知系统它支持多种语言。如需了解详情,请参阅有关创建资源文件并在应用的清单文件中声明资源的说明。

  • 其他 API:借助这些公共 API(例如 LocaleManager 中的 setApplicationLocales()getApplicationLocales() 方法),应用可以在运行时设置不同于系统语言的其他语言。

    这些 API 会自动与系统设置同步;因此,使用这些 API 创建自定义应用内语言选择器的应用将确保用户获得一致的用户体验,无论他们在何处选择语言偏好设置。公共 API 还有助于减少样板代码量、支持拆分 APK,并且支持应用自动备份,以存储应用级的用户语言设置。

    为了向后兼容以前的 Android 版本,AndroidX 中也提供了等效的 API。不过,对于 Android 12(API 级别 32)及更低版本,向后兼容的 API 可与 AppCompatActivity 上下文一起使用,而非应用上下文。请使用 Appcompat 1.6.0 或更高版本访问向后兼容的 API。

此功能的实现方式概览

下表根据不同的用例展示了推荐的实现方式。

用例 推荐的实现方式
您的应用没有应用内语言选择器
  1. 使用应用清单中的 android:localeConfig 属性可将您的应用的语言添加到系统设置中。
  2. (可选)如果您想添加应用内语言选择器:请使用 AndroidX 库并选择接受 API 实现,以通过 autoStoreLocales 支持向后兼容性。
您的应用已有应用内语言选择器
  1. 使用应用清单中的 android:localeConfig 属性可将您的应用的语言添加到系统设置中。
  2. 迁移应用的自定义逻辑以使用公共 API,确保用户获得一致的体验。
  3. 处理以下极端情况:
    1. 应用在搭载 Android 13 的设备上首次运行时调用 AppCompatDelegate.setApplicationLocales()
    2. 针对以下情况,调用 AppCompatDelegate.setApplicationLocales() 以向系统提供用户请求的现有语言区域:

面向用户的系统设置

从 Android 13 开始,Android 在系统设置中引入了一个集中位置,以用于设置各应用语言偏好设定。为了确保在搭载 Android 13 或更高版本的设备上,在系统设置中可以配置应用的语言,请创建一个 locales_config XML 文件,并使用 android:localeConfig 属性将其添加到应用的清单中。省略 android:localeConfig 清单条目表明用户不应该能够在他们的系统设置中独立于系统语言来设置您的应用语言。

使用 android:localeConfig 将受支持的语言添加到系统设置中

若要将应用支持的语言添加到用户的系统设置中,请执行以下操作:

  1. 创建一个名为 res/xml/locales_config.xml 的文件,并指定您的应用的语言,包括应用的最终回退语言区域(即在 res/values/strings.xml 中指定的语言区域)。

    将语言代码与可选的脚本代码和区域代码(使用短划线将各部分隔开)组合在一起即可构成语言区域名称。

    • 语言:使用由两个或三个字母组成的 ISO 639-1 代码。

    • 脚本(可选):使用 ISO 15924 代码。

    • 区域(可选):使用由两个字母组成的 ISO 3166-1-alpha-2 代码或三位数的 UN_M.49 代码。

    如需查看常用语言区域的列表,请参阅示例 locale_config.xml 文件中的推荐格式。

    例如,对于支持以下语言的应用,可以为 locales_config.xml 文件设置如下格式:

    • 使用“英语(美国)”作为最终回退语言区域
    • 英语(英国)
    • 法语
    • 日语
    • 中文(简体,澳门)
    • 中文(繁体,澳门)
    <?xml version="1.0" encoding="utf-8"?>
    <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
       <locale android:name="en"/>
       <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>
    
  2. 在清单中,添加一行指向这个新文件的代码:

    <manifest>
        ...
        <application
            ...
            android:localeConfig="@xml/locales_config">
        </application>
    </manifest>
    

在 Gradle 中指定受支持的语言

请使用应用的模块级 build.gradle 文件中的 resourceConfigurations 属性指定相同的语言(如果这些语言尚不存在):

android {
  ...
  defaultConfig {
    resourceConfigurations += ["en", "en-rGB", "fr", "ja", "b+zh+Hans+MO", "b+zh+Hant+MO"]
  }
}

当存在 resourceConfigurations 属性时,构建系统仅在 APK 中包含这些指定语言的语言资源,以防止从可能支持相应应用所不支持的语言的其他库中添加经过翻译的字符串。如需了解详情,请参阅指定应用支持的语言

用户在系统设置中如何选择应用语言

用户可以通过系统设置为每个应用选择首选语言。他们可以通过以下两种方式访问这些设置:

  • 通过系统设置访问

    设置 > 系统 > 语言和输入法 > 应用语言 >(选择一款应用)

  • 通过应用设置访问

    设置 > 应用 >(选择一款应用)> 语言

处理应用内语言选择器

对于已经具有或想要使用应用内语言选择器的应用,请使用公共 API 而不是自定义应用逻辑来处理相关设置和获取用户对应用的首选语言设置。如果您将公共 API 用于应用内语言选择器,则设备的系统设置会自动更新,以匹配用户通过您的应用内体验选择的任何语言。

为了向后兼容以前的 Android 版本,强烈建议使用 AndroidX 支持库来实现应用内语言选择器。不过,如有需要,您也可以直接实现框架 API

使用 AndroidX 支持库来实现

使用 Appcompat 1.6.0 或更高版本中的 setApplicationLocales()getApplicationLocales() 方法。请注意,对于 Android 12(API 级别 32)及更低版本,向后兼容的 API 可与 AppCompatActivity 上下文一起使用,而非应用上下文。

例如,如需设置用户的首选语言,您需要让用户在语言选择器中选择语言区域,然后在系统中设置该值:

Kotlin

val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags("xx-YY")
// Call this on the main thread as it may require Activity.restart()
AppCompatDelegate.setApplicationLocales(appLocale)

Java

LocaleListCompat appLocale = 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 diskReaddiskWrite 违规行为(如果您记录线程违规行为)。如需了解详情,请参阅 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() 方法。

例如,如需设置用户的首选语言,您需要让用户在语言选择器中选择语言区域,然后在系统中设置该值:

// 1. Inside an activity, in-app language picker gets an input locale "xx-YY"
// 2. App calls the API to set its locale
mContext.getSystemService(LocaleManager.class
    ).setApplicationLocales(new LocaleList(Locale.forLanguageTag("xx-YY")));
// 3. The system updates the locale and restarts the app, including any configuration updates
// 4. The app is now displayed in "xx-YY" language

如需获取用户当前的首选语言以显示在语言选择器中,您的应用可以从系统中取回该值:

// 1. App calls the API to get the preferred locale
LocaleList currentAppLocales =
    mContext.getSystemService(LocaleManager.class).getApplicationLocales();
// 2. App uses the returned LocaleList to display languages to the user

其他最佳实践

请注意以下最佳实践。

在其他应用中调用 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() 将应用语言区域重置为系统语言区域,如以下代码段所示:

Kotlin

// 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()

Java

// 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
mContext.getSystemService(LocaleManager.class)
  .setApplicationLocales(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="en-XA"/> <!-- English (Pseudo-Accents) -->
   <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="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="my-MM"/> <!-- Burmese (Myanmar) -->
   <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="tl"/> <!-- Filipino -->
   <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-CN"/> <!-- Chinese (Simplified) -->
   <locale android:name="zh-HK"/> <!-- Chinese (Hong Kong) -->
   <locale android:name="zh-TW"/> <!-- Chinese (Traditional) -->
   <locale android:name="zu"/> <!-- Zulu -->
</locale-config>