Java国际化主题大合集

本文详细介绍了Java中的国际化和本地化,包括Locale、ResourceBundle的使用,日期时间及字符串处理,以及Collator和编码转换。通过示例展示了如何进行日期格式化、数字和信息的格式化以及字符串的比较和排序,帮助开发者更好地实现多语言支持。
摘要由CSDN通过智能技术生成

Java提供给我们软件国际化的解决方案,这些国际化API基于Unicode标准,并且包括文本、(货币)数字、日期以及用户自定义对象的适配,从而使得软件能够应用到任何国家或地区。国际化英文为“Internationalization”,通常简写成i18n(实际开发经常使用简写)。更多有关Java国际化的内容可以参考Oracle的相关网站

文字国际化

Java语言基于Unicode字符集。Unicode是一种国际字符集标准,支持世界上所有主要文字以及常见技术符号。早期Unicode规定所有字符固定16bit宽(也就是UCS-2),但是Unicode标准中的字符早已超过16bit所能表示的范围,现规定的代码点范围在U+0000到U+10FFFF之间。Java标准使用的UTF-16定义的编码方式允许使用一个或两个16bit单位来表示所有的Unicode代码点。

Java基本数据类型char是一个无符号的16bit整数,可以表示U+0000到U+FFFF范围内的码点(BMP平面字符),同时也可作为UTF-16的码元,即32bit编码中的一半(两个char拼成一个字的辅助平面字符,但这种字符使用频率很低)。

更多有关字符编码的知识可以参考我的这篇文章

Java平台中表示字符序列的各种类型如:char[]、java.lang.CharSequence的实现类(如String类)以及java.text.CharacterIterator的实现类都是UTF-16序列。大多数Java源代码是使用ASCII(一种7bit字符编码方式)或ISO-8859-1(西欧8bit字符编码)或GBK(中国国标扩展编码)所编写的,但在处理之前都被转换为UTF-16编码。

Character类作为基本数据类型char的包装类,里面有很多确定字符属性的静态方法,如isLowerCaseisTitleCaseisDigit等,在Java5之前这些方法只有char作为参数,所以只接受U+0000到U+FFFF范围内的码点,Java5之后这些方法有了int类型作为参数的重载方法,这样就能表示所有Unicode代码点。

区域识别与本地化

1. Locale—地区性

Java平台将语言和地区分开定义,但均使用Locale类表示,Locale类通常代表特定地理位置,政权或文化区域。国际化的API中都有重载的方法要求提供一个Locale类的实例,而Locale类中有许多的静态常量用于表示常见的国家和地区的本地化对象。比如:Local.CHINA,Local.CANADA,Local.JAPEN

官网有Java支持的所有国家或地区语言的列表

2. ResourceBundle—国际化资源

通常我们的应用或系统不会将字符(串)等信息硬编码编译到Java字节码中,而是将这些信息以资源文件的形式存储在磁盘中,这是我们通常需要ResourceBundle这个类。

ResourceBundle是一个抽象类,代表着程序中所使用资源集合(字符串或图片路径等)。将资源和Java类一起打包,可以利用Java类加载机制来查找资源,ResourceBundle中包含特定区域的资源对象,当一个程序需要该区域的资源,程序可以从资源包中加载它,这样开发人员编写代码,可以不用关注特定区域的资源问题(资源文件可以单独维护)。

Java平台资源文件主要是properties格式

ResourceBundle中有几个重载的静态方法getBundle可以用来根据资源名(不包括区域信息的基本名称)来获取ResourceBundle对象,有ResourceBundle对象后即可通过getStringgetObject等方法传入Key即可获取对应的Value。

示例:

比如你有两个特定区域的资源:MyResource_en.propertiesMyResource_zh.properties

# properties文件内容
key=value

java代码获取资源文件内容

java
// Java会根据当前系统默认区域获取对应资源,如果没有则使用无区域信息的默认资源
ResourceBundle res = ResourceBundle.getBundle("MyResource");
// 通过key-value的方式取得资源内的信息
String value = res.getString("key");

ResourceBundle的子类

ResourceBundle

ResourceBundle有两个子类:ListResourceBundle和PropertyResourceBundle。

1、ListResourceBundle是一个抽象类,需要我们继承并实现Object[][] getContents()方法:

// 默认 (美国英语)
 public class MyResources extends ResourceBundle {
   
     public Object handleGetObject(String key) {
         if (key.equals("okKey")) return "Ok";
         if (key.equals("cancelKey")) return "Cancel";
         return null;
     }

     public Enumeration<String> getKeys() {
         return Collections.enumeration(keySet());
     }

     // 重写handleKeySet方法并在getKeys方法中调用keySet方法
     // keySet可以获取handleKeySet的返回值
     protected Set<String> handleKeySet() {
         return new HashSet<String>(Arrays.asList("okKey", "cancelKey"));
     }
 }

 // 中文
 public class MyResources_zh extends MyResources {
   
     public Object handleGetObject(String key) {
         // 对于不需要重写的内容可以从父类中获取
         if (key.equals("cancelKey")) return "取消";
         return null;
     }

     protected Set<String> handleKeySet() {
         return new HashSet<String>(Arrays.asList("cancelKey"));
     }
 }

