Spring核心——MessageSource实现国际化

Spring的国际化

上下文与IoC对ApplicationContext以及Context相关的设计模式进行了介绍。ApplicationContext作为一个Context在应用的运行层提供了IoC容器、事件、国际化等功能接口。

Spring的国际化(i18n)功能是通过MessageSource接口实现的,他提供了MessageSource::getMessage方法从预设的资源中获取对应的数据。

Java标准资源绑定

在介绍MessageSource之前,得先说清楚Java(J2SE)对国际化的基本实现——ResourceBundle,因为MessageSource是用它实现的。ResourceBundle很好理解,他就是按照规范的格式放置*.properties资源文件,然后根据输入的语言环境来返回资源。看一个代码例子就很容易明白了。

(可执行代码请到gitee下载,本文的示例代码在chkui.springcore.example.hybrid.i18n包中。)

我们有3个资源文件放置在classpath的根目录(本例是放在src/main/resource)中,文件名分别为i18n_en_US.propertiesi18n_zh_CN.propertiesi18n_web_BASE64.properties。文件中的内容如下:

#i18n_en_US.propertiessay=Hallo world!#i18n_zh_CN.propertiessay=\u5927\u5BB6\u597D\uFF01#i18n_web_BASE64.propertiessay=+-+-+-ABC

然后我们通过ResourceBundle类来使用这些i18n的资源文件:

packagechkui.springcore.example.hybrid.i18n;publicclassI18nApp{publicstaticvoidmain(String[] args){//使用当前操作系统的语言环境ResourceBundle rb = ResourceBundle.getBundle("i18n", Locale.getDefault());System.out.println(rb.getString("say"));//指定简体中文环境rb = ResourceBundle.getBundle("i18n",newLocale("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 =newLocale("web","BASE64");rb = ResourceBundle.getBundle("i18n", locale);System.out.println(rb.getString("say"));}}

按照开发文档的要求,使用ResourceBundle加载的资源文件都必须放置在根目录,并且必须按照${name}_${language}_${region}的方式来命名。这个命名方式正好能对应ResourceBundle::getBundle方法中的参数,例如ResourceBundle.getBundle("i18n", new Locale("zh", "CN"))。"i18n"对应${name},"zh"定位${language},而“CN”对应${region}。这样我们就可以通过传导参数来使用不同的资源。如果不指定${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的资源文件。

MessageSource的使用

MessageSource的功能就是用Java标准库的ResourceBundle实现的,所以使用起来也差不多。

首先得将用于处理国际化资源的Bean添加到IoC容器中:

@ConfigurationpublicclassI18nApp{@Bean("messageSource")ResourceBundleMessageSourceresourceBundleMessageSource(){ResourceBundleMessageSource messageSource =newResourceBundleMessageSource();messageSource.setBasenames(newString[] {"i18n","extend"});//添加资源名称returnmessageSource;}}

i18nextend

切记一定要标记id=messageSource。basenames这个Setter用于指定*.properties资源文件的名称,规则和前面介绍的ResourceBundle一样。然后就可以通过ApplicationContext::getMessage方法获取对应的资源了:

@ConfigurationpublicclassI18nApp{@Bean("messageSource")ResourceBundleMessageSourceresourceBundleMessageSource(){ResourceBundleMessageSource messageSource =newResourceBundleMessageSource();messageSource.setBasenames(newString[] {"i18n","extend"});returnmessageSource;}publicstaticvoidmain(String[] args){ApplicationContext context =newAnnotationConfigApplicationContext(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,newLocale("web","BASE64")));System.out.println("Spring Argument:"+ context.getMessage("info",newString[] {"chkui"},null));System.out.println("Spring Info:"+ context.getMessage("say",null,null));}}

占位符替换

注意上面的示例代码的这一行: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的符号时需要保留上面这种ACS的格式,现在大部分IDE都会自动处理的,切记不要为了方便看内容将*.properties的编码格式切换为UTF-8。

获取MessageSource接口

我们有三种方式获取MessageSource接口:

//直接使用ApplicationContext context =newAnnotationConfigApplicationContext(I18nApp.class);context.getMessage("say",null, Locale.getDefault()));//MessageSourceAware(ApplicationContextAware)接口publicclassExtendBeanimplementsMessageSourceAware{@OverridepublicvoidsetMessageSource(MessageSource messageSource){this.setterMs = messageSource;}}//从容器直接注入publicclassExtendBeanimplementsMessageSourceAware{@AutowiredprivateMessageSource autowiredMs;}

需要注意的是,使用@Autowired等方式直接获取MessageSource类型的数据得到的是添加到容器的那个Bean,而其他方式获取到的是ApplicationContext。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容