Chapter 14. 国际化和主题

Seam通过提供几个内置部件来为UI提供多语言支持,从而使构建国际化的应用程序变得十分容易。

14.1. 本地化

每一个用户登录会话都有一个相关的 java.util.Locale 实例(以名为 locale 的组件形式提供给应用程序)。 一般情况下,不需要做任何特别的配置设置locale,Seam 委托JSF来判断当前的活动locale:

  • 如果HTTP请求中有一个相关联的locale(浏览器的locale),并且这个locale在 faces-config.xml 的支持列表中,那么其他会话也使用该locale。
  • 否则,如果在 faces-config.xml 指定了默认的locale, 那么其他会话也使用该locale。
  • 否则,使用服务器的默认locale。

通过Seam的以下几个配置属性来手工设置locale也是你 可能的org.jboss.seam.international.localeSelector.languageorg.jboss.seam.international.localeSelector.countryorg.jboss.seam.international.localeSelector.variant,但是并不推荐这种做法。

然而,允许用户通过应用程序的用户界面来手工设置locale也是很有益处的。Seam提供了内置的功能来覆盖通过上述算法决定的locale。你所要做的只是 在JSP或Facelet的Form中增加以下代码段:

<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 支持的所有locale的列表,就用:

<h:selectOneMenu value="#{localeSelector.localeString}">
    <f:selectItems value="#{localeSelector.supportedLocales}"/>
</h:selectOneMenu>
<h:commandButton action="#{localeSelector.select}" value="#{messages['ChangeLanguage']}"/>

当在下拉列表中选择一项,并按下按钮后,随后会话中Seam和JSF的locale就被刷新了。

14.2. 标签

JSF 通过使用 <f:loadBundle /> 来支持用户界面标签和描述文本的国际化。这个方法同样可以用在Seam应用程序中。 或者,可以利用Seam的 messages 组件用内嵌的EL表达式来显示模板标签。

14.2.1. 定义标签

Seam提供了一个 java.util.ResourceBundle (以org.jboss.seam.core.resourceBundle 的名字提供给应用程序)。 你需要通过这个指定的资源包来使你的国际化标签可用。默认情况下,Seam 使用名为messages的资源包, 你需要在 messages.propertiesmessages_en.propertiesmessages_en_AU.properties 等文件中定义你的标签。这些文件通常在 WEB-INF/classes 目录下。

因此,在 messages_en.properties中:

Hello=Hello

和在 messages_en_AU.properties中:

Hello=G'day

你可以通过设置Seam的配置属性 org.jboss.seam.core.resourceLoader.bundleNames 为资源包选择一个不同的名字。 甚至可以指定一个资源包名称列表,以深度优先进行消息的搜索。

<core:resource-loader>
    <core:bundle-names>
        <value>mycompany_messages</value>
        <value>standard_messages</value>       
    </core:bundle-names>
</core:resource-loader>

如果想为一个特殊页定义消息,可在以一个和JSF View id同名的资源包中指定,去掉前置 / 和文件扩展名。 这样,如果我们只想在 /welcome/hello.jsp 中显示消息,就把它置于 welcome/hello_en.properties 中。

你还可以在 pages.xml 中指定一个显式的绑定名称:

<page view-id="/welcome/hello.jsp" bundle="HelloMessages"/>

这样,我们就可以在 /welcome/hello.jsp 中使用定义在 HelloMessages.properties 中的消息了。

14.2.2. 标签显示

如果使用Seam的资源包来定义标签,就不用每页再写 <f:loadBundle ... /> 了,可以使用这种简单的形式:

<h:outputText value="#{messages['Hello']}"/>

或者:

<h:outputText value="#{messages.Hello}"/>

更好的一点是,message自身可以包含EL表达式:

Hello=Hello, #{user.firstName} #{user.lastName}
Hello=G'day, #{user.firstName}

你也可以在代码中这样使用消息:

@In private Map<String, String> messages;
@In("#{messages['Hello']}") private String helloMessage;

14.2.3. Faces Messages

facesMessages 组件是一个向用户显示成功或者失败消息的非常方便的途径。 我们之前描述的功能对Faces Messages同样有效:

@Name("hello")
@Stateless
public class HelloBean implements Hello {
    @In FacesMessages facesMessages;
    
    public String sayIt() {
        facesMessages.addFromResourceBundle("Hello");
    }
}

这将根据用户的locale显示 Hello, Gavin King 或者 G'day, Gavin

14.3. 时区

Seam中还有一个session范围的 java.util.Timezone 实例,叫做 org.jboss.seam.international.timezone, 和一个名为 org.jboss.seam.international.timezoneSelector 的用于设置时区的组件。默认情况下,时区取服务器的默认时区。 不幸的是,JSF规范中讲所有的日期和时间都假设是UTC 的,并且显示为UTC,除非使用 <f:convertDateTime> 明确地为其指定时区。 这是一个非常不方便的默认行为。

Seam覆写了这个行为,默认所有的日期和时间都是Seam的时区。另外,Seam提供了 <s:convertDateTime> 标签,用来处理Seam 时区的转化。

14.4. 主题

Seam应用程序可以很方便地改变皮肤。Theme 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样式或图片的路径和facelet模板(不像本地化资源包那样通常是文本)。

现在我们可以在JSP或者Facelet页面中使用这些内容了。例如,一个Facelet页的风格可以这样:

<link href="#{theme.css}" rel="stylesheet" type="text/css" />

或者,当页面定义在一个子目录中时可以这样:

<link href="#{facesContext.externalContext.requestContextPath}#{theme.css}" 
    rel="stylesheet" type="text/css" />

最强大的是,Facelet让我们通过 <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}">

正如locale选择器一样,有一个内置的主题选择器允许用户在各主题间自由地切换:

<h:selectOneMenu value="#{themeSelector.theme}">
    <f:selectItems value="#{themeSelector.themes}"/>
</h:selectOneMenu>
<h:commandButton action="#{themeSelector.select}" value="Select Theme"/>

14.5. 使用cookie保存locale和主题设置

locale选择器、主题选择器和时区选择器全都支持持久化,把参数保存到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"/>