首页 > 代码库 > 深入分析JavaWeb Item22 -- 国际化(i18n)

深入分析JavaWeb Item22 -- 国际化(i18n)

一、国际化开发概述

  软件的国际化:软件开发时,要使它能同一时候应对世界不同地区和国家的訪问,并针对不同地区和国家的訪问。提供对应的、符合来訪者阅读习惯的页面或数据。
  
  国际化(internationalization)又称为 i18n(读法为i 18 n,据说是由于internationalization(国际化)这个单词从i到n之间有18个英文字母,i18n的名字由此而来)

二、合格的国际化软件

  软件实现国际化,需具备以下两个特征:
  
  1、对于程序中固定使用的文本元素。比如菜单条、导航条等中使用的文本元素、或错误提示信息,状态信息等,须要依据来訪者的地区和国家,选择不同语言的文本为之服务
  2、对于程序动态产生的数据,比如(日期,货币等)。软件应能依据当前所在的国家或地区的文化习惯进行显示。

三、固定文本元素的国际化

  对于软件中的菜单条、导航条、错误提示信息,状态信息等这些固定不变的文本信息,能够把它们写在一个properties文件里。并依据不同的国家编写不同的properties文件。这一组properties文件称之为一个资源包。
  
在JavaAPI中提供了一个ResourceBundle 类用于描写叙述一个资源包,而且 ResourceBundle类提供了对应的方法getBundle,这种方法能够依据来訪者的国家地区自己主动获取与之对应的资源文件予以显示。

3.1、创建资源包和资源文件

  一个资源包中的每一个资源文件都必须拥有共同的基名

除了基名,每一个资源文件的名称中还必须有标识其本地信息的附加部分。

比如:一个资源包的基名是“myproperties”,则与中文、英文环境相对应的资源文件名称则为: “myproperties_zh.properties” “myproperties_en.properties

技术分享
  
  每一个资源包都应有一个默认资源文件,这个文件不带有标识本地信息的附加部分。若ResourceBundle对象在资源包中找不到与用户匹配的资源文件,它将选择该资源包中与用户最相近的资源文件,假设再找不到,则使用默认资源文件。

比如:myproperties.properties

3.2、资源文件的书写格式

  资源文件的内容通常採用“关键字=值”的形式,软件依据关键字检索值显示在页面上。一个资源包中的全部资源文件的关键字必须相同,值则为对应国家的文字。


  
而且资源文件里採用的是properties格式文件。所以文件里的全部字符都必须是ASCII字码。属性(properties)文件是不能保存中文的,对于像中文这种非ACSII字符。须先进行编码。

比如:

  国际化的中文环境的properties文件

  技术分享

  国际化的英文环境的properties文件

  技术分享

  java提供了一个native2ascII工具用于将中文字符进行编码处理。native2ascII的使用方法例如以下所看到的:

  技术分享

3.3、编程实现固定文本的国际化

  在JavaAPI中提供了一个ResourceBundle 类用于描写叙述一个资源包,而且 ResourceBundle类提供了对应的方法getBundle,这种方法能够依据来訪者的国家地区自己主动获取与之对应的资源文件予以显示。

  ResourceBundle类提供了一个静态方法getBundle,该方法用于装载资源文件,并创建ResourceBundle实例:

 Locale currentLocale = Locale.getDefault();
 ResourceBundle myResources =ResourceBundle.getBundle(basename, currentLocale);

  basename为资源包基名(且必须为完整路径)。


  假设与该locale对象匹配的资源包子类找不到。普通情况下,则选用默认资源文件予以显示。
  载入资源文件后。 程序就能够调用ResourceBundle 实例对象的 getString 方法获取指定的资源信息名称所对应的值。

 String value =  http://www.mamicode.com/myResources.getString(“key");

范例:依据国家地区自己主动获取与之对应的资源文件

package me.gacl.i18n;

import java.util.Locale;
import java.util.ResourceBundle;
/**
* @ClassName: I18NTest
* @Description: 编程实现固定文本的国际化
* @author: 孤傲苍狼
* @date: 2014-8-29 下午9:34:05
*
*/ 
public class I18NTest {

