随笔-314  评论-209  文章-0  trackbacks-0
因为希望把SpringSide搞成国际化项目,i18n就成了必做的事情。
照抄appfuse,折腾了很久后才发现appfuse式的sample总是只顾着演示自己的一亩三分地而忽略了很多其他东西。

   1.从基础开始,没有Spring时,Java的i18n是这样的:
   1.1 jsp环境
    首先写一个messages.zh_CN.properties文件,放在class-path也就是/WEB-INF/classes里     welcome=欢迎    然后用native2ascii.exe把它转为 welcome=\u6b22\u8fce
    在web.xml中定义messages文件     
       <context-param>
            <param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
            <param-value>messages</param-value>
        </context-param>
    最后在jsp里使用
<%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>
<fmt:message key="welcome"/>
如果有多个Resource Bundle文件, 就要在jsp里用<ftm:bundle>定义了.

1.2 pure Java环境
    ResourceBundle rb = ResourceBundle.getBundle("messages");
    String welcome = rb.getString("welcome");

2.Spring的增强及appfuse的做法
   Spring增加了MessageSource的概念,一是ApplicationContext将充当一个单例的角色,不再需要每次使用i18时都初始化一次ResourceBundle,二是可以代表多个Resource Bundle.

   在ApplicationContext的定义文件中,增加如下节点: 
   <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="messages"/>
    </bean>
   则在pure java环境中。  context.getMessage("welcome", null, Locale.CHINA)

  而在jsp环境中,Controller调用JSTL viewResolver再调用Jsp时,<fmt:message>将继续发挥它的功效。

  因此,appfuse等sample都是在appfuse-servlet.xml 中定义一个<messageSource>。

3.Better Practice
3.1 要不要定义javax.servlet.jsp.jstl.fmt.localizationContext[定义]
      Appfuse等sample,都是假定大家完全使用Controller作访问入口,jsp甚至藏在了/web-inf/中。而很不幸,大家的项目可能还是有很多直接访问jsp的地方,而直接访问jsp时,<messageSource>节点是没有作用的。
     但如果定义了javax...localizationContext, 又会让MessageSource失效......


3.2 messageSource定义在ApplicationContext.xml还是appfuse-servlet.xml
     ApplicationContext*.xml由ContextLoaderListener载入而appfuse-servlet.xml靠dispatchServlet载入并拥有一个指向ApplcationContex*.xml指针。所以,appfuse-servlet.xml能看到定义在ApplcationContext里的东西,而反之做不到。
     明显, 把<messageSource>定义在ApplicationContext.xml 能获得更好的可见性。
     但是appfuse没有在pure Java代码中使用i18n,也就没有考虑这个问题。

3.3 坚决不用鸡肋级<spring:message> tag
      连appfuse也不用它,可见多么鸡肋。因为fmt在找不到资源时,最多显示???welcome???,而<spring:message>则会抛出异常,谁会喜欢这种定时炸弹阿。

3.4 有趣的theme 解决"做成图片的文字"的国际化
       theme也就是把message的原理发挥了一下,让不同语言的美术字图片的路径也可以定义在theme_zh_CN.properties和theme_en_US.properties中。终于有一个不那么鸡肋的spring tag了。

4.简单归纳

1. jstl中仍然使用标准的<ftm:message>及其定义?

2.java中使用spring的<messageSource>实现单例

3.用<spring:theme>解决那些做成图片的文字的国际化问题

4.Spring 还有session,cookie locale resolver, 到时可以看一下.

posted on 2008-11-22 16:40 xzc 阅读(1465) 评论(3)  编辑  收藏 所属分类: Java

评论:
# re: Spring i18n的better practice(相对于appfuse) 2008-11-22 16:46 | xzc
<%@page contentType="text/html; charset=UTF-8"%>
<%@page import="java.util.*"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x"%>
<html>
<head>
<title>language choose</title>
</head>
<body bgcolor="#ffffff">

copyright @ lizongbo @ donews.net


<c:if test="${param['locale'] != null}">
<fmt:setLocale value="${param['locale']}" scope="session"/>
<fmt:setTimeZone value="${param['locale']}" scope="session"/>
</c:if>
<c:if test="${param['locale'] == null}">
<fmt:setLocale value="${header['locale']}" scope="session"/>
<fmt:setTimeZone value="${header['locale']}" scope="session"/>
</c:if>
<%
Locale crtl = Locale.getDefault();
Object cobj = session.getAttribute("javax.servlet.jsp.jstl.fmt.locale.session");
if (cobj != null && cobj instanceof Locale) {
crtl = (Locale) cobj;
}
Locale[] la = java.text.NumberFormat.getAvailableLocales();
%>
<form method="POST" action="">
language choose:
<br />
<select name="locale">
<%for (int i = 0; i < la.length; i++) {%>
<option value="<%=la[i]%>" <%if (la[i].equals(crtl)) {
out.print("selected=\"selected\"");
}%>><%= la[i].getDisplayName(crtl)%></option>
<%
}
%>
</select>
<br />
<input type="submit" value="change"/>
</form>
</body>
</html>


  回复  更多评论
  
