Java 国际化

 

 i18n在Java Web中的基本使用和配置

见:http://www.cnblogs.com/xdp-gacl/p/3945800.html

 

SpringBoot中的i18n

SpringBoot项目默认会进行i18n的自动配置,因此在class path下增加messages.properties、messages_en.properties等文件即可。

注:不带语言串的文件是fallback方案,即——会根据指定的语言(如en)从对应的文件(如messages_en.properties)中获取指定key对应的值,若 不存在与指定语言对应的文件 或 对应的文件中找不到指定key对应的值,则会从messages.properties中找,若仍找不到则报错。

当然,也可以他通过修改配置来修改默认行为。有两种方式:

通过配置文件修改,相关配置示例如下:

spring:
  messages:
    basename: static/i18n/messages, web_base/i18n/messages, validation/i18n/messages
    default-lang: zh_CHS  # non-built-in property
#    encoding: UTF-8
#    cache-seconds: 10
#    always-use-message-format: false
#    fallback-to-system-local: true
#    use-code-as-default-message: false
#    other config ...

通过代码修改——自定义一个MessageSource Bean,在Bean的内部逻辑中指定参数值。示例如下(如下代码实际上就是 MessageSourceAutoConfiguration 源码中的一部分):

 1     @Bean
 2     public MessageSource messageSource() {
 3         ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
 4         if (StringUtils.hasText(this.basename)) {            messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
 5                     StringUtils.trimAllWhitespace(this.basename)));
 6         }
 7         if (this.encoding != null) {
 8             messageSource.setDefaultEncoding(this.encoding.name());
 9         }
10         messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale);
11         messageSource.setCacheSeconds(this.cacheSeconds);
12         messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat);
13         return messageSource;
14     }
MessageSource Bean

 

