最近在做Kettle8.1的国际化工作,闲暇之余,就看了看Java的国际化处理,明白程序怎么样找到对应的国际化文件。

说到国际化,经常看到一个东西叫i18n,其实是internationalization的缩写(ps:以后起昵称什么的就可以仿照这个规则信手拈来,又专业又好记)。

Java中相关的有以下3个类:

  • ResourceBundle:国际化资源包。
  • Locale:表示了特定的地理、政治和文化地区。
  • MessageFormat:用以实现动态填充国际化文件的占位符。

问题一:基本使用

话不多说,直接上案例,java代码如下:

package com.szh;

import java.util.Locale;
import java.util.ResourceBundle;

public class TestI18n {
    public static void main(String[] args) throws Exception {
        Locale l1 = new Locale("zh", "CN");
        ResourceBundle rb1 = ResourceBundle.getBundle("test", l1);
        System.out.println(rb1.getString("str1"));

        ResourceBundle rb2 = ResourceBundle.getBundle("test", Locale.getDefault());
        System.out.println(rb2.getString("str1"));

        Locale l3 = new Locale("en", "US");
        ResourceBundle rb3 = ResourceBundle.getBundle("test", l3);
        System.out.println(rb3.getString("str1"));
    }
}

下面3个国际化文件目录如下:

java 实现国际化动态加载语言 java程序国际化_System

test_zh_CN.properties

str1=\u4F60\u597D!!

test_en_US.properties

str1=Hello!!

test.properties(当在对应的语言环境的国际化文件中找不到key时,则去此文件中拿):

str1=\u4F60\u597D!

此时,可以看到test_zh_CN.properties和test.properties显示出来的是ASCII码,jdk提供了cmd命令native2ascii.exe,能够查看对应的ASCII码,如下所示:

C:\Users\szh>native2ascii
你好!
\u4f60\u597d!

运行结果1:

你好!!
你好!!
Hello!!

运行结果2(我的语言环境为中文,当test_zh_CN.properties中没有str1时,则会去打印test.properties中str1的值):

你好!
你好!
Hello!!

问题二:动态填充占位符

另外一个问题,经常见到需要动态填充国际化字符串文本的情况,即需要填充占位符。此时需要类MessageFormat登场了。

首先修改test_en_US.properties代码如下:

str1=Hello!!{0}, {2}, {1} and {3}

主程序如下(四个乱序的占位符,我们只传3个看看效果):

package com.szh;

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

public class TestI18n {
    public static void main(String[] args) throws Exception {
        Locale l3 = new Locale("en", "US");
        ResourceBundle rb3 = ResourceBundle.getBundle("test", l3);
        String s1 = MessageFormat.format(rb3.getString("str1"), new Object[] { "aa", "bb", "cc" });
        String s2 = new MessageFormat(rb3.getString("str1")).format(new Object[] { "aa", "bb", "cc" });
        System.out.println(s1);
        System.out.println(s2);
    }
}

运行结果(依次按照占位符序号填充,第四个参数未传,则按照字符串原样打印):

Hello!!aa, cc, bb and {3}
Hello!!aa, cc, bb and {3}

问题三:如何找到任意位置的国际化文件

上面的例子均放在src根目录下,那么如何能够像Kettle中一样,拿到自定义位置的国际化文件呢?

首先,我们将test_zh_CN.properties复制一份到com.szh包下:

java 实现国际化动态加载语言 java程序国际化_国际化_02

test_zh_CN.properties:

str1=\u665A\u5B89

主程序如下:

package com.szh;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;

public class TestI18n {

    @SuppressWarnings("rawtypes")
    public static void main(String[] args) throws Exception {
        Class cls = TestI18n.class;
        // 将message_zh_CN.properties放到TestI18n.java同级
        System.out.println("自定义位置:\n" + cls.getResource("message_zh_CN.properties"));
        InputStream is = null;
        try {
            is = cls.getResourceAsStream("message_zh_CN.properties");
            // PropertyResourceBundle继承于ResourceBundle
            ResourceBundle rb = new PropertyResourceBundle(new InputStreamReader(is, "UTF-8"));
            String str = rb.getString("str1");
            System.out.println(str);
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:

自定义位置:
file:/E:/J2SE_workspace/TestI18n/bin/com/szh/message_zh_CN.properties
晚安

通过查看Kettle国际化部分的源码,其思想同上,即通过传入不同的class参数,来对应找到不同位置的国际化文件。

另附,Kettle8.1,Spoon设计器界面公共部分(除转换、作业组件自身)需要汉化的整理文档: