target
了解國(guó)際化的意義
了解Java程序?qū)崿F(xiàn)國(guó)際化的過(guò)程
了解如何使用SpringMVC實(shí)現(xiàn)國(guó)際化
1. 概述
國(guó)際化是商業(yè)軟件系統(tǒng)的一個(gè)基本要求,因?yàn)楫?dāng)今的軟件系統(tǒng)需要面對(duì)全球的瀏覽者积瞒。國(guó)際化的目的就是根據(jù)用戶的語(yǔ)言環(huán)境的不同向用戶輸出與之相應(yīng)的頁(yè)面杠输,以示友好化借。
程序國(guó)際化已成為 Web 應(yīng)用的基本要求丧叽。隨著網(wǎng)絡(luò)的發(fā)展干跛,大部分 Web 站點(diǎn)面對(duì)的已經(jīng)不再是本地或者本國(guó)的瀏覽者稳析,而是來(lái)自全世界各國(guó)狡忙、各地區(qū)的瀏覽者梳虽,因此國(guó)際化成為了 Web 應(yīng)用不可或缺的一部分。
Java國(guó)際化的思想是將程序中的信息放在資源文件中灾茁,程序根據(jù)支持的國(guó)家及語(yǔ)言環(huán)境讀取相應(yīng)的資源文件窜觉。資源文件是 key-value 對(duì),每個(gè)資源文件中的 key 是不變的北专,但 value 隨不同國(guó)家/語(yǔ)言變化禀挫。
Java 程序的國(guó)際化主要通過(guò)兩個(gè)類來(lái)完成。
1.1 java.util.Locale
用于提供本地信息拓颓,通常稱它為語(yǔ)言環(huán)境语婴。不同的語(yǔ)言、不同的國(guó)家和地區(qū)采用不同的 Locale 對(duì)象來(lái)表示。
1.2 java.util.ResourceBundle
該類稱為資源包砰左,包含了特定于語(yǔ)言環(huán)境的資源對(duì)象匿醒。當(dāng)程序需要一個(gè)特定于語(yǔ)言環(huán)境的資源時(shí)(例如字符串資源),程序可以從適合當(dāng)前用戶語(yǔ)言環(huán)境的資源包中加載它缠导。采用這種方式可以編寫?yīng)毩⒂谟脩粽Z(yǔ)言環(huán)境的程序代碼廉羔,而與特定語(yǔ)言環(huán)境相關(guān)的信息則通過(guò)資源包來(lái)提供。
為了實(shí)現(xiàn) Java 程序的國(guó)際化酬核,必須事先提供程序所需要的資源文件蜜另。資源文件的內(nèi)容由很多 key-value 對(duì)組成适室,其中 key 是程序使用的部分嫡意,而 value 是程序界面的顯示。
資源文件的命名可以有如下 3 種形式:
- baseName.properties
- baseName_language.properties
- baseName_language_country.properties
baseName 是資源文件的基本名稱捣辆,由用戶自由定義蔬螟,但是 language 和 country 必須為 Java 所支持的語(yǔ)言和國(guó)家/地區(qū)代碼。例如:
- 中國(guó)大陸:baseName_zh_CN.properties
- 美國(guó):baseName_en_US.properties
Java 中的資源文件只支持 ISO-8859-1 編碼格式字符汽畴,直接編寫中文會(huì)出現(xiàn)亂碼旧巾。用戶可以使用 Java 命令 native2ascii.exe
解決資源文件的中文亂碼問(wèn)題,使用 Eclipse 編寫資源屬性文件忍些,在保存資源文件時(shí) Eclipse 自動(dòng)執(zhí)行 native2ascii.exe
命令鲁猩,因此在 Eclipse 中資源文件不會(huì)出現(xiàn)中文亂碼問(wèn)題。
2. Java 支持的語(yǔ)言和國(guó)家
java.util.Locale類的常用構(gòu)造方法如下:
public Locale(String language)罢坝;
public Locale(String language, String country)
其中廓握,language 表示語(yǔ)言,它的取值是由小寫的兩個(gè)字母組成的語(yǔ)言代碼嘁酿。country 表示國(guó)家或地區(qū)隙券,它的取值是由大寫的兩個(gè)字母組成的國(guó)家或地區(qū)代碼。
實(shí)際上闹司,Java 并不能支持所有國(guó)家和語(yǔ)言娱仔,如果需要獲取 Java 所支持的語(yǔ)言和國(guó)家,開(kāi)發(fā)者可以通過(guò)調(diào)用 Locale 類的 getAvailableLocales 方法獲取游桩,該方法返回一個(gè) Locale 數(shù)組牲迫,該數(shù)組中包含了 Java 所支持的語(yǔ)言和國(guó)家。
下面的 Java 程序簡(jiǎn)單示范了如何獲取 Java 所支持的國(guó)家和語(yǔ)言:
public class Test {
public static void main(String[] args) {
// 返回Java所支持的語(yǔ)言和國(guó)家的數(shù)組
Locale locales[] = Locale.getAvailableLocales();
// 遍歷數(shù)組元素借卧,依次獲取所支持的國(guó)家和語(yǔ)言
for (int i = 0; i < locales.length; i++) {
// 打印出所支持的國(guó)家和語(yǔ)言
System.out.println(locales[i].getDisplayCountry() + "="
+ locales[i].getCountry() + ""
+ locales[i].getDisplayLanguage() + "="
+ locales[i].getLanguage());
}
}
}
3. Java 程序的國(guó)際化
假設(shè)有如下簡(jiǎn)單 Java 程序:
public class TestI18N {
public static void main(String[] args) {
System.out.println("我要向把不同國(guó)家的人民問(wèn)好:您好盹憎!");
}
}
為了讓該程序支持國(guó)際化,需要將“我要向不同國(guó)家的人民問(wèn)好:您好谓娃!”對(duì)應(yīng)不同語(yǔ)言環(huán)境的字符串脚乡,定義在不同的資源文件中。
在 Web 應(yīng)用的 src 目錄下新建文件 messageResource_zh_CN.properties
和 messageResource_ en_US.properties
。然后給資源文件 messageResource_zh_CN.properties 添加“hello=我要向不同國(guó)家的人民問(wèn)好:您好奶稠!”內(nèi)容俯艰,保存后可看到如圖所示的效果。
圖中顯示的內(nèi)容看似是很多亂碼锌订,實(shí)際上是 Unicode 編碼文件內(nèi)容竹握。至此,資源文件 messageResource_zh_CN.properties 創(chuàng)建完成辆飘。
最后給資源文件 messageResource_en_US.properties 添加“hello=I want to say hello to all world啦辐!”內(nèi)容。
現(xiàn)在將 TestI18N.java 程序修改成如下形式:
public class TestI18N {
public static void main(String[] args) {
// 取得系統(tǒng)默認(rèn)的國(guó)家語(yǔ)言環(huán)境
Locale lc = Locale.getDefault();
// 根據(jù)國(guó)家語(yǔ)言環(huán)境加載資源文件
ResourceBundle rb = ResourceBundle.getBundle("messageResource", lc);
// 打印出從資源文件中取得的信息
System.out.println(rb.getString("hello"));
}
}
上面程序中的打印語(yǔ)句打印的內(nèi)容是從資源文件中讀取的信息蜈项。如果在中文環(huán)境下運(yùn)行程序芹关,將打印“我要向不同國(guó)家的人民問(wèn)好:您好!”紧卒。
如果在“控制面板”中將計(jì)算機(jī)的語(yǔ)言環(huán)境設(shè)置成美國(guó)侥衬,然后再次運(yùn)行該程序,將打印“I want to say hello to all world跑芳!”轴总。需要注意的是,如果程序找不到對(duì)應(yīng)國(guó)家/語(yǔ)言的資源文件博个,系統(tǒng)該怎么辦怀樟?
假設(shè)以簡(jiǎn)體中文環(huán)境為例,先搜索如下文件:messageResource_zh_CN.properties
如果沒(méi)有找到國(guó)家/語(yǔ)言都匹配的資源文件盆佣,再搜索語(yǔ)言匹配文件往堡,即搜索如下文件:messageResource_zh.properties
如果上面的文件還沒(méi)有搜索到,則搜索 baseName 匹配的文件罪塔,即搜索如下文件:messageResource.properties
如果上面 3 個(gè)文件都找不到投蝉,則系統(tǒng)將出現(xiàn)異常。
4. 帶占位符的國(guó)際化信息
在資源文件中消息文本可以帶有參數(shù)征堪,例如:
welcome={0}瘩缆,歡迎學(xué)習(xí) Spring MVC。
花括號(hào)中的數(shù)字是一個(gè)占位符佃蚜,可以被動(dòng)態(tài)的數(shù)據(jù)替換庸娱。在消息文本中占位符可以使用 0~9 的數(shù)字,也就是說(shuō)消息文本的參數(shù)最多可以有 10 個(gè)谐算。例如:
welcome={0}熟尉,歡迎學(xué)習(xí) Spring MVC,今天是星期{1}洲脂。
如果要替換消息文本中的占位符斤儿,可以使用 java.text.MessageFormat 類剧包,該類提供了一個(gè)靜態(tài)方法 format,用來(lái)格式化帶參數(shù)的文本往果。format 方法的定義如下:
public static String format(String pattern,Object ...arguments)
其中疆液,pattern 字符串就是一個(gè)帶占位符的字符串,消息文本中的數(shù)字占位符將按照方法參數(shù)的順序(從第二個(gè)參數(shù)開(kāi)始)被替換陕贮。
替換占位符的示例代碼如下:
public class TestFormat {
public static void main(String[] args) {
// 取得系統(tǒng)默認(rèn)的國(guó)家語(yǔ)言環(huán)境
Locale lc = Locale.getDefault();
// 根據(jù)國(guó)家語(yǔ)言環(huán)境加載資源文件
ResourceBundle rb = ResourceBundle.getBundle("messageResource", lc);
// 從資源文件中取得的信息
String msg = rb.getString("welcome");
// 替換消息文本中的占位符堕油,消息文本中的數(shù)字占位符將按照參數(shù)的順序
// (從第二個(gè)參數(shù)開(kāi)始)被替換,即“我”替換{0}肮之、“5”替換{1}
String msgFor = MessageFormat.format(msg, "我", "5");
System.out.println(msgFor);
}
}
Spring MVC 的國(guó)際化是建立在 Java 國(guó)際化的基礎(chǔ)之上的掉缺,Spring MVC 框架的底層國(guó)際化與 Java 國(guó)際化是一致的,作為一個(gè)良好的 MVC 框架戈擒,Spring MVC 將 Java 國(guó)際化的功能進(jìn)行了封裝和簡(jiǎn)化眶明,開(kāi)發(fā)者使用起來(lái)會(huì)更加簡(jiǎn)單、快捷峦甩。
國(guó)際化和本地化應(yīng)用程序時(shí)需要具備以下兩個(gè)條件:
- 將文本信息放到資源屬性文件中赘来。
- 選擇和讀取正確位置的資源屬性文件现喳。
下面講解第二個(gè)條件的實(shí)現(xiàn)凯傲。
5. Spring MVC加載資源屬性文件
在 Spring MVC 中不能直接使用 ResourceBundle 加載資源屬性文件,而是利用 bean(messageSource)告知 Spring MVC 框架要將資源屬性文件放到哪里嗦篱。示例代碼如下:
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleM essageSource">
<property name="basenames">
<list>
<value>/WEB-INF/resource/messages</value>
<value>/WEB-INF/resource/labels</value>
</list>
</property>
</bean>
6. 語(yǔ)言區(qū)域的選擇
在 Spring MVC 中可以使用語(yǔ)言區(qū)域解析器 bean 選擇語(yǔ)言區(qū)域冰单,該 bean 有 3 個(gè)常見(jiàn)實(shí)現(xiàn),即 AcceptHeaderLocaleResolver灸促、SessionLocaleResolver 和 CookieLocaleResolver诫欠。
6.1 AcceptHeaderLocaleResolver
根據(jù)瀏覽器 Http Header 中的 accept-language 域設(shè)定(accept-language 域中一般包含了當(dāng)前操作系統(tǒng)的語(yǔ)言設(shè)定,可通過(guò) HttpServletRequest.getLocale 方法獲得此域的內(nèi)容)浴栽。
改變 Locale 是不支持的荒叼,即不能調(diào)用 LocaleResolver 接口的 setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale)方法設(shè)置 Locale。
6.2 SessionLocaleResolver
根據(jù)用戶本次會(huì)話過(guò)程中的語(yǔ)言設(shè)定決定語(yǔ)言區(qū)域(例如用戶進(jìn)入首頁(yè)時(shí)選擇語(yǔ)言種類典鸡,則此次會(huì)話周期內(nèi)統(tǒng)一使用該語(yǔ)言設(shè)定)被廓。
6.3 CookieLocaleResolver
根據(jù) Cookie 判定用戶的語(yǔ)言設(shè)定(Cookie 中保存著用戶前一次的語(yǔ)言設(shè)定參數(shù))。
由上述分析可知萝玷,SessionLocaleResolver 實(shí)現(xiàn)比較方便用戶選擇喜歡的語(yǔ)言種類嫁乘,教程中使用該方法進(jìn)行國(guó)際化實(shí)現(xiàn)。
下面是使用 SessionLocaleResolver 實(shí)現(xiàn)的 bean 定義:
<bean id="localeResolver" class="org.springframework.web.servlet.il8n.SessionLocaleResolver">
<property name="defaultLocale" value="zh_CN"></property>
</bean>
如果采用基于 SessionLocaleResolver 和 CookieLocaleResolver 的國(guó)際化實(shí)現(xiàn)球碉,必須配置 LocaleChangeInterceptor 攔截器蜓斧,示例代碼如下:
<mvc:interceptors>
<bean class="org.springframework.web.servlet.il8n.LocaleChangeInterceptor"/>
</mvc:interceptors>
7. 使用 message 標(biāo)簽顯示國(guó)際化信息
在 Spring MVC 框架中可以使用 Spring 的 message 標(biāo)簽在 JSP 頁(yè)面中顯示國(guó)際化消息。在使用 message 標(biāo)簽時(shí)需要在 JSP 頁(yè)面的最前面使用 taglib 指令聲明 spring 標(biāo)簽睁冬,代碼如下:
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
message 標(biāo)簽有以下常用屬性挎春。
- code:獲得國(guó)際化消息的 key。
- arguments:代表該標(biāo)簽的參數(shù)。如果替換消息中的占位符直奋,示例代碼為
<spring:message code="third" arguments="888狼荞,999" />
,third 對(duì)應(yīng)的消息有兩個(gè)占位符 {0} 和 {1}帮碰。 - argumentSeparator:用來(lái)分隔該標(biāo)簽參數(shù)的字符相味,默認(rèn)為逗號(hào)。
- text:code 屬性不存在殉挽,或指定的 key 無(wú)法獲取消息時(shí)所顯示的默認(rèn)文本信息丰涉。
8. 實(shí)例
Spring MVC也可以允許用戶自行選擇程序語(yǔ)言。通過(guò) Web 應(yīng)用 SpringMVC-13 演示用戶自定義切換語(yǔ)言斯碌,在該應(yīng)用中使用 SessionLocaleResolver 實(shí)現(xiàn)國(guó)際化一死,具體步驟如下:
8.1 創(chuàng)建應(yīng)用
創(chuàng)建應(yīng)用 SpringMVC-13,并導(dǎo)入 SpringMVC 相關(guān)的 JAR 包傻唾。
8.2 創(chuàng)建國(guó)際化資源文件
在 WEB-INF/resource 目錄下創(chuàng)建中英文資源文件 messages_en_US.properties 和 messages_zh_CN.properties投慈。
messages_en_US.properties 的內(nèi)容如下:
first=first
second=second
third={0} third{1}
language.en=English
language.cn=Chinese
messages_zh_CN.properties 的內(nèi)容如下:
first=\u7B2C\u4E00\u9875
second=\u7B2C\u4E8C\u9875
third={0} \u7B2C\u4E09\u9875 {1}
language.cn=\u4E2D\u6587
language.en=\u82F1\u6587
8.3 創(chuàng)建視圖 JSP 文件
在 WEB-INF/views 目錄下創(chuàng)建 3 個(gè) JSP 文件,即 first.jsp冠骄、second.jsp 和 third.jsp伪煤。
first.jsp 的代碼如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="${pageContext.request.contextPath }/my/first?locale=zh_ CN">
<spring:message code="language.cn" /> </a>
<a href="${pageContext.request.contextPath }/my/first?locale=en_US">
<spring:message code="language.en" /> </a>
<br>
<spring:message code="first" />
<br>
<a href="${pageContext.request.contextPath }/my/second">
<spring:message code="second" />
</a>
</body>
</html>
second.jsp 的代碼如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<spring:message code="second"/><br><br>
<a href="${pageContext.request.contextPath }/my/third">
<spring:message code="third" arguments="888,999"/>
</a>
</body>
</html>
third.jsp 的代碼如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="${pageContext.request.contextPath }/my/first">
<spring:message code="first" />
</a>
<br>
<spring:message code="third" arguments="888,999" />
</body>
</html>
8.4 創(chuàng)建控制器類
MyController 的代碼如下:
@Controller
@RequestMapping("/my")
public class MyController {
@RequestMapping("/first")
public String first() {
return "first";
}
@RequestMapping("/second")
public String second() {
return "second";
}
@RequestMapping("/third")
public String third() {
return "third";
}
}
8.5 創(chuàng)建配置文件
在 src 目錄下創(chuàng)建配置文件 springmvc.xml, 在 WEB-INF 目錄下創(chuàng)建 web.xml凛辣。web.xml 的代碼與 Spring MVC 簡(jiǎn)單應(yīng)用的相同抱既,這里不再贅述。springmvc.xml 的代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 使用掃描機(jī)制掃描包 -->
<context:component-scan base-package="com.lee.controller" />
<!-- 配置視圖解析器 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 國(guó)際化操作攔截器扁誓,如果采用基于Session/Cookie則必須配置 -->
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
</mvc:interceptors>
<!-- 存儲(chǔ)區(qū)域設(shè)置信息 -->
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="zh_CN"></property>
</bean>
<!-- 加載國(guó)際化資源文件 -->
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!-- <property name="basename" value="classpath:messages" /> -->
<property name="basename" value="/WEB-INF/resource/messages" />
</bean>
</beans>
8.6 發(fā)布應(yīng)用并測(cè)試
首先將 springMVCDemo09 應(yīng)用發(fā)布到 Tomcat 服務(wù)器并啟動(dòng) Tomcat 服務(wù)器防泵,然后訪問(wèn):http://localhost:8080/springMVC-13/my/first