欢迎参加我们将于 6 月 3 日举行的 #Android11:Beta 版发布会

本地化您的应用

许多地区的许多设备上都搭载了 Android。为了覆盖大多数用户,您的应用应该以适合其所在语言区域的方式处理文本、音频文件、数字、货币和图形。

本文档将向您介绍将 Android 应用本地化的最佳做法。

在阅读本文之前,您需要先具备 Java 编程语言的实战知识,熟悉 Android 资源加载在 XML 中声明界面元素Activity 生命周期等开发注意事项以及国际化和本地化的基本原则。

使用 Android 资源框架将应用的本地化部分与基于 Java 的核心功能尽可能分开,是一种很好的做法:

  • 您可以将应用界面的大部分内容或全部内容放在资源文件中,如本文档和提供资源中所述。
  • 另一方面,界面的行为由基于 Java 的代码来驱动。例如,如果用户输入的数据需要根据语言区域使用不同的格式或排序方式,则可以使用 Java 编程语言以编程方式处理数据。本文档不讨论如何本地化基于 Java 的代码。

有关如何将应用中的字符串本地化的简短指南,请参阅支持其他语言培训课程。

概述:Android 中的资源切换

资源是指文本字符串、布局、声音、图形和您的 Android 应用需要的任何其他静态数据。应用可以包含多组资源,每组资源针对不同的设备配置进行定制。当用户运行应用时,Android 会自动选择并加载与设备最匹配的资源。