    public static void main(String[] args) {
        //资源包基名(包名+myproperties)
        String basename = "me.gacl.i18n.resource.myproperties";
        //设置语言环境
        Locale cn = Locale.CHINA;//中文
        Locale us = Locale.US;//英文
        //依据基名和语言环境载入对应的语言资源文件
        ResourceBundle myResourcesCN = ResourceBundle.getBundle(basename,cn);//载入myproperties_zh.properties
        ResourceBundle myResourcesUS = ResourceBundle.getBundle(basename,us);//载入myproperties_en.properties

        //载入资源文件后, 程序就能够调用ResourceBundle实例对象的 getString方法获取指定的资源信息名称所对应的值。

//String value = http://www.mamicode.com/myResources.getString(“key"); String usernameCN = myResourcesCN.getString("username"); String passwordCN = myResourcesCN.getString("password"); String usernameUS = myResourcesUS.getString("username"); String passwordUS = myResourcesUS.getString("password"); System.out.println(usernameCN+"--"+passwordCN); System.out.println(usernameUS+"--"+passwordUS); } }

  执行结果:

  技术分享

3.4、在WEB应用中实现固定文本的国际化

例如以下所看到的:

<%@ page language="java"  import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
  <head>
    <title>国际化(i18n)測试</title>
  </head>
  <%
      //载入i18n资源文件,request.getLocale()获取訪问用户所在的国家地区
      ResourceBundle myResourcesBundle = ResourceBundle.getBundle("me.gacl.i18n.resource.myproperties",request.getLocale());
  %>
  <body>
        <form action="" method="post">
            <%=myResourcesBundle.getString("username")%><input type="text" name="username"/><br/>
            <%=myResourcesBundle.getString("password")%><input type="password" name="password"/><br/>
            <input type="submit" value="<%=myResourcesBundle.getString("submit")%>">
        </form>
  </body>
</html>

执行结果:

  浏览器语言是中文环境下的显示效果:

  技术分享

  浏览器语言是英文环境下的显示效果:

  技术分享

  相同一个页面,在不同语言环境的浏览器下显示出了不同的语言文字效果,这样就实现了固定文本的国际化。

  IE浏览器切换使用语言:工具→Internet选项

  技术分享

技术分享
 
技术分享 

四、动态数据的国际化

  数值。货币,时间,日期等数据由于可能在程序执行时动态产生,所以无法像文字一样简单地将它们从应用程序中分离出来。而是须要特殊处理。Java 中提供了解决这些问题的 API 类(位于 java.util 包和 java.text 包中)

4.1、Locale 类

  Locale 实例对象代表一个特定的地理,政治、文化区域。
  一个 Locale 对象本身不会验证它代表的语言和国家地区信息是否正确。仅仅是向本地敏感的类提供国家地区信息,与国际化相关的格式化和解析任务由本地敏感的类去完毕。(若JDK中的某个类在执行时须要依据 Locale 对象来调整其功能,这个类就称为本地敏感类)

4.2、DateFormat类(日期格式化)

  DateFormat 类能够将一个日期/时间对象格式化为表示某个国家地区的日期/时间字符串。
  DateFormat 类除了可按国家地区格式化输出日期外,它还定义了一些用于描写叙述日期/时间的显示模式的 int 型的常量,包括FULL, LONG, MEDIUM, DEFAULT, SHORT。实例化DateFormat对象时,能够使用这些常量,控制日期/时间的显示长度。

4.2.1、实例化DateFormat类

  实例化DateFormat类有九种方式,以下三种为带參形式,以下列出的三种方式也能够分别不带參,或仅仅带显示样式的參数。

  • getDateInstance(int style, Locale aLocale):以指定的日期显示模式和本地信息来获得DateFormat实例对象。该实例对象不处理时间值部分。
  • getTimeInstance(int style, Locale aLocale):以指定的时间显示模式和本地信息来获得DateFormat实例对象。该实例对象不处理日期值部分。
  • getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale):以单独指定的日期显示模式、时间显示模式和本地信息来获得DateFormat实例对象。

4.2.2、DateFormat 对象的方法    