可以看出JDK的sun包中有很多这种方式书写的资源:

ListResourceBundle

2、PropertyResourceBundle就是ResourceBundle通过get方法获取资源的形式,它使用的是Properties工具类获取.properties文件中的信息。

日期与时间的处理

java.util.Date类代表着毫秒精度的日期时间,该类有几个方法可以获取日期中的年、月、日、时、分、秒等信息。但是由于该类依赖于时区,与国际化不兼容,大部分方法已经被弃用,通常我们使用Calendar类来转换日期和时间,并通过DateFormat类来格式化或解析日期时间字符串。

Calendar

Calendar是一个抽象类,它用于一个整数的计算机纪元点时间转换成年、月、日、星期等信息。GregorianCalendar类是Calendar实现类,它根据格林尼治时间历法(公历)来实现。同时Calendar类提供了getInstance静态工厂方法来创建Calendar实例对象。

TimeZone

TimeZone是一个抽象类,封装了一个相对于GMT(格林尼治标准时间)的偏移值。TimeZone.getTimeZone工厂方法能通过指定zoneID来创建一个TimeZone实例对象。另外TimeZone还有一个SimpleTimeZone实现类可以创建TimeZone对象。

Calendar类及其子类会使用TimeZone进行本地时间与UTC(通用标准时间)之间的转换。

格式化

i18n中重要的一个部分就是文本格式化,这些功能主要在java.text包中:

Format继承图

主要分为三类:

  • DateFormat:日期格式化。根据区域不同使用不同的日期格式,可以自定义日期显示的格式。
  • MessageFormat:信息格式化。根据区域不同使用不同语言的字符显示信息,比如谷歌的网站对于不同的国家会以不同的语言显示网页信息。
  • NumberFormat:数字格式化。根据不同区域使用不同的数字格式,比如货币显示,西方国家会每隔三位加一个空格或逗号。

Format类主要有两个抽象方法需要子类去实现:

  • format方法用于将对象格式化成字符串。
  • parseObject方法用于将字符串按格式解析成对象。

DateFormat和NumberFormat这两个是抽象类,它们都有静态工厂方法getXxxInstance来获取该类的实例对象,同时它们也提供了子类可以实例化。

日期时间格式化

DateFormat

DateFormat有几个静态工厂方法getXxxInstance,DateFormat提供了几个标准的时间格式:

    /**
     * Constant for full style pattern.
     */
    public static final int FULL = 0;
    /**
     * Constant for long style pattern.
     */
    public static final int LONG = 1;
    /**
     * Constant for medium style pattern.
     */
    public static final int MEDIUM = 2;
    /**
     * Constant for short style pattern.
     */
    public static final int SHORT = 3;

默认时期格式为:

    /**
     * Constant for default style pattern.  Its value is MEDIUM.
     */
    public static final int DEFAULT = MEDIUM;

通过getXxxInstance方法可以传入这些常量设置日期或时间的格式,这几个getXxxInstance方法最终都辗转调用下面这个方法:

private static DateFormat get(int timeStyle, int dateStyle, int flags, Locale loc)

其中timeStyle代表时间的显示格式(时分秒部分),dateStyle代表日期的显示格式(年月日部分),这两个参数就是上面四个常量;flags参数有三种状态:1表示值显示时间部分,2表示只显示日期部分,3表示日期时间都显示;loc参数用于指定国家和地区,从而应对不同国家的时间格式。

DateFormat底层使用Calendar和TimeZone(Calendar内部)来解析时间,可以通过这两个属性的getter/setter方法来设置日期和时区。

有了DateFormat对象并设置了日期时间后即可调用它的format(格式化日期)或parse(解析日期)方法。

        DateFormat df = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG,
                Locale.CHINA);
        String timeString = df.format(System.currentTimeMillis());
        System.out.println(timeString);

        Date parse = df.parse(timeString);
        System.out.println(parse);
        System.out.println(parse.getTime());

输出结果:

2017年8月4日 上午11时04分31秒
Fri Aug 04 11:04:31 CST 2017
1501815871000

SimpleDateFormat

SimpleDateFormat是DateFormat的子类,可以由开发者自己定义解析格式。

示例:

        SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        String timeStr1 = df.format(System.currentTimeMillis());
        System.out.println(timeStr1);
        df.applyPattern("yyyy年MM月dd日-HH时mm分ss秒S毫秒");
        String timeStr2 = df.format(System.currentTimeMillis());
        System.out.println(timeStr2);

输出结果:

2017/08/04 11:03:23
2017年08月04日-11时03分23秒119毫秒