内部实现:相关的类可参阅 MessageSourcePropertiesMessageSourceAutoConfiguration 等,从这些类的内容可以知道配置的默认值、对basename的解析规则等。

  1 package org.springframework.boot.autoconfigure.context;
  2 
  3 import java.nio.charset.Charset;
  4 import java.nio.charset.StandardCharsets;
  5 import java.time.Duration;
  6 import java.time.temporal.ChronoUnit;
  7 
  8 import org.springframework.boot.convert.DurationUnit;
  9 
 10 /**
 11  * Configuration properties for Message Source.
 12  *
 13  * @author Stephane Nicoll
 14  * @author Kedar Joshi
 15  * @since 2.0.0
 16  */
 17 public class MessageSourceProperties {
 18 
 19     /**
 20      * Comma-separated list of basenames (essentially a fully-qualified classpath
 21      * location), each following the ResourceBundle convention with relaxed support for
 22      * slash based locations. If it doesn't contain a package qualifier (such as
 23      * "org.mypackage"), it will be resolved from the classpath root.
 24      */
 25     private String basename = "messages";
 26 
 27     /**
 28      * Message bundles encoding.
 29      */
 30     private Charset encoding = StandardCharsets.UTF_8;
 31 
 32     /**
 33      * Loaded resource bundle files cache duration. When not set, bundles are cached
 34      * forever. If a duration suffix is not specified, seconds will be used.
 35      */
 36     @DurationUnit(ChronoUnit.SECONDS)
 37     private Duration cacheDuration;
 38 
 39     /**
 40      * Whether to fall back to the system Locale if no files for a specific Locale have
 41      * been found. if this is turned off, the only fallback will be the default file (e.g.
 42      * "messages.properties" for basename "messages").
 43      */
 44     private boolean fallbackToSystemLocale = true;
 45 
 46     /**
 47      * Whether to always apply the MessageFormat rules, parsing even messages without
 48      * arguments.
 49      */
 50     private boolean alwaysUseMessageFormat = false;
 51 
 52     /**
 53      * Whether to use the message code as the default message instead of throwing a
 54      * "NoSuchMessageException". Recommended during development only.
 55      */
 56     private boolean useCodeAsDefaultMessage = false;
 57 
 58     public String getBasename() {
 59         return this.basename;
 60     }
 61 
 62     public void setBasename(String basename) {
 63         this.basename = basename;
 64     }
 65 
 66     public Charset getEncoding() {
 67         return this.encoding;
 68     }
 69 
 70     public void setEncoding(Charset encoding) {
 71         this.encoding = encoding;
 72     }
 73 
 74     public Duration getCacheDuration() {
 75         return this.cacheDuration;
 76     }
 77 
 78     public void setCacheDuration(Duration cacheDuration) {
 79         this.cacheDuration = cacheDuration;
 80     }
 81 
 82     public boolean isFallbackToSystemLocale() {
 83         return this.fallbackToSystemLocale;
 84     }
 85 
 86     public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
 87         this.fallbackToSystemLocale = fallbackToSystemLocale;
 88     }
 89 
 90     public boolean isAlwaysUseMessageFormat() {
 91         return this.alwaysUseMessageFormat;
 92     }
 93 
 94     public void setAlwaysUseMessageFormat(boolean alwaysUseMessageFormat) {
 95         this.alwaysUseMessageFormat = alwaysUseMessageFormat;
 96     }
 97 
 98     public boolean isUseCodeAsDefaultMessage() {
 99         return this.useCodeAsDefaultMessage;
100     }
101 
102     public void setUseCodeAsDefaultMessage(boolean useCodeAsDefaultMessage) {
103         this.useCodeAsDefaultMessage = useCodeAsDefaultMessage;
104     }
105 
106 }
MessageSourceProperties
  1 package org.springframework.boot.autoconfigure.context;
  2 
  3 import java.time.Duration;
  4 
  5 import org.springframework.boot.autoconfigure.AutoConfigureOrder;
  6 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  7 import org.springframework.boot.autoconfigure.condition.ConditionMessage;
  8 import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
  9 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 10 import org.springframework.boot.autoconfigure.condition.SearchStrategy;
 11 import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
 12 import org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration.ResourceBundleCondition;
 13 import org.springframework.boot.context.properties.ConfigurationProperties;
 14 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 15 import org.springframework.context.MessageSource;
 16 import org.springframework.context.annotation.Bean;
 17 import org.springframework.context.annotation.ConditionContext;
 18 import org.springframework.context.annotation.Conditional;
 19 import org.springframework.context.annotation.Configuration;
 20 import org.springframework.context.support.ResourceBundleMessageSource;
 21 import org.springframework.core.Ordered;
 22 import org.springframework.core.io.Resource;
 23 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 24 import org.springframework.core.type.AnnotatedTypeMetadata;
 25 import org.springframework.util.ConcurrentReferenceHashMap;
 26 import org.springframework.util.StringUtils;
 27 
 28 /**
 29  * {@link EnableAutoConfiguration Auto-configuration} for {@link MessageSource}.
 30  *
 31  * @author Dave Syer
 32  * @author Phillip Webb
 33  * @author Eddú Meléndez
 34  */
 35 @Configuration
 36 @ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT)
 37 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
 38 @Conditional(ResourceBundleCondition.class)
 39 @EnableConfigurationProperties
 40 public class MessageSourceAutoConfiguration {
 41 
 42     private static final Resource[] NO_RESOURCES = {};
 43 
 44     @Bean
 45     @ConfigurationProperties(prefix = "spring.messages")
 46     public MessageSourceProperties messageSourceProperties() {
 47         return new MessageSourceProperties();
 48     }
 49 
 50     @Bean
 51     public MessageSource messageSource(MessageSourceProperties properties) {
 52         ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
 53         if (StringUtils.hasText(properties.getBasename())) {
 54             messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
 55                     StringUtils.trimAllWhitespace(properties.getBasename())));
 56         }
 57         if (properties.getEncoding() != null) {
 58             messageSource.setDefaultEncoding(properties.getEncoding().name());
 59         }
 60         messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
 61         Duration cacheDuration = properties.getCacheDuration();
 62         if (cacheDuration != null) {
 63             messageSource.setCacheMillis(cacheDuration.toMillis());
 64         }
 65         messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
 66         messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
 67         return messageSource;
 68     }
 69 
 70     protected static class ResourceBundleCondition extends SpringBootCondition {
 71 
 72         private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<>();
 73 
 74         @Override
 75         public ConditionOutcome getMatchOutcome(ConditionContext context,
 76                 AnnotatedTypeMetadata metadata) {
 77             String basename = context.getEnvironment()
 78                     .getProperty("spring.messages.basename", "messages");
 79             ConditionOutcome outcome = cache.get(basename);
 80             if (outcome == null) {
 81                 outcome = getMatchOutcomeForBasename(context, basename);
 82                 cache.put(basename, outcome);
 83             }
 84             return outcome;
 85         }
 86 
 87         private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context,
 88                 String basename) {
 89             ConditionMessage.Builder message = ConditionMessage
 90                     .forCondition("ResourceBundle");
 91             for (String name : StringUtils.commaDelimitedListToStringArray(
 92                     StringUtils.trimAllWhitespace(basename))) {
 93                 for (Resource resource : getResources(context.getClassLoader(), name)) {
 94                     if (resource.exists()) {
 95                         return ConditionOutcome
 96                                 .match(message.found("bundle").items(resource));
 97                     }
 98                 }
 99             }
100             return ConditionOutcome.noMatch(
101                     message.didNotFind("bundle with basename " + basename).atAll());
102         }
103 
104         private Resource[] getResources(ClassLoader classLoader, String name) {
105             String target = name.replace('.', '/');
106             try {
107                 return new PathMatchingResourcePatternResolver(classLoader)
108                         .getResources("classpath*:" + target + ".properties");
109             }
110             catch (Exception ex) {
111                 return NO_RESOURCES;
112             }
113         }
114 
115     }
116 
117 }
MessageSourceAutoConfiguration
  1 package org.springframework.boot.autoconfigure.context;
  2 
  3 import java.time.Duration;
  4 
  5 import org.springframework.boot.autoconfigure.AutoConfigureOrder;
  6 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  7 import org.springframework.boot.autoconfigure.condition.ConditionMessage;
  8 import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
  9 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 10 import org.springframework.boot.autoconfigure.condition.SearchStrategy;
 11 import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
 12 import org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration.ResourceBundleCondition;
 13 import org.springframework.boot.context.properties.ConfigurationProperties;
 14 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 15 import org.springframework.context.MessageSource;
 16 import org.springframework.context.annotation.Bean;
 17 import org.springframework.context.annotation.ConditionContext;
 18 import org.springframework.context.annotation.Conditional;
 19 import org.springframework.context.annotation.Configuration;
 20 import org.springframework.context.support.ResourceBundleMessageSource;
 21 import org.springframework.core.Ordered;
 22 import org.springframework.core.io.Resource;
 23 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 24 import org.springframework.core.type.AnnotatedTypeMetadata;
 25 import org.springframework.util.ConcurrentReferenceHashMap;
 26 import org.springframework.util.StringUtils;
 27 
 28 /**
 29  * {@link EnableAutoConfiguration Auto-configuration} for {@link MessageSource}.
 30  *
 31  * @author Dave Syer
 32  * @author Phillip Webb
 33  * @author Eddú Meléndez
 34  */
 35 @Configuration
 36 @ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT)
 37 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
 38 @Conditional(ResourceBundleCondition.class)
 39 @EnableConfigurationProperties
 40 public class MessageSourceAutoConfiguration {
 41 
 42     private static final Resource[] NO_RESOURCES = {};
 43 
 44     @Bean
 45     @ConfigurationProperties(prefix = "spring.messages")
 46     public MessageSourceProperties messageSourceProperties() {
 47         return new MessageSourceProperties();
 48     }
 49 
 50     @Bean
 51     public MessageSource messageSource(MessageSourceProperties properties) {
 52         ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
 53         if (StringUtils.hasText(properties.getBasename())) {
 54             messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
 55                     StringUtils.trimAllWhitespace(properties.getBasename())));
 56         }
 57         if (properties.getEncoding() != null) {
 58             messageSource.setDefaultEncoding(properties.getEncoding().name());
 59         }
 60         messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
 61         Duration cacheDuration = properties.getCacheDuration();
 62         if (cacheDuration != null) {
 63             messageSource.setCacheMillis(cacheDuration.toMillis());
 64         }
 65         messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
 66         messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
 67         return messageSource;
 68     }
 69 
 70     protected static class ResourceBundleCondition extends SpringBootCondition {
 71 
 72         private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<>();
 73 
 74         @Override
 75         public ConditionOutcome getMatchOutcome(ConditionContext context,
 76                 AnnotatedTypeMetadata metadata) {
 77             String basename = context.getEnvironment()
 78                     .getProperty("spring.messages.basename", "messages");
 79             ConditionOutcome outcome = cache.get(basename);
 80             if (outcome == null) {
 81                 outcome = getMatchOutcomeForBasename(context, basename);
 82                 cache.put(basename, outcome);
 83             }
 84             return outcome;
 85         }
 86 
 87         private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context,
 88                 String basename) {
 89             ConditionMessage.Builder message = ConditionMessage
 90                     .forCondition("ResourceBundle");
 91             for (String name : StringUtils.commaDelimitedListToStringArray(
 92                     StringUtils.trimAllWhitespace(basename))) {
 93                 for (Resource resource : getResources(context.getClassLoader(), name)) {
 94                     if (resource.exists()) {
 95                         return ConditionOutcome
 96                                 .match(message.found("bundle").items(resource));
 97                     }
 98                 }
 99             }
100             return ConditionOutcome.noMatch(
101                     message.didNotFind("bundle with basename " + basename).atAll());
102         }
103 
104         private Resource[] getResources(ClassLoader classLoader, String name) {
105             String target = name.replace('.', '/');
106             try {
107                 return new PathMatchingResourcePatternResolver(classLoader)
108                         .getResources("classpath*:" + target + ".properties");
109             }
110             catch (Exception ex) {
111                 return NO_RESOURCES;
112             }
113         }
114 
115     }
116 
117 }
MessageSourceAutoConfiguration

 

posted @ 2016-05-30 21:34  March On  阅读(281)  评论(0编辑  收藏  举报
top last
Welcome user from
(since 2020.6.1)