Java 资源本地化与国际化

时间:2021-09-28 06:52:14

资源包

在编写应用程序的时候,需要面对的一个问题是如何来处理与locale相关的一些信息。比如,页面上的一些静态文本就希望能够以用户习惯的语言显示。最原始的做法是将这些信息硬编码到程序中(可能是一大串判断语句),但是这样就将程序代码和易变的locale信息捆绑在一起,以后如果需要修改locale信息或者添加其它的locale信息,你就不得不重新修改代码。而资源包可以帮助你解决这个问题,它通过将可变的locale信息放入资源包中来达到两者分离的目的。应用程序可以自动地通过当前的locale设置到相应的资源包中取得所要的信息。资源包的概念类似于Windows编程人员使用的资源文件(rc文件)。

一般来说,资源包需要完成两个功能:和具体的locale进行绑定以及读取locale相关信息。

ResourceBundle类

你可以把资源包看作为一个由许多成员(子类)组成的大家庭,其中每个成员关联到不同的locale对象,那它是如何完成关联功能的呢?

资源包中的每个成员共享一个被称作基名(basename)的名称,然后在此基础上根据一定的命名规范进行扩展。下面就列出了一些成员的名称:

LabelResources

LabelResources_de

LabelResources_de_CH

LabelResources_de_CH_UNIX

可见这些子类依据这样的命名规范:baseName_language_country_variant,其中language等几个变量就是你在构造Locale类时所使用的。而资源包正是通过这个符合命名规范的名称来和locale进行关联的,比如LabelResource_de_CH就对应于由德语(de)和瑞士(CH)组成的locale对象。

Locale currentLocale = new Locale("de", "CH", "UNIX");
ResourceBundle myResources =
      ResourceBundle.getBundle("LabelResources", currentLocale);

ResourceBundle.getBundle(message)//获取当前系统所使用的区域环境获得指定资源文件
ResourceBundle.getBundle(message,locale)//根据指定的区域获取对应的资源文件

当你的应用程序需要查找特定locale对象关联的资源包时,它可以调用ResourceBundle的getBundle方法,并将locale对象作为参数传入。

ResourceBundle.getBundle(message)//获取当前系统所使用的区域环境获得指定资源文件
ResourceBundle.getBundle(message,locale)//根据指定的区域获取对应的资源文件

如果该locale对象匹配的资源包子类找不到,getBundle将试着查找最匹配的一个子类。具体的查找策略是这样的:getBundle使用基名,locale对象和缺省的locale来生成一个候选资源包名称序列。如果特定locale对象的语言代码、国家代码和可选变量都是空值,则基名是唯一的候选资源包名称。否则的话,具体locale对象(language1,country1和variant1)和缺省locale(language2,country2和variant2)将产生如下的序列:

baseName +"_" + language1 + "_" + country1 + "_" + variant1

baseName +"_" + language1 + "_" + country1

baseName +"_" + language1

baseName +"_" + language2 + "_" + country2 + "_" + variant2

baseName +"_" + language2 + "_" + country2

baseName +"_" + language2

baseName

然后,getBundle方法按照产生的序列依次查找匹配的资源包子类并对结果子类初始化。首先,它将寻找类名匹配候选资源包名称的类,如果找到将创建该类的一个实例,我们称之为结果资源包。否则,getBundle方法将寻找对应的资源文件,它通过候选资源包名称来获得资源文件的完整路径(将其中的“.”替换为“/”,并加上“.properties”后缀),如果找到匹配文件,getBundle方法将利用该资源文件来创建一个PropertyResourceBundle实例,也就是最终的结果资源包。与此同时,getBundle方法会将这些资源包实例缓存起来供以后使用。

如果没有找到结果资源包,该方法将抛出MissingResourceException异常。所以为了防止异常的抛出,一般来说都需要至少实现一个基名资源包子类。

注意:基名参数必须是一个完整的类名称(比如LabelResources,resource.LabelResources等),就相当于你引用一个类时需要指定完整的类路径。但是,为了和以前的版本保持兼容,在使用PropertyResourceBundles时也允许使用“/”来代替“.”表示路径。

比如你有以下这些资源类和资源文件:

MyResources.class,

MyResources_fr_CH.properties,

MyResources_fr_CH.class,

MyResources_fr.properties,

MyResources_en.properties,

MyResources_es_ES.class

你利用以下的locale设置来调用getBundle方法,你将会得到不同的结果资源包(假设缺省locale为Locale(“en”, “UK”))