官方API文档中给出了SimpleDateFormat的各种pattern的规则。

数字格式化

NumberFormat

NumberFormat和DateFormat类似,也提供了几个标准的数字格式:

    private static final int NUMBERSTYLE = 0;      // 默认数字风格
    private static final int CURRENCYSTYLE = 1;    // 货币风格
    private static final int PERCENTSTYLE = 2;     // 百分比风格
    private static final int SCIENTIFICSTYLE = 3;  // 科学计数风格
    private static final int INTEGERSTYLE = 4;     // 整数风格

我们不需要指定数字格式,NumberFormat已经为我们封装了这几个风格样式并提供给我们静态方法:

public final static NumberFormat getInstance();                        // NUMBERSTYLE
public final static NumberFormat getInstance(Locale inLocale);         // NUMBERSTYLE
public final static NumberFormat getNumberInstance();                  // NUMBERSTYLE
public final static NumberFormat getNumberInstance(Locale inLocale);   // NUMBERSTYLE
public final static NumberFormat getIntegerInstance();                 // INTEGERSTYLE
public final static NumberFormat getIntegerInstance(Locale inLocale);  // INTEGERSTYLE
public final static NumberFormat getCurrencyInstance();                // CURRENCYSTYLE
public final static NumberFormat getCurrencyInstance(Locale inLocale); // CURRENCYSTYLE
public final static NumberFormat getPercentInstance();                 // PERCENTSTYLE
public final static NumberFormat getPercentInstance(Locale inLocale);  // PERCENTSTYLE
/*public*/ final static NumberFormat getScientificInstance();          // SCIENTIFICSTYLE
/*public*/ static NumberFormat getScientificInstance(Locale inLocale); // SCIENTIFICSTYLE

示例:

        NumberFormat[] formats = {
                NumberFormat.getInstance(),
                NumberFormat.getNumberInstance(),
                NumberFormat.getIntegerInstance(),
                NumberFormat.getCurrencyInstance(),
                NumberFormat.getPercentInstance()
        };

        for (int i = 0; i < formats.length; i++) {
            System.out.println(formats[i].format(100L));
            System.out.println(formats[i].format(123.456F));
        }

输出结果:

100
123.456
100
123.456
100
123100.00123.46
10,000%
12,346%

DecimalFormat

该类允许我们自定义十进制数字的格式化。

示例:

        DecimalFormat format;
        format = new DecimalFormat("####,####,###0.0# 圆整");

        System.out.println(format.format(12345678.90F));
        System.out.println(format.format(-12345678.90F));

        format.applyPattern("0.0#####E0####");

        System.out.println(format.format(1234567894987654321.90F));
        System.out.println(format.format(12.34F));

        format.applyPattern("00.0#E0##");

        System.out.println(format.format(1234567894987654321.90F));
        System.out.println(format.format(12.34F));

运行结果:

1234,5679.0 圆整
-1234,5679.0 圆整
1.2345679396E18
1.2340000153E1
12.3457E17
12.34E0

信息格式化

所谓“信息”格式化,其实就是字符串格式化,可以通过MessageFormat这个类构造一个字符串显示给终端用户。不过在这之前要说说JDK1.0开始就已经存在的老牌API——java.util.Formatter

java.util.Formatter

我们知道String类中有一个静态方法String.format(String format, Object... args)和C语言中的sprintf函数极为相似,这个方法底层就是用了java.util.Formatter类来实现,而待会儿要讲的MessageFormat类功能远比这个类功能强大(指的是格式化字符串这方面),所以在这之前我们先来说说java.util.Formatter这个类。

我们知道C语言中有几个I/O流的函数:printfsprintffprintf(在C11标准中还提供了这几个方法的安全版本,防止溢出)。

java.util.Formatter类能实现类似于C语言中的这几个函数,只需要在构造方法中传入不同的输出流即可,下面列一个表把C语言中的函数与Java中的Formatter类的应用进行对比:

C语言函数 Java API 备注
printf(const char *format, ...) System.out.format(String format, Object ... args) System.out是一个PrintStream对象,该对象底层使用java.util.Formatter对象进行格式化输出。
sprintf( char *buffer, const char *format, ... ) String.format(String format, Object... args) String.format方法每次调用都会new一个Formatter对象。
fprintf( FILE *stream, const char *format, ... ) new Formatter(new File(filename)).format(String format, Object ... args) Formatter类可以直接封装输出流,你可以直接传入一个文件名或文件对象。

但是由于Java是面向对象的语言,format参数是Object类型,所以如果我们想要格式化输出自定义对象,则该对象必须实现java.util.Formattable接口,但实际上JDK中没有任何类实现了该接口,这样做还不如重载Object.toString方法。另外由于java中String是不可变的对象,所以String.format方法并没有像sprintf函数一样灵活性。

而相反MessageFormat类中有一个方法:


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值