按应用设定语言偏好

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

在许多情况下,多语言用户会将其系统语言设置为某一种语言(例如英语),但又想为特定应用选择其他语言(例如荷兰语、中文或印地语)。为了帮助应用为这些用户提供更好的体验,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。

此功能的实现方式概览

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

用例 推荐的实现方式
您的应用没有应用内语言选择器
  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 会将此默认语言区域和您指定的所有其他语言区域(使用 res 文件夹中的 values-* 目录)添加到自动生成的 LocaleConfig 文件中。

如何构成语言区域名称

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

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

例如,如果您的默认语言区域是美式英语:

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>
    

您可以使用以下代码动态更新应用的 localeConfigLocaleManager.setOverrideLocaleConfig,用于自定义语言集 显示在 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 了解当前应用的界面语言,以便将键盘语言更新为 显示:

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 支持库来实现

使用 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="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>