Android 在许多地区的不同设备上运行。为了触达最多的用户,请确保您的应用以适合其使用区域的方式处理文本、音频文件、数字、货币和图形。
本页面介绍了本地化 Android 应用的最佳实践。
您需要掌握 Kotlin 或 Java 编程语言的基础知识,并熟悉Android 资源加载、在 XML 中声明用户界面元素、Activity 生命周期等开发注意事项,以及国际化和本地化的通用原则。
最佳实践是使用 Android 资源框架,尽可能将应用的本地化方面与核心应用功能分开。
- 将应用用户界面的大部分或所有内容放入资源文件,如本页面和应用资源概览中所述。
- 另一方面,用户界面的行为由您的 Kotlin 或 Java 代码驱动。例如,如果用户输入的数据需要根据语言区域进行不同的格式化或排序,那么您需要使用 Kotlin 或 Java 编程语言以编程方式处理数据。本页面不介绍如何本地化您的 Kotlin 或 Java 代码。
有关本地化应用字符串的简短指南,请参阅支持不同的语言和文化。
概览:Android 中的资源切换
资源是文本字符串、布局、声音、图形以及 Android 应用所需的任何其他静态数据。应用可以包含多组资源,每组都针对不同的设备配置进行了自定义。当用户运行应用时,Android 会自动选择并加载最符合设备的资源。
本页面重点介绍本地化和语言区域。有关资源切换以及您可以指定的所有配置类型(例如屏幕方向或触摸屏类型)的完整说明,请参阅提供替代资源。
在编写应用时,您需要为应用创建默认资源和替代资源。当用户运行应用时,Android 系统会根据设备的语言区域选择要加载的资源。要创建资源,您需要将文件放置在项目 res/
目录中特殊命名的子目录中。
为什么默认资源很重要
当应用在您未提供特定于语言区域的文本的任何语言区域中运行时,Android 会从 res/values/strings.xml
加载默认字符串。如果此默认文件不存在,或者缺少应用所需的字符串,则您的应用将无法运行并显示错误。以下示例说明了当默认文本文件不完整时可能发生的情况。
示例
应用的 Kotlin 或 Java 代码仅引用两个字符串:text_a
和 text_b
。该应用包含一个本地化资源文件 (res/values-en/strings.xml
),其中用英语定义了 text_a
和 text_b
。该应用还包含一个默认资源文件 (res/values/strings.xml
),其中包含 text_a
的定义,但没有 text_b
的定义。
- 当此应用在语言区域设置为英语的设备上启动时,该应用可能会正常运行,因为
res/values-en/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
文件,每个文件都存储在特定于语言区域的资源目录中:
res/values/strings.xml
包含应用使用的所有字符串的英文文本,包括名为title
的字符串的文本。res/values-fr/strings.xml
包含所有字符串的法文文本,包括title
。res/values-ja/strings.xml
包含除title
之外的所有字符串的日文文本。
如果您的 Kotlin 或 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 如何找到最匹配的资源。所有限定符都在应用资源概览中进行了描述并按优先级顺序列出。
在代码中引用资源
在您应用的 Kotlin 或 Java 代码中,您可以使用 R.resource_type.resource_name
或 android.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 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 管理中心。它允许您即时获取报价并向翻译公司下订单。您可以订购将应用界面字符串、Play 商店商品详情文本、应用内购买商品名称和广告系列文本翻译成一种或多种语言的服务。
测试本地化应用
在设备上或使用 Android 模拟器测试您的本地化应用。特别是,测试您的应用以确保包含所有必要的默认资源。
在设备上测试
请记住,您正在测试的设备可能与消费者在其他地方可用的设备显著不同。您设备上可用的语言区域可能与其他设备上可用的语言区域不同。此外,设备屏幕的分辨率和密度可能不同,这可能会影响您界面中字符串和可绘制对象的显示。
要更改设备上的语言区域或语言,请使用“设置”应用。
在模拟器上测试
有关使用模拟器的详细信息,请参阅在 Android 模拟器上运行应用。
创建和使用自定义语言区域
“自定义”语言区域是 Android 系统映像未明确支持的语言或区域组合。您可以通过在模拟器中创建自定义语言区域来测试您的应用在自定义语言区域中的运行情况。有两种方法可以做到这一点:
- 使用“自定义语言区域”应用,可从应用标签页访问。创建自定义语言区域后,通过长按语言区域名称进行切换。
- 从
adb
shell 切换到自定义语言区域,如下一节所述。
当您将模拟器设置为 Android 系统映像中不可用的语言区域时,系统本身会以其默认语言显示。但是,您的应用会正确本地化。
通过 adb shell 更改模拟器语言区域
要使用 adb
shell 更改模拟器中的语言区域,请执行以下操作:
- 选择您要测试的语言区域并确定其 BCP-47 语言标签,例如加拿大法语的
fr-CA
。
- 启动模拟器。
- 在主机计算机的命令行 shell 中,运行以下命令:
adb shell
或者,如果您连接了设备,请通过添加-e
选项指定您要使用模拟器:
adb -e shell
- 在
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
这将导致模拟器重新启动。主屏幕再次出现后,重新启动您的应用,应用将以新的语言区域启动。
测试默认资源
要测试应用是否包含其所需的所有字符串资源,请执行以下操作:
- 将模拟器或设备设置为您的应用不支持的语言。例如,如果应用在
res/values-fr/
中有法语字符串,但在res/values-es/
中没有任何西班牙语字符串,则将模拟器的语言区域设置为西班牙语。您可以使用“自定义语言区域”应用将模拟器设置为不支持的语言区域。 - 运行应用。
- 如果应用显示错误消息和“强制关闭”按钮,则它可能正在查找不可用的字符串。确保您的
res/values/strings.xml
文件包含应用使用的每个字符串的定义。
如果测试成功,请针对其他类型的配置重复测试。例如,如果应用有一个名为 res/layout-land/main.xml
的布局文件,但没有名为 res/layout-port/main.xml
的文件,则将模拟器或设备设置为纵向,并查看应用是否运行。