  format:将date对象格式化为符合某个本地环境习惯的字符串。
  parse:将字符串解析为日期/时间对象
  注意:parse和format全然相反。一个是把date时间转化为对应地区和国家的显示样式,一个是把对应地区的时间日期转化成date对象。该方法在使用时,解析的时间或日期要符合指定的国家、地区格式,否则会抛异常。
  DateFormat 对象通常不是线程安全的,每一个线程都应该创建自己的 DateFormat 实例对象

4.2.3、DateFormat使用范例

package me.gacl.i18n;

import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.Locale;

/**
* @ClassName: DateFormatTest
* @Description: DateFormat类測试
* DateFormat类能够将一个日期/时间对象格式化为表示某个国家地区的日期/时间字符串
* @author: 孤傲苍狼
* @date: 2014-8-29 下午10:03:26
*
*/ 
public class DateFormatTest {

    public static void main(String[] args) throws ParseException {
        Date date = new Date(); // 当前这一刻的时间(日期、时间)

        // 输出日期部分
        DateFormat df = DateFormat.getDateInstance(DateFormat.FULL,Locale.GERMAN);
        String result = df.format(date);
        System.out.println(result);

        // 输出时间部分
        df = DateFormat.getTimeInstance(DateFormat.FULL, Locale.CHINA);
        result = df.format(date);
        System.out.println(result);

        // 输出日期和时间
        df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG,Locale.CHINA);
        result = df.format(date);
        System.out.println(result);

        // 把字符串反向解析成一个date对象
        String s = "10-9-26 下午02时49分53秒";
        df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG,Locale.CHINA);
        Date d = df.parse(s);
        System.out.println(d);
    }
}

4.3、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实例对象
    范例:
package me.gacl.i18n;

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

/**
* @ClassName: NumberFormatTest
* @Description: NumberFormat类測试
* @author: 孤傲苍狼
* @date: 2014-8-29 下午10:25:29
*
*/ 
public class NumberFormatTest {

    public static void main(String[] args) throws ParseException {
        int price = 89;

        NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.CHINA);
        String result = nf.format(price);
        System.out.println(result);

        String s = "¥89.00";
        nf = NumberFormat.getCurrencyInstance(Locale.CHINA);
        Number n = nf.parse(s);
        System.out.println(n.doubleValue() + 1);

        double num = 0.5;
        nf = NumberFormat.getPercentInstance();
        System.out.println(nf.format(num));
    }
}

  执行结果:

  技术分享

4.4、MessageFormat(文本格式化)

  假设一个字符串中包括了多个与国际化相关的数据,能够使用MessageFormat类对这些数据进行批量处理。


  比如:At 12:30 pm on jul 3,1998, a hurricance destroyed 99 houses and caused $1000000 of damage
  以上字符串中包括了时间、数字、货币等多个与国际化相关的数据,对于这种字符串,能够使用MessageFormat类对其国际化相关的数据进行批量处理。
  
  MessageFormat 类怎样进行批量处理呢?
    1.MessageFormat类同意开发者用占位符替换掉字符串中的敏感数据(即国际化相关的数据)。
    2.MessageFormat类在格式化输出包括占位符的文本时。messageFormat类能够接收一个參数数组,以替换文本中的每一个占位符。

4.4.1、模式字符串与占位符

模式字符串:

  At {0} on {1},a destroyed {2} houses and caused {3} of damage

  字符串中的{0}、{1}、{2}、{3}就是占位符

4.4.2、格式化模式字符串

  1、实例化MessageFormat对象。并装载对应的模式字符串。

  2、使用format(object obj[])格式化输出模式字符串,參数数组中指定占位符对应的替换对象。

范例:

package me.gacl.i18n;

import java.text.MessageFormat;
import java.util.Date;
import java.util.Locale;

/**
* @ClassName: MessageFormatTest
* @Description: MessageFormat类測试
* @author: 孤傲苍狼
* @date: 2014-8-29 下午10:29:19
*
*/ 
public class MessageFormatTest {

    public static void main(String[] args) {
        //模式字符串
        String pattern = "On {0}, a hurricance destroyed {1} houses and caused {2} of damage.";
        //实例化MessageFormat对象,并装载对应的模式字符串
        MessageFormat format = new MessageFormat(pattern, Locale.CHINA);
        Object arr[] = {new Date(), 99, 100000000};
        //格式化模式字符串,參数数组中指定占位符对应的替换对象
        String result = format.format(arr);
        System.out.println(result);
    }
}

执行结果:

  技术分享

