国际化

此时,您已创建了 StockWatcher 应用程序的初始实现。

在本教程中,您将学习如何准备应用程序以支持其他语言和数据格式,方法是将 StockWatcher 用户界面翻译成德语。具体来说,您将

  1. 选择国际化技术。
  2. 通过为每种支持的语言创建翻译来国际化 StockWatcher。
  3. 通过为上下文(语言环境)选择适当的翻译来本地化 StockWatcher。

注意:有关国际化 GWT 应用程序的更广泛指南,请参阅 国际化

设计

确定需要翻译的内容……什么可以本地化

如果您查看 StockWatcher 当前的英语界面,您会发现两种可以本地化的文本类型:常量和消息。

screenshot: StockWatcher English UI

选择国际化技术

国际化 GWT 应用程序时,您可以选择多种技术。由于 StockWatcher 的用户界面中只有少数常量和参数化消息,因此您将使用静态字符串国际化。

静态字符串国际化

静态字符串初始化在运行时几乎没有开销,因此对于翻译常量字符串和参数化字符串来说是一种非常高效的技术。它也是最简单的实现技术。静态字符串国际化使用标准 Java 属性文件存储翻译后的字符串和参数化消息,然后实现强类型 Java 接口来检索其值。

动态字符串国际化

动态字符串国际化比静态字符串国际化速度慢,但非常灵活。使用此技术的应用程序在模块的主页中查找本地化字符串;因此,当您添加新的语言环境时,它们不需要重新编译。如果您需要将 GWT 应用程序与现有的服务器端本地化系统集成,则应考虑动态字符串国际化。

可本地化接口

最强大的技术是实现 Localizable 接口。实现 Localizable 允许您超越简单的字符串替换,并创建自定义类型的本地化版本。这是一种高级国际化技术,您可能不会经常使用它。

国际化 StockWatcher:为每种支持的语言创建翻译

过程概述:静态字符串国际化

您将遵循的静态字符串国际化过程很简单。

  1. 首先,您将实现两个 Java 接口

    • 一个用于字符串常量,GWT Constants 接口 (StockWatcherConstants.java)
    • 一个用于参数化消息,GWT Messages 接口 (StockWatcherMessages.java) 这些接口使用注释指定默认翻译。
  2. 然后,对于您支持的每种新语言,您将创建两个 Java 属性文件

    • 一个用于字符串常量 (StockWatcherConstants_de.properties)
    • 一个用于参数化消息 (StockWatcherMessages_de.properties)
  3. 最后,您将用对适当接口的方法调用替换 Java 源代码中硬编码的所有字符串。

提示:GWT 提供了一个命令行工具 i18nCreator,该工具可自动创建 Java 接口以访问属性文件中的字符串。此工具非常有用,特别是如果您有要重复使用的现有本地化属性文件。

实现 Constants 接口

首先创建访问包含每个翻译的属性文件的 Java 接口 (StockWatcherConstants)。该接口使用注释指定默认翻译。此接口实现 GWT Constants 接口。由于其名称,此接口会自动绑定到您创建的任何 StockWatcherConstants*.properties 文件。

StockWatcherConstants 接口包含属性文件中每个常量的方法。在运行时,当调用这些方法之一时,返回值来自与语言环境相对应的属性文件。(我们将在下一分钟向您展示如何设置语言环境。)如果未设置语言环境,StockWatcher 使用注释指定的默认翻译。例如,如果将语言环境设置为德语,则 stockWatcher 方法将返回 AktieWatcher;如果未设置语言环境,则 stockWatcher 方法将返回 StockWatcher。

创建 StockWatcherConstants

  1. 在客户端子包中,创建一个接口并将其命名为 StockWatcherConstants。

    • 在 Eclipse 中,在“包资源管理器”窗格中,选择包

    com.google.gwt.sample.stockwatcher.client

    • 从 Eclipse 菜单栏中,选择 文件 > 新建 > 接口
    • Eclipse 将打开一个新建 Java 接口窗口。
  2. 填写新建 Java 接口窗口。
    • 在名称处输入 StockWatcherConstants
    • 接受其他字段的默认值。
    • 完成
    • Eclipse 将为 StockWatcherConstants 接口创建存根代码。
  3. 将存根替换为以下代码。
    • 注意使用注释来设置默认值。
package com.google.gwt.sample.stockwatcher.client;

import com.google.gwt.i18n.client.Constants;

public interface StockWatcherConstants extends Constants {
    @DefaultStringValue("StockWatcher")
    String stockWatcher();

    @DefaultStringValue("Symbol")
    String symbol();

    @DefaultStringValue("Price")
    String price();

    @DefaultStringValue("Change")
    String change();

    @DefaultStringValue("Remove")
    String remove();

    @DefaultStringValue("Add")
    String add();
}

实现说明:GWT 提供了另一个接口 (ConstantsWithLookup),它与 Constants 类似,只是它还包含在运行时通过名称动态查找本地化字符串的方法。

创建德语翻译的常量

国际字符集的编码

当您国际化应用程序的界面时,请记住您支持的语言可能包含不在 ASCII 字符集中的字符。因此,在 HTML 主页 (StockWatcher.html) 和包含翻译的 Java 属性文件中,您都必须将编码设置为 UTF-8。

  1. 检查应用程序主页的编码。

    • 打开 StockWatcher.html。
    • 如果编码尚未设置为 UTF-8,请将以下代码复制到 <head> 元素中。
    <meta charset="utf-8">
    

创建 StockWatcherConstant_de.properties

  1. 在客户端子包中,创建一个 Java 属性文件。

    • 在输入或选择父文件夹处,选择

    StockWatcher/src/com/google/gwt/sample/stockwatcher/client

    • 在文件名处输入

    StockWatcherConstants_de.properties

  2. 将文件的编码更改为 UTF-8。

    • 选择文件,然后从 Eclipse 菜单栏中,选择 文件 > 属性 或右键单击。
    • Eclipse 将打开“属性”窗口。
    • 在“文本文件编码”处,选择“其他 UTF-8”。应用并保存更改。
    • 注意:根据您的 Eclipse 配置,当您应用更改时,您可能会收到以下警告:UTF-8 与内容类型中定义的编码冲突 (ISO-8859-1)。您是否希望无论如何都设置它?您可以忽略警告并应用更改。
  3. 添加德语用户界面中静态文本的映射。

    • 将以下文本复制并粘贴到 StockWatcherConstant_de.properties 文件中。
    stockWatcher = Aktienbeobachter
    symbol = Symbol
    price = Kurs
    change = &Auml;nderung
    remove = Entfernen
    add = Hinzuf&uuml;gen
    
注意:属性文件的后缀

如果您以前从未处理过国际化,您可能想知道为什么 _de 后缀附加到德语属性文件。_de 后缀是德语 (**De**utsch) 的标准语言标记。语言标记是缩写,用于指示文档或应用程序的语言环境。除了指定语言之外,它们还可以包含一个子标记,用于指示语言环境的区域。例如,法语加拿大语言环境的语言标记是 fr_CA。

在 GWT 中,属性文件使用语言代码后缀指示语言环境(就像 Java 资源包一样)。例外是默认语言环境的属性文件。当在运行时未显式设置语言环境时,将使用没有语言代码后缀的属性文件。对于 StockWatcher,您已使用注释指定了默认翻译,而不是使用默认属性文件。

实现 Messages 接口

首先创建 Java 接口(StockWatcherMessages)。它访问包含每个参数化消息翻译的属性文件。此接口实现 GWT Messages 接口。与 StockWatcherConstants 接口不同,此接口中的方法包含参数。当您调用这些方法时,您传递的参数将替换您在属性文件中字符串中留下的占位符。由于其名称,此接口会自动绑定到您创建的任何 StockWatcherMessages*.properties 文件。

国际化日期格式

参数化消息不限于弹出警报和错误消息。应用程序中任何您在 Label 小部件上设置文本的地方都有可能成为参数化消息。例如,在 StockWatcher 中,时间戳是一个参数化消息;您不仅传递日期值,而且日期格式也会因语言环境而异。

  1. 在客户端子包中,创建一个接口并将其命名为 StockWatcherMessages。
  2. 将存根替换为以下代码。
package com.google.gwt.sample.stockwatcher.client;

import com.google.gwt.i18n.client.Messages;

import java.util.Date;

public interface StockWatcherMessages extends Messages {
  @DefaultMessage("''{0}'' is not a valid symbol.")
  String invalidSymbol(String symbol);

  @DefaultMessage("Last update: {0,date,medium} {0,time,medium}")
  String lastUpdate(Date timestamp);

}

有关格式化参数化消息的提示

指定参数数量

请注意,消息字符串中都嵌入了 {0}。这些是将在运行时由传递给 StockWatcherMessages 接口方法的参数替换的占位符。

如果您的字符串需要多个参数,请按顺序对占位符进行编号。例如:myString = 第一个参数是 {0},第二个参数是 {1},第三个参数是 {2}。

处理引号文本