# re: Spring i18n的better practice(相对于appfuse) 2008-11-22 16:47 | xzc
使用JSTL解决国际化问题非常简单,首先在WEB-INF/classes放上资源文件,例如messages.properties、messages_zh_CN.properties等文件。

使用JSTL fmt库:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>
<fmt:setLocale value="${header['accept-language']}"/>
<fmt:setBundle basename="messages"/>

setLocale设置地区,一般从HTTP Header里面取就比较方便一点。
setBundle设置资源文件的名称。

通过如下方式就可以应用了。
<fmt:message key="SampleMessage"/>

还可以加参数。
<fmt:message key="LoginMessage">
<fmt:param value="${loginName}"/>
</fmt:message>  回复  更多评论
  
# re: Spring i18n的better practice(相对于appfuse) 2008-11-22 16:59 | xzc
9.5 I18N formatting 标签库
看到 I18N 就应该想到知识“国际化”, I18N formatting 标签库就是用于在 JSP 页面中做国际化的动作。在该标签库中的标签一共有 12 个,被分为了两类,分别是:

q 国际化核心标签: <fmt:setLocale> 、 <fmt:bundle> 、 <fmt:setBundle> 、 <fmt:message> 、 <fmt:param> 、 <fmt:requestEncoding> 。

q 格式化标签: <fmt:timeZone> 、 <fmt:setTimeZone> 、 <fmt:formatNumber> 、 <fmt:parseNumber> 、 <fmt:formatDate> 、 <fmt:parseDate> 。

下面只选择其中常见的一些标签和属性进行介绍。

9.5.1 用于设置本地化环境的 <fmt:setLocale> 标签
<fmt:setLocale> 标签用于设置 Locale 环境。它的属性和描述如表 9.17 所示:

表 9.17   <fmt:setLocale> 标签属性和说明

属性
描述

value
Locale 环境的指定,可以是 java.util.Locale 或 String 类型的实例

scope
Locale 环境变量的作用范围(可选)


下面看一个示例:

<fmt:setLocale value="zh_TW"/>

表示设置本地环境为繁体中文。

9.5.2 用于资源文件绑定的 <fmt:bundle> 、 <fmt:setBundle> 标签
这两组标签用于资源配置文件的绑定,唯一不同的是 <fmt:bundle> 标签将资源配置文件绑定于它标签体中的显示, <fmt:setBundle> 标签则允许将资源配置文件保存为一个变量,在之后的工作可以根据该变量来进行。

根据 Locale 环境的不同将查找不同后缀的资源配置文件,这点在国际化的任何技术上都是一致的,通常来说,这两种标签单独使用是没有意义的,它们都会与 I18N formatting 标签库中的其他标签配合使用。它们的属性和描述如表 9.18 所示:

表 9.18   <fmt:bundle> 、 <fmt:setBundle> 标签属性和说明

属性
描述

basename
资源配置文件的指定,只需要指定文件名而无须扩展名,二组标签共有的属性

var
<fmt:setBundle> 独有的属性,用于保存资源配置文件为一个变量

scope
变量的作用范围


下面看一个示例

<fmt:setLocale value="zh_CN"/>

<fmt:setBundle basename="applicationMessage" var="applicationBundle"/>

该示例将会查找一个名为 applicationMessage_zh_CN.properties 的资源配置文件,来作为显示的 Resource 绑定。

9.5.3 用于显示资源配置文件信息的 <fmt:message> 标签
用于信息显示的标签,将显示资源配置文件中定义的信息。它的属性和描述如表 9.19 所示:

表 9.19   <fmt:message> 标签属性和说明

属性
描述

key
资源配置文件的“键”指定

bundle
若使用 <fmt:setBundle> 保存了资源配置文件,该属性就可以从保存的资源配置文件中进行查找

var
将显示信息保存为一个变量

scope
变量的作用范围


下面看一个示例:

<fmt:setBundle basename="applicationMessage" var="applicationBundle"/>

<fmt:bundle basename="applicationAllMessage">

<fmt:message key="userName" />

<p>

<fmt:message key="passWord" bundle="${applicationBundle}" />

</fmt:bundle>

该示例使用了两种资源配置文件的绑定的做法,“ applicationMessage ”资源配置文件利用 <fmt:setBundle> 标签被赋于了变量“ applicationBundle ”,而作为 <fmt:bundle> 标签定义的“ applicationAllMessage ”资源配置文件作用于其标签体内的显示。

q 第一个 <fmt:message> 标签将使用“ applicationAllMessage ”资源配置文件中“键”为“ userName ”的信息显示。

q 第二个 <fmt:message> 标签虽然被定义在 <fmt:bundle> 标签体内,但是它使用了 bundle 属性,因此将指定之前由 <fmt:setBundle> 标签保存的“ applicationMessage ”资源配置文件,该“键”为“ passWord ”的信息显示。

