Java国际化工具详解,国际化工具实现
本工具具有很强的实用性,可以拷贝代码直接使用
1.MessageFormatter
这是一个字符串格式化工具,主要完成两个功能
1.对于字符串“ hello {0}, {1}“在转换过程中会将{0}替换为所传参数数组下标为0的参数值,{1}则替换为下标为1的参数值。好处是支持任意的下标值,而jdk自带的MessageFormatter下标值只能是0-9
调用方式MessageFormatter.formatter(str, params);
2.将字符串中${xx},替换为一个外部的值,比如默认的MessageFormatter遇到${xx}后会试图通过System.getProperty(xx)获取值,然后将${xx}替换为获取之后的值,外部值获取主要通过PropertySource来取,构造器可以传入PropertySource的实现类。
看代码:
/**
*
* @author zhaoming
*
*/
public class MessageFormatter {
private PropertySource source;
private static final char DELIM_START = '{';
private static final char DELIM_START_LEN = 1;
private static final char DELIM_END = '}';
private static final char DELIM_END_LEN = 1;
private static final char ESCAPE_CHAR = '\\';
private static final char REF_CHAR = '$';
public MessageFormatter(PropertySource source) {
if (source == null) {
source = new SystemPropertySource();
} else {
this.source = source;
}
}
public MessageFormatter() {
source = new SystemPropertySource();
}
public String format(String pattern) {
return this.format(pattern, null);
}
public String format(String pattern, Object[] args) {
return this.format(pattern, args, 0);
}
private String format(String pattern, Object[] args, int argIdx) {
try {
if (pattern == null) {
return pattern;
}
if (args == null) {
args = new Object[0];
}
StringBuilder sb = new StringBuilder(pattern.length() + 20);
int start = 0;
int sidx = -1;
int eidx = -1;
String argStr = "";
while (start < pattern.length() && (sidx = pattern.indexOf(DELIM_START, start)) >= 0) {
if (hasEscapeChar(pattern, sidx)) {
if (hasDoubleEscapeChar(pattern, sidx)) {
sb.append(pattern, start, sidx - 1);
} else {
sb.append(pattern, start, sidx - 1).append(DELIM_START);
start = sidx + DELIM_START_LEN;
continue;
}
} else {
sb.append(pattern, start, sidx);
}
eidx = pattern.indexOf(DELIM_END, sidx);
if (eidx == -1) {
sb.append(pattern, sidx, pattern.length());
start = pattern.length();
} else {
argStr = pattern.substring(sidx + DELIM_START_LEN, eidx).trim();
if (argStr.isEmpty() || isNumeric(argStr)) {
appendIndexParam(sb, getArgInt(argStr, argIdx++), args, argStr);
} else {
appendRefParam(sb, argIdx, args, argStr);
}
start = eidx + DELIM_END_LEN;
}
}
if (start < pattern.length()) {
sb.append(pattern, start, pattern.length());
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
}
return pattern;
}
private boolean isNumeric(String s) {
int len = s.length();
for (int i = 0; i < len; i++) {
if (!Character.isDigit(s.charAt(i))) {
return false;
}
}
return true;
}
private int getArgInt(String param, int v) {
try {
if ("".equals(param)) {
return v;
}
return Integer.parseInt(param);
} catch (NumberFormatException e) {
}
return -1;
}
private void appendRefParam(StringBuilder sb, int argIdx, Object[] args, String orgParam) {
int len = sb.length();
boolean isRef = true;
if (len == 0 || sb.charAt(len - 1) != REF_CHAR) {
isRef = false;
}
if (hasEscapeChar(sb, len - 1) && !hasDoubleEscapeChar(sb, len - 1)) {
isRef = false;
}
if (isRef) {
String v = source.getProperty(orgParam);
if (v != null) {
sb.setLength(len - 1);
sb.append(this.format(v, args, argIdx));
return;
}
}
sb.append(DELIM_START).append(orgParam).append(DELIM_END);
}
private void appendIndexParam(StringBuilder sb, int argIdx, Object[] args, String orgParam) {
if (argIdx < 0 || argIdx >= args.length) {
sb.append(DELIM_START).append(orgParam).append(DELIM_END);
} else {
deeplyAppendParam(sb, args[argIdx], new HashMap<Object[], Object>());
}
}
private void deeplyAppendParam (StringBuilder sb, Object o, Map<Object[], Object> seenMap) {
if (o == null) {
sb.append("null");
return;
}
if (!o.getClass().isArray()) {
appendToString(sb, o);
} else {
if (o.getClass() == Object[].class) {
objectArrayAppend(sb, (Object[]) o, seenMap);
} else {
int len = Array.getLength(o);
sb.append("[");
for (int i = 0; i < len; i++) {
appendToString(sb, Array.get(o, i));
if (i != len - 1) {
sb.append(", ");
}
}
sb.append(']');
}
}
}
private void objectArrayAppend(StringBuilder sb, Object[] a, Map<Object[], Object> seenMap) {
sb.append('[');
if (!seenMap.containsKey(a)) {
seenMap.put(a, null);
final int len = a.length;
for (int i = 0; i < len; i++) {
deeplyAppendParam(sb, a[i], seenMap);
if (i != len - 1)
sb.append(", ");
}
seenMap.remove(a);
} else {
sb.append("...");
}
sb.append(']');
}
private void appendToString(StringBuilder sb, Object o) {
try {
if(o == null) {
sb.append("null");
} else {
sb.append(o.toString());
}
} catch (Exception e) {
sb.append("[failed toString]");
}
}
private boolean hasEscapeChar(StringBuilder pattern, int idx) {
if (idx > 0 && pattern.charAt(idx - 1) == ESCAPE_CHAR) {
return true;
}
return false;
}
private boolean hasEscapeChar(String pattern, int idx) {
if (idx > 0 && pattern.charAt(idx - 1) == ESCAPE_CHAR) {
return true;
}
return false;
}
private boolean hasDoubleEscapeChar(StringBuilder pattern, int idx) {
if (idx >= 2 && pattern.charAt(idx - 2) == ESCAPE_CHAR) {
return true;
}
return false;
}
private boolean hasDoubleEscapeChar(String pattern, int idx) {
if (idx >= 2 && pattern.charAt(idx - 2) == ESCAPE_CHAR) {
return true;
}
return false;
}
class SystemPropertySource implements PropertySource {
@Override
public String getProperty(String key) {
try {
return System.getProperty(key);
} catch (Exception e) {
}
return null;
}
}
}
PropertySource接口声明:
public interface PropertySource {
public String getProperty(String key);
}
2.ResourceBundleManager国际化字符串查找工具类:
/**
*
* @author zhaoming
*
*/
public class ResourceBundleManager {
private Locale locale;
private ResourceBundle bundler;
private MessageFormatter mf = new MessageFormatter(new BundlePropertySource());
private static Map<String, Map<Locale, ResourceBundleManager>> mgts = Collections
.synchronizedMap(new HashMap<String, Map<Locale, ResourceBundleManager>>());
private ResourceBundleManager(String baseName, Locale locale) {
ResourceBundle bd = null;
try {
bd = ResourceBundle.getBundle(baseName, locale);
} catch (Exception e) {
LogUtil.getLogger().error(e.getMessage(), e);
try {
bd = ResourceBundle.getBundle(baseName, locale, Thread.currentThread().getContextClassLoader());
} catch (Exception e1) {
}
}
bundler = bd;
if (bundler != null) {
this.locale = bundler.getLocale();
if (Locale.ROOT.equals(this.locale)) {
locale = Locale.ENGLISH;
}
}
}
public static ResourceBundleManager getManager(String baseName, Locale locale) {
Map<Locale, ResourceBundleManager> map = mgts.get(baseName);
if (map == null) {
map = new HashMap<Locale, ResourceBundleManager>();
mgts.put(baseName, map);
}
ResourceBundleManager mgt = map.get(locale);
if (mgt == null) {
mgt = new ResourceBundleManager(baseName, locale);
map.put(locale, mgt);
}
return mgt;
}
private String getValue(String key) {
try {
if (key != null) {
if (bundler != null) {
return bundler.getString(key);
}
}
} catch (Exception e) {
}
return null;
}
public String getString(String key) {
String v = getValue(key);
if (v != null) {
return mf.format(v);
}
return key;
}
public String getString(String key, String defaultV) {
String v = getValue(key);
if (v != null) {
return mf.format(v);
}
return defaultV;
}
public String getString(String key, Object... params) {
try {
String v = getValue(key);
if (v != null) {
return mf.format(v, params);
}
} catch (Exception e) {
}
return key;
}
public String getString(String key, String defaultV, Object... params) {
try {
String v = getValue(key);
if (v != null) {
return mf.format(v, params);
} else {
return defaultV;
}
} catch (Exception e) {
}
return key;
}
class BundlePropertySource implements PropertySource {
@Override
public String getProperty(String key) {
try {
return getValue(key);
} catch (Exception e) {
}
return null;
}
}
}
直接使用ResourceBundleManager比较不方便,因为首先必须先调用ResourceBundleManager.getManager(baseName, locale),并传入资源文件基名称和Locale对象才能调用getString(String key)去获取相应资源文件的key的值。实际项目中我们一般会有一个默认的资源文件基名称,同时Locale对象一般也是通过浏览器访问,后端服务器自动为我们创建。因此引入最终的一个工具类,支持默认资源名称,和传递线程上下文的Locale对象。
3.I18nManager:
/**
*
* @author zhaoming
*
*/
public class I18nManager {
//这里是默认的资源文件名称,可以配死也可以读取配置文件动态获取
private final static String DEFAULT_BASE_NAME = "com.test.myresource";
private static ThreadLocal<Locale> local = new ThreadLocal<Locale>();
public static String getString(String key) {
ResourceBundleManager mgt = ResourceBundleManager.getManager(DEFAULT_BASE_NAME, getLocale());
return mgt.getString(key);
}
public static String getString(String key, String defaultV) {
ResourceBundleManager mgt = ResourceBundleManager.getManager(DEFAULT_BASE_NAME, getLocale());
return mgt.getString(key, defaultV);
}
public static String getString(String key, Object... params) {
ResourceBundleManager mgt = ResourceBundleManager.getManager(DEFAULT_BASE_NAME, getLocale());
return mgt.getString(key, params);
}
public static String getString(String key, String defaultV, Object... params) {
ResourceBundleManager mgt = ResourceBundleManager.getManager(DEFAULT_BASE_NAME, getLocale());
return mgt.getString(key, defaultV, params);
}
public static String getString(Locale locale, String key) {
if (locale == null) {
locale = getLocale();
}
ResourceBundleManager mgt = ResourceBundleManager.getManager(DEFAULT_BASE_NAME, locale);
return mgt.getString(key);
}
public static String getString(Locale locale, String key, String defaultV) {
if (locale == null) {
locale = getLocale();
}
ResourceBundleManager mgt = ResourceBundleManager.getManager(DEFAULT_BASE_NAME, locale);
return mgt.getString(key, defaultV);
}
public static String getString(Locale locale, String key, Object... params) {
if (locale == null) {
locale = getLocale();
}
ResourceBundleManager mgt = ResourceBundleManager.getManager(DEFAULT_BASE_NAME, locale);
return mgt.getString(key, params);
}
public static String getString(Locale locale, String key, String defaultV, Object... params) {
if (locale == null) {
locale = getLocale();
}
ResourceBundleManager mgt = ResourceBundleManager.getManager(DEFAULT_BASE_NAME, locale);
return mgt.getString(key, defaultV, params);
}
public static String getStringBy(String baseName, String key) {
if (baseName == null) {
baseName = DEFAULT_BASE_NAME;
}
ResourceBundleManager mgt = ResourceBundleManager.getManager(baseName, getLocale());
return mgt.getString(key);
}
public static String getStringBy(String baseName, String key, String defaultV) {
if (baseName == null) {
baseName = DEFAULT_BASE_NAME;
}
ResourceBundleManager mgt = ResourceBundleManager.getManager(baseName, getLocale());
return mgt.getString(key, defaultV);
}
public static String getStringBy(String baseName, String key, Object... params) {
if (baseName == null) {
baseName = DEFAULT_BASE_NAME;
}
ResourceBundleManager mgt = ResourceBundleManager.getManager(baseName, getLocale());
return mgt.getString(key, params);
}
public static String getStringBy(String baseName, String key, String defaultV, Object... params) {
if (baseName == null) {
baseName = DEFAULT_BASE_NAME;
}
ResourceBundleManager mgt = ResourceBundleManager.getManager(baseName, getLocale());
return mgt.getString(key, defaultV, params);
}
public static String getStringBy(Locale locale, String baseName, String key) {
if (locale == null) {
locale = getLocale();
}
if (baseName == null) {
baseName = DEFAULT_BASE_NAME;
}
ResourceBundleManager mgt = ResourceBundleManager.getManager(baseName, locale);
return mgt.getString(key);
}
public static String getStringBy(Locale locale, String baseName, String key, String defaultV) {
if (locale == null) {
locale = getLocale();
}
if (baseName == null) {
baseName = DEFAULT_BASE_NAME;
}
ResourceBundleManager mgt = ResourceBundleManager.getManager(baseName, locale);
return mgt.getString(key, defaultV);
}
public static String getStringBy(Locale locale, String baseName, String key, Object... params) {
if (locale == null) {
locale = getLocale();
}
if (baseName == null) {
baseName = DEFAULT_BASE_NAME;
}
ResourceBundleManager mgt = ResourceBundleManager.getManager(baseName, locale);
return mgt.getString(key, params);
}
public static String getStringBy(Locale locale, String baseName, String key, String defaultV, Object... params) {
if (locale == null) {
locale = getLocale();
}
if (baseName == null) {
baseName = DEFAULT_BASE_NAME;
}
ResourceBundleManager mgt = ResourceBundleManager.getManager(baseName, locale);
return mgt.getString(key, defaultV, params);
}
public static void setThreadLocale(Locale locale) {
if (locale != null) {
local.set(locale);
}
}
private static Locale getLocale() {
Locale l = local.get();
if (l != null) {
return l;
}
return Locale.getDefault();
}
}
4.使用:
以structs为例,在action构造器中使用如下代码设置线程上下文的Locale对象
I18nManager.setThreadLocale(this.getLocale());
String s = I18nManager.getString("a", "Tom", "Cat");//结果: hello Jerry my name is Tom Cat
资源文件内容:
a=hello ${b} my name is {0} {1}
b=Jerry