如果您的消息包含单引号 ('),就像 StockWatcher 中的许多消息一样,您需要在 Java 属性文件中将它们替换为两个连续的单引号。一般来说,GWT 消息的格式规则与 Java 的 MessageFormat 类相同的规则。

创建参数化消息的德语翻译

  1. 创建一个 Java 属性文件。

    • 在输入或选择父文件夹处,选择

    StockWatcher/src/com/google/gwt/sample/stockwatcher/client

    • 在文件名处输入

    StockWatcherMessages_de.properties

  2. 将文件的编码更改为 UTF-8。

  3. 添加英文用户界面中参数化文本的映射。
    • 将以下文本复制并粘贴到 StockWatcherMessages_de.properties 文件中。
lastUpdate = Letzte Aktualisierung: {0,date,medium} {0,time,medium}
invalidSymbol = ''{0}'' ist kein g&uuml;ltiges Aktiensymbol.

用生成的本地化字符串替换硬编码字符串

国际化 StockWatcher 的下一步是将源代码中的所有硬编码字符串替换为对两个新接口之一的方法调用。

替换 HTML 主页中硬编码的字符串

目前,StockWatcher 应用程序有一个字符串不是以编程方式生成的:标题,StockWatcher。它是在主机页面 (StockWatcher.html) 中的一个 HTML <h1> 标题。

在构建示例 GWT 应用程序教程中,我们想向您展示将静态 HTML 元素与 StockWatcher 在同一页面上生成的元素混合起来是可能的。这还是一种快速简便的方法,可以在股票表格周围放置静态文本。但是,现在您正在国际化 StockWatcher,您可以看到这不是最灵活的策略。

生成此标题的一种简单方法是将 <h1> 元素内部的文本替换为 GWT Label 小部件并调用其 setText(String) 方法。请记住,GWT 小部件不能直接嵌入到 HTML 主页中;因此,首先将其用 Root 面板包装。

  1. 打开 StockWatcher.html。
    • 通过为其分配一个“appTitle”的 id,将一个 Root 面板与 <h1> 标题相关联。
    • 删除 <h1> 标题中的文本。
<body>

  <img src="images/GoogleCode.png"/>

  <h1 id="appTitle"></h1>

现在,您应该能够在运行时设置 StockWatcher 的所有本地化字符串。

替换以编程方式设置的字符串

遍历 StockWatcher 类并替换所有硬编码文本的字符串。

  1. 创建 StockWatcherConstants 和 StockWatcherMessages 接口的实例。
    • 在 StockWatcher 类中,添加以下实例字段对。
private ArrayList<String> stocks = new ArrayList<String>();
private StockWatcherConstants constants = GWT.create(StockWatcherConstants.class);
private StockWatcherMessages messages = GWT.create(StockWatcherMessages.class);
  • 因为这些是接口而不是类,所以您不能直接实例化它们。相反,您使用 GWT.create(Class) 方法。然后,您将能够使用这些接口的访问器方法来检索相应的字符串。
  1. Eclipse 标记 GWT 并建议您包含导入声明。
import com.google.gwt.core.client.GWT;
  1. 用对常量类的方法调用替换所有硬编码字符串。
    • 从常量属性文件中获取窗口标题、应用程序标题、添加股票按钮以及 flex 表的列标题的值。
public void onModuleLoad() {
    // Set the window title, the header text, and the Add button text.
    Window.setTitle(constants.stockWatcher());
    RootPanel.get("appTitle").add(new Label(constants.stockWatcher()));
    addStockButton = new Button(constants.add());

    // Create table for stock data.
    stocksFlexTable.setText(0, 0, constants.symbol());
    stocksFlexTable.setText(0, 1, constants.price());
    stocksFlexTable.setText(0, 2, constants.change());
    stocksFlexTable.setText(0, 3, constants.remove());

    ...
  1. 替换参数化错误消息。
    • 在 addStock 方法中,替换用于无效股票代码条目的警报消息。
    • 更改
private void addStock() {
    final String symbol = newSymbolTextBox.getText().toUpperCase().trim();
    newSymbolTextBox.setFocus(true);

    // Stock code must be between 1 and 10 chars that are numbers, letters, or dots.
    if (!symbol.matches("^[0-9a-zA-Z\\.]{1,10}$")) {
      Window.alert(messages.invalidSymbol(symbol));
      newSymbolTextBox.selectAll();
      return;
    }

    ...
  1. 将确定日期格式的逻辑移到 Messages 接口。
    • 在 updateTable(StockPrice[] prices) 方法中,用对 lastUpdate 方法的调用替换变量 timestamp。
    • 更改
private void updateTable(StockPrice[] prices) {
  for (int i = 0; i < prices.length; i++) {
    updateTable(prices[i]);
  }

  // Display timestamp showing last refresh.
  lastUpdatedLabel.setText(messages.lastUpdate(new Date()));

}

本地化 StockWatcher

此时,您已创建了 StockWatcher 用户界面的两个本地化版本。但是 GWT 如何知道在运行时加载哪一个呢?GWT 使用客户端属性通过称为延迟绑定的机制生成 GWT 应用程序的自定义 JavaScript 编译。为了在运行时选择要提供的 StockWatcher 的正确本地化版本,GWT 会评估客户端属性 locale

国际化和延迟绑定

您在构建示例 GWT 应用程序教程中看到,GWT 使用延迟绑定来生成应用程序的不同排列,每个排列都针对不同的 Web 浏览器。在运行时,GWT 启动代码根据最终用户使用的浏览器提供相应的排列。创建这些特定于浏览器的编译是因为 用户代理 是一个 GWT 客户端属性。同样,GWT 将语言环境表示为客户端属性。这意味着 GWT 编译器将生成代表每个受支持语言环境的国际化应用程序的自定义版本。

当存在多个客户端属性时,GWT 会为每个可能的客户端属性值组合生成一个唯一的编译。因此,例如,如果 GWT 支持 5 个 Web 浏览器,并且您将应用程序翻译成 4 种不同的语言,则 GWT 编译器总共会生成 20 个不同版本的应用程序。但是,您的应用程序的每个用户只会被提供与他或她的特定 Web 浏览器和语言环境组合相匹配的排列中的代码。

识别 StockWatcher 的受支持语言环境

您可以通过扩展客户端属性 locale 的值集来告诉 GWT 编译器 StockWatcher 现在支持德语 (de) 语言环境。

  1. 将德语识别为支持的语言。
    • 打开 StockWatcher.gwt.xml 并添加以下属性。
  <entry-point class='com.google.gwt.sample.stockwatcher.client.StockWatcher'/>
  <extend-property name="locale" values="de"/>
</module>
  1. 在开发模式下刷新 StockWatcher。
    • 默认情况下会加载英文版本。
  2. 加载德语版本。
  3. 输入包含无效字符的股票代码。

    • 错误消息应以德语显示。
    • 另外请注意,日期和货币格式已本地化。

    screenshot: StockWatcher German UI

  4. 编译 StockWatcher 并以生产模式打开它。

    • Web 浏览器显示 StockWatcher 的默认界面。
  5. 测试德语界面。
    • 将语言环境追加到 URL ?locale=de
    • Web 浏览器显示 StockWatcher 的德语界面。
  6. 查看生成的的文件。
    • 您应该看到的文件排列数量是创建德语界面之前的两倍。

确定用户的语言环境

在运行时,您如何确定用户的语言环境?您可能会像许多网站一样,向用户展示语言或语言环境列表供其手动选择。您还可以让 Web 服务器检查浏览器 HTTP 请求中的 Accept-Language 字段以确定正确的语言环境。但是,如果您这样做,请务必提供一种方法让用户覆盖 Accept-Language 字段中的值并选择其语言首选项。

设置语言环境

现在 StockWatcher 已国际化,GWT 如何知道在运行时加载哪个语言环境?答案是它使用客户端属性 locale 的值。您可以通过两种方式设置此客户端属性

  • 在应用程序主机页面的 <head> 中添加 HTML <meta> 标记,其中包含属性名称和值

    <meta name="gwt:property" content="locale=de">

  • 将客户端属性值追加到 URL 的查询字符串

    http://www.example.org/myapp.html?locale=de

如果您在 <meta> 标记和 URL 中都指定了客户端属性(例如 locale),则 URL 值优先。

在多个浏览器页面之间保留语言环境

GWT 模块的语言环境设置仅适用于该特定模块的特定实例。这意味着,如果您的应用程序包含指向您网站上的其他 GWT 主页或非 GWT 网页的链接,则语言环境设置不会传递到这些页面。因此,如果您想保留用户指定的语言环境,您需要将语言环境传递到 GWT 应用程序中所有链接的查询字符串中,或者您需要将语言环境设置存储在服务器上的某个位置,然后该位置可以将相应的 <meta> 标记插入到加载的任何 GWT 模块的主页中。

关于国际化的更多信息

此时,您已使用静态字符串国际化来生成 GWT 应用程序的德语排列。您已实现本地化,以便根据用户的语言环境加载相应的排列。

要了解有关所有三种国际化技术、GWT I18N 模块或如何使用 GWT i18nCreator 重用现有的本地化属性文件的更多信息,请参阅开发者指南 国际化