9.5.4 用于参数传递的 <fmt:param> 标签
<fmt:param> 标签应该位于 <fmt:message> 标签内,将为该消息标签提供参数值。它只有一个属性 value 。

<fmt:param> 标签有两种使用版本,一种是直接将参数值写在 value 属性中,另一种是将参数值写在标签体内。

9.5.6 用于为请求设置字符编码的 <fmt:requestEncoding> 标签
<fmt:requestEncoding> 标签用于为请求设置字符编码。它只有一个属性 value ,在该属性中可以定义字符编码。

9.5.7 用于设定时区的 <fmt:timeZone> 、 <fmt:setTimeZone> 标签
这两组标签都用于设定一个时区。唯一不同的是 <fmt:timeZone> 标签将使得在其标签体内的工作可以使用该时区设置, <fmt:setBundle> 标签则允许将时区设置保存为一个变量,在之后的工作可以根据该变量来进行。它们的属性和描述如表 9.20 所示:

表 9.20   <fmt:timeZone> 、 <fmt:setTimeZone> 标签 属性和说明

属性
描述

value
时区的设置

var
<fmt:setTimeZone> 独有的属性,用于保存时区为一个变量

scope
变量的作用范围


9.5.8 用于格式化数字的 <fmt:formatNumber> 标签
<fmt: formatNumber > 标 签用于格式化数字。它的属性和描述如表 9.21 所示:

表 9.21   <fmt:formatNumber> 标签属性和说明

属性
描述

value
格式化的数字,该数值可以是 String 类型或 java.lang.Number 类型的实例

type
格式化的类型

pattern
格式化模式

var
结果保存变量

scope
变量的作用范围

maxIntegerDigits
指定格式化结果的最大值

minIntegerDigits
指定格式化结果的最小值

maxFractionDigits
指定格式化结果的最大值,带小数

minFractionDigits
指定格式化结果的最小值,带小数


<fmt:formatNumber> 标签实际是对应 java.util.NumberFormat 类, type 属性的可能值包括 currency (货币)、 number (数字)和 percent (百分比)。

下面看一个示例。

<fmt:formatNumber value="1000.888" type="currency" var="money"/>

该结果将被保存在“ money ”变量中,将根据 Locale 环境显示当地的货币格式。


9.5.9 用于解析数字的 <fmt:parseNumber> 标签
<fmt:parseNumber> 标签用于解析一个数字,并将结果作为 java.lang.Number 类的实例返回。 <fmt:parseNumber> 标签看起来和 <fmt:formatNumber> 标签的作用正好相反。它的属性和描述如表 9.22 所示:

表 9.22   <fmt:parseNumber> 标签属性和说明

属性
描述

value
将被解析的字符串

type
解析格式化的类型

pattern
解析格式化模式

var
结果保存变量,类型为 java.lang.Number

scope
变量的作用范围

parseLocale
以本地化的形式来解析字符串,该属性的内容应为 String 或 java.util.Locale 类型的实例


下面看一个示例。

<fmt:parseNumber value="15%" type="percent" var="num"/>

解析之后的结果为“ 0.15 ”。

9.5.10 用于格式化日期的 <fmt:formatDate> 标签
<fmt:formatDate> 标签用于格式化日期。它的属性和描述如表 9.23 所示:

表 9.23   <fmt:formatDate> 标签属性和说明

属性
描述

value
格式化的日期,该属性的内容应该是 java.util.Date 类型的实例

type
格式化的类型

pattern
格式化模式

var
结果保存变量

scope
变量的作用范围

timeZone
指定格式化日期的时区


<fmt:formatDate> 标签与 <fmt:timeZone> 、 <fmt:setTimeZone> 两组标签的关系密切。若没有指定 timeZone属性, 也可以通过 <fmt:timeZone> 、 <fmt:setTimeZone> 两组标签设定的时区来格式化最后的结果。

9.5.11 用于解析日期的 <fmt:parseDate> 标签
<fmt:parseDate> 标签用于解析一个日期,并将结果作为 java.lang.Date 类型的实例返回。 <fmt:parseDate> 标签看起来和 <fmt:formatDate> 标签的作用正好相反。它的属性和描述如表 9.24 所示:

表 9.24   <fmt:parseDate> 标签属性和说明

属性
描述

value
将被解析的字符串

type
解析格式化的类型

pattern
解析格式化模式

var
结果保存变量,类型为 java.lang.Date

scope
变量的作用范围

parseLocale
以本地化的形式来解析字符串,该属性的内容为 String 或 java.util.Locale 类型的实例

timeZone
指定解析格式化日期的时区


<fmt:parseNumber> 和 <fmt:parseDate> 两组标签都实现解析字符串为一个具体对象实例的工作,因此,这两组解析标签对 var 属性的字符串参数要求非常严格。就 JSP 页面的表示层前段来说,处理这种解析本不属于份内之事,因此 <fmt:parseNumber> 和 <fmt:parseDate> 两组标签应该尽量少用,替代工作的地方应该在服务器端表示层的后段,比如在 Servlet 中。

  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: