[Java] JDK与Spring国际化支持
Posted 千千寰宇
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Java] JDK与Spring国际化支持相关的知识,希望对你有一定的参考价值。
0 序言
在Spring context
与Spring IoC
对ApplicationContext
以及Context
相关的设计模式进行了介绍。
ApplicationContext
作为一个Context
在应用的运行层提供了IoC容器、事件、国际化等功能接口。
Spring
的国际化(i18n)功能是通过MessageSource
接口实现的,他提供了MessageSource::getMessage
方法从预设的资源中获取对应的数据。
1 JDK 国际化核心组件 : Java 标准资源绑定
1.1 java.util.ResourceBundle
1.1.1 概述
在介绍
MessageSource
之前,得先说清楚Java(J2SE)对国际化的基本实现——ResourceBundle
,因为MessageSource
是用它实现的。
ResourceBundle
很好理解,他就是按照规范的格式放置*.properties
资源文件,然后根据输入的语言环境来返回资源。
ResourceBundle
类提供了软件国际化的捷径。通过此类,可以使您所编写的程序可以:
- 轻松地本地化或翻译成不同的语言
- 一次处理多个语言环境
- 以后可以轻松地进行修改,支持更多的语言环境
说的简单点,这个类的作用就是读取资源属性文件(properties),然后根据.properties文件的名称信息(本地化信息),匹配当前系统的国别语言信息(也可以程序指定),然后获取相应的properties文件的内容。
使用这个类,要注意的一点是,这个properties文件的名字是有规范的:一般的命名规范是: 自定义名_语言代码_国别代码.properties
如果是默认的,直接写为:自定义名.properties
比如:
myres_en_US.properties
myres_zh_CN.properties
myres.properties
当在中文操作系统下,如果myres_zh_CN.properties、myres.properties两个文件都存在,则优先会使用myres_zh_CN.properties,当myres_zh_CN.properties不存在时候,会使用默认的myres.properties。
- 没有提供语言和地区的资源文件是系统默认的资源文件。
- 资源文件都必须是
ISO-8859-1
编码。 - 因此,对于所有非西方语系的处理,都必须先将之转换为
Java Unicode Escape
格式。转换方法是通过JDK
自带的工具native2ascii
.
1.1.2 示例1
- Step1 我们有3个资源文件放置在
classpath
的根目录(本例是放在src/main/resource
)中,文件名分别为: - i18n_en_US.properties
- i18n_zh_CN.properties
- i18n_web_BASE64.properties
文件中的内容如下:
#i18n_en_US.properties
say=Hallo world!
#i18n_zh_CN.properties
say=\\u5927\\u5BB6\\u597D\\uFF01
#i18n_web_BASE64.properties
say=+-+-+-ABC
- Step2 然后,我们通过ResourceBundle类来使用这些i18n的资源文件:
public class I18nApp
public static void main(String[] args)
//使用当前操作系统的语言环境
ResourceBundle rb = ResourceBundle.getBundle("i18n", Locale.getDefault());
System.out.println(rb.getString("say"));
//指定简体中文环境
rb = ResourceBundle.getBundle("i18n", new Locale("zh", "CN"));
System.out.println(rb.getString("say"));
//通过预设指定简体英文环境
rb = ResourceBundle.getBundle("i18n", Locale.SIMPLIFIED_CHINESE);
System.out.println(rb.getString("say"));
//指定美国英语
rb = ResourceBundle.getBundle("i18n", Locale.US);
System.out.println(rb.getString("say"));
//使用自定义的语言环境
Locale locale = new Locale("web", "BASE64");
rb = ResourceBundle.getBundle("i18n", locale);
System.out.println(rb.getString("say"));
按照开发文档的要求:
- 使用
ResourceBundle
加载的资源文件都必须放置在根目录
- 且必须按照
$resourceName_$language_$region
的方式来命名。这个命名方式正好能对应ResourceBundle::getBundle
方法中的参数。
例如:
ResourceBundle.getBundle("i18n", new Locale("zh", "CN"))
- "i18n"对应
$name
- "zh"对应
$language
- 而“CN”对应$
这样我们就可以通过传导参数来使用不同的资源。
如果不指定$language
和$region
,该文件就是一个默认文件。
Locale
类预设了很多资源类型,比如:Locale.SIMPLIFIED_CHINESE
、Locale.US
,实际上他们就等价于new Locale("zh", "CN")
和new Locale("en", "US")
。只是Java的开发人员做了一些静态的预设。
除了预设内容的Locale
,我们还可以像Locale locale = new Locale("web", "BASE64")
这样添加自定义的内容,他对应名为i18n_web_BASE64.properties
的资源文件。
1.1.3 示例2
定义三个资源文件,放到src的根目录下面(必须这样,或者你放到自己配置的calsspath下面。
- myres.properties
bbb=thanks
- myres_en_US.properties
aaa=good
bbb=thanks
- myres_zh_CN.properties
aaa=\\u597d
bbb=\\u591a\\u8c22
- 使用
import java.util.Locale;
import java.util.ResourceBundle;
/**
* 国际化资源绑定测试
*
* @author leizhimin 2009-7-29 21:17:42
*/
public class TestResourceBundle
public static void main(String[] args)
Locale locale1 = new Locale("zh", "CN");
ResourceBundle resb1 = ResourceBundle.getBundle("myres", locale1);
System.out.println(resb1.getString("aaa"));
ResourceBundle resb2 = ResourceBundle.getBundle("myres", Locale.getDefault());
System.out.println(resb1.getString("aaa"));
Locale locale3 = new Locale("en", "US");
ResourceBundle resb3 = ResourceBundle.getBundle("myres", locale3);
System.out.println(resb3.getString("aaa"));
运行结果:
好
好
good
Process finished with exit code 0
如果使用默认的Locale
,那么在英文操作系统上,会选择myres_en_US.properties
或myres.properties
资源文件。
1.2 Locale
Locale
对象表示了特定的地理
、政治
和文化地区
。
需要 Locale 来执行其任务的操作称为语言环境敏感的操作,它使用 Locale 为用户量身定制信息。例如,显示一个数值就是语言环境敏感的操作,应该根据用户的国家、地区或文化的风俗/传统来格式化该数值。
使用此类中的构造方法来创建 Locale:
- Locale(String language)
- Locale(String language, String country)
- Locale(String language, String country, String variant)
创建完 Locale 后,就可以查询有关其自身的信息。
使用 getCountry 可获取 ISO 国家代码,使用 getLanguage 则获取 ISO 语言代码。
可用使用 getDisplayCountry 来获取适合向用户显示的国家名。
同样,可用使用 getDisplayLanguage 来获取适合向用户显示的语言名。
有趣的是,getDisplayXXX 方法本身是语言环境敏感的,它有两个版本:一个使用默认的语言环境作为参数,另一个则使用指定的语言环境作为参数。
语言参数是一个有效的 ISO 语言代码。这些代码是由 ISO-639 定义的小写两字母代码。在许多网站上都可以找到这些代码的完整列表,如:
http://www.loc.gov/standards/iso639-2/englangn.html。
国家参数是一个有效的 ISO 国家代码。这些代码是由 ISO-3166 定义的大写两字母代码。在许多网站上都可以找到这些代码的完整列表,如:
http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html。
1.3 native2ascii | 中文资源文件的转码
如果觉得麻烦,可以直接将中文粘贴到里面,回车就可以看到转码后的结果了。
2 Spring 国际化 核心组件
2.1 MessageSource 接口
2.1.1 概述
ApplicationContext
接口扩展了MessageSource
接口,因而提供了消息处理的功能(i18n
或国际化
)。
与HierarchicalMessageSource
一起使用,它还能够处理嵌套的消息,这些是spring提供的处理消息的基本接口。
2.1.2 核心方法
让我们快速浏览一下它所定义的方法:
- String getMessage(String code, Object[] args, String default, Locale loc)
用来从MessageSource获取消息的基本方法。如果在指定的locale中没有找到消息,则使用默认的消息。args中的参数将使用标准类库中的MessageFormat来作消息中替换值。
- String getMessage(String code, Object[] args, Locale loc)
本质上和上一个方法相同,其区别在:没有指定默认值,如果没找到消息,会抛出一个NoSuchMessageException异常。
- String getMessage(MessageSourceResolvable resolvable, Locale locale)
上面方法中所使用的属性都封装到一个MessageSourceResolvable实现中,而本方法可以指定MessageSourceResolvable实现。
2.1.3 ApplicationContext与MessageSource的工作原理
- 当一个
ApplicationContext
被加载时,它会自动在context
中查找已定义为MessageSource
类型的bean。
此
bean
的名称须为messageSource
。
-
如果找到,则:所有对上述方法的调用将被委托给该
bean
。 -
否则,
ApplicationContext
会在其父类中查找是否含有同名的bean
。如果有,就把它作为MessageSource
。 -
如果它最终没有找到任何的消息源,一个空的
StaticMessageSource
将会被实例化,使它能够接受上述方法的调用。
2.1.4 MessageSource 接口的实现
Spring
目前提供了两个MessageSource
的实现:
- ResourceBundleMessageSource
- StaticMessageSource
它们都继承
NestingMessageSource
以便能够处理嵌套的消息。
StaticMessageSource
很少被使用,但能以编程方式向消息源(Message Source)添加消息(Message)。
ResourceBundleMessageSource
会用得更多一些,为此提供了一下实例化的示例:
<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>format</value>
<value>exceptions</value>
<value>windows</value>
</list>
</property>
</bean>
</beans>
2.1.5 SpringMVC中MessageSource的获取方法
// 资源文件ApplicationResources.properties放在src目录下,也就是classes目录
// 配置文件messages.xml也放在src目录下,也就是classes目录
// 第1种方法
MessageSource resources = new ClassPathXmlApplicationContext("messages.xml");
String msg = resources.getMessage("XXXXX", new String[] "OOOOO" , null);
// 第2种方法
ReloadableResourceBundleMessageSource messageSource;
messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/ApplicationResources");
messageSource.setUseCodeAsDefaultMessage(true);
msg = messageSource.getMessage("XXXXX", new String[] "OOOOO" , Locale.CHINA);
// 第3种方法 | 此方式中, messageSource Bean 是配置在 spring-servlet.xml 中的
WebApplicationContext wac = RequestContextUtils.getWebApplicationContext(request);
msg = wac.getMessage("XXXXX", new String[] "OOOOO" , Locale.CHINA);
2.2 ResourceBundleMessageSource
2.2.1 概述
ResourceBundleMessageSource
: 提供国际化的类。
ResourceBundleMessageSource
的底层实现依赖于java.util.ResourceBundle
说的简单点,这个类的作用就是读取资源属性文件(.properties
)
然后,根据.properties
文件的名称信息(本地化信息),匹配当前系统的国别语言信息(也可以程序指定)
然后,获取相应的properties
文件的内容。
2.2.2 配置步骤:示例1
ResourceBundleMessageSource
的功能就是用Java标准库的ResourceBundle
实现的,所以使用起来和ResourceBundle
也差不多。
- Step1 首先,得将用于处理国际化资源的Bean添加到IoC容器中:
@Configuration
public class I18nApp
@Bean("messageSource")
ResourceBundleMessageSource resourceBundleMessageSource()
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames(new String[] "i18n", "extend" );//添加资源名称
return messageSource;
或
<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>i18n</value>
<value>extend</value>
</list>
</property>
</bean>
</beans>
切记一定要标记id=messageSource
。basenames
这个Setter
用于指定*.properties
资源文件的名称,规则和前面介绍的ResourceBundle
一样。
- Step2 然后,就可以通过
ApplicationContext::getMessage
方法获取对应的资源了:
@Configuration
public class I18nApp
@Bean("messageSource")
ResourceBundleMessageSource resourceBundleMessageSource()
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames(new String[] "i18n", "extend" );
return messageSource;
public static void main(String[] args)
ApplicationContext context = new AnnotationConfigApplicationContext(I18nApp.class);
System.out.println("Spring Default 1:" + context.getMessage("say", null, Locale.getDefault()));
System.out.println("Spring Default 2:" + context.getMessage("say", null, null));
System.out.println("Spring Chinese:" + context.getMessage("say", null, Locale.SIMPLIFIED_CHINESE));
System.out.println("Spring Us English:" + context.getMessage("say", null, Locale.US));
System.out.println("Spring Custom:" + context.getMessage("say", null, new Locale("web", "BASE64")));
System.out.println("Spring Argument:" + context.getMessage("info", new String[] "chkui",null));
System.out.println("Spring Info:" + context.getMessage("say", null, null));
2.2.2 配置步骤:示例2
- Step1 新建国际化资源文件
# i18n_en_US.properties
message=welcome:0
# i18n_zh_CN.properties
message=欢迎:0
- Step2 配置 Spring Bean
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n" />
</bean>
- Step3 使用
@Test
public void test()
ApplicationContext context = new ClassPathXmlApplicationContext("SpringMVC.xml");
System.out.println(context.getMessage("message", new Object[]"lgh", Locale.SIMPLIFIED_CHINESE));
System.out.println(context.getMessage("message", new Object[]"lgh", Locale.US));
@Controller
public class TestController
@Autowired
private ResourceBundleMessageSource messageSource;
@RequestMapping(value = "/test4", method = RequestMethod.GET)
public String test4(Locale locale)
System.out.println(messageSource.getMessage("message", null, locale));
return "i18n";
2.3 占位符替换
注意上面的示例代码的这一行:
context.getMessage("info", new String[] "chkui",null))
这里的getMessage
向方法传递了一个数组,它用于替换 资源文件
中的占位符号。
在例子中我们除了
i18n
还加载了一个extend.properties
文件,文件内容如下:
info=0\\u5E05\\u7684\\u8BA9\\u4EBA\\u6CA1\\u813E\\u6C14\\u3002
文件中的0
表示这个位置用数组中的[0]
位置的元素替换。
还有一点需要注意的是,*.properties
文件输入中文等UTF-8
的符号时需要保留上面这种ACSII
的格式,现在大部分IDE都会自动处理的,切记不要为了方便看内容将*.properties
的编码格式切换为UTF-8
。
2.4 获取MessageSource接口
我们有三种方式获取MessageSource接口:
//直接使用
ApplicationContext context = new AnnotationConfigApplicationContext(I18nApp.class);
context.getMessage("say", null, Locale.getDefault()));
//MessageSourceAware(ApplicationContextAware)接口
public class ExtendBean implements MessageSourceAware
@Override
public void setMessageSource(MessageSource messageSource)
this.setterMs = messageSource;
//从容器直接注入
public class ExtendBean implements MessageSourceAware
@Autowired
private MessageSource autowiredMs;
需要注意的是,使用@Autowired等方式直接获取MessageSource类型的数据得到的是添加到容器的那个Bean,而其他方式获取到的是ApplicationContext。
X 参考文献
本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!
Spring学习系列——Spring中的国际化
1.Java中的国际化
国际化(Internationalization)简称 i18n,简单来说就是不同国家/地区/语言的用户,访问同一个程序,得到对应的本地化资源。
首先要确定国家/地区/语言(Locale类)
然后根据不同的Locale获取对应的本地化资源(locale敏感的)。
而本地化资源相关的主要有两种:ResourceBundle和Format抽象类的子类。
①Jdk为ResourceBundle抽象类提供了两个子类(ListResourceBundle和PropertyResourceBundle)
当然也可以参考ResourceBundle编写自定义的子类MyResources和XMLResourceBundle等(源码注释部分)。
(FORMAT_DEFAULT默认使用java.class和java.properties两种格式)其中properties的命名规则basename_language_country.properties。
②Format,相对简单不再过多描述。
Format子类中关于Local的使用。
2.Spring中的国际化
首先说明Spring中国际化的支持是AbstractApplicationContext中定义的默认bean(ApplicationContext实现了MessageSource接口), private MessageSource messageSource;
而在基于Spring的Web应用中使用我们更多关注的是如何确定Local的问题,而对资源的获取则更多的是基于配置。
①通过LocaleResolver获取Local
- 读取客户端浏览器的默认语言,根据Request Headers中的Accept-language来判断。
- 客户端传参,并基于Session和Cookie的对Local进行管理(例如一些网站的语言切换选项)。
使用RequestContextUtils.getLocale(request)获取Local;
参考DispatcherServlet的initLocaleResolver()方法,在bean中配置不同的LocaleResolver实现
②通过MessageSource获取ResoureBundle
- 对与一些简单的资源,我们可以采用properties中配置
- 在Web应用中,如果资源文件过多,使用properties维护将是个麻烦,可以采用数据库存储结合缓存技术实现。
③messageResoure的配置
核心是AbstractApplicationContext中initMessageSource()中涉及的HierarchicalMessageSource接口;
由AbstractBeanFactory中的containsLocalBean(String name)区分为两类:使用配置的HierarchicalMessageSource实现和DelegatingMessageSource(HierarchicalMessageSource的简单实现)
而在messageSource配置class时,有两个常用的HierarchicalMessageSource实现可供选择:ResourceBundleMessageSource和ReloadableResourceBundleMessageSource
两者都是利用资源名通过getMessage()接口就可以加载整套的国际化资源文件,唯一区别在于ReloadableResourceBundleMessageSource可以定时刷新资源文件,以便在应用程序不重启的情况下感知资源文件的变化。很多生产系统都需要长时间持续运行,系统重启会给运行带来很大的负面影响,这时通过该实现类就可以解决国际化信息更新的问题。上面的配置中cacheSeconds属性让ReloadableResourceBundleMessageSource每5秒钟刷新一次资源文件(在真实的应用中,刷新周期不能太短,否则频繁的刷新将带来性能上的负面影响,一般不建议小于30分钟)。cacheSeconds默认值为-1表示永不刷新,此时,该实现类的功能就蜕化为ResourceBundleMessageSource的功能。
④简单的代码示例
a.在spring配置文件中定义messageSource<bean>
b.定义不同Local的properties文件,命名规则basename_language_country.properties和java中的一致。
c.代码
ApplicationContext applicationContext=new FileSystemXmlApplicationContext("classpath:spring-config.xml"); String message=applicationContext.getMessage("HelloWorld",new Object[]"hello",new Date(), Locale.CHINA);
小结:可以看出使用spring的国际化更加灵活(ReloadableResourceBundleMessageSource的动态读取),基于MessageFormat的参数化列表,以及中文支持可以设置编码格式(java中处理相对麻烦)。
3.总结
Java中:重点了解ResoureBundle的使用参考源码解析图。
Spring中:重点分两部分,①通过LocaleResolver获取Local,②通过通过MessageSource获取ResoureBundle
Spring的国际化是基于Java(Jdk)国际化的扩展,在Web项目中使用更加灵活。
阅读Java(其实这里使用Jdk更合适)源码还能够捋清思路,Spring源码阅读着有些吃力,目前阶段只能结合一些博客挑重点部分的源码进行解读了。
4.参考资料
以上是关于[Java] JDK与Spring国际化支持的主要内容,如果未能解决你的问题,请参考以下文章