Spring 国际化简介

ResourceBundle

Java 提供了ResourceBundle类,可以实现特定区域(语言环境)的资源加载。以便我们在编码时可以使用一个ResourceBundle而不用考虑特定的语言环境信息,程序自动通过当前的用户的语言环境信息来加载特定资源。
ResourceBundle的使用比较简单,如下面的代码。

public class ResourceBundleDemo {

    public static void main(String[] args) throws UnsupportedEncodingException {
        ResourceBundle rb = ResourceBundle.getBundle("test");
        System.out.println(rb.getString("key1"));
        rb = ResourceBundle.getBundle("test", Locale.GERMAN);
        System.out.println(rb.getString("key1"));
    }
}

ResourceBundle通过了getBundle 工厂方法创建的实例默认被缓存。如果ResourceBundle被缓存多次调用getBundle 方法返回同一个实例。getBundle 的客户端可以清除缓存、管理缓存的缓存时间。参考:clearCache方法、ResourceBundle.Control.getTimeToLive和ResourceBundle.Control.needsReload。

Java平台提供了ResourceBundle两个子类ListResourceBundle 和PropertyResourceBundle。

ResourceBundle工厂方法在加载资源的过程中需要ResourceBundle.Control提供必须要的信息。ResourceBundle.Control 提供包括:bundle名字、创建Bundle、资源寻找等信息。默认的ResourceBundle.Control 可以被覆盖。默认的ResourceBundle.Control实现了两种格式资源的加载,class 格式和properties 格式。例如下面的代码:

ResourceBundle  rb = ResourceBundle.getBundle(“MyResource”); 

那么会加载MyResource.class(及相关语言区域后缀的class),如果class找不到,会加载MyResource.properties(及相关语言区域后缀的properties)的相关文件(通过PropertyResourceBundle)。

MessageFomat

MessageFormat 提供了以与语言无关方式生成连接消息的方式。使用此方法构造向终端用户显示的消息。MessageFormat 本身不实现特定于语言环境的行为。特定于语言环境的行为是由所提供的模式和用于已插入参数的子格式来定义的。
MessageFormat 简单实例如下代码:

 int planet = 7;
 String event = "a disturbance in the Force";

 String result = MessageFormat.format(
     "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.",
     planet, new Date(), event);

MessageFormat具体使用以及pattern模式可以参考javadoc。
MessageFormat可以和ResourceBundle配合做到个不同语言环境的用户显示。

public static void main(String[] args) {
    Date d = new Date();
    MessageFormat format = new MessageFormat("今天是{0,date ,yyyy-MM-dd}");
    System.out.println(format.format(new Object[]{d}));

    format.applyPattern("today is {0,date, dd/MM/yy}");
    System.out.println(format.format(new Object[]{d}));

}

Spring MessageSource

Spring MessageSource接口,用来支持参数化和国际化消息。

public interface MessageSource {
    @Nullable
    String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
    String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;

    String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;

}

Spring 提供了两个开箱即用的MessageSource实现,ResourceBundleMessageSource 和ReloadableResourceBundleMessageSource。

在AbstractApplicationContext中的initMessageSource方法,检测有无配置名称为MESSAGE_SOURCE_BEAN_NAME(messageSource)的bean,如果没有配置则生成一个DelegatingMessageSource 进行注册。initMessageSource的源码如下:

/**
     * Initialize the MessageSource.
     * Use parent's if none defined in this context.
     */
    protected void initMessageSource() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
            this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
            // Make MessageSource aware of parent MessageSource.
            if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
                HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
                if (hms.getParentMessageSource() == null) {
                    // Only set parent context as parent MessageSource if no parent MessageSource
                    // registered already.
                    hms.setParentMessageSource(getInternalParentMessageSource());
                }
            }
            if (logger.isTraceEnabled()) {
                logger.trace("Using MessageSource [" + this.messageSource + "]");
            }
        }
        else {
            // Use empty MessageSource to be able to accept getMessage calls.
            DelegatingMessageSource dms = new DelegatingMessageSource();
            dms.setParentMessageSource(getInternalParentMessageSource());
            this.messageSource = dms;
            beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
            if (logger.isTraceEnabled()) {
                logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
            }
        }
    }

DelegatingMessageSource的默认代理MesageSource对象来源于getInternalParentMessageSource。要么是parent的mesageSource要么是,null。

    protected MessageSource getInternalParentMessageSource() {
        return (getParent() instanceof AbstractApplicationContext ?
                ((AbstractApplicationContext) getParent()).messageSource : getParent());
    }

AbstractApplicationContext也实现了MessageSource的接口,相关的实现都是通过委托内部的messageSource进行处理的。源码如下:

 @Override
public String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) {
   return getMessageSource().getMessage(code, args, defaultMessage, locale);
}

@Override
public String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException {
   return getMessageSource().getMessage(code, args, locale);
}

@Override
public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
   return getMessageSource().getMessage(resolvable, locale);
}

private MessageSource getMessageSource() throws IllegalStateException {
   if (this.messageSource == null) {
      throw new IllegalStateException("MessageSource not initialized - " +
            "call 'refresh' before accessing messages via the context: " + this);
   }
   return this.messageSource;
}

项目中,我们如果想使用国际化的内容,需要配置一个名字为“messageResource”的MessageResouce实例,可以使用spring开箱即用的现有实现(ResourceBundle 和 MessageFormat的合集)。

你可能感兴趣的:(spring)