国际化(internationalization)常常缩写为i18n,这是因为这个单词是以i开头,n结尾,在它们之间有18个字母。国际化是开发支持多语言和数据格式的应用程序的技术,无需编写变成逻辑。
还有一个术语是本地化,这是将国际化应用程序改为支持特定语言区域(Locale)的技术。语言区域是指一个特定的地理,政治或者文化区域。要考虑到语言区域的一个操作,就成为区分语言区域的操作,例如:显示日期就是一个区分语言区域的操作,因为日期必须以用户所在国家或地区使用格式显示.2018年1月27日,在美国显示为01/27/2018,但是在澳大利亚则显示为27/01/2018.与国际化缩写为i18n一样,本地化缩写为i10n(Localization).
国际化应用程序的具体方法取决于有多少静态资源需要以不同需要显示出来。这里有两个方法:
- 如果大量数据是静态的,就要针对每一个语言区域单独创建一个资源版本。这种方式一般适用于带有大量静态HTML页面的Web应用程序。
- 如果需要国际化的静态资源数据量有限,就可以将文本元素,如元件标签和错误信息隔离为文本文件。每个文本文件中都保存着一个语言区域的所有文本元素译文。随后,应用程序会自动获取每一个元素。我们主要讨论的就是这种技术。
语言区域
Java.util.Locale类表示一个语言区域。一个Locale对象包括3个主要元件:language,country和variant。language无疑是最为重要的,但是语言本身并不足以区分一个语言区域。例如:讲英语的国际很多,如美国和英国。但是·,美国英语和英国英语还是存在不同的。因此,必须指定语言国家。
参数variant是一个特定于供应商或者特定浏览器的代号。例如,用WIN表示Windows,用MAC表示Macintosh。
构建Locale对象时,要使用Locale类的其中一个构造函数:
public Locale(java.lang.String language); public Locale(java.lang.String language,java.lang.String country); public Locale(java.lang.String language,java.lang.String country,java.lang.String variant)
语言代号(语言码)是一个有效的ISO语言码。部分ISO639语言码:
代码 | 语言 |
zh | 汉语 |
en | 英语 |
fr | 法语 |
de | 德语 |
es | 西班牙语 |
参数country是一个有效的ISO国家码,有两个字母组成,部分ISO3166国家码:
国家 | 代码 |
中国 | CN |
英国 | GB |
美国 | US |
法国 | FR |
德国 | DE |
要构造一个加拿大所应用的英语的Locale对象,可以像下面这样编写:
Locale locale=new Locale("en","CA");
此外,Locale类提供了static final 域,用来返回特定国家或语言区域,如CHINA,CHINESE,FRENCH,UK,US等,可以直接调用其static域来构建Locale对象。
Locale locale=Locale.CHINA;
此外,静态的getDefault方法会返回用户计算机的语言区域:
Locale locale=Locale.getDefault();
国际化SpringMVC应用程序
国际化和本地化应用程序有两个步骤:
- 将文本组件隔离为属性文件
- 选择和读取正确的属性文件
将文本组件隔离为属性文件
国际化的应用程序是将每一个语言区域的文本都单独保存在一个独立的塑像文件中。每个属性文件中都包含key/value对,并且每个key都唯一表示一个特定语言区域的对象,key始终是字符串,value则可以是字符串,也可以是其他任意类型的对象。
汉语版的属性文件:
i18n.username=\u8BF7\u8F93\u5165\u7528\u6237\u540D
i18n.password=\u8BF7\u8F93\u5165\u5BC6\u7801
英文版属性文件:
i18n.username=UserName
i18n.password=Password
如果使用Eclipse的properties编辑器编写中文字符·,需要使用Unicode编码,如果之间写中文可能会出现乱码问题,建议在图形界面编写中文,这样就不会乱码,它自动也会转码为Unicode
属性文件命名是由要求的:
basename_languageCode_contryCode
选择和读取正确的属性文件
java.util.ResourceBundle类,它能使你轻松地选择和读取于用户语言区域属性,以及查找值。ResourceBundle是一个抽象类,尽管如此,还是可以通过它的静态getBundle方法来获得一个ResourceBundle实例。它的重载签名如下:
public static ResourceBundle getBundle(java.lang.String baseName) public static ResourceBundle getBundle(java.lang.String baseName,Locale locale)
例如:
ResourceBundle rb=ResourceBundle.getBundle("i18n",Locale.CHINA);
如果没有找到合适的属性文件,ResourceBundle对象就会返回到默认的属性文件。默认属性文件的名称为基准名加一个扩展名properties。在这里默认文件就是i18n.properties。如果没有找到这个默认属性文件,将会抛出java.util.MissingResourceException.
随后,读取值,利用ResourceBundle类的getString方法传入一个key。
public String getString(String key)
如果没有找到指定key的入口,将会抛出java.util.MissingResourceException.
在SpringMVC中,不直接使用ResourceBundle,而是利用messageSource bean告诉SpringMVC要将属性文件保存在哪里。
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basenames"> <list> <value>classpath:i18n</value> </list> </property> <property name="defaultEncoding" value="UTF-8"/> <property name="cacheSeconds" value="120"/> </bean>
为了解析properties文件中的中文不乱码,还需要添加defaultEncoding属性。
这里我们使用的是ReloadResourceBundleMessageSource类作为实现。还记得我们在使用Validator验证器时,也是使用的这个类加载属性文件。另一个messageBane的实现是ResourceBundleMessageSource,它是不能重新加载的。这意味着,如果在任意属性文件中修改了某一个属性的key或者value,并且正在使用ResourceBundleMessageSource,那么要使修改生效,就必须先重启JVM。这两个实现之间的另一个区别是:使用ReloadableResourceBundleMessageSource时,是在应用程序下搜索这些属性文件。而使用RessourceBundleMessageSource时,属性文件则必须放在类路径下,即WEB-INF/class目录下。
如果只有一组属性文件,则可以使用basename属性代替basenames:
<property name="basename" value="classpth:i18n"></property>
告诉SpringMVC使用哪个语言区域
为用户选择语言区域时,最常用的方法或许是读取用户浏览器的accept-language标题值。accept-language标题提供了关于用户偏好哪种语言的信息。
当然选择语言区域的其他方式还包括读取session属性或者cookie。在SpringMVC中选择语言区域,可以使用语言区域解析器bean。它有几个实现,包括:
- AcceptHeaderLocaleResolver
- SessionLocaleResolver
- CookieLocaleResolver
所有这些实现都是org.springframework.web.ervlet.i18n 包的组成部分。AcceptHeaderLocaleResolver或许是其中最为容易使用的一个。如果选择使用这个语言区域解析器,SpringMVC将会读取浏览器的accept-language标题,来确定浏览器要接受哪个(些)语言区域。如果浏览器的某个语言区域与SpringMVC应用程序支持的某个语言区域匹配,就会使用这个语言区域。如果没有找到匹配的语言区域,则使用默认的语言区域。
下面是使用AcceptHeaderLocaleResovler的LocaleResovler bean定义:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean>
使用message标签
在SpringMVC中显示本地化消息的最容易方法是使用Spring的message标签。为了使用这个标签,要在使用该标签的所有JSP页面最前面声明如下taglib指令:
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
message标签部分属性:
属性 | 描述 |
code | 必选,获取消息的key |
text | 如果code属性不存在,或者指定的码无法获取消息,所显示的默认文本 |
argumentSeparator | 用来分隔该标签参数的字符 |
htmlEscape | 接受true或false,表示被渲染文本是否应该进行HTML转义 |
javaScriptEscape | 接受true或false,表示渲染文本是否进行JavaScript转义 |
code为必须属性,但是建议也写上text,因为找不到code的值,就会去找text的值,再找不到就会报异常(为了较少异常建议都加上该属性)
为了实现本地化,JSP页面中的每一段文本都要用message标签代替。
这些code值都是在i18n.properties(i18n_zh_CN.properties/i18n_en_US.properties)文件
例子:
我这里使用sessionLocaleResolver,实现在页面上点击语言选项就切换为对应的语言文本,而不是根据accept-language的值设置。
要使用SessionLocaleResovler还需要配置一个LocaleChangeInterceptor的拦截器:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean> <mvc:interceptors> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean> </mvc:interceptors>
文本配置文件也较为简单:
页面使用spring的message标签显示文本,而不是将文本写死,再来两个超链接用于切换中英文:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!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>测试国际化</title> </head> <body> <spring:message code="i18n.username" text="用户名"/><br> <spring:message code="i18n.password" text="密码"/><br> <a href=‘<c:url value="/i18n?locale=zh_CN"/>‘>中文</a> <a href=‘<c:url value="/i18n?locale=en_US"/>‘>美国英文</a> </body> </html>
注意:这两个超链接的url有些要求:我们需要加上参数locale=语言码_国家码,只有这样才能根据传入的locale值进行切换,如果没有找到对应的语言区域属性文件,就会找默认的属性文件还没有找到,就会报异常。