4.4.3、占位符的三种书写方式

  {argumentIndex}: 0-9 之间的数字。表示要格式化对象数据在參数数组中的索引號
  {argumentIndex,formatType}: 參数的格式化类型
  {argumentIndex,formatType,FormatStyle}: 格式化的样式。它的值必须是与格式化类型相匹配的合法模式、或表示合法模式的字符串。

范例:

package me.gacl.i18n;

import java.text.MessageFormat;
import java.util.Date;
import java.util.Locale;

/**
* @ClassName: MessageFormatTest
* @Description: MessageFormat类測试
* @author: 孤傲苍狼
* @date: 2014-8-29 下午10:29:19
*
*/ 
public class MessageFormatTest {

    public static void main(String[] args) {
        //模式字符串
        String pattern = "At {0, time, short} on {0, date}, a destroyed {1} houses and caused {2, number, currency} of damage.";
        //实例化MessageFormat对象。并装载对应的模式字符串
        MessageFormat format = new MessageFormat(pattern, Locale.US);
        Object arr[] = {new Date(), 99, 100000000};
        //格式化模式字符串,參数数组中指定占位符对应的替换对象
        String result = format.format(arr);
        System.out.println(result);
    }
}

执行结果:

  技术分享

五、在WEB应用中使用国际化标签库实现固定文本的国际化

5.1、国际化标签

<fmt:setLocale> 设置一个全局的地区代码
<fmt:requestEncoding> 设置统一的请求编码
实例:

<%@ page language="java" contentType="text/html; charset=gb2312" import="java.util.*"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
    <title>Insert title here</title>
</head>
<body>
     <c:set var="todayValue" value="<%=new Date() %>"/>
     中文-大陆:<fmt:setLocale value="zh_CN"/> <fmt:formatDate value="${todayValue}"/><br>
    英文:<fmt:setLocale value="en_US"/> <fmt:formatDate value="${todayValue}"/>
</body>
</html>   

页面输出:

中文-大陆: 2015-12-16     英文: Dec 16, 2015 

5.2、信息显示标签

<fmt:bundle> 设置暂时要读取的资源文件
<fmt:message> 通过key取得value
<fmt:setBundle> 设置一个要读取的全局的资源文件

实例:

 <%@ page language="java" contentType="text/html; charset=gb2312" import="java.util.*"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>bundle test</title>
</head>
<body>
    <fmt:bundle basename="dbconn">
           数据库驱动程序名:<fmt:message key="driverName"/><br>
           连接字符串:<fmt:message key="connString"/><br>
           用户名:<fmt:message key="userName"/><br>
           密码:<fmt:message key="password" var="password"/> <c:out value="${password}"/><br>
           名字:<fmt:message key="name"/><br>
           动态提示信息:<fmt:message key="messageTemp"/><br>
    </fmt:bundle>
    <!-- 改动.properties文件里某个键的动态值 -->
    <c:set var="todayTemp" value="<%=new Date() %>"/>
    <fmt:setBundle basename="dbconn"/>
    动态提示信息:
          <fmt:message key="messageTemp">
                 <fmt:param>张三</fmt:param>
                 <fmt:param value="${todayTemp}"></fmt:param>
           </fmt:message>
</body>
</html>

其对应的读取文件为dbconn.properties(当然是放在web-inf/classes下了),内容为:

#SQL Server
driverName=com.microsoft.jdbc.sqlserver.SQLServerDriver
connString=jdbc:microsoft:sqlserver://localhost:1433;
DatabaseName=testDatabase
userName=sa password=123456 name=小平果
messageTemp=myname is {0},today is {1,date}

其页面输出为:

数据库驱动程序名:com.microsoft.jdbc.sqlserver.SQLServerDriver     
连接字符串:jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=testDatabase     
用户名:sa     密码: 123456     名字: Wallace     
动态提示信息:myname is {0},today is {1,date}     动态提示信息: myname is小平果,today is 2015-12-16

解释一下当中的几个标签:

<fmt:bundle>标签用于绑定数据源.properties文件。

<fmt:bundle basename="源文件名称,且不能带后缀哦,如上例就能够了" prefix=""> 语句,代码等 </fmt:bundle>

<fmt::message>标签用于从指定的资源文件里把指定的键值取出来;

<fmt:message key="" [var="varname"] [bundle=""] [scope="page|..."]/> 假设用到var的话就不会在页面直接输出,而须要用到<c:out>标签来进行页面的输出。如上例;

