Java WEB开发--国际化(一)
Java WEB开发--国际化
一、国际化开发概述
软件的国际化:软件开发时,要使它能同时应对世界不同地区和国家的访问,并针对同地区和国家的访问,提供相应的、符合来访者阅读习惯的页面或数据。
国际化又称为 i18n:internationalization
二、合格的国际化软件
对于程序中固定使用的文本元素,例如菜单栏、导航条等中使用的文本元素、或错误提示信息,状态信息等,需要根据来访者的地区和国家,选择不同语言的文本为之服务。
对于程序动态产生的数据,例如(日期,货币等),软件应能根据当前所在的国家或地区的文化习惯进行显示
三、固定文本元素的国际化
对于软件中的菜单栏、导航条、错误提示信息,状态信息等这些固定不变的文本信息,可以把它们写在一个properties文件中,并根据不同的国家编写不同的properties文件。这一组properties文件称之为一个资源包。
在JavaAPI中提供了一个ResourceBundle 类用于描述一个资源包,并且 ResourceBundle类提供了相应的方法getBundle,这个方法可以根据来访者的国家地区自动获取与之对应的资源文件予以显示。
四、创建资源包和资源文件
一个资源包中的每个资源文件都必须拥有共同的基名。除了基名,每个资源文件的名称中还必须有标识其本地信息的附加部分。例如:一个资源包的基名是“myproperties”,则与中文、英文环境相对应的资源文件名则为: “myproperites_zh.properties” “myproperites_en.properties
每个资源包都应有一个默认资源文件,这个文件不带有标识本地信息的附加部分。若ResourceBundle对象在资源包中找不到与用户匹配的资源文件,它将选择该资源包中与用户最相近的资源文件,如果再找不到,则使用默认资源文件。例如:myproperites.properties
五、资源文件的书写格式
资源文件的内容通常采用“关键字=值”的形式,软件根据关键字检索值显示在页面上。一个资源包中的所有资源文件的关键字必须相同,值则为相应国家的文字。
并且资源文件中采用的是properties格式文件,所以文件中的所有字符都必须是ASCII字码,对于像中文这样的非ACSII字符,须先进行编码。(java提供了一个native2ascii命令用于编码)。例:
username=username username=\u7528\u6237\u540d
password=password password=\u5bc6\u7801
submit=submit submit=\u63d0\u4ea4
resource_en.properties文件 resource_zh.properties文件
六、编程实现固定文本的国际化
ResourceBundle类提供了一个静态方法getBundle,该方法用于装载资源文件,并创建ResourceBundle实例:
Locale currentLocale = Locale.getDefault();
ResourceBundle myResources =
ResourceBundle.getBundle(basename, currentLocale);
basename为资源包基名(且必须为完整路径)。
如果与该locale对象匹配的资源包子类找不到。一般情况下,则选用默认资源文件予以显示。
加载资源文件后,程序就可以调用ResourceBundle 实例对象的 getString 方法获取指定的资源信息名称所对应的值。
Stringvalue = myResources.getString(“key");
在WEB应用中实现固定文本的国际化
七、动态数据的国际化
数值,货币,时间,日期等数据由于可能在程序运行时动态产生,所以无法像文字一样简单地将它们从应用程序中分离出来,而是需要特殊处理。Java 中提供了解决这些问题的API 类(位于 java.util 包和 java.text 包中)
Locale 类
Locale 实例对象代表一个特定的地理,政治、文化区域。
一个 Locale 对象本身不会验证它代表的语言和国家地区信息是否正确,只是向本地敏感的类提供国家地区信息,与国际化相关的格式化和解析任务由本地敏感的类去完成。(若JDK中的某个类在运行时需要根据 Locale 对象来调整其功能,这个类就称为本地敏感类)
八、DateFormat类(国际化日期)
SateFormat类可以将一个日期/时间对象格式化为表示某个国家地区的日期/时间字符串
DateFormat类除了可按国家地区格式化输出日期外,它还定义了一些用于描述日期/时间的显示模式的int型的常量,包括FULL、LONG、MEDIUM、DEFAULT、SHORT,实例化DateFormat对象时,可以使用这些常量,控制日期/时间的显示长度。
九、DateFormat类
例如,对于日期/时间“2005年9月15日下午4时41分20秒”,如设置了DateFormat的显示模式,则日期/时间显示样式分别为:
SHORT模式完全是数字的,在中文环境下显示为“05-9-15 下午4:41”;在英文环境下为“9/15/05 4:41 PM”。
MEDIUM模式比SHORT模式长些,在中文环境显示为“2005-9-15 16:41:20”;在英文环境下显示为“Sep 15, 2005 4:41:20 PM”。
LONG模式比MEDIUM模式更长一些,在中文环境下显示为“2005年9月15日 下午04时41分20秒”;在英文环境下显示为“September 15, 20054:41:20 PM CST”。
FULL模式指定日期/时间的完整格式,在中文环境下显示为“2005年9月15日 星期四 下午04时41分20秒 CST”;在英文环境下,这个日期/时间显示为“Thursday, September 15, 20054:41:20 PM CST”。
十、实例化DateFormat类
实例化DateFormat类有九种方式,以下三种为带参形式,下面列出的三种方式也可以分别不带参,或只带显示样式的参数。
getDateInstance(int style, Locale aLocale):以指定的日期显示模式和本地信息来获得DateFormat实例对象,该实例对象不处理时间值部分。
getTimeInstance(int style, Locale aLocale):以指定的时间显示模式和本地信息来获得DateFormat实例对象,该实例对象不处理日期值部分。
getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale):以单独指定的日期显示模式、时间显示模式和本地信息来获得DateFormat实例对象。
十一、DateFormat类
DateFormat对象的方法:
format: 将date对象格式化为符合某个本地环境习惯的字符串。
parse:将字符串解析为日期/时间对象
注意:parse和format完全相反,一个是把date时间转化为相应地区和国家的显示样式,一个是把相应地区的时间日期转化成date对象,该方法在使用时,解析的时间或日期要符合指定的国家、地区格式,否则会抛异常。
DateFormat 对象通常不是线程安全的,每个线程都应该创建自己的 DateFormat 实例对象
十二、NumberFormat类
NumberFormat 可以将一个数值格式化为符合某个国家地区习惯的数值字符串,也可以将符合某个国家地区习惯的数值字符串解析为对应的数值
NumberFormat 类的方法:
format 方法:将一个数值格式化为符合某个国家地区习惯的数值字符串
parse 方法:将符合某个国家地区习惯的数值字符串解析为对应的数值。
实例化NumberFormat类时,可以使用locale对象作为参数,也可以不使用,下面列出的是使用参数的。
getNumberInstance(Locale locale):以参数locale对象标识的本地信息来获得具有多种用途的NumberFormat实例对象
getIntegerInstance(Locale locale):以参数locale对象所标识的本地信息来获得处理整数的NumberFormat实例对象
getCurrencyInstance(Locale locale):以参数locale对象所标识的本地信息来获得处理货币的NumberFormat实例对象
getPercentInstance(Locale locale):以参数locale对象所标识的本地信息来获得处理百分比数值的NumberFormat实例对象
期待已久的日子即将到来: 最新版JavaServer Pages (JSP)2.0规范即将和其他的J2EE 1.4一同发布。新的JSP版本有一个新的飞跃,采用了新的方式:由于新的语言表达式(Expression Language,以下简称为EL)和JSP标准标签库(JSP Standard Tag Library ,以下简称为JSTL)这两种新的方式,在页面中不需要用java,对于开发一般的应用来说,重用代码变得更加容易。更具体来说,JSP 2.0带来了以下的优点:
首次被JSTL 1.0引入的EL现在被合并到JSP规范中,就像应用template text一样地使用所有的标准的和定制的组件。
新的EL已经被扩展,具备一个函数调用机制,JSTL1.1整合了一系列经常需要使用的函数。
新增加的变量和servlet 规范定义的错误处理机制被更好地组织起来。通过新增加的变量,JSP error pages 现在可以提供更多的错误信息。
容器因为更加严格的语法检查可以更容易地找出发生的错误。
所有的J2EE 1.4规范(包括JSP 2.0 和 Servlet 2.4),为了声明部署的规则描述而应用了XML schema。这样的好处之一是你现在可以通过任何顺序列出web.xml文件中的描述。JSP 2.0也增加了一些新的配置选项用于部署描述,允许通过全局的配置来代替基于每页的配置。
由于更具伸缩性的规则和新的自定义action element,现在就像编写XML文件一样,编写JSP页面变得更加容易。
定制的标签库现在可以开发成一系列的标签文件(具有JSP元素的文本文件),标签处理器可以使用新的、简化的标签处理器的API。与此同时,新规范加入了一些新的特性,比如:支持在jsp页面上显示动态属性列表和可执行片断属性。
在众多的书籍中,这是头一个讲解JSP 2.0新特性的文章。在这一部分,我们将看到和EL相关的信息,其他的新特性留到后面。在这里我假定读者已经熟悉JSP 1.2,而且至少听说过JSTL。
你可能对这本第三版的《JavaServer Pages》感兴趣。这本书中,我尽可能在细节上讲述所有的内容,而且并不认为你对JSP或者JSTL了解一切。这本书预计在2003年12月 出版,但是你现在可以在http://www.amazon.com、Barnes&Noble,或者其他在线书店预订。
EL(The Expression Language)
如果过去使用过JSTL,那么你可能已经熟悉了EL。EL在JSTL 1.0规范中被引入,用来在运行期间对Java表达式中action element属性赋值提供另一种选择。当JSTL EL已经非常迅速的流行起来情况下,还是存在一个问题: JSTL EL 表达式仅仅可以与JSTL和custom action一起使用,怎样才能使用非标准API对EL表达式求值?
JSP 2.0中,JSP容器自己可以理解EL表达式。这使你在所有过去只能应用Java表达式的地方应用EL表达式成为可能,比如:标准和定制action的属性值,模板文本。
在我们看具体的例子前,让我们更进一步的看看什么是EL。EL是从JavaScript中获得启发的一种语言,XPath(一种用来访问XML文档的语言),但是EL在对变量的null值和执行更多数据类型的自动类型转换的处理上更加宽松。这些新特性对于web应用非常重要,在这些应用中输入通常通过html表单的request parameter来得到。这些参数可能仅仅在某些请求下才能体现出来,而且浏览器经常将request parameter作为文本发送,然而应用程序经常需要把他们作为数字类型、布尔类型(true 或者 false)来使用。通过EL,你根本就很少需要关心缺少某些参数的值或者类型转换。
一个EL表达式包含变量和操作符。任何存储在某个JSP作用范围(如:page、 request、session、application)的bean能被作为一个EL变量来使用。另外,EL支持以下预定义的变量:
变量名称
说明
pageScope
一个包含所有page scope范围的变量集合 (a java.util.Map)
requestScope
一个包含所有request scope范围的变量集合 (a java.util.Map)
sessionScope
一个包含所有session scope范围的变量集合 (a java.util.Map)
applicationScope
一个包含所有application scope范围的变量集合 (a java.util.Map)
param
一个包含所有请求参数的集合 (a java.util.Map),通过每个参数对应一个String值的方式赋值
paramValues
一个包含所有请求参数的集合 (a java.util.Map),通过每个参数对应一个String数组的方式赋值
header
一个包含所有请求的头信息的集合, (a java.util.Map) ,通过每个头信息对应一个String值的方式赋值
headerValues
一个包含所有请求的头信息的集合 (a java.util.Map) ,通过每个头信息的值都保存在一个String数组的方式赋值
cookie
一个包含所有请求的 cookie集合 (a java.util.Map), 通过每一个cookie(javax.servlet.http.Cookie)对应一个cookie值的方式赋值
initParam
一个包含所有应用程序初始化参数的集合(a java.util.Map) ,通过每个参数分别对应一个String值的方式赋值
pageContext
一个javax.servlet.jsp.PageContext类的实例, 用来提供访问不同的请求数据
操作符描述了你对变量所期望的操作。如果你之前曾经使用过任何编程语言的话,在EL表达式中所使用的操作符对你来说可能看起来很熟悉。因为它们和那些在大多数语言中所支持的操作符一样。
Operator
Description
.
访问一个bean属性或者 Map entry
[]
访问一个数组或者链表元素
()
对子表达式分组,用来改变赋值顺序
? :
条件语句,比如: 条件 ? ifTrue : ifFalse.如果条件为真,表达式值为前者,反之为后者
+
数学运算符,加操作
-
数学运算符,减操作或者对一个值取反
*
数学运算符,乘操作
/ or div
数学运算符,除操作
% or mod
数学运算符,模操作(取余)
== or eq
逻辑运算符,判断符号左右两端是否相等,如果相等返回true,否则返回false
!= or ne
逻辑运算符,判断符号左右两端是否不相等,如果不相等返回true,否则返回false
< or lt
逻辑运算符,判断符号左边是否小于右边,如果小于返回true,否则返回false
> or gt
逻辑运算符,判断符号左边是否大于右边,如果大于返回true,否则返回false
<= or le
逻辑运算符,判断符号左边是否小于或者等于右边,如果小于或者等于返回true,否则返回false
>= or ge
逻辑运算符,判断符号左边是否大于或者等于右边,如果大于或者等于返回true,否则返回false
&& or and
逻辑运算符,与操作赋。如果左右两边同为true返回true,否则返回false
|| or or
逻辑运算符,或操作赋。如果左右两边有任何一边为true返回true,否则返回false
! or not
逻辑运算符,非操作赋。如果对true取运算返回false,否则返回true
empty
用来对一个空变量值进行判断: null、一个空String、空数组、 空Map、没有条目的Collection集合
func(args)
调用方法, func是方法名,args是参数,可以没有,或者有一个、多个参数.参数间用逗号隔开
一个EL表达式可以包含:数字、文本(在单引号或者双引号之间)、布尔值、null值。
因为一个EL表达式可以出现在静态文本出现的地方,因此你必须告诉JSP容器它应该被当作一个EL表达式来处理。你可以通过使用定界符来做到这一点。一个EL表达式总是以”${ }”来标记(一个“$”符号和一个左花括号,右花括号)。这里有一个EL表达式,它将一个命名为amount的变量加5:
${amount + 5}
如果你想要将5加到一个bean的property上,可以使用property访问操作符:
${order.amount + 5}
在当前这个指定的bean或者collection集合中,Property访问操作符(一个“.“符号)告诉EL去寻找名字为amount的property。
${order[‘amount‘] + 5}
在[]之间的值必须是一个property的名字(就像上面的例子中那样)或者是一个保存property名字的变量(或者是一个完整的EL子表达式)。
EL表达式可以被用来赋值给任何标准的或者定制的JSP行为属性(action attribute),这些行为属性被标记为可以接受动态值(或者请求期间的属性值,就象它被正式调用一样):
<c:out value="${order.amount + 5}"/>
在JSP 2.0之前,你不得不使用Java表达式去给一个属性动态赋值。在过去的很多年中,这已经成为语法混乱的一个普遍根源。
最后,EL表达式可以在页面中和模板直接混合使用。当你生成HTML并且需要设置一个动态值给一个属性的时候,这非常方便:
<input name="firstName" value="${customer.firstName}">
JSP 1.2中,你不得不使用JSTL的<c:out>来实现同样的事情,最后把各种不同类型的元素混合起来,这导致程序理解起来非常的困难:
<input name="firstName"
value="<c:out value="${customer.firstName}"/>" >
新JSTL 1.1 Tag Library 标识符
JSTL1.1发布的是一个初级的版本,主要目的是用来整合JSTL和JSP2.0 。最明显的变化是JSTL1.0 “孪生函数库”(一组库用来接受EL表达式,另外一组用来接受JAVA表达式),而它们已经被一组既可以用于EL表达式也可以用于JAVA表达式的函数库所代替。
在JSTL 1.1中使用以下标识符:
库
URI
前缀
Core
http://java.sun.com/jsp/jstl/core
c
XML processing
http://java.sun.com/jsp/jstl/xml
x
I18N formatting
http://java.sun.com/jsp/jstl/fmt
fmt
Database access
http://java.sun.com/jsp/jstl/sql
sql
Functions
http://java.sun.com/jsp/jstl/functions
fn
如果你曾经使用过JSTL1.0,你可能会注意到新的标识符和旧的EL库标试符一模一样,除了加入了“/jsp path” element。你也可能注意到在JSTL1.1中有一个库,包含了EL的函数。我们稍后就会看到。
一个新的EL操作符
在JSP页面中一个非常普遍的需求就是:当某个条件为真时,要在网页中包含一些文字。在JSP1.2和JSTL1.1中,用具有代表性的<c:if>来实现,但是这样做非常繁琐。JSP2.0增加了一个新的条件操作符用于EL,以更加优雅的方式来处理这样的情况。这个条件操作符存在于很多编程语言中(比如:Java,C,JavaScript),因此你可能以前就见过它。它判断一个布尔的条件,当条件为真或者假时,分别取不同的结果。
一个能清楚说明它如何工作的例子:
<select name="artist">
<option value="1" ${param.artist == 1 ? ‘selected‘ : ‘‘}>
Vesica Pisces
<option value="2" ${param.artist == 2 ? ‘selected‘ : ‘‘}>
Cortical Control
<option value="3" ${param.artist == 3 ? ‘selected‘ : ‘‘}>
Vida Vierra
</select>
在这里,我使用了EL表达式和条件操作符来选择是否包含 html 中的 “selected”属性,只有符合条件的 “option” 才被添加 “selected” 属性。如果条件(param.artist==1)为真时,前面的“selected” 才被添加到网页中;否则就添加后面的(在这里是空字符串 ‘’)到页面中。
EL函数
当EL从JSTL规范中移到JSP规范中,它使用了一个如何进行函数调用的技巧。这个EL函数语法非常简单:方法名,紧接着在圆括号中有一组参数:
<%@ taglib prefix="fn"
uri="http://java.sun.com/jsp/jstl/functions" %>
${fn:length(myCollection)}
这是一个属于标签库中的函数,并且函数名字在页面中所包含的前缀要指定taglib库。在这个例子中,我使用了前缀fn,这是JSTL function库默认的前缀。
标签库描述符(Tag Library Descriptor,TLD)将函数名称映射到一个由JAVA实现的静态方法中:
<function>
<description>
Returns the number of items in a collection or the number of characters in a string.
</description>
<name>length</name>
<function-class>
org.apache.taglibs.standard.functions.Functions
</function-class>
<function-signature>
int length(java.lang.Object)
</function-signature>
</function>
在这里最有趣的element是<function-signature>。它包含一个函数返回类型的声明,静态的方法的名字,在圆括号中声明该方法所有参数的类型(可以没有参数或者有多个,参数间用逗号间隔开)。返回值类型和参数类型必须是java的原始类型(Object)或者是其他合法类型。
这个静态方法 length()在Jakarta Taglibs标准库中用类似于下面的代码实现的:
public static int length(Object obj)
throws JspTagException {
if (obj == null)
return 0;
if (obj instanceof String)
return ((String)obj).length();
if (obj instanceof Collection)
return ((Collection)obj).size();
if (obj instanceof Map)
return ((Map)obj).size();
int count = 0;
if (obj instanceof Iterator) {
Iterator iter = (Iterator) obj;
count = 0;
while (iter.hasNext()) {
count++;
iter.next();
}
return count;
}
if (obj instanceof Enumeration) {
Enumeration enum = (Enumeration) obj;
count = 0;
while (enum.hasMoreElements()) {
count++;
enum.nextElement();
}
return count;
}
try {
count = Array.getLength(obj);
return count;
} catch (IllegalArgumentException ex) {}
throw new JspTagException("Unsupported type"));
}
就像你所看到的,在那里没有什么出奇的地方。它是一个常规的静态方法,这个函数中通过对运行期中的参数类别的判断,找出参数的长度。
除了在这个方法中使用的length()方法,JSTL1.1标签库还包含了许多其它经常使用的函数:
函数
描述
fn:contains(string, substring)
如果参数string中包含参数substring,返回true
fn:containsIgnoreCase(string, substring)
如果参数string中包含参数substring(忽略大小写),返回true
fn:endsWith(string, suffix)
如果参数 string 以参数suffix结尾,返回true
fn:escapeXml(string)
将有特殊意义的XML (和HTML)转换为对应的XML character entity code,并返回
fn:indexOf(string, substring)
返回参数substring在参数string中第一次出现的位置
fn:join(array, separator)
将一个给定的数组array用给定的间隔符separator串在一起,组成一个新的字符串并返回。
fn:length(item)
返回参数item中包含元素的数量。参数Item类型是数组、collection或者String。如果是String类型,返回值是String中的字符数。
fn:replace(string, before, after)
返回一个String对象。用参数after字符串替换参数string中所有出现参数before字符串的地方,并返回替换后的结果
fn:split(string, separator)
返回一个数组,以参数separator 为分割符分割参数string,分割后的每一部分就是数组的一个元素
fn:startsWith(string, prefix)
如果参数string以参数prefix开头,返回true
fn:substring(string, begin, end)
返回参数string部分字符串, 从参数begin开始到参数end位置,包括end位置的字符
fn:substringAfter(string, substring)
返回参数substring在参数string中后面的那一部分字符串
fn:substringBefore(string, substring)
返回参数substring在参数string中前面的那一部分字符串
fn:toLowerCase(string)
将参数string所有的字符变为小写,并将其返回
fn:toUpperCase(string)
将参数string所有的字符变为大写,并将其返回
fn:trim(string)
去除参数string 首尾的空格,并将其返回
结束语:
在这篇文章中,我从EL讲到JSTL1.1规范、EL新特色和JSTL 1.1函数库。接下来的部分我将要告诉你:关于JSP error-page的改进和增强; jsp:id 属性带来的益处;新的配置属性描述符;JSP2.0如何使JSP操作XML变得更加容易;自定义标签库的新特性。