Java国际化简单介绍

本文主要介绍国际化中比较重要的类LocaleResourceBundleMessageFormat

java.util.Locale

Locale表示地区,在国际化中是一个非常重要的类。

基本方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Locale;

public class LocaleTest {

public static void main(String[] args) {
Locale locale = Locale.getDefault();
System.out.println(locale); // zh_CN
System.out.println(locale.getCountry()); // CN
System.out.println(locale.getLanguage()); // zh
System.out.println(locale.getDisplayCountry()); // 中国
System.out.println(locale.getDisplayLanguage()); // 中文
}

}

可以看出Locale其实是由两部分组成,即{语言}_{地区}

获取

有多种方法获取Locale,常用的如下:

1
2
3
4
5
6
7
8
9
10
// 获取Java虚拟机默认的语言环境,可以通过setDefault(Locale newLocale)修改
Locale.getDefault();

// Locale类提供了一些静态对象,可以直接使用
Locale.CHINA;

// 通过构造方法创建
Locale(String language) // 通过语言创建
Locale(String language, String country) // 通过语言和地区创建
Locale(String language, String country, String variant) // 通过语言,地区,语言变体(可以理解成一种方言,见rfc5646<https://tools.ietf.org/html/rfc5646>)创建

示例

下面通过示例演示在不同Locale下日期和货币表现形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.Date;
import java.util.Locale;

public class LocaleTest {

public static void main(String[] args) {
Locale CNLocale = Locale.CHINA;
Locale USLocale = Locale.US;

// 货币表现形式
System.out.println(NumberFormat.getCurrencyInstance(CNLocale).format(1.2344)); // ¥1.23
System.out.println(NumberFormat.getCurrencyInstance(USLocale).format(1.2344)); // $1.23

// 时间表现形式
Date date = new Date();
System.out.println(DateFormat.getDateInstance(DateFormat.FULL, CNLocale).format(date)); // 2019年3月5日 星期二
System.out.println(DateFormat.getDateInstance(DateFormat.FULL, USLocale).format(date)); // Tuesday, March 5, 2019

}
}

java.util.ResourceBundle

ResourceBundle可以加载和读取不同语言包,展示不同语言文本。

语言资源文件命名

1
2
// 加载语言资源文件
ResourceBundle resourceBundle = ResourceBundle.getBundle("i18n/server", Locale.CHINESE);

上面的代码表示加载i18n文件夹下所有baseName为server的资源文件。资源文件命名规则如下:

摘自ResourceBundle.getBundle(String baseName, Locale locale, ClassLoader loader)方法注释

1
2
3
4
5
6
baseName + "_" + language + "_" + script + "_" + country + "_" + variant
baseName + "_" + language + "_" + script + "_" + country
baseName + "_" + language + "_" + script
baseName + "_" + language + "_" + country + "_" + variant
baseName + "_" + language + "_" + country
baseName + "_" + language

常用的资源文件命名如下:

1
2
baseName + "_" + language + "_" + country
baseName + "_" + language

如以下命名:

1
2
server_en
server_zh_TW

语言资源文件加载

假设默认Locale为Locale("en"),以常用的properties文件为例,有以下资源文件:

1
2
3
4
server.properties
server_zh.properties
server_zh_TW.properties
server_en.properties

调用方法ResourceBundle.getBundle(String baseName, Locale targetLocale)

1
2
3
如果 targetLocale = Locale("zh"),加载文件 server_zh.properties,server.properties;
如果 targetLocale = Locale("zh", "TW"),加载文件 server_zh_TW.properties,server_zh.properties,server.properties;
如果 targetLocale = Locale("fr"),加载文件 server_en.properties,server.properties;
1
2
//获取对应语言文本
ResourceBundle.getString(String key)

上面代码中,参数key即为资源文件中的键,当获取到第一个不为null的值时,返回结果

示例

新建资源文件

新建文件夹i18n,并在文件夹下新建国际化资源文件,结构如下:

1
2
3
4
5
6
7
8
- src/main/
- java
- resources
- i18n
- server_en.properties // 英语配置
- server_zh_TW.properties // 繁体中文配置
- server_zh.properties // 简体中文配置
- server.properties // 默认配置

在资源文件中写入如下内容:

1
2
3
4
5
6
7
8
9
10
11
# 在server_en.properties写入
say.hello=hello

# 在server_zh_TW.properties写入
say.hello=妳好

# 在server_zh.properties写入
say.hello=你好

# 在server.properties写入
say.hello=helloa

新建测试类

1
2
3
4
5
6
7
8
9
10
11
12
public class ResourceBundleTest {

public static void main(String[] args) {
// 设置默认地区为法语地区
Locale.setDefault(Locale.FRENCH);

System.out.println(ResourceBundle.getBundle("i18n/server", Locale.TRADITIONAL_CHINESE).getString("say.hello")); // "妳好" 繁体中文地区 -> server_zh_TW.properties
System.out.println(ResourceBundle.getBundle("i18n/server", Locale.CHINESE).getString("say.hello")); // "你好" 简体中文地区 -> server_zh.properties
System.out.println(ResourceBundle.getBundle("i18n/server", Locale.US).getString("say.hello")); // "hello" 美国地区 -> server_en.properties
System.out.println(ResourceBundle.getBundle("i18n/server", Locale.ITALY).getString("say.hello")); // "helloa" 意大利语地区 -> server.properties
}
}

java.text.MessageFormat

MessageFormat用于替换字符串(类似String.format(String format, Object... args)),生成不同语言环境下的文本。

1
2
3
4
5
6
7
8
public class MessageFormatTest {

public static void main(String[] args) {
String message = "this is {0}";
String value = MessageFormat.format(message, new Object[] { "Zs" });
System.out.println(value); // this is Zs
}
}

使用占位符

占位符索引从0开始,即非负整数。

摘自MessageFormat类注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
FormatElement:
{ ArgumentIndex }
{ ArgumentIndex , FormatType }
{ ArgumentIndex , FormatType , FormatStyle }

FormatType: 范围如下:
number:调用NumberFormat进行格式化
date:调用DateFormat进行格式化
time:调用DateFormat进行格式化
choice:调用ChoiceFormat进行格式化

FormatStyle: 范围如下:
short
medium
long
full
integer
currency
percent
SubformatPattern (子格式模式,如#.#)

如:

1
MessageFormat.format("this is {0,number,#.##}", 1.2444); // this is 1.24

使用单引号

单个单引号会被忽略,两个单引号显示为一个单引号

1
2
3
MessageFormat.format("{0} is 'a cat", "dd"); // dd is a cat
MessageFormat.format("{0} is 'a' cat", "dd"); // dd is a cat
MessageFormat.format("{0} is ''a'' cat", "dd"); // dd is 'a' cat

单个单引号会使其后面的占位符失效

1
2
3
MessageFormat.format("a b' {0} is", "c"); // a b {0} is
MessageFormat.format("a 'b' {0} is", "c"); // a b c is
MessageFormat.format("a 'b {0} i's", "c"); // a b {0} is

使用左花括号

单个左花括号不支持

1
2
3
MessageFormat.format("a { {0} i's", "c"); // IllegalArgumentException
MessageFormat.format("a {} {0} i's", "c"); // IllegalArgumentException
MessageFormat.format("a '{' {0} i's", "c"); // a { c is