<fmt:message>标签能够配合<fmt:param>标签来进行设定<fmt:message>标签指向键的动态值,如上例;

<fmt:setBundle>标签用于设置默认的数据来源;

<fmt:setBundle>标签用来设置默认的数据来源; <fmt:setBundle basename="" [ var=""] [scope="" ] />

5.3、数字及日期格式化标签

<fmt:formatDate> 日期的格式化、把普通的日期显示格式化为标准的日期格式
<fmt:parseDate> 日期的反格式化、把已经格式化过后的标准日期格式转化为普通类型、比如字符串
<fmt:formatNumber> 数字的格式化、把普通的数字格式化为标准的数字格式
<fmt:parseNumber> 数字的反格式化、把已经格式化过后的数字反过来转换成普通的数字
<fmt:setTimeZone> 设置一个全局的时区
<fmt:timeZone> 设置一个暂时的时区

实例:

<%@ page contentType="text/html" pageEncoding="GBK"%>
<%@ page import="java.util.*"%>
<%@ taglib prefix="fmt" uri=“http://java.sun.com/jsp/jstl/fmt” %>
<html>
<head><title></title></head>
<body>
<%
 pageContext.setAttribute("dateref" , new Date()) ;
%>
 <fmt:formatDate value="${dateref}" type="both" dateStyle="default" timeStyle="default" var="date"/>
 <h3>default显示日期时间:${date}</h3>

 <fmt:formatDate value="${dateref}" type="both" pattern="yyyy年MM月dd日 HH时mm分ss秒SSS毫秒" var="date"/>
 <h3>自己定义格式显示日期时间:${date}</h3>
</body>
</html>

—type:指定要格式化的形式、比方说是仅仅格式化日期、或者仅仅格式化时间、或者是两者一起、默觉得date;一般为both
—datestyle:指定日期的显示样式,默觉得default
—timestyle:指定时间的显示样式,默觉得default
—pattern:在自己定义日期显示格式的时候、须要指定的格式、比如:yyyy年MM月dd日 HH时mm分ss秒SSS毫秒(要区分大写和小写、目的是为了不与同字母混淆)

<fmt:formatNumber value="351989.356789" maxIntegerDigits="7" maxFractionDigits="3" groupingUsed="true" var="num"/>
<h3>格式化数字:${num}</h3>
<fmt:formatNumber value="351989.356789" pattern="##.###E0" var="num"/>
<h3>科学计数法:${num}</h3>

—-maxIntegerDigits:能够显示的最大整数位
—-maxFractionDigits:能够显示的最大小数位
—-groupingUsed:是否在数字中加“,”

   <fmt:timeZone value="HST">
         <fmt:formatDate value="${dateref}" type="both" dateStyle="full" timeStyle="full" var="date"/>
   </fmt:timeZone>

最后再举个栗子:

<%@ page language="java"  import="java.util.*" pageEncoding="UTF-8"%>
<%--导入国际化标签库 --%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE HTML>
<html>
  <head>
    <title>国际化(i18n)測试</title>
  </head>
  <%--
      //载入i18n资源文件,request.getLocale()获取訪问用户所在的国家地区
      ResourceBundle myResourcesBundle = ResourceBundle.getBundle("me.gacl.i18n.resource.myproperties",request.getLocale());
  --%>
  <body>
        <%--
        <form action="" method="post">
            <%=myResourcesBundle.getString("username")%><input type="text" name="username"/><br/>
            <%=myResourcesBundle.getString("password")%><input type="password" name="password"/><br/>
            <input type="submit" value="<%=myResourcesBundle.getString("submit")%>">
        </form>
        --%>

    <fmt:setBundle var="bundle"  basename="me.gacl.i18n.resource.myproperties" scope="page"/>
    <form action="">
        <fmt:message key="username" bundle="${bundle}"/><input type="text" name="username"><br/>
        <fmt:message key="password" bundle="${bundle}"/><input type="password" name="password"><br/>
        <input type="submit" value="<fmt:message key="submit" bundle="${bundle}"/>">
    </form>
  </body>
</html>

  以上就是JavaWeb开发中国际化的总结内容。

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

深入分析JavaWeb Item22 -- 国际化(i18n)