在JAVA开发或是Android开发中,项目代码可能会应用到不同的国家或地区。此时就需要根据不同国家设置不同的语言文字,这里会存在两个问题:

  • 如何确定项目代码当前运行的语言环境
  • 如何实现语种切换,资源文件和语言应如何匹配

这里看一下具体是怎么实现的。

使用Locale类定义语言环境

Locale类为java.util.Locale,使用此类可以定义用户使用的语言环境:

Locale(String language) // Construct a locale from a language code.
Locale(String language, String country) // Construct a locale from language and country.
Locale(String language, String country, String variant) // Construct a locale from language, country and variant.
static Locale getDefault() // Gets the current value of the default locale for this instance of the Java Virtual Machine.

这里看一下上面代码的结果:

import java.util.Locale;

public class Demo {
	public static void main(String[] args) throws Exception {
		Locale lo = Locale.getDefault();
		System.out.println(lo);
		
		lo = new Locale("en");
		System.out.println(lo);
		
		lo = Locale.CHINA;
		System.out.println(lo);
	}
}

执行结果为:

zh_CN
en
zh_CN

使用getDault可以获得当前语言环境中默认的Locale对象,同时也就获得了默认的语言环境,如zh_CN(语言标记_国家标记),其中zh表示语言为中文,CN表示国家为中国。而使用构造或者使用类中的属性也可以修改Locale实例化对象,也就修改了语言。

利用ResourceBundle读取资源文件

一般情况下,资源文件包含一些配置,大多以属性值形式存在,以key=value的形式进行保存,如此就可以在进行信息读取时根据key来获得value。

不过资源文件的文件名称需要以.properties作为文件后缀,而如果要读取该类文件,可以使用java.util.ResourceBundle类进行读取,该类方法主要有:

static final ResourceBundle getBundle(String baseName) // Gets a resource bundle using the specified base name, the default locale, and the caller module.
static final ResourceBundle getBundle(String baseName, Locale locale) // Gets a resource bundle using the specified base name and locale, and the caller module.
final String getString(String key) // Gets a string for the given key from this resource bundle or one of its parents.

这里定义一个资源文件:

// Country.properties
country = china
language = chinese
area = shanghai

实际调用为:

import java.util.ResourceBundle;

public class Demo {
	public static void main(String[] args) throws Exception {
		ResourceBundle rb = ResourceBundle.getBundle("Country");
		
		System.out.println(rb.getString("country"));
	}
}

执行结果为:

china

这里getBundle直接使用了Country作为参数,这里的Country是资源文件的名字。

同时资源文件必须保存在CLASSPATH目录下,也就是说资源文件可以保存在包中。在实际项目中,由于会存在多个资源文件,而不同包中的资源文件也可能同名,因此实际项目中需要用完整的包名作为参数,例如com.wood.demo.Country这种形式。同时资源文件的命名规范与类名称相同,每个单词的首字母必须大写。

同时资源文件可以通过占位符在进行动态内容设置,此时需要使用到java.text.MessageFormat类来实现。其具体方法为:

static String format(String pattern, Object... arguments) // Creates a MessageFormat with the given pattern and uses it to format the given arguments.

之前提到一个java.text.SimpleDateFormat类,而java.text.MessageFormat与其属于相同的包,实际上这两个类继承自相同的父类:

java.lang.Object
  java.text.Format
    java.text.DateFormat
      java.text.SimpleDateFormat

java.lang.Object
  java.text.Format
    java.text.MessageFormat

即两者都来自Format类,而java.text包中定义的类都是与国家化相关的类。

这里将上边的资源文件修改为:

country = china
language = chinese
// 0:辖区 1:地点
area = shanghai,{0},{1}

代码修改为:

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

public class Demo {
	public static void main(String[] args) throws Exception {
		ResourceBundle rb = ResourceBundle.getBundle("Country");
		
		System.out.println(MessageFormat.format(rb.getString("area"),"黄埔区","霞飞路76号"));
	}
}

执行结果为:

shanghai,黄埔区,霞飞路76号

上面就利用了MessageFormat进行了动态的资源属性读取。

多资源读取

既然Locale类可以设置语言环境,而ResourceBundle可以读取资源文件,那么这两个类结合就可以实现多资源文件的读取,这些资源文件可以根据不同的语言环境读取不同的资源文件。这样的话,在国际化项目中就可以进行资源识别和读取。

对于多个资源文件来说,其文件命名一般为“文件名称_语言编码_国家编码”。而之前提到读取资源文件只需要指明文件名称即可,具体的语言资源文件则是由程序自己区分的。

同时在进行资源文件定义存在两种:

  • 公共资源(不区分语言和国家,通用属性)
  • 具体的资源文件(区分语言和国家,独立属性)

既然存在这样的区分,那么在读取时的顺序为:

  • 首先读取具体的资源文件
  • 如果读取不到,就读取公共资源

这里先设置资源文件:

// Country_zh_CN.properties
country = china
language = chinese

// Country_en_US.properties
country = us
language = english

// Country.properties
country = xxx
language = xxxx

代码为:

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

public class Demo {
	public static void main(String[] args) throws Exception {
		Locale zh = new Locale("zh","CN");
		Locale en = new Locale("en","US");
		Locale id = new Locale("id","ID");
		
		ResourceBundle zhRB = ResourceBundle.getBundle("Country",zh);
		ResourceBundle enRB = ResourceBundle.getBundle("Country",en);
		ResourceBundle idRB = ResourceBundle.getBundle("Country",id);
		
		System.out.println(zhRB.getString("country"));
		System.out.println(enRB.getString("country"));
		System.out.println(idRB.getString("country"));
	}
}

执行结果为:

china
us
china

上面的代码中设置了三个Locale对象,但是实际项目中可以直接利用Locale的getDaulf方法获取默认Locale对象,这样的话就可以直接获取资源文件。或者修改Locale对象,获取别的资源文件。而在没有Locale对象对应的资源文件的话,就调用了当前默认语言环境中的资源文件。

但同时应该保持不同的资源文件结构是一致的,否则在统一的代码调用中会存在较大的风险。