第16章. 国际化、本地化和主题
Seam使构建国际化应用程序变得十分容易。首先,让我们初排一下需要国际化和本地化你的应用程序的所有场景。然后,我们看一看组件Seam bundles.Seam。
16.1. 国际化你的应用程序
一个JEE应用程序由许多组件组成,并且对你的应用程序本地化,它们都必须适当地被配置。
第一步是从底部开始,确保你的数据库服务器和客户机对你的区域设置使用了正确的字符编码。通常情况下你希望使用UTF-8,这个怎样做已超出本教程的范围。
16.1.1. 应用程序服务器配置
为确保应用程序服务器根据客户机请求的正确编码收到请求参数,你必须配置tomcat连接器。如果你使用了Tomcat 或 JBoss AS,增加URIEncoding="UTF-8"属性到连接器配置。对
JBoss AS 4.2,改变${JBOSS_HOME}/server/(default)/deploy/jboss-web.deployer/server.xml:
<Connector port="8080" URIEncoding="UTF-8"/> 有一个更好的替代方法。你可以告诉JBoss AS,请求参数的编码将从请求获取:
<Connector port="8080" useBodyEncodingForURI="true"/>
16.1.2. 转换应用程序字符串
你也需要本地化在你的应用程序中的所有消息字符串(如在你视窗上的标签字段)。首先你需要确定你的资源包使用了想得到的字符编码进行编码。默认使用ASCII。尽管ASCII对大部分语言足够了,但它并不为所有语言提供字符。
资源包必须用ASCII 创建,或者使用代表Unicode字符的Unicode转义码。因为你没有编译属性文件成字节码,就没有办法告诉JVM使用了那种字符集。所以,你必须使用ASCII字符或没有在ASCII字符集中的转义字符。你能在任何Java文件中使用\uXXXX表示Unicode字符,这里的XXXX是十进制字符表示。
你能使用本地编码为你的资源包编写转化的标签(<xlink>Labels</xlink>),并且然后使用JDK 提供的工具native2ascii
转换文件的内容为转义格式。这个工具会转换你用本地编码编写的文件成为一个非ASCII字符表示,作为Unicode转义序列。
这个工具的用法被描述在http://java.sun.com/j2se/1.5.0/docs/tooldocs/index.html#intl或http://java.sun.com/javase/6/docs/technotes/tools/#intl。例如,根据UTF-8转换一个文件。
$ native2ascii -encoding UTF-8 messages_cs.properties > messages_cs_escaped.properties
16.1.3. 其它的编码设置
我们需要确保视窗显示你的本地化数据、使用正确的字符集的消息以及任何使用正确的编码提交的数据。
为设置显示字符编码,你需要使用<f:view locale="cs_CZ"/>标签(这里我们告诉JSF使用
捷克地区)。你可能希望改变
xml文档本身
的编码,如果你想嵌入本地化字符在xml中。为做这个,视需要而定改变在xml中声明的编码属性<?xml version="1.0" encoding="UTF-8"?>。
JSF/Facelets不仅应当提交使用了指定字符编码的任何请求,而且还应确保没有指定编码的任何请求,你可以使用servlet过滤器强迫请求编码:
<web:character-encoding-filter encoding="UTF-8"
override-client="true"
url-pattern="*.seam" />
16.2. 区域
每个用户的login会话有一个关联的java.util.Locale(可用到应用程序作为一个命名区域的组件)实例。在一般情况下,为设置区域你不需要做任何特殊配置。
Seam只是委派JSF决定活动的区域:
· 如果有一个与HTTP 请求关联的区域(浏览器区域),并且这个区域是在来自faces-config.xml
支持的区域的列表内,对会话的其余部分会使用这个区域。
· 否则,如果在faces-config.xml
中指定了默认区域,对会话的其余部分会使用这个区域。
· 否则, 使用服务器的默认区域。
通过Seam配置属性org.jboss.seam.international.localeSelector.language
, org.jboss.seam.international.localeSelector.country
和org.jboss.seam.international.localeSelector.variant
可以手动地配置区域,但我们想不出任何充分的理由一直这样做。
然而,允许用户通过应用程序接口手动设置区域是有用的,Seam提供内建功能用,用上面的算法重载确定的区域。你不得不增加下面全部片段到一个JSP 或Facelets页面的表单:
<h:selectOneMenu value="#{localeSelector.language}">
<f:selectItem itemLabel="English" itemValue="en"/>
<f:selectItem itemLabel="Deutsch" itemValue="de"/>
<f:selectItem itemLabel="Francais" itemValue="fr"/>
</h:selectOneMenu>
<h:commandButton action="#{localeSelector.select}"
value="#{messages['ChangeLanguage']}"/>
或者,如果你想要一个来自faces-config.xml
支持的所有区域的列表,只需使用:
<h:selectOneMenu value="#{localeSelector.localeString}">
<f:selectItems value="#{localeSelector.supportedLocales}"/>
</h:selectOneMenu>
<h:commandButton action="#{localeSelector.select}"
value="#{messages['ChangeLanguage']}"/>
当用户从下接列表中选一个项目时,那么点命令按钮,Seam 和JSF区域会覆盖会话的其余部分。
对支持区域带给我们的问题是定义。 通常情况下,你提供一个区域列表,用于你拥有的在JSF配置文件(/META-INF/faces-config.xml) 中的<locale-config>元素相
匹配的资源包。然而,你已经意识到Seam的组件配置机制比Java EE提供的更强大。出于这一原因,使用内建的名为org.jboss.seam.international.localeConfig
的组件,你能配置支持的区域,和默认服务器的区域。为使用它,你首先用Seam组件描述符声明一个Seam的国际化包的XML命名空间。然后,你定义默认区域和支持的区域,象下面:
<international:locale-config default-locale="fr_CA" supported-locales="en fr_CA fr_FR"/>
当然,如果你声明了你支持的一个区域,就你较好地提供了一个资源包匹配它!下次,你会学习如何定义特定语言的标签。
16.3. 标签
JSF支持用户接口标签和描述文本的国际化,通过<f:loadBundle />的
使用。你在Seam 应用程序中可以使用这个方法。另外,你能利用Seam messages
组件显示具有嵌入式EL表达式的模板标签。
16.3.1. 定义标签
Seam提供一个java.util.ResourceBundle
(供应用程序作为org.jboss.seam.core.resourceBundle使用)。通过这个特殊资料包你必会让你的国际化标签可用。默认时, Seam使用的资料包被命名为messages
,并且因此你需要在名为messages.properties、messages_en.properties
和 messages_en_AU.properties等等
文件中定义你的
标签。这些文件通常应归入WEB-INF/classes目录。
所以, 在 messages_en.properties中
:
Hello=Hello
在messages_en_AU.properties中
:
Hello=G'day
通过设置名为org.jboss.seam.core.resourceLoader.bundleNames
的Seam配置属性,你也能给资源包选一个不同的名字。你甚至能指定一个搜索(深度优先)消息的资源包列表名。
<core:resource-loader>
<core:bundle-names>
<value>mycompany_messages</value>
<value>standard_messages</value>
</core:bundle-names>
</core:resource-loader>
如果你只想为一个特殊页面定义一个消息,你能在一个删除头/和尾的
文件扩展名就与JSF视窗id同样名的资源包中指定它。所以,如果我们只想在/welcome/hello.jsp中显示消息,我们能放我们的消息在welcome/hello_en.properties中。
你甚至能在pages.xml中
指定一个明确的名字:
<page view-id="/welcome/hello.jsp" bundle="HelloMessages"/>
然后,我们能在/welcome/hello.jsp
上使用定义在HelloMessages.properties
中的消息。
16.3.2. 显示标签
如果你使用Seam资料包定义了你的标签,你可以使用他们,不必在每一页上输入<f:loadBundle ... />
。 实事上,你简单地输入:
<h:outputText value="#{messages['Hello']}"/>
或者:
<h:outputText value="#{messages.Hello}"/>
甚至更好, 消息自身或以包含EL表达式:
Hello=Hello, #{user.firstName} #{user.lastName}
Hello=G'day, #{user.firstName}
你甚至能在你的代码中使用消息:
@In private Map<String, String> messages;
@In("#{messages['Hello']}") private String helloMessage;
16.3.3. Faces消息
facesMessages
组件是显示成功或失败信息给用户的一个超级方便的方法。我们刚描述的功能也为faces 消息工作:
@Name("hello") @Stateless public class HelloBean implements Hello { @In FacesMessages facesMessages; public String sayIt() { facesMessages.addFromResourceBundle("Hello"); } }
这会显示Hello、 Gavin King
、 G'day或Gavin
, 依赖于用户的区域。
16.4. 时区
也有一个会话范围的java.util.Timezone实例
, 名为org.jboss.seam.international.timezone
, 和一个改变时区的Seam组件名为org.jboss.seam.international.timezoneSelector
. 默认时,时区是缺省的服务器时区。不幸的是,JSF的规范说所有的日期和时间应假定为UTC(Universal Time Coordinated),并显示为UTC ,除非明确地用<f:convertDateTime>指定时区。这是一个极其不便的默认行为
。
Seam 覆盖这种行为,并且默认所有日期和时间是Seam时区。 另外,Seam提供了<s:convertDateTime>
标签,其始终用Seam时区执行转换。
16.5. 主题
Seam应用程序也非常容易换肤的。 主题API与本地化API是非常相似的,但是,当然这两个问题是正交,一些应用程序支持本地化和主题两者。
首先,配置支持的主题集:
<theme:theme-selector cookie-enabled="true">
<theme:available-themes>
<value>default</value>
<value>accessible</value>
<value>printable</value>
</theme:available-themes>
</theme:theme-selector>
注意:第一个列的主题是默认主题。
主题被定义在一个与主题同名的属性文件中。例如,default
主题定义一组条目在default.properties
文件中。例如default.properties
可以定义:
css ../screen.css
template /template.xhtml
通常在一个主题资料包的条目将指出CSS样式、图像或facelets模板名的路径(不像本地化资料包,它们通常是文本)。
现在在JSP或facelets页面我们能使用这些条目。例如,在facelets页面中主题化样式表。
<link href="#{theme.css}" rel="stylesheet" type="text/css" />
或者,当页面定义在一个子目录中:
<link href="#{facesContext.externalContext.requestContextPath}#{theme.css}"
rel="stylesheet" type="text/css" />
facelets通过一个 <ui:composition>让我们主题化模板最有力:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
template="#{theme.template}">
就象区域选择器,有一个内建的主题选择器,允许用户自由切换主题:
<h:selectOneMenu value="#{themeSelector.theme}">
<f:selectItems value="#{themeSelector.themes}"/>
</h:selectOneMenu>
<h:commandButton action="#{themeSelector.select}" value="Select Theme"/>
16.6. 通过cookies 持久化区域设置和主题参数
区域选择器、主题选择器和时区选择器全部支持区域和优化主题持久化到cookie。在components.xml中仅设置cookie-enabled属性:
<theme:theme-selector cookie-enabled="true">
<theme:available-themes>
<value>default</value>
<value>accessible</value>
<value>printable</value>
</theme:available-themes>
</theme:theme-selector>
<international:locale-selector cookie-enabled="true"/>