(本文档主要关注本地化和语言区域。有关资源切换和可以指定的所有配置类型(屏幕方向、触摸屏类型等)的完整说明,请参阅提供备用资源

编写应用时,您需要为应用创建默认资源和备用资源。当用户运行您的应用时,Android 系统会根据设备的语言区域选择要加载的资源。要创建资源,您需要将文件放在项目的 res/ 目录下特别命名的子目录中。

为什么默认资源很重要

当应用在您没有提供特定于该语言区域的文本的语言区域中运行时,Android 就会从 res/values/strings.xml 加载默认字符串。如果缺少此默认文件,或者此文件缺少应用需要的字符串,那么应用就不会运行,而是显示错误。以下示例说明了默认文本文件不完整时可能会发生的情况。

示例:

应用的基于 Java 的代码仅引用两个字符串:text_atext_b。此应用包含一个本地化的资源文件 (res/values-en/strings.xml),该文件用英语定义了 text_atext_b。此应用还包含一个默认的资源文件 res/values/strings.xml,该文件包含 text_a 的定义,但不包含 text_b 的定义:

  • 当此应用在语言区域设置为英语的设备上启动时,应用可能不会出现问题,因为 res/values-en/strings.xml 包含所需的两个文本字符串。
  • 但是,当应用在语言区域设置为非英语的设备上启动时,用户会看到错误消息和“强行关闭”按钮。应用无法加载。

为避免这种情况,请确保 res/values/strings.xml 文件存在,并且它定义了所需的每个字符串。这种情况适用于所有类型的资源,而不仅仅是字符串:您需要创建一组包含应用会调用的所有资源(布局、可绘制对象、动画等)的默认资源文件。有关测试方面的信息,请参阅测试默认资源

使用资源进行本地化

如何创建默认资源

将应用的默认文本放在 res/values/strings.xml 中。

res/values/strings.xml 中的文本字符串应使用默认语言,也就是您预计应用的大多数用户会使用的语言。

默认资源集还必须包含任何默认的可绘制对象和布局,还可以包含其他类型的资源,例如动画:

  • res/drawable/(必需的目录,包含至少一个图形文件,用作 Google Play 上的应用图标)
  • res/layout/(必需的目录,包含定义默认布局的 XML 文件)
  • res/anim/(如果您有任何 res/anim-<qualifiers> 文件夹,则为必需)
  • res/xml/(如果您有任何 res/xml-<qualifiers> 文件夹,则为必需)
  • res/raw/(如果您有任何 res/raw-<qualifiers> 文件夹,则为必需)

提示:在代码中,检查对 Android 资源的每个引用。确保为每个资源定义了默认资源。此外,还需要确保默认字符串文件是完整的:本地化的字符串文件可包含部分字符串,但默认字符串文件必须包含所有字符串。

如何创建备用资源

本地化应用的很大一部分工作是提供不同语言的备用文本。在某些情况下,您还需要提供备用的图形、声音、布局和其他特定于语言区域的资源。

一个应用可以指定多个 res/<qualifiers>/ 目录,每个目录具有不同的限定符。要为不同的语言区域创建备用资源,您需要使用限定符来指定语言或语言区域组合。(资源目录的名称必须符合提供备用资源中所述的命名方案,否则您的应用将无法编译。)

示例:

假设您应用的默认语言是英语。再假设您希望将应用中的所有文本本地化为法语,并将应用中的大部分文本(应用标题除外)本地化为日语。在本例中,您可以创建三个备用 strings.xml 文件,每个文件存储在一个特定于语言区域的资源目录中:

  1. res/values/strings.xml
    包含应用使用的所有字符串的英语文本,包括名为 title 的字符串的文本。
  2. res/values-fr/strings.xml
    包含所有字符串(包括 title)的法语文本。
  3. res/values-ja/strings.xml
    包含除 title 之外的所有字符串的日语文本。

如果基于 Java 的代码引用了 R.string.title,则运行时会发生以下情况:

  • 如果设备设置为非法语的语言,Android 会从 res/values/strings.xml 文件加载 title
  • 如果设备设置为法语,则 Android 会从 res/values-fr/strings.xml 文件加载 title

请注意,如果设备设置为日语,Android 会在 res/values-ja/strings.xml 文件中查找 title。但由于该文件中不包含此类字符串,因此 Android 会退回到默认状态,从 res/values/strings.xml 文件中加载英语 title

哪些资源优先?

如果多个资源文件与设备的配置匹配,则 Android 会遵循一组规则来确定使用哪个文件。在可通过资源目录名称指定的限定符中,语言区域几乎总是处于优先地位

示例:

假设某个应用包含一组默认图形和另外两组其他图形,这两组图形中的每一组都针对不同的设备设置进行了优化:

  • res/drawable/
    包含默认图形。
  • res/drawable-small-land-stylus/
    包含经过优化的图形,专用于需要使用触控笔输入且配有横向 QVGA 低密度屏幕的设备。
  • res/drawable-ja/
    包含针对日语环境优化的图形。

如果应用在配置为使用日语的设备上运行,则 Android 会从 res/drawable-ja/ 加载图形,即使设备恰好需要使用触控笔输入,并且配有横向 QVGA 低密度屏幕也是如此。

例外:在选择过程中,唯一优先于语言区域的限定符是 MCC(移动国家代码)和 MNC(移动网络代码)。

示例:

假设您遇到以下情况:

  • 应用代码调用 R.string.text_a
  • 有两个相关的资源文件:
    • res/values-mcc404/strings.xml,其中包含采用应用的默认语言的 text_a,在本例中为英语。
    • res/values-hi/strings.xml,其中包含采用印地语的 text_a
  • 该应用在具有以下配置的设备上运行:
    • SIM 卡连接到印度的移动网络 (MCC 404)。
    • 语言设置为印地语 (hi)。

即使设备设置为印地语,Android 仍会从 res/values-mcc404/strings.xml(采用英语)加载 text_a。这是因为在资源选择过程中,Android 会优先考虑 MCC 匹配而不是语言匹配。

选择过程并不总是像这些示例所显示的那样简单。有关此过程的更详细的说明,请参阅 Android 如何寻找最匹配的资源“提供备用资源”的表 2 中按优先顺序介绍和列出了所有限定符。

在代码中引用资源

在应用的基于 Java 的代码中,您使用 R.resource_type.resource_nameandroid.R.resource_type.resource_name 语法来引用资源。 有关详情,请参阅访问资源

管理要进行本地化的字符串

将所有字符串放入 strings.xml 中

在构建应用时,请不要硬编码任何字符串,而应将所有字符串声明为默认的 strings.xml 文件中的资源,以便于更新和本地化。然后,strings.xml 文件中的字符串可以轻松地提取、翻译并再次集成到您的应用中(使用适当的限定符),而无需更改已编译的代码。

如果要生成带有文本的图片,也应将这些字符串放在 strings.xml 中,并在翻译后重新生成图片。

遵循针对界面字符串的 Android 指南

在设计和开发界面时,一定要密切关注您与用户交流的方式。一般而言,应使用友好而简练的风格,并且整个界面应采用一致的风格。

请务必阅读并遵循 Material Design 关于写作风格和字词选择的建议。这样做能够让您的应用显得更有品质,并且有助于用户快速理解您的界面。

另外,还应尽可能地使用 Android 标准术语,例如对于界面元素,使用“操作栏”、“选项菜单”、“系统栏”、“通知”等。正确、一致地使用 Android 术语可使翻译变得更容易,最终可为用户提供更好的产品。

为声明的字符串提供充分的上下文

strings.xml 文件中声明字符串时,请确保清楚说明使用该字符串的上下文。这些信息对翻译人员非常有用,可帮助提高翻译质量,还有助于您更有效地管理字符串。

示例如下:

<!-- The action for submitting a form. This text is on a button that can fit 30 chars -->
    <string name="login_submit_button">Sign in</string>
    

可以考虑提供以下上下文信息:

  • 字符串的用途是什么?它会在何时何处呈现给用户?
  • 它在布局中的什么位置?例如,按钮文字的翻译不如文本框中文字的翻译灵活。

标记消息中不应该翻译的部分

字符串中常常包含不应翻译成其他语言的文本。常见的示例包括一段代码、某个值的占位符、特殊符号或名称。准备字符串进行翻译时,请查找并标记应保持原样不进行翻译的文本,以避免翻译人员对其进行更改。

要标记不应翻译的文本,可以使用 <xliff:g> 占位符标记。以下标记示例可以确保文本“%1$s”在翻译过程中不会被更改(否则它会破坏消息):

    <string name="countdown">
      <xliff:g id="time" example="5 days">%1$s</xliff:g> until holiday
    </string>
    

在声明占位符标记时,请务必添加一个 ID 属性来说明此占位符的用途。如果您的应用稍后会替换占位符的值,请务必提供属性示例来说明预期的用途。

以下是一些其他占位符标记的示例:

    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <!-- Example placeholder for a special unicode symbol -->
    <string name="star_rating">Check out our 5
        <xliff:g id="star">\u2605</xliff:g>
    </string>
    <!-- Example placeholder for a for a URL -->
    <string name="app_homeurl">
        Visit us at <xliff:g
        id="application_homepage">http://my/app/home.html</xliff:g>
    </string>
    <!-- Example placeholder for a name -->
    <string name="prod_name">
        Learn more at <xliff:g id="prod_gamegroup">Game Group</xliff:g>
    </string>
    <!-- Example placeholder for a literal -->
    <string name="promo_message">
        Please use the "<xliff:g id="promotion_code">ABCDEFG</xliff:g>" to get a discount.
    </string>
    ...
    </resources>
    

本地化核对清单

有关本地化和分发 Android 应用的过程的完整概述,请参阅本地化核对清单文档。

关于本地化的建议

将应用设计为可在任何语言区域中使用

您不能对用户用来运行您的应用的设备做任何假设。设备可能包含您没有预料到的硬件,或者可能设置为您未计划支持或无法测试的语言区域。请将您的应用设计为无论在什么设备上都能正常运行,并在无法运行时能够安全地退出。

重要提示:请确保您的应用包含一套完整的默认资源。

确保包含 res/drawable/res/values/ 文件夹(文件夹名称中没有任何其他修饰符),这两个文件夹中包含了应用需要的所有图片和文本。

即使应用只缺少一个默认资源,它也无法在设置为不受支持的语言区域的设备上运行。例如,res/values/strings.xml 默认文件可能缺少应用所需的一个字符串:当应用在不受支持的语言区域中运行并尝试加载 res/values/strings.xml 时,用户会看到错误消息和“强制应用关闭”按钮。

如需了解详情,请参阅测试默认资源

设计灵活的布局

如果您需要重新排列布局以适应某种语言(例如单词较长的德语),您可以针对该语言创建备用布局(例如 res/layout-de/main.xml)。不过,这样做会增加应用的维护难度。最好是创建一个更灵活的布局。

另一种典型的情况是,语言对布局的要求有所不同。例如,当应用以日语运行时,可能需要包含两个名称字段的联系人表单,而当应用以其他语言运行时,则可能需要包含三个名称字段的联系人表单。您可以通过以下方法之一来处理此问题:

  • 创建一个布局,在其中包含您可以根据语言以编程方式启用或停用的字段,或者
  • 在主布局中包含一个带有可更改字段的布局。从属布局可以针对不同的语言提供不同的配置。

避免创建超出您需要的资源文件和文本字符串

您可能不需要为应用中的每个资源创建特定于语言区域的备用资源。例如,在 res/layout/main.xml 文件中定义的布局可以在任何语言区域中使用,在这种情况下,就不需要创建任何备用布局文件。

此外,您可能也不需要为每一个字符串创建替代文本。例如,假设存在以下情况:

  • 您应用的默认语言是美式英语。应用使用的每个字符串都已使用美式英语拼写在 res/values/strings.xml 中定义。
  • 对于一些重要的短语,您需要提供英式英语拼写。您希望您的应用在英国的设备上运行时使用这些备用字符串。

为此,您可以创建一个名为 res/values-en-rGB/strings.xml 的小文件,该文件仅包含应用在英国的设备上运行时应该有所不同的字符串。对于所有其他字符串,应用会退回到默认状态,使用在 res/values/strings.xml 中定义的字符串。

使用 Android Context 对象手动查找语言区域

您可以使用 Android 提供的 Context 对象查找语言区域:

Kotlin

    val primaryLocale: Locale = context.resources.configuration.locales[0]
    val locale: String = primaryLocale.displayName
    

Java

    Locale primaryLocale = context.getResources().getConfiguration().getLocales().get(0);
    String locale = primaryLocale.getDisplayName();
    

使用应用翻译服务

应用翻译服务集成在 Play 管理中心,也可从 Android Studio 访问。通过该服务,您可以快速、简便地获得即时报价并向翻译公司下订单。您可以为应用界面字符串、Play 商店商品详情文本、IAP 名称和广告系列文本订购一种或多种语言的翻译。

测试本地化应用

在设备上测试

请注意,您测试的设备可能与其他地区的消费者所使用的设备有很大差异。您设备上提供的语言区域可能与其他设备上提供的语言区域不同。此外,设备屏幕的分辨率和密度也可能不同,这可能会影响界面中的字符串和可绘制对象的显示。

要更改设备上的语言区域或语言,请使用“设置”应用。

在模拟器上测试

要详细了解如何使用模拟器,请参阅 Android 模拟器

创建和使用自定义语言区域

“自定义”语言区域是 Android 系统映像未明确支持的语言/区域组合。您可以通过在模拟器中创建自定义语言区域来测试应用在该语言区域中的运行情况。您可以通过以下两种方式执行此操作:

  • 使用“自定义语言区域”应用(可从应用标签页访问)。(创建自定义语言区域后,可以通过按住语言区域的名称来切换到该语言区域。)
  • 通过 adb shell 更改到自定义语言区域,如下所述。

当您将模拟器设置为 Android 系统映像中未提供的语言区域时,系统本身会以其默认语言显示。不过,您的应用可以正确本地化。

通过 adb shell 更改模拟器语言区域

要使用 adb shell 更改模拟器中的语言区域,请执行以下操作。

  1. 选择您要测试的语言区域并确定其 BCP-47 语言标记,例如加拿大法语为 fr-CA
  2. 启动模拟器。
  3. 在主机的命令行 shell 中,运行以下命令:
    adb shell如果您连接了设备,请添加 -e 选项表明您希望使用模拟器: adb -e shell
  4. 在 adb shell 提示符 (#) 下运行以下命令:
    setprop persist.sys.locale [BCP-47 language tag];stop;sleep 5;start
    使用步骤 1 中的相应代码替换用括号括住的部分。

例如,要用加拿大法语进行测试,请运行以下命令:

setprop persist.sys.locale fr-CA;stop;sleep 5;start

这会导致模拟器重启。(这看起来像是完全重新启动,实则不是。)再次出现主屏幕后,重新启动您的应用,这时应用就会以新的语言区域启动。

测试默认资源

下面介绍如何测试应用是否包含所需的全部字符串资源:

  1. 将模拟器或设备设置为应用不支持的语言。例如,如果应用在 res/values-fr/ 中包含法语字符串,但在 res/values-es/ 中未包含任何西班牙语字符串,则将模拟器的语言区域设置为西班牙语。(您可以使用“自定义语言区域”应用将模拟器设置为不支持的语言区域。)
  2. 运行应用。
  3. 如果应用显示错误消息和“强制应用关闭”按钮,则表示它可能正在查找一个未提供的字符串。请确保 res/values/strings.xml 文件中包含应用使用的每个字符串的定义。

如果测试成功,请针对其他类型的配置重复此测试。例如,如果应用具有名为 res/layout-land/main.xml 的布局文件,但不包含名为 res/layout-port/main.xml 的文件,则将模拟器或设备设置为竖屏模式,看看应用是否会运行。