关于程序员:讲讲Java的国际化

6次阅读

共计 7014 个字符,预计需要花费 18 分钟才能阅读完成。

每个面向国内的语言都须要有本人的国际化解决方案,Java 从一开始就是用 Unicode 来解决所有的字符串,这让 Java 具备了国际化的一个重要个性。除了提供 Unicode 之外,Java 还须要解决不同区域的示意模式。

Locale

java.util 包中提供了 Locale 类用来解决不同的语言和区域上的国际化问题。应用 Locale 类能够定制一个区域的信息,而 Locale 类提供了如下构造方法来结构 Locale 对象。

public final class Locale implements Cloneable, Serializable {
    // 从语言,国家和变体结构语言环境
    public Locale(String language, String country, String variant) {···}
    // 从语言和国家结构语言环境
    public Locale(String language, String country) {···}
    // 从语言代码结构语言环境
    public Locale(String language) {···}
}

由构造方法可知,结构一个 Locale,能够设置语言、国家或地区、和变体。语言是由 2 个或 3 个小写字母示意,例如 zhende。这里能够查看 ISO-639-1 语言代码标识。

Locale 中预约义了大量的 Locale 语言,他们只设定了语言而没有设定国家,如下所示。

// 英语
public static final Locale ENGLISH = createConstant("en", "");
// 法语
public static final Locale FRENCH = createConstant("fr", "");
// 德语
public static final Locale GERMAN = createConstant("de", "");
// 意大利语
public static final Locale ITALIAN = createConstant("it", "");
// 日语
public static final Locale JAPANESE = createConstant("ja", "");
// 韩语
public static final Locale KOREAN = createConstant("ko", "");
// 汉语
public static final Locale CHINESE = createConstant("zh", "");
// 简体中文
public static final Locale SIMPLIFIED_CHINESE = createConstant("zh", "CN");
// 繁体中文
public static final Locale TRADITIONAL_CHINESE = createConstant("zh", "TW");

国家和地区也是由 2 个大小字母或 3 个数字示意,例如 CNUSDE。这里能够查看 ISO-3166-1 中的国家或地区标识。

Locale 中也为各个国家预约义了 Locale 对象,如下所示。

// 法国
public static final Locale FRANCE = createConstant("fr", "FR");
// 德国
public static final Locale GERMANY = createConstant("de", "DE");
// 意大利
public static final Locale ITALY = createConstant("it", "IT");
// 日本
public static final Locale JAPAN = createConstant("ja", "JP");
// 韩国
public static final Locale KOREA = createConstant("ko", "KR");
// 中国
    public static final Locale CHINA = SIMPLIFIED_CHINESE;
// 中华人名共和国
public static final Locale PRC = SIMPLIFIED_CHINESE;
// 台湾
public static final Locale TAIWAN = TRADITIONAL_CHINESE;
// 英国
public static final Locale UK = createConstant("en", "GB");
// 美国
public static final Locale US = createConstant("en", "US");
// 加拿大
public static final Locale CANADA = createConstant("en", "CA");
// 法国裔加拿大
public static final Locale CANADA_FRENCH = createConstant("fr", "CA");
// 示意根语言环境的常量
public static final Locale ROOT = createConstant("","");

变体,用于指定各种杂项个性,如果有多个变量值,每个变量值都批示其本人的语义,则这些值应按重要性排序,并用下划线分隔。变体字段辨别大小写。

Locale 除了构造方法能够结构 Locale 对象,Java SE 7 还提供了 forLanguageTag 静态方法来构建 Locale 对象,如下所示:

Locale zhChinese = Locale.forLanguageTag("zh-CN");
Locale twChinese = Locale.forLanguageTag("zh-TW");
System.out.println(zhChinese.getDisplayName());
System.out.println(twChinese.getDisplayName());
// 中文(中国)// 中文(台湾)

LocalesetDefault 办法能够将更改默认地区,并应用 getDefault 办法获取以后的 Locale 对象,如下所示:

System.out.println(Locale.getDefault());
Locale.setDefault(Locale.US);
System.out.println(Locale.getDefault());
// zh_CN
// en-US

Locale 中提供的 getAvailableLocale 静态方法会返回由 JVM 所可能辨认的所有 Locale 形成的数组。

ResourceBundle

应用 Locale 设置具体的语言和区域后,就能够应用 ResourceBundle 依据不同的 Locale 对资源进行加载。资源须要放在 resources 资源包中,并依据不同 Locale 设置对应的资源文件。如下所示,通过 ResourceBundle 类提供的 getBundle 静态方法,来依据 locale 主动绑定对应的资源文件。

Locale locale = Locale.CHINA;
ResourceBundle bundle = ResourceBundle.getBundle("i18n", locale);

查找一个具体的字符串,能够调用

String language = bundle.getString("language");

这里的资源文件的名称应用对立的命名规定,而后依据不同的区域,要标识其本地信息的附加局部。例如,一个资源包的命名是 i18n,则与中文、英文环境绝对应的资源如下图所示。

这里能够把默认资源放在一个没有后缀 i18n 文件中,而后 getBundle 办法定位 i18n_zh_CN 时,还会持续查找文件 i18n_zh 和默认文件 i18n 这两个文件,如果这些文件存在,它们在资源档次中会成为 i18n_zh_CN 的父文件。当前,当查找一个资源时,如果以后资源文件中没有找到,就会去查找其父资源文件。

后缀名为 .properties 的文件中,属性是采纳 = 分隔的键值对的模式,如下所示:

language= 中文
color= 红色 

ResourceBundle 是一个抽象类,如下所示。

public abstract class ResourceBundle {...}

继承了 ResourceBundle 的类有具体的实现类 PropertyResourceBundle 和抽象类 ListResourceBundle。而 PropertyResourceBundle 是用来反对 ResourceBundle 读取 properties 资源文件的具体子类,但不须要间接应用 PropertyResourceBundleResourceBundle.getBundle 会主动查找相应的属性文件,并创立一个援用它的 PropertyResourceBundle。然而 properties 资源文件提供的值只能是字符串。

如果想提供以外的资源,须要继承 ListResourceBundle 抽象类并把所有资源放到一个对象数组中并提供查找性能,如下所示。

public class I18N extends ListResourceBundle {
    @Override
    protected Object[][] getContents() {return new Object[][]{{"color", "yellow"}
        };
    }
}
public class I18N_zh extends ListResourceBundle {
    @Override
    protected Object[][] getContents() {return new Object[][]{{"language", new String[]{"简体中文", "繁体中文"}}
        };
    }
}
public class I18N_zh_CN extends ListResourceBundle {
    @Override
    protected Object[][] getContents() {return new Object[][]{{"language", "简体中文"},
                {"color", new String[]{"红色", "黄色", "彩色"} }
        };
    }
}

这里的类的命名规定也应用规范命名规定来命名。而后应用 getBundle 办法来加载对应的类:

ResourceBundle bundle = ResourceBundle.getBundle("I18N", Locale.forLanguageTag("zh-CN"));
System.out.println(bundle.getString("language"));
System.out.println(Arrays.toString(bundle.getStringArray("color")));

除此之外,资源包类也能够继承 ResourceBundle 类进行扩大,然而须要实现两个办法,一是枚举所有键,二是用给定的键查找相应的值:

Enumeration<String> getKeys();
Object handleGetObject(String key);

ResourceBundle 类的 getObject 办法会调用你提供的 handleGetObject 办法。

NumberFormat

java.text 包中的 NumberFormat 能够依据不同的 Locale 对数值进行格式化和解析。

应用 getNumberInstance 静态方法获取对数字进行格式化和解析的实例,而后将对应的数字进行格式化,看相应的 Locale 实例对应的格式化如何不同。

NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.SIMPLIFIED_CHINESE);
String result = numberFormat.format(2021.0731);
System.out.println(result);

后果是 2,021.073,更换成美国、德国等的后果别离是 2,021.0732.021,073。当数字值为货币的时候,应用 getCurrencyInstance 静态方法,如下所示。

NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(Locale.SIMPLIFIED_CHINESE);
String result = currencyFormat.format(2021.0731);
System.out.println(result);

后果是 ¥2,021.07,更换成美国、德国等的后果别离是 $2,021.072.021,07 €

解决货币时,能够应用 Currency 类来管制货币,能够通过 Currency.getInstance 静态方法传入一个货币标识并返回 Currency 对象,而后调用 NumberFormat 中的 setCurrency 办法。上面的例子是给德国用户设置人民币的格局,如下所示。

NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(Locale.SIMPLIFIED_CHINESE);
currencyFormat.setCurrency(Currency.getInstance("CNY"));
String result = currencyFormat.format(2021.0731);
System.out.println(result);    // 2.021,07 CN¥

货币标识符由 ISO-4217 定义,上面就简略的介绍几种货币标识符。

货币值 标识符 货币值 标识符
U.S.Dollar USD Chinese Renminbi(Yuan) CNY
Euro EUR Indian Rupee INR
British Pound GBP Russian Ruble RUB
Japanese Yen JPY

DateTimeFormatter

每个国家和地区对日期和工夫的显示都不可能雷同,Java SE 8 提供了 java.time.DateTimeFormatter 类来解决与 Locale 相干的日期和工夫,格式化和解析出合乎本地日期和工夫的示意办法。如下所示,应用 DateTimeFormatter 中的 withLocale 设置不同的 Locale

DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).withLocale(Locale.SIMPLIFIED_CHINESE);

之后,就能够格式化 LocaleDateLocaleDateTimeLocaleTimeZonedDateTime 了。

ZonedDateTime appointment = ZonedDateTime.now();
String formatted = formatter.format(appointment);

输入的后果是 2021 年 7 月 31 日星期六 ,更换成美国、德国的后果别离是 Saturday, July 31, 2021Samstag, 31. Juli 2021。输入一个地区的第一天时,能够通过 Locale 设置,如下所示。

DayOfWeek first = WeekFields.of(locale).getFirstDayOfWeek();

MessageFormat

一段音讯中也会依据不同的 Locale 进行变更的可变数据,Java 提供了 java.text.MessageFormat 来对音讯进行格式化。如下一段话:

String msg = "On {2}, {0} destroyed {1} houses and caused {3} of damage."

括号中的数字是占位符,能够通过 MessageFormat.format 静态方法应用理论的值来替换占位符。它是一个 varargs 办法,所以能够通过上面的办法提供参数:

String result = MessageFormat.format(msg, "a hurricane", 99, new GregorianCalendar(1999, 0, 1).getTime(), 10.0E8);

输入后果是:

On 1/1/99 12:00 AM, a hurricane destroyed 99 houses and caused 100,000,000 of damage.

下面的例子还能够为占位符提供可选的格局,将打印的内容变得更为精密。

On {2, date, long}, {0} destroyed {1} houses and caused {3, number, currency} of damage.

输入后果是:

On January 1, 1999, a hurricane destroyed 99 houses and caused $100,000,000 of damage.

一般来说,占位符索引前面能够跟一个 type 和一个 style,它们之间用逗号隔开。

MessageFormat.format 静态方法应用以后的 Locale 对值进行格式化。要想用任意的 Locale 进行格式化,须要为这个类提供能够应用的 varargs 办法。你须要把将要格式化的值置于 Object[] 数组中,如下所示。

MessageFormat mf = new MessageFormat(pattern, locale);
String msg = mf.format(new Object[] {values});

更多内容请关注公众号「海人为记」

正文完
 0