locale设置与结果资源包

locale设置        结果资源包

Locale("fr","CH")         MyResources_fr_CH.class

Locale("fr","FR")          MyResources_fr.properties

Locale("de","DE")       MyResources_en.properties

Locale("en","US")       MyResources_en.properties

Locale("es","ES")       MyResources_es_ES.class

创建了具体的资源包子类实例以后,就需要获得具体的信息。信息在资源包中是以键值对的方式存储的

LabelResources.properties

# This isLabelResources.properties file
greetings = 您好!
farewell = 再见。
inquiry = 您好吗?

其中等号左边的字符串表示主键,它们是唯一的。为了获得主键对应的值,你可以调用ResourceBundle类的getString方法,并将主键作为参数。此外,文件中以“#”号开头的行表示注释行。

ListResourceBundle和PropertyResourceBundle子类

抽象类ResourceBundle具有两个子类:ListResourceBundle和PropertyResourceBundle,它们表示资源包子类两种不同的实现方式。

PropertyResourceBundle是和资源文件配对使用的,一个属性文件就是一个普通的文本文件,你只需要为不同的locale设置编写不同名称的资源文件。但是,在资源文件中只能包含字符串,如果需要存储其它类型对象,你可以使用ListResourceBundle。

ListResourceBundle是将键值对信息保存在类中的列表中,而且你必须实现ListResourceBundle的具体子类。

如果ListResourceBundle和PropertyResourceBundle不能够满足你的需要,你可以实现自己的ResourceBundle子类,你的子类必须覆盖两个方法:handleGetObject和getKeys

使用资源文件

使用资源包最简单的方法就是利用资源文件,利用资源文件一般需要以下几个步骤:

1、创建一个缺省的资源文件

为了防止找不到资源文件,你最好实现一个缺省的资源文件,该文件的名称为资源包的基名加上.properties后缀。

2、创建所需的资源文件

为你准备支持的locale设置编写对应的资源文件。

3、设置locale

你必须在程序中的某个地方提供locale的设置或者切换功能,或者将其放入配置文件中。

4、根据locale设置创建资源包

ResourceBundleresource =

ResourceBundle.getBundle("LabelBundle",currentLocale);

5、通过资源包获取locale相关信息

String value = resource.getString("welcome");

注意:在使用基名的时候,特别要注意给出完整的类名(或者路径名),比如你的应用程序所在的类包为org.javaresearch.j2seimproved.i18n,而你的资源文件在你的应用程序下的resource子目录中,那你的基名就应该是org.javaresearch.j2seimproved.i18n.resource.LabelBundleBundle而不是resource.LabelBundleBundle。

使用ListResourceBundle

使用ListResourceBundle和使用资源文件的步骤基本上一样,只不过你需要用ListResourceBundle子类来替换相应的资源文件。比如你的应用程序的基名是LabelBundle,而且准备支持Locale("en","US")和Locale("zh","CN"),那你需要提供以下几个Java文件,注意类名和locale的对应关系。

LabelBundle_en_US.java

LabelBundle_zh_CN.java

LabelBundle.java(缺省类)

下面的代码列出的是LabelBundle_zh_CN.java的源代码,相对于资源文件中“key = value”的写法,在此文件中你首先利用键值对来初始化一个二维数组,并在getContents方法中返回该数组。

LabelBundle_zh_CN.java

public class LabelBundle_zh_CN extendsListResourceBundle {
  public Object[][] getContents() {
    return contents;
  }
  private Object[][] contents = {
    {"title", "称谓"},
    {"surname", "姓"},
    {"firstname", "名"},
  };
}

创建完资源类以后,同样需要设置locale以及根据locale来创建资源包。在通过资源包获取具体值的时候,你不能再使用getString方法,而应该调用getObject方法,而且由于getObject方法返回一个Object对象,你还需要进行正确的类型转换。其实,为了你的程序通用性,我们建议在使用资源文件的时候你也应该调用getObject方法,而不是getString方法。

title = (String)resource.getObject("title");

如果系统同时存在资源文件、类文件,系统将以类文件为主,而不会调用资源文件。

MessageFormat类

上面我们讲到利用资源文件来分离代码和可变的信息。但是在实际过程中,有些信息并不能够完全事先定义好,其中可能会用到运行时的一些结果,最典型例子的就是错误提示代码,比如提示某个输入必须在一定范围内。利用上面所讲的资源文件并不能够很好地解决这个问题,所以Java中引入了MessageFormat类。

MessageFormat提供一种语言无关的方式来组装消息,它允许你在运行时刻用指定的参数来替换掉消息字符串中的一部分。你可以为MessageFormat定义一个模式,在其中你可以用占位符来表示变化的部分,比如你有这样一句话:

您好,clf!欢迎来到csdn!当前时间是:2015-3-27 24:43:12

其中斜体带下划线的部分为可变化的,你需要根据当前时间和不同的登录用户来决定最终的显示。我们用占位符来表示这些变化的部分,可以得到下面这个模式:

您好,{0}!欢迎来到csdn!当前时间是:{1,date}{1,time}。

占位符的格式为{ArgumentIndex , FormatType , FormatStyle },详细说明可以参考MessageFormat的API说明文档。这里我们定义了两个占位符,其中的数字对应于传入的参数数组中的索引,{0}占位符被第一个参数替换,{1}占位符被第二个参数替换,依此类推。

最多可以设置10个占位符,而且每个占位符可以重复出现多次,而且格式可以不同,比如{1,date}和{1,time}。而通过将这些模式定义放到不同的资源文件中,就能够根据不同的locale设置,得到不同的模式定义,并用参数动态替换占位符。

下面我们就以MessageFormatSample.java程序为例,来详细说明其中的每个步骤。

1、找出可变的部分,并据此定义模式,将模式放入不同的资源文件中。

比如针对上面的模式,定义了下面两个资源文件:

MessagesBundle_en_US.properties

Welcome = Hi,{0}! Welcome to Java Research Organization!

MessagesBundle_zh_CN.properties

Welcome = 您好,{0}!欢迎来到csdn!

2、创建MessageFormat对象,并设置其locale属性。

  MessageFormat formatter = newMessageFormat("");
      formatter.setLocale(currentLocale);

3、从资源包中得到模式定义,以及设置参数。

messages = ResourceBundle.getBundle(  "MessagesBundle",currentLocale);
Object [] testArgs = {"peachpi",new Date()};

4、利用模式定义和参数进行格式化。

 System.out.println(formatter.format(messages.getString("welcome"),testArgs));

关于资源包的组织

一般来说,你是按照资源的用途来组织资源包的,比如会把所有的页面按钮的信息放入一个名为ButtonResources的资源包中。在实际的应用过程中,以下几个原则可以帮你决定如何组织资源包:

1、要易于维护。

2、最好不要将所有的信息都放入一个资源包中,因为这样资源包载入内存时将会很耗时。

3、最好将一个大的资源包分为几个小的资源包,这样可以在使用的时候才导入必须的资源,减少内存消耗。

JSTL 国际化标签

  <fmt:bundle> 功能:指定消息资源使用的文件

  <fmt:message>功能:显示消息资源文件中指定key的消息,支持带参数消息

  <fmt:param> 功能:给带参数的消息置参数值

  <fmt:setBundle>功能:设置消息资源文件

  

  一个支持按模块的多资源文件的国际化例子

  

  resources\IAMResources_zh_CN.properties,内容为

  test.common.message= test.common.message {0}

  

  resources\UserSynResources_zh_CN.properties内容为

  test.usersyn.message= test.usersyn.message {0}

  

includeTld.jsp:

<fmt:setBundle basename="resources.IAMResources" var="commonBundle"/>
<fmt:setBundle basename="resources.UserSynResources"var="userSynBundle"/>

  

test.jsp

  

<%@include file="/includeTld.jsp"%>
  
  <fmt:message key="test.common.message" bundle="${commonBundle}">
       <fmt:param value="clf"/>
  </fmt:message>
  
      <fmt:bunble basename=" ${commonBundle}">
       <fmt:message key="test.usersyn.message" >
              <fmt:param value="clf"/>
       </fmt:message>
      </fmt:bundle>

输出:test.common.message clf

test. usersyn.message clf

Java 资源本地化与国际化的更多相关文章

  1. iOS开发——高级技术&amp&semi;本地化与国际化详解

    本地化与国际化详解 效果如下:   英语:                                                                    中文: 具体实现如下: ...

  2. Java资源大全中文版(Awesome最新版)(转载)

    原文地址:http://www.cnblogs.com/best/p/5876559.html 目录 业务流程管理套件 字节码操作 集群管理 代码分析 编译器生成工具 构建工具 外部配置工具 约束满足 ...

  3. 史诗级Java资源大全中文版

    本文来自GitHub 上 Awesome - java 系列的资源整理.awesome-java 就是 akullpp 发起维护的 Java 资源列表,内容包括:构建工具.数据库.框架.模板.安全.代 ...

  4. 用VC资源动态链接库解决国际化问题

    http://daixinghe.blog.163.com/blog/static/1843615920097181952979/ 随着计算机应用的普及,应用软件跨国使用越来越频繁,如何实现应用软件的 ...

  5. 【转载】国外程序员整理的Java资源大全

    以下转载自: 推荐!国外程序员整理的Java资源大全中文版    https://github.com/akullpp/awesome-java英文版 Java 几乎是许多程序员们的入门语言,并且也是 ...

  6. 【转载】Java资源大全中文版

    Java资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列的资源整理.awesome-java 就是 akullpp 发起维护的 Java 资源列表,内容 ...

  7. Android Webview H5资源本地化

    Android Webview H5资源本地化 一. 创建读取资源项目独立模块 1. 项目依赖的好处 符合模块化的思想,他们相互独立.一个项目持有另一个项目的引用,修改更加方便. (注:compile ...

  8. Java资源大全中文版(Awesome最新版)

    Awesome系列的Java资源整理.awesome-java 就是akullpp发起维护的Java资源列表,内容包括:构建工具.数据库.框架.模板.安全.代码分析.日志.第三方库.书籍.Java 站 ...

  9. AngularJS开发指南7:AngularJS本地化,国际化,以及兼容IE低版本浏览器

    AngularJS本地化,国际化 国际化,简写为i18n,指的是使产品快速适应不同语言和文化. 本地化,简称l10n,是指使产品在特定文化和语言市场中可用. 对开发者来说,国际化一个应用意味着将所有的 ...

随机推荐

  1. android 资讯阅读器

    最近找申请到了一个不错的接口 , 非常适合拿来写一个资讯类的app. 现在着手写,随写随更.也算是抛砖引玉.烂尾请勿喷.╭(╯^╰)╮ android 资讯阅读器 第一阶段目标样式(滑动切换标签 , ...

  2. 谈&quot&semi;自驱力&quot&semi;

    最新说明: 1.标题是为了博眼球取的,请不大家不要纠结具体薪资数字,我瞎取的 2.请注意素质,不要满口喷粪,不要搞人身攻击,尊重别人,就是尊重你自己 3.请大家就事论事,不要胡乱臆想,请站在全局的角度 ...

  3. C&num;获取类以及类下的方法&lpar;用于Asp&period;Net MVC&rpar;

    在C#中,实现动态获取类和方法主要通过反射来实现,要引用System.Reflection. public ActionResult GetControllerAndAction() List< ...

  4. No&period;001:Two Sum

    问题: Given an array of integers, return indices of the two numbers such that they add up to a specifi ...

  5. JQuery&period;tmpl&lpar;&rpar;的用法

    动态请求数据来更新页面是现在非常常用的方法,现在通过Ajax请求返回的数据更多的是json对象, 为了解决js动态拼接数据这方面的问题,JavaScript 也可以利用模版来解决这些问题,比如基于 j ...

  6. python3列表(元组)练习

    列表和元组一起练习l = [] 或者 li = list() 列表表示t = () 元组表示,元组不能修改,元组中只有两个方法 count().index(),同列表相同 li = [11,22,33 ...

  7. React-Native 真机调试踩坑指南

    继上一篇基础安装踩坑继续我们的踩坑之旅,备注一下以下仅针对Mac环境-- 安卓 1.adb 找不到命令? Adb的全称为Android Debug Bridge,就是起到调试桥的作用,真机调试安卓必备 ...

  8. 7&period;计算N元等式&lbrack;穷举&rsqb;

    穷举的一种应用,计算x+2y+3z=50的非负整数解.先约束每个变量的最大值,x=50,y=25,z=50/3. #include <iostream> using namespace s ...

  9. 每天一个linux命令-vi

    进入vi的命令 vi filename :打开或新建文件,并将光标置于第一行首 vi +n filename :打开文件,并将光标置于第n行首 vi + filename :打开文件,并将光标置于最后 ...

  10. loadrunner运行乱码解决方法

    最近进行项目压力测试,选择用loadrunner来进行测试,当在回放时间,脚本中的中文由乱码.快照,经研究,经过一下3步可以解决,分享一下 第一步:当在进行新建脚本时间,选择选项中-〉高级-〉选择“支 ...