國際化主要是引入了MessageSource,咱們簡單看下如何使用,以及其原理。java
1.1 設置資源文件
在 properties新建i18n目錄spring
新建message文件:緩存
messages.propertiesapp
error.title=Your request cannot be processed
messages_zh_CN.propertieside
error.title=您的請求沒法處理
1.2 配置
修改properties文件的目錄:在application.yml或者application.properties中配置 spring.message.basenamefetch
spring: application: name: test-worklog messages: basename: i18n/messages encoding: UTF-8
1.3 使用
引用自動註解的MessageSource,調用messageSource.getMessage
便可,注意,須要經過 LocaleContextHolder.getLocale()
獲取當前的地區。ui
@Autowired private MessageSource messageSource; /** * 國際化 * * @param result * @return */ public String getMessage(String result, Object[] params) { String message = ""; try { Locale locale = LocaleContextHolder.getLocale(); message = messageSource.getMessage(result, params, locale); } catch (Exception e) { LOGGER.error("parse message error! ", e); } return message; }
如何設置個性化的地區呢? forLanguageTag
便可this
Locale locale = Locale.forLanguageTag(user.getLangKey());
1.4 原理分析
MessageSourceAutoConfiguration
中,實現了autoconfigurl
@Configuration @ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Conditional(ResourceBundleCondition.class) @EnableConfigurationProperties @ConfigurationProperties(prefix = "spring.messages") public class MessageSourceAutoConfiguration {
該類一方面讀取配置文件,一方面建立了MessageSource的實例:spa
@Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); if (StringUtils.hasText(this.basename)) { messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray( StringUtils.trimAllWhitespace(this.basename))); } if (this.encoding != null) { messageSource.setDefaultEncoding(this.encoding.name()); } messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale); messageSource.setCacheSeconds(this.cacheSeconds); messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat); return messageSource; }
所以,默認是加載的ResourceBundleMessageSource
,該類派生與於AbstractResourceBasedMessageSource
@Override public final String getMessage(String code, Object[] args, String defaultMessage, Locale locale) { String msg = getMessageInternal(code, args, locale); if (msg != null) { return msg; } if (defaultMessage == null) { String fallback = getDefaultMessage(code); if (fallback != null) { return fallback; } } return renderDefaultMessage(defaultMessage, args, locale); }
最終是調用resolveCode來獲取message,經過ResourceBundle來獲取message
@Override protected MessageFormat resolveCode(String code, Locale locale) { // 遍歷語言文件路徑 Set<String> basenames = getBasenameSet(); for (String basename : basenames) { ResourceBundle bundle = getResourceBundle(basename, locale); if (bundle != null) { MessageFormat messageFormat = getMessageFormat(bundle, code, locale); if (messageFormat != null) { return messageFormat; } } } return null; } // 獲取ResourceBundle protected ResourceBundle getResourceBundle(String basename, Locale locale) { if (getCacheMillis() >= 0) { // Fresh ResourceBundle.getBundle call in order to let ResourceBundle // do its native caching, at the expense of more extensive lookup steps. return doGetBundle(basename, locale); } else { // Cache forever: prefer locale cache over repeated getBundle calls. synchronized (this.cachedResourceBundles) { Map<Locale, ResourceBundle> localeMap = this.cachedResourceBundles.get(basename); if (localeMap != null) { ResourceBundle bundle = localeMap.get(locale); if (bundle != null) { return bundle; } } try { ResourceBundle bundle = doGetBundle(basename, locale); if (localeMap == null) { localeMap = new HashMap<Locale, ResourceBundle>(); this.cachedResourceBundles.put(basename, localeMap); } localeMap.put(locale, bundle); return bundle; } catch (MissingResourceException ex) { if (logger.isWarnEnabled()) { logger.warn("ResourceBundle [" + basename + "] not found for MessageSource: " + ex.getMessage()); } // Assume bundle not found // -> do NOT throw the exception to allow for checking parent message source. return null; } } } } // ResourceBundle protected ResourceBundle doGetBundle(String basename, Locale locale) throws MissingResourceException { return ResourceBundle.getBundle(basename, locale, getBundleClassLoader(), new MessageSourceControl()); }
最後來看getMessageFormat:
/** * Return a MessageFormat for the given bundle and code, * fetching already generated MessageFormats from the cache. * @param bundle the ResourceBundle to work on * @param code the message code to retrieve * @param locale the Locale to use to build the MessageFormat * @return the resulting MessageFormat, or {@code null} if no message * defined for the given code * @throws MissingResourceException if thrown by the ResourceBundle */ protected MessageFormat getMessageFormat(ResourceBundle bundle, String code, Locale locale) throws MissingResourceException { synchronized (this.cachedBundleMessageFormats) { // 從緩存讀取 Map<String, Map<Locale, MessageFormat>> codeMap = this.cachedBundleMessageFormats.get(bundle); Map<Locale, MessageFormat> localeMap = null; if (codeMap != null) { localeMap = codeMap.get(code); if (localeMap != null) { MessageFormat result = localeMap.get(locale); if (result != null) { return result; } } } // 緩存miss,從bundle讀取 String msg = getStringOrNull(bundle, code); if (msg != null) { if (codeMap == null) { codeMap = new HashMap<String, Map<Locale, MessageFormat>>(); this.cachedBundleMessageFormats.put(bundle, codeMap); } if (localeMap == null) { localeMap = new HashMap<Locale, MessageFormat>(); codeMap.put(code, localeMap); } MessageFormat result = createMessageFormat(msg, locale); localeMap.put(locale, result); return result; } return null; } }
做者:Jadepeng 出處:jqpeng的技術記事本--http://www.cnblogs.com/xiaoqi 您的支持是對博主最大的鼓勵,感謝您的認真閱讀。 本文版權歸做者全部,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。