i18n UIBinder
本文档探讨了 UiBinder 模板的国际化功能。有关 UiBinder 的更多一般信息,请参阅 使用 UiBinder 进行声明式布局。
- 背景
- Bonjour, Tout Le Monde
- 消息中的简单 HTML 标签
- 包含不可修改部分的消息
- 包含运行时计算值的 消息
- 包含小部件的消息(仅限 HTMLPanel)
- 需要翻译的 HTML 属性
- 具有多种含义的词语
背景
UiBinder 模板可以标记为可本地化。您可以使用 <ui:msg>
和 <ui:attribute>
元素来指示模板的哪些部分应该被翻译,然后在构建应用程序时提供包含本地化消息版本属性文件。
与主 UiBinder 页面 一样,本页面的其余部分通过一系列典型的用例解释如何使您的 UI 模板可本地化。
注意 您会看到许多与使用 消息 系统类似的地方,这是有道理的:UiBinder 的 I18n 功能是通过为每个模板生成隐藏的 com.google.gwt.i18n.client.Messages
接口来实现的。除了复数形式之外,您可以在 Messages 中执行的任何操作,都应该可以在模板中执行。
Bonjour, Tout Le Monde
以下是开启该功能的方法。
原始
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'>
<div>Hello, world.</div>
</ui:UiBinder>
标记
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
ui:generateFormat='com.google.gwt.i18n.server.PropertyCatalogFactory'
ui:generateKeys="com.google.gwt.i18n.server.keygen.MD5KeyGenerator"
ui:generateLocales="default">
<div><ui:msg description="Greeting">Hello, world.</ui:msg></div>
</ui:UiBinder>
我们在这里做了两件事。我们通过向根 <ui:UiBinder>
元素添加一些属性来配置 UiBinder 的 I18N 功能,并标记了我们的文本作为需要翻译的单个消息。
首先查看我们的“Hello, world.” 文本。通过将其放在 <ui:msg>
元素中,我们将其标识为需要翻译的单个文本片段,即一个消息。消息的 description
属性将与消息一起传递给翻译人员,以解释其用途。描述可能是翻译人员看到的唯一上下文,因此即使它是可选属性,您也应该始终提供一个。
现在我们有了需要翻译的内容,我们通过根 <ui:UiBinder>
元素上的属性来设置我们的配置,以说明应该如何执行翻译。这些 ui:generate*
属性对应于 LocalizableResource 的 @Generate 注释的参数。以下是它们的含义。
- `ui:generateFormat`
- 我们希望生成一个以 java 属性格式的文件。
- `ui:generateKeys`
- 我们希望属性键是消息内容的 MD5 哈希值,这样翻译在消息在模板中移动时就能保留下来。(并且如果我们对如何管理翻译后的属性文件小心谨慎,恰好在多个模板中使用的一条消息只需要翻译一次。)
- `ui:generateLocales`
- 我们希望生成的属性是默认语言环境的一部分。(有关默认语言环境和回退属性的更多信息,请参阅 [GWT 中的语言环境](DevGuideI18nLocale.html)。)
当您编译应用程序时,将 -extras 参数传递给 gwt 编译器,以告诉它生成其“额外”辅助文件。将为每个模板生成一个属性文件,其中包含每个标记为可本地化的消息的条目,如下所示
# Generated from my.app.HelloWorldMyBinderImplGenMessages
# for locale default
# Description: Greeting
022A824F26735ED0582324BE34F3CAE1=Hello, world.
生成的文件名称有点不幸,基于您声明的 UiBinder 接口。例如,如果此模板由 com.example.app.client.Hello.Binder
使用;在一个名为 App
的模块中;并且您已使用 -extra /tmp/gwt-extras
运行 gwt 编译器;您将在 /tmp/gwt-extras/com.example.app.App/com.example.app.client.HelloBinderImplGenMessages.properties
中找到该文件。没错。希望这将在工具包的未来版本中得到改进。
然后,您的翻译人员可以创建此文件的本地化版本,这些版本可以位于您的 ui.xml 文件旁边。继续上面的例子,此属性文件的墨西哥西班牙语版本将被命名为 HelloBinderImplGenMessages_es_MX.properties
(请注意,“com.example…” 前缀被删除了)。
您不必使用 md5 键。如果您希望创建自己的键,<ui:msg>
元素接受一个 key
属性,允许您这样做。另一方面,如果您坚持使用 md5 键,您可以将特定语言环境的所有应用程序翻译都收集到一个文件中,而不是将它们分散在代码库中。神奇的名字是 LocalizableResource_<locale>.properties
,您必须将其放在 com/google/gwt/i18n/client
包中。(这是可行的,因为 i18n 属性文件查找会沿着类继承树向上遍历,而所有 Messages 接口都继承自 LocalizableResource。)
一些项目使用脚本来维护这些统一的翻译文件。请参阅 puzzlebazar 项目的这个 wiki 条目,了解一个示例。
在 <ui:UiBinder>
元素上可以设置几个其他的 I18N 属性,对应于其他 Localizable 注释,但您很少会将它们从其默认值更改。
- `ui:defaultLocale`
- 有关详细信息,请参阅 @DefaultLocale
- `ui:generateFilename`
- 有关详细信息,请参阅 @Generate(fileName = "...")
- `ui:baseMessagesInterface`
- 设置用于生成消息的基础接口。该值必须是扩展 [`Messages`](/javadoc/latest/com/google/gwt/i18n/client/Messages.html) 的接口的完全限定类名。然后,您可以在那里放置任何您想要的注释,从而轻松地拥有公司或项目范围的设置,这些设置只需在一个地方更改即可。如果需要,您仍然可以使用其他属性来覆盖从该接口继承的默认值。
消息中的简单 HTML 标签
在 HTML 适用的上下文中,消息元素可以包含 HTML 标记。
原始
We <b>strongly</b> urge you to reconsider.
标记
<ui:msg>We <b>strongly</b> urge you to reconsider.</ui:msg>
将简单的格式放在翻译人员面前是合理的,因此 UiBinder 支持消息中的 HTML,而不仅仅是文本。
包含不可修改部分的消息
有时您需要在消息中放入不透明的块,以防止翻译人员破坏您的应用程序。
原始
<!-- Uh oh, don't want translator to mess up brand CSS or the trademark span -->
<div><span class="brand">Colgate</span>, with MFP!<span class="tm">TM</span></div>
标记
<div>
<ui:msg description="blurb"><span class="brand" ui:ph="brandedSpan">Colgate</span>,
with MFP!<ui:ph name="trademark"><span class="tm">TM</span></ui:ph></ui:msg>
</div>
生成
# Description: blurb
# 0=arg0 (Example: <span class='brand'>), 1=arg1 (Example: </span>), 2=arg2 (Example: <span class='tm'>TM</span>)
6E8B421C6A7C1FEAE23FAA9D43C90D5E={0}Colgate{1}, with MFP\!{2}
这里有两个例子。首先,您会看到一个 ui:ph
属性,可以添加到 ui:msg
的任何子元素中,以指示应该创建占位符来保护它免受翻译人员的影响。将创建两个占位符,用于元素的开始和结束标签(在本例中,为 brandedSpanOpen
和 brandedSpanClose
)。
其次,我们看到了一个元素,也称为 ui:ph
,它可以围绕任意标记片段以保护其完整性(在本例中,为商标占位符)。
因此,您既有元素来围绕不可翻译的运行:<ui:ph>
不要翻译 </ui:ph>
,也有属性可以放入任意元素中以隐藏其开始和结束标签,但保留其内容作为可翻译消息的一部分:<span ui:ph>attribute</span>
。请注意,您可以将 ui:ph
属性放在任何 DOM 元素中,它不是特定于 <span>
的。
包含运行时计算值的 消息
当您想在运行时更改模板的部分内容时,通常会在 span 或其他元素中放置一个 ui:field 并处理其 HTML。当您在 <ui:msg>
中执行此操作时,它会自动被保护免受翻译。
原始
<!-- Java code will make calls like getClosingDate().setInnerText(closingDate()) -->
(closed <span ui:field="closingDate" /> through <span ui:field="reopeningDate"/>)
标记
<ui:msg description='closed for business message'>
(closed <span ui:field='closingDate' /> through <span ui:field='reopeningDate'/>)
</ui:msg>
生成
# Description: closed for business message
# 0=arg0 (Example: <span id=''>), 1=arg1 (Example: </span>), 2=arg2 (Example: <span id=''>), 3=arg3 (Example: </span>)
E30D43242E1AD2AC2EFA1AEEEFDFCC33=(closed {0}{1} through {2}{3})
这里有好消息和坏消息。好消息是,您不必添加任何ui:ph
属性或元素来保护用ui:field
属性标记的跨度开始和结束标签。坏消息是,没有什么可以阻止翻译人员在这些开始和结束标签之间插入任意内容。(注意{0}{1}
和{2}{3}
。)
如果这是一个问题,您需要将命名跨度放在<ui:ph>
元素内,以使其不透明,如下所示
标记
<ui:msg>
(closed <ui:ph name='closingDate' example="7/12/2008"><span ui:field="closingDate"/></ui:ph>
through <ui:ph name='reopeningDate' example="7/12/2008"><span ui:field="reopeningDate"/></ui:ph>)
</ui:msg>
生成
# 0=arg0 (Example: 7/12/2008), 1=arg1 (Example: 7/12/2008)
53B9CF65553DFAA091435791E5C731E7=(closed {0} through {1})
example
属性是可选的,它允许您向翻译人员提供更有效的解释,说明您的占位符是做什么的。
包含小部件的消息(仅限HTMLPanel)
当使用<g:[HTMLPanel](/javadoc/latest/com/google/gwt/user/client/ui/HTMLPanel.html)>
元素时,您可能会发现自己将小部件放在消息中。没问题!
原始
<g:HTMLPanel>
Meeting starts at
<my:TimePicker ui:field="startPicker"/>
and ends at
<my:TimePicker ui:field="endPicker"/>.
</g:HTMLPanel>
标记
<g:HTMLPanel>
<ui:msg>Meeting starts at
<my:TimePicker ui:field="startPicker"/>
and ends at
<my:TimePicker ui:field="endPicker"/>.
</ui:msg>
</g:HTMLPanel>
# 0=arg0 (Example: <span>), 1=arg1 (Example: </span>), 2=arg2 (Example: <span>), 3=arg3 (Example: </span>)
23CBEA252C9901BF84D757FAD4968289=Meeting starts at {0}{1} and ends at {2}{3}.
请注意,小部件上没有ui:ph
属性。它们没有必要,因为当小部件出现在消息中间时,必须做什么没有歧义。还要注意,您只能在HTMLPanel内部进行这种操作(消息中的小部件),HTMLPanel是GWT集合中唯一将标记和子小部件混合在一起的小部件。
您可能想知道的更多信息:您可能已经注意到,在生成的属性文件中,消息为每个小部件都有“过多”的占位符,这意味着翻译人员可能会在运行时将被小部件替换的跨度中引入不需要的文本。如果发生这种情况,除了浪费翻译人员的时间外,不会造成任何伤害,因为当小部件到位时,文本将丢失。您的用户不会看到它。
当您将具有文本正文的小部件放在 HTMLPanel 中的消息中时,事情会变得更加有趣。(也就是说,一个实现了HasText或HasHTML的小部件。)
原始
<g:HTMLPanel>
To do the thing, <g:Hyperlink targetHistoryToken="/doThe#thing">click here</g:Hyperlink>
and massage vigorously.
</g:HTMLPanel>
标记
<g:HTMLPanel>
<ui:msg>
To do the thing, <g:Hyperlink targetHistoryToken="/doThe#thing">click here</g:Hyperlink>
and massage vigorously.
</ui:msg>
</g:HTMLPanel>
生成
# 0=arg0 (Example: <span>), 1=arg1 (Example: </span>)
8EFBF967A3FEFE78C41C8A298562A094=To do the thing, {0}click here{1} and massage vigorously.
需要翻译的 HTML 属性
正文文本不是唯一需要翻译的内容 - 属性可能需要相同的处理。标题属性(用于工具提示文本)和<img>
的alt
标签是最常见的示例。
原始
<th title="Gross recipts">Gross</th>
标记
<th title="Gross receipts">
<ui:attribute ui:name='title' ui:description='Tooltip text for gross column'/>
<ui:msg description='name of gross column'>Gross</ui:msg>
</th>
具有多种含义的词语
小心那些在不同上下文中含义不同的词语。
原始
Favorite Color:
<ui:RadioButton name="color">Red</ui:RadioButton>
<ui:RadioButton name="color">Orange</ui:RadioButton>
Favorite Fruit:
<ui:RadioButton name="fruit">Apple</ui:RadioButton>
<ui:RadioButton name="fruit">Orange</ui:RadioButton>
标记
Favorite Color:
<ui:RadioButton name="color"><ui:msg>Red</ui:msg></ui:RadioButton>
<ui:RadioButton name="color"><ui:msg meaning="the color"/>Orange</ui:msg></ui:RadioButton>
Favorite Fruit:
<ui:RadioButton name="fruit"><ui:msg>Apple</ui:msg></ui:RadioButton>
<ui:RadioButton name="fruit"><ui:msg meaning="the fruit">Orange<ui:msg></ui:RadioButton>
生成
# Meaning: the color
4404BE8C34552617D633271BBC1FAB07=Orange
# Meaning: the fruit
7A6DCA1ACC86B4A7D7574CD6BDD4E0C1=Orange
9F6290F4436E5A2351F12E03B6433C3C=Apple
EE38E4D5DD68C4E440825018D549CB47=Red
这里的关键是,翻译人员很可能只使用您在单个消息上设置的属性来工作。而且,如果您设置为共享一个由其 MD5 哈希总和区分的大型翻译池,您可能根本无法提供此处需要的两种橙子的翻译。
您可以使用可选的meaning
属性来解决这个问题。与description
不同,消息的含义实际上会影响其哈希 ID。