每个应用程序的语言偏好

系统设置中的每个应用语言

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

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

    您可以配置您的应用以自动生成支持每个应用语言偏好的必要文件,并在系统设置中显示。要了解更多信息,请参阅有关 启用每个应用自动语言支持 的说明。

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

    这些 API 会自动与系统设置同步;因此,使用这些 API 创建自定义应用内语言选择器的应用将确保其用户拥有始终如一的用户体验,无论他们在何处选择其语言偏好。这些公共 API 还可帮助您减少样板代码的数量,它们支持分包 APK,并支持 应用的自动备份 以存储应用级用户语言设置。

    为了与之前的 Android 版本向后兼容,AndroidX 中也提供了等效的 API。但是,对于 Android 12(API 级别 32)及更早版本,向后兼容的 API 使用 AppCompatActivity 上下文,而不是应用程序上下文。使用 Appcompat 1.6.0 或更高版本访问向后兼容的 API。

实施此功能的概述

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

用例 推荐实现
您的应用没有应用内语言选择器
  1. 启用自动每应用语言支持以生成 LocaleConfig 文件,并将您的应用语言添加到系统设置中。
  2. 可选地,如果您想添加应用内语言选择器:使用 AndroidX 库并选择加入我们的 API 实现 以通过 autoStoreLocales 支持向后兼容性。
您的应用已经拥有应用内语言选择器
  1. 启用自动每应用语言支持以生成 LocaleConfig 文件,并将您的应用语言添加到系统设置中。
  2. 迁移您的应用自定义逻辑以使用 公共 API,以确保用户看到一致的体验。
  3. 处理以下极端情况
    1. 在您的应用首次在运行 Android 13 的设备上运行时,调用 AppCompatDelegate.setApplicationLocales()
    2. 调用 AppCompatDelegate.setApplicationLocales() 以将预先存在的用户请求的语言环境提供给系统,用于以下情况

用户系统设置

从 Android 13 开始,Android 在系统设置中包含一个集中式位置,用于设置每应用语言首选项。为了确保您的应用语言可以在运行 Android 13 或更高版本的设备上的系统设置中配置,请启用自动每应用语言支持(推荐)或 手动配置支持

启用自动每应用语言支持

从 Android Studio Giraffe 和 AGP 8.1 开始,您可以配置您的应用以自动支持 每应用语言首选项。根据您的项目资源,AGP 会生成 LocaleConfig 文件并在最终清单文件中添加对其的引用,因此您不再需要手动执行此操作。AGP 使用您应用模块的 res 文件夹以及任何库模块依赖项中的资源来确定要包含在 LocaleConfig 文件中的语言环境。这意味着,如果您为您的应用添加了新语言的资源,则无需担心更新 LocaleConfig 文件。

请注意,自动每应用语言功能支持运行 Android 13(API 级别 33)或更高版本的应用。要使用此功能,您必须将 compileSdkVersion 设置为 33 或更高。要为早期版本的 Android 配置每应用语言首选项,您仍然需要 使用 API 和应用内语言选择器

要启用自动每应用语言支持,请执行以下步骤

  1. 要启用此功能,请在模块级 build.gradle.kts 文件(如果您使用的是 Groovy,则为 build.gradle 文件)的 androidResources {} 块中使用 generateLocaleConfig 设置。默认情况下,此功能处于关闭状态。

    Kotlin

        android {
          androidResources {
            generateLocaleConfig = true
          }
        }
        

    Groovy

        android {
          androidResources {
            generateLocaleConfig true
          }
        }
        
  2. 指定默认语言环境
    1. 在应用模块的 res 文件夹中,创建一个名为 resources.properties 的新文件。
    2. resources.properties 文件中,使用 unqualifiedResLocale 标签设置默认语言环境。有关语言环境名称格式的说明,请参阅 如何构成语言环境名称

AGP 将此默认语言环境以及您使用 values-* 目录在 res 文件夹中指定的任何 备用语言环境 添加到自动生成的 LocaleConfig 文件中。

如何构成语言环境名称

要构成语言环境名称,请将语言代码与可选的脚本和区域代码组合起来,并用连字符隔开每个代码

例如,如果您的默认语言环境是美式英语

unqualifiedResLocale=en-US

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

您可以手动设置您的应用,以确保其语言可以在运行 Android 13 或更高版本的设备上的系统设置中配置。为此,请创建一个 locales_config XML 文件,并使用 android:localeConfig 属性将其添加到您的应用清单中。省略 android:localeConfig 清单条目表示用户不应该能够在系统设置中独立于其系统语言来设置您的应用语言。

要手动将您应用支持的语言添加到用户的系统设置中

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

    有关格式要求,请参阅 如何构成语言环境名称。有关最常用语言环境的列表,请参阅 示例 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-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>
    
  2. 在清单中,添加一行指向此新文件的指针

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

您可以使用 LocaleManager.setOverrideLocaleConfig 动态更新您应用的 localeConfig,以自定义在 Android 设置中每应用语言列表中显示的语言集。这使您可以根据地区自定义语言列表,运行 A/B 测试,并在您的应用使用服务器端本地化推送时提供更新的语言环境,如以下示例所示

Kotlin

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

Java

//For setOverrideLocaleConfig
mContext.getSystemService(LocaleManager.class).setOverrideLocaleConfig(new LocaleConfig(LocaleList.forLanguageTags("en-US,ja-JP,zh-Hans-SG")));

//For getOverrideLocaleConfig
// The app calls the API to get the override LocaleConfig
LocaleConfig overrideLocaleConfig = mContext.getSystemService(LocaleManager.class).getOverrideLocaleConfig();
// If the returned overrideLocaleConfig isn't equal to NULL, then the app calls the API to get the supported Locales
LocaleList supportedLocales = overrideLocaleConfig.getSupportedLocales();

此外,IME 现在可以使用 LocaleManager.getApplicationLocales 来了解当前应用的 UI 语言,以便更新键盘语言,如以下示例所示

Kotlin

val currentAppLocales: LocaleList = applicationContext.getSystemService(LocaleManager::class.java).getApplicationLocales(appPackageName)

Java

LocaleList currentAppLocales =
    mContext.getSystemService(LocaleManager.class).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,设备的系统设置将自动更新为与用户通过应用内体验选择的语言匹配。

为了与之前的 Android 版本向后兼容,我们强烈建议您在实现应用内语言选择器时使用 AndroidX 支持库。但是,如果您需要,也可以 直接实现框架 API

使用 AndroidX 支持库实现

使用 setApplicationLocales()getApplicationLocales() 方法,这些方法位于 Appcompat 1.6.0 或更高版本中。请注意,向后兼容 API 与 AppCompatActivity 上下文一起使用,而不是与应用上下文一起使用,用于 Android 12(API 级别 32)和更早版本。

例如,要设置用户的首选语言,您将要求用户在语言选择器中选择语言环境,然后在系统中设置该值

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)及更低版本的设备,请告诉 AndroidX 通过将 autoStoreLocales 值设置为 true 以及将 android:enabled 设置为 false 来处理语言环境存储,如以下代码段所示

<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 表示您正在处理自己的存储。在这种情况下,您必须在活动生命周期中的 onCreate 之前提供存储的区域设置,并在 Android 12(API 级别 32)或更低版本中限制对 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

其他最佳实践

请注意以下最佳实践。

在调用另一个应用中的意图时考虑语言

以语言为中心的意图可能允许您指定您希望调用的应用使用的语言。一个例子是语音识别 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="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>