为了简化WEB应用的国际化开发,JSTL中提供了一个用于实现国际化和格式化功能的标签库,我们将其简称为国际化标签库,JSP规范为国际化标签库建议的前缀名为fmt。国际化标签库中包括了一组用于实现WEB国际化功能的标签,这组标签封装了Java语言中java.util和java.text这两个包中与国际化相关的API类的功能。国际化标签库中提供了绑定资源包和从资源包中的本地资源文件内读取文本内容的标签,也提供了对数值和日期等本地敏感的数据按本地化信息进行显示和解析的标签,还提供了按本地特定的时区来调整时间的标签。
国际化标签库中的标签可以根据客户端浏览器的信息来自动确定本地信息,也可以由JSP页面作者显式地指定本地信息。如果没有特别指定,本章案例假设默认的本地信息为“中文(中国)”。
在举例讲解国际化标签库中的标签时, 通常要使用到包含有多个资源文件的资源包,这个资源包中的各个资源文件分别对应几个不同本地信息。这里先创建好一组资源包文件。首先按例程8-25和例程8-26创建两个名称分别为greetings.properties和temp.properties的资源文件。
例程8-25 greetings.properties
org.it315.heading=The first WEB application
org.it315.welcome=Welcome to www.it315.org !
org.it315.okKey=Today is {0,date,full}, you have {1,number,currency} dollars.
例程8-26 temp.properties
org.it315.heading=第一个WEB应用程序
org.it315.welcome=欢迎访问www.it315.org网站!
org.it315.okKey=今天是{0,date,full},你有{1,number,currency}元。
接着使用JDK中提供的native2ascii程序将temp.properties文件中的中文字符转换成其Unicode码形式的转义序列。在命令行窗口中进入temp.properties文件所在的目录,执行如下命令:
native2ascii -encoding GB2312 temp.properties greetings_zh.properties
上面的命令执行后产生的greetings_zh.properties文件内容如例程8-27所示。
例程8-27 greetings_zh.properties
org.it315.heading=\u7b2c\u4e00\u4e2aWEB\u5e94\u7528\u7a0b\u5e8f
org.it315.welcome=\u6b22\u8fce\u8bbf\u95eewww.it315.org\u7f51\u7ad9!
org.it315.okKey=\u4eca\u5929\u662f{0,date,full}\uff0c\u4f60\u6709{1,number,
currency}\u5143\u3002
英文本地环境所对应的资源文件只需复制greetings.properties文件,并改名为greetings_en.properties即可。最后将greetings.properties、greetings_en.properties和greetings_zh.properties这三个资源文件保存到
[variant="variant"] [scope="{page|request|session|application}"] /> 表8.12 属性名 是否支持EL 属性类型 属 性 描 述 value true String或java.util.Locale 指定用户的本地化信息,可以是一个字符串或java.util.Locale实例对象。如果是字符串,则必须包含小写形式的语言编码,其后也可以带有大写形式的国家编码,两者中间用“-”或“_”连接 variant true String 指定创建Locale实例对象时设置的变量部分,它用于标识开发商或特定浏览器为实现扩展功能而自定义的信息 scope false String 指定将构造出的Locale实例对象保存在哪个Web作用域中 如果 例程8-28 fmt_setLocale.jsp 例程8-28中的 在浏览器地址栏输入如下地址访问fmt_setLocale.jsp页面: http://localhost:8080/JSTL/fmt_setLocale.jsp?locale=en_US 例程8-28的运行结果如图8.22所示。 将上面的访问地址中的locale参数设置为zh_CN后访问fmt_setLocale.jsp页面,例程8-28的运行结果如图8.23所示。 [var="varName"] [scope="{page|request|session|application}"] /> 表8.13 属性名 是否支持EL 属性类型 属 性 描 述 basename true String 指定创建ResourceBundle实例对象的基名 var false String 指定将创建出的ResourceBundle实例对象保存到Web域中的属性名称 scope false String 指定将创建出的ResourceBundle实例对象保存在哪个Web作用域中 (1)如果basename属性的值为null或空字符串,或找不到basename属性指定的资源, (2)如果指定了var属性, (3)如果没有指定var属性, 在前面的例程8-28中已经使用了 [prefix="prefix"]> body content 表8.14 属性名 是否支持EL 属性类型 属 性 描 述 basename true String 指定创建ResourceBundle实例对象的基名 prefix true String 指定追加到嵌套在 如果设置了 例程8-29是一个使用 例程8-29 fmt_bundle.jsp 例程8-29的运行结果如图8.24所示。 语法1,没有标签体的情况: [bundle="resourceBundle"] [var="varName"] [scope="{page|request|session|application}"] /> 语法2,在标签体中指定格式化文本串中的占位符参数的情况: [bundle="resourceBundle"] [var="varName"] [scope="{page|request|session|application}"]> 语法3,在标签体中指定消息关键字和可选择的占位符参数: [var="varName"] [scope="{page|request|session|application}"]> key optional 表8.15 属性名 是否支持EL 属性类型 属 性 描 述 key true String 指定要输出的信息的关键字 bundle true LocalizationContext 指定ResourceBundle对象在Web域中的属性名称 var false String 用于指定将格式化结果保存到某个Web域中的某个属性的名称 scope false String 指定将格式化结果保存到哪个Web域中 例程8-30是一个使用 例程8-30 fmt_message.jsp 例程8-30的运行结果如图8.25 所示。 语法1,用value属性指定参数值: 语法2,在标签体中指定参数的值的情况: body content 说明: 其中的value属性用于指定请求消息的字符集编码,其类型为String,支持动态属性值。 关于 (1)为了能够正确解码请求参数值中的非ISO-8859-1编码的字符,应该调用这个标签来设置请求消息的字符集编码,并且必须在获取任何请求参数(包括使用EL表达式获取参数)之前进行调用。 (2)因为很多浏览器没有完全遵守HTTP规范,在请求消息中没有包含Content-Type请求头,所以使用这个标签来设置请求消息的字符集编码是很有必要的。 (3) 例程8-31是一个使用 例程8-31 fmt_requestEncoding.jsp 例程8-31的运行结果如图8.26所示,在文本框中输入“英语”,单击其中的OK按钮后的运行结果如图8.27所示。 body content 其中的value属性支持动态属性值,它的值可以是一个命名时区的字符串,也可以是java.util.TimeZone类的一个实例对象。如果value属性的值为null或空字符串,标签体中的内容就使用GMT的0基准时区。如果value属性的值是表示时区名称的字符串,这个字符串通过java.util.TimeZone.getTimeZone()静态方法被解析为java.util.TimeZone类的实例对象。 例程8-32是一个使用 例程8-32 fmt_timeZone.jsp 例程8-32的运行结果如图8.28 所示。 [var="varName"] [scope="{page|request|session|application}"] /> 表8.16 属性名 是否支持EL 属性类型 属 性 描 述 value true String或java.util.TimeZone 指定表示时区的ID字符串或TimeZone对象 var false String 指定将创建出的TimeZone实例对象保存到Web域中的属性名称 scope false String 指定将创建出的TimeZone实例对象保存到哪个Web域中 其中,value属性的设置值的细节与 例程8-33是一个使用 例程8-33 fmt_setTimeZone.jsp 例程8-33的运行结果如图8.29所示。 [type="{time|date|both}"] [dateStyle="{default|short|medium|long|full}"] [timeStyle="{default|short|medium|long|full}"] [pattern="customPattern"] [timeZone="timeZone"] [var="varName"] [scope="{page|request|session|application}"] /> 表8.17 属性名 是否支持EL 属性类型 属 性 描 述 value true java.util.Date 指定要格式化的日期或时间 type true String 指定是格式化输出日期部分,还是格式化输出时间部分,还是两者都输出 dateStyle true String 指定日期部分的输出格式,其可用的设置值可以参照java.text.DateFormat类的讲解。该属性仅在type属性取值为date或both时才有效 timeStyle true String 指定时间部分的输出格式,其可用的设置值请参照java.text.DateFormat类的讲解。该属性仅在type属性取值为time或both时才有效 pattern true String 指定一个自定义的日期和时间输出格式 timeZone true String或java.util.timeZone 指定当前采用的时区 var false String 用于指定将格式化结果保存到某个Web域中的某个属性的名称 scope false String 指定将格式化结果保存到哪个Web域中 例程8-34是一个使用 例程8-34 fmt_formatDate.jsp 例程8-34的运行结果如图8.30 所示。 语法1,没有标签体的情况: [type="time|date|both"] [dateStyle="default|short|medium|long|full"] [timeStyle="default|short|medium|long|full"] [pattern="customPattern"] [timeZone="timeZone"] [parseLocale="parseLocale"] [var="varName"] [scope="{page|request|session|application}"] /> 语法2, 有标签体的情况,在标签体中指定要被解析的日期和/或时间值: [dateStyle="default|short|medium|long|full"] [timeStyle="default|short|medium|long|full"] [pattern="customPattern"] [timeZone="timeZone"] [parseLocale="parseLocale"] [var="varName"] [scope="{page|request|session|application}"]> date value to be parsed 表8.18 l value属性的值必须是合法的日期/时间字符串,否则 在前面的例程8-34中已经使用了 语法1,没有标签体的情况: [type="{number|currency|percent}"] [pattern="customPattern"] [currencyCode="currencyCode"] [currencySymbol="currencySymbol"] [groupingUsed="{true|false}"] [maxIntegerDigits="maxIntegerDigits"] [minIntegerDigits="minIntegerDigits"] [maxFractionDigits="maxFractionDigits"] [minFractionDigits="minFractionDigits"] [var="varName"] [scope="{page|request|session|application}"] /> 语法2,有标签体的情况,在标签体中指定要被格式化的数值: [pattern="customPattern"] [currencyCode="currencyCode"] [currencySymbol="currencySymbol"] [groupingUsed="{true|false}"] [maxIntegerDigits="maxIntegerDigits"] [minIntegerDigits="minIntegerDigits"] [maxFractionDigits="maxFractionDigits"] [minFractionDigits="minFractionDigits"] [var="varName"] [scope="{page|request|session|application}"]> 要被格式化的数值 如果指定scope属性,就必须指定var属性。 表8.19 例程8-35是一个使用 例程8-35 fmt_formatNumber.jsp 例程8-35的运行结果如图8.31 所示。 语法1,没有标签体的情况: [type="{number|currency|percent}"] [pattern="customPattern"] [parseLocale="parseLocale"] [integerOnly="{true|false}"] [var="varName"] [scope="{page|request|session|application}"] /> 语法2,有标签体的情况,在标签体中指定要被解析的数值: [pattern="customPattern"] [parseLocale="parseLocale"] [integerOnly="{true|false}"] [var="varName"] [scope="{page|request|session|application}"]> numeric value to be parsed 表8.20 例程8-36是一个使用 例程8-36 fmt_parseNumber.jsp 例程8-36的运行结果如图8.32 所示。 注意:在分层设计的软件架构中,JSP页面通常仅用于实现系统的表示层,数据解析的任务不适合在表示层进行处理,而最好是业务逻辑层进行处理,所以,为了增强软件的易维护性和可扩展性,读者应尽量避免在JSP 页面中使用 为了简化在JSP页面操作字符串,JSTL中提供了一套EL自定义函数,这些函数包含了JSP页面制作者经常要用到的字符串操作。例如,fn:toLowerCase将字符串中的字符变为小写,fn:indexOf返回一个指定字符串在另一个字符串中第一次出现的索引位置。 JSTL中提供的EL自定义函数必须在EL表达式中使用,例如,${fn:toUpperCase("www.it315.org")}。 fn:toLowerCase函数将一个字符串中包含的所有字符转换为小写形式,并返回转换后的字符串,它接收一个字符串类型的参数。 fn:toLowerCase函数的应用举例: l fn:toLowerCase("Www.IT315.org") 的返回值为字符串“www.it315.org” l fn:toLowerCase("")的返回值为空字符串 fn:toUpperCase函数将一个字符串中包含的所有字符转换为大写形式,并返回转换后的字符串,它接收一个字符串类型的参数。 fn:toUpperCase函数的应用举例: l fn:toUpperCase("Www.IT315.org") 的返回值为字符串“WWW.IT315.ORG” l fn:toUpperCase("")的返回值为空字符串 fn:trim函数删除一个字符串的首尾的空格,并返回删除空格后的结果字符串,它接收一个字符串类型的参数。需要注意的是,fn:trim函数不能删除字符串中间位置的空格。 例如,fn:trim(" www.it 315.org ") 的返回值为字符串“www.it 315.org”。 fn:escapeXml函数将字符串中的需要进行转义的HTML特殊字符按表8.2进行HTML编码转换,并返回转换后的字符串,这样就可以在浏览器中显示出HTML特殊字符。fn:escapeXml函数接收一个字符串类型的参数。 例如,运行包含如下代码的JSP文件后浏览器将跳转到“http://www.it315.org”页面,这是因为这些代码被浏览器作为HTML标签解释执行: 如果用fn:escapeXml函数处理上面的代码,运行JSP文件后就在浏览器窗口输出这行代码,例程8-54是使用fn:escapeXml函数处理上面的代码的例子程序。 例程8-54 fn_escapeXml.jsp fn_escapeXml.jsp页面的运行结果如图8.46所示。 fn:length函数用于返回一个集合或数组对象中包含的元素的个数,或返回一个字符串中包含的字符的个数,返回值为int类型。fn:length函数接收一个参数,这个参数可以是 如果fn:length函数的参数为null或者是元素个数为0的集合或数组对象,则函数返回0;如果参数是空字符串,则函数返回0。例程8-55是使用fn:length函数的实例程序。 例程8-55 fn_length.jsp fn_length.jsp页面的运行结果如图8.47所示。 fn:split函数以指定字符串作为分隔符,将一个字符串分割成字符串数组并返回这个字符串数组。fn:split函数接收两个字符串类型的参数,第一个参数表示要分割的字符串,第二个参数表示作为分隔符的字符串。 l fn:split函数实现java.util.StringTokenizer类分割字符串的操作,得到的字符串数组中的各字符串元素不包含分隔符本身,例如,fn:split("www.it315.org", ".")[1]的返回值为字符串“it315”。 fn:join函数以一个字符串作为分隔符,将一个字符串数组中的所有元素合并为一个字符串并返回合并后的结果字符串。fn:join函数接收两个参数,第一个参数是要操作的字符串数组,第二个参数是作为分隔符的字符串。 如果fn:join函数的第二个参数是空字符串,则fn:join函数的返回值是不使用任何分隔符将字符串数组中的各元素连接起来的结果字符串。 fn:join函数的应用举例: fn:indexOf函数返回指定字符串在一个字符串中第一次出现的索引值,返回值为int类型。fn:indexOf函数接收两个字符串类型的参数,如果第一个参数字符串中包含第二个参数字符串,那么,不管第二个参数字符串在第一个参数字符串中出现几次,fn:indexOf函数总是返回第一次出现的索引值;如果第一个参数中不包含第二个参数,则fn:indexOf函数返回-1。如果第二个参数为空字符串,则fn:indexOf函数总是返回0。 fn:indexOf函数的应用举例: fn:contains函数检测一个字符串中是否包含指定的字符串,返回值为布尔类型。fn:contains函数在比较两个字符串是否相等时是大小写敏感的。 fn:contains函数接收两个字符串类型的参数,如果第一个参数字符串中包含第二个参数字符串,则fn:contains函数返回true,否则返回false。如果第二个参数的值为空字符串,则fn:contains函数总是返回true。实际上,fn:contains(string, substring)等价于fn:indexOf(string, substring) != -1。 fn:contains函数的应用举例: fn:containsIgnoreCase函数检测一个字符串中是否包含指定的字符串,在比较两个字符串是否相等时大小写不敏感。fn:containsIgnoreCase函数的参数和返回值的类型都与fn:contains函数相同,只是前者在比较两个字符串是否相等时不考虑字符的大小写,而后者考虑字符的大小写,fn:containsIgnoreCase(string, substring)等价于fn:contains(fn:toUpperCase(string),fn:toUpperCase(substring))。 fn:containsIgnoreCase函数的应用举例: fn:startsWith函数用于检测一个字符串是否是以指定字符串开始的,返回值为布尔类型。 fn:startsWith函数接收两个字符串类型的参数,如果第一个参数字符串以第二个参数字符串开始,则函数返回true,否则函数返回false。如果第二个参数为空字符串,则fn:startsWith函数总是返回true。实际上,fn:startsWith(string, prefix)等价于表达式fn:indexOf(string, prefix) == 0。 fn:startsWith函数的应用举例: fn:endsWith函数用于检测一个字符串是否是以指定字符串结束的,返回值为布尔类型。 fn:endsWith函数接收两个字符串类型的参数,如果第一个参数字符串以第二个参数字符串结束,则函数返回true,否则函数返回false。如果第二个参数为空字符串,则fn:endsWith函数总是返回true。 fn:endsWith函数的应用举例: fn:replace函数将一个字符串中包含的指定子字符串替换为其它的指定字符串,并返回替换后的结果字符串。fn:replace方法接收三个字符串类型的参数,第一个参数表示要操作的源字符串,第二个参数表示源字符串中要被替换的子字符串,第三个参数表示要被替换成的字符串。 fn:replace函数按照下面的说明执行字符串的替换操作: (1)源字符串中所有的第二个参数指定的子字符串都被替换成第三个参数指定的字符串,如果替换后的结果字符串中包含第二个参数指定的子字符串,则不再进行替换。例如, (2)如果第一个参数是空字符串,则fn:replace函数返回空字符串,例如,fn:replace("", " ", ".")的返回值为空字符串。 (3)如果第二个参数是空字符串,则fn:replace函数不执行任何替换操作,返回第一个参数指定的字符串。例如,fn:replace("www.it315.org", "", " ") 的返回值为字符串“www.it315.org”。 (4)如果第三个参数是空字符串,则fn:replace函数删除源字符串中包含的第二个参数指定的子字符串。例如,fn:replace("www.it315.org", ".", "")的返回值为字符串“wwwit315org”。 fn:substring函数用于截取一个字符串的子字符串并返回截取到的子字符串。fn:substring函数接收三个参数,第一个参数是用于指定要操作的源字符串,第二个参数是用于指定截取子字符串开始的索引值,第三个参数是用于指定截取子字符串结束的索引值,第二个参数和第三个参数都是int类型,其值都从0开始。 fn:substring函数按照下面的说明截取一个字符串的子字符串: fn:substring函数的应用举例(字符串“www.it315.org”中包含13个字符): fn:substringAfter函数用于截取并返回一个字符串中的指定子字符串第一次出现之后的子字符串。fn:substringAfter函数接收两个字符串类型的参数,第一个参数表示要操作的源字符串,第二个参数表示指定的子字符串,例如,fn:substringAfter("www.it315.org", ".")的返回值为字符串“it315.org”。 fn:substringAfter函数按照下面的说明截取一个字符串的子字符串: fn:substringBefore函数用于截取并返回一个字符串中的指定子字符串第一次出现之前的子字符串。fn:substringBefore函数接收两个字符串类型的参数,第一个参数表示要操作的源字符串,第二个参数表示指定的子字符串,例如,fn:substringBefore("www.it315.org", ".") 的返回值为字符串“www”。 fn:substringBefore函数按照下面的说明截取一个字符串的子字符串:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=gb2312" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=gb2312" %>
没有设置prefix属性的情况:
8.4.5
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=gb2312" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=gb2312" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=gb2312" %>
<%@ page import="java.util.TimeZone" %>
<%
TimeZone tz = TimeZone.getDefault();
pageContext.setAttribute("tz", tz);
%>
使用默认的时区,value为java.util.TimeZone的一个实例:
使用“America/Los_Angeles”时区:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=gb2312" %>
“GMT+1:00”时区的现在时间是:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=gb2312" %>
<%@ page import="java.util.TimeZone" %>
<%
session.setAttribute("tz", TimeZone.getTimeZone("GMT+10"));
%>
指定时区为“GMT+10”:
指定自定义的格式,月.日.年:
格式化用字符串表示的日期:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=gb2312" %>
<%@ page import="java.util.Currency,java.util.Locale"%>
将数值格式化为货币格式:
将数值格式化为百分数格式:
将数值格式化为自定义的格式:
<%
String code = Currency.getInstance(Locale.CHINA).getCurrencyCode();
String symbol = Currency.getInstance(Locale.US).getSymbol();
session.setAttribute("code", code);
session.setAttribute("symbol", symbol);
%>
同时指定currencyCode和currencySymbol属性,前者优先于后者:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=gb2312" %>
本地环境为“en_US”解析字符串“$123,456,789.00”:
解析整个数值字符串“123,456,789%”:
只解析“123,456,789%”的整数部分:
同时设置type和pattern属性,后者优先于前者:
JSTL函数
fn:toLowerCase函数
fn:toUpperCase函数
fn:trim函数
fn:escapeXml函数
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
${fn:escapeXml('')}
fn:length函数
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ page contentType="text/html;charset=gb2312" %>
<%@ page import="java.util.*, org.it315.UserBean" %>
<%
Collection users = new ArrayList();
for(int i=0; i<5; i++)
{
UserBean user = new UserBean();
user.setUserName("user" + i);
user.setPassword("guest" + i);
users.add(user);
}
session.setAttribute("users", users);
%>
There are ${fn:length(users)} users online.
字符串“www.it315.org”中包含 ${fn:length("www.it315.org")} 个字符
fn:split函数
fn:join函数
fn:indexOf函数
fn:contains函数
fn:containsIgnoreCase函数
fn:startsWith函数
fn:endsWith函数
fn:replace函数
fn:substring函数
fn:substringAfter函数
fn:substringBefore函数