您好,登錄后才能下訂單哦!
這篇文章主要介紹“Spring Boot如何實現國際化”,在日常操作中,相信很多人在Spring Boot如何實現國際化問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Spring Boot如何實現國際化”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
在開發中,國際化(Internationalization),也叫本地化,指的是一個網站(或應用)可以支持多種不同的語言,即可以根據用戶所在的語言類型和國家/地區,顯示不同的文字。能夠讓不同國家,不同語種的用戶方便使用,提高用戶體驗性。
實現國際化,比較簡單的實現方案就是根據不同的國家和語言開發不同的程序,分別用相應的語言文字顯示,例如Oracle英文官網地址:https://www.oracle.com/index.html,中文官網地址:https://www.oracle.com/cn/index.html。
一般比較大型的公司會使用這種根據不同的國家和語言開發不同的程序的形式實現國家化,其一人家公司有資源投入開發,其二可以根據不同國家,不同語種用戶習慣開發更加符合當地人的布局樣式,交互等。
還有另外一種國家化實現方案,就是開發一套程序,可以根據用戶所在區域顯示不同的語言文字,但是網站/應用的布局樣式等不會發生很大變化。這個方案也是我們要將的i18n國際化實現,i18n其實就是英文單詞Internationalization(國際化)的縮寫,i和n代表單詞首尾字母,18代表中間的18個字母。
在Java中,通過java.util.Locale類表示本地化對象,它通過語言類型和國家/地區等元素來確定創建一個本地化對象 。Locale對象表示具體的地理,時區,語言,政治等。
我們可以通過以下方法,獲取本地系統的語言,國家等信息;以及獲取代表指定地區的語言,國家信息Local對象。當然你也可以調用 Locale.getAvailableLocales()
方法查看所有可用的Local對象。
package com.nobody; import java.util.Locale; /** * @Description * @Author Mr.nobody * @Date 2021/4/15 * @Version 1.0 */ public class LocalTest { public static void main(String[] args) { Locale defaultLocale = Locale.getDefault(); Locale chinaLocale = Locale.CHINA; Locale usLocale = Locale.US; Locale usLocale1 = new Locale("en", "US"); System.out.println(defaultLocale); System.out.println(defaultLocale.getLanguage()); System.out.println(defaultLocale.getCountry()); System.out.println(chinaLocale); System.out.println(usLocale); System.out.println(usLocale1); } } // 輸出結果 zh_CN zh CN zh_CN en_US en_US
我們一般會將不同的語言的屬性值存放在不同的配置文件中,ResourceBundle類可以根據指定的baseName和Local對象,就可以找到相應的配置文件,從而讀取到相應的語言文字,從而構建出ResourceBundle對象,然后我們可以通過ResourceBundle.getString(key)就可以取得key在不同地域的語言文字了。
Properties配置文件命名規則:baseName_local.properties
假如baseName為i18n,則相應的配置文件應該命名為如下:
中文的配置文件:i18n_zh_CN.properties
英文的配置文件:i18n_en_US.properties
然后在兩個配置文件中,存放著鍵值對,對應不同的語言文字
# 在i18n_zh_CN.properties文件中 userName=陳皮 # 在i18n_en_US.properties文件中 userName=Peel
我們通過如下方式,就可以獲取相應語言環境下的信息了,如下:
Locale chinaLocale = Locale.CHINA; ResourceBundle resourceBundle = ResourceBundle.getBundle("i18n", chinaLocale); String userName = resourceBundle.getString("userName"); System.out.println(userName); Locale usLocale = Locale.US; resourceBundle = ResourceBundle.getBundle("i18n", usLocale); userName = resourceBundle.getString("userName"); System.out.println(userName); // 輸出結果 陳皮 Peel
對于不同地域語言環境的用戶,我們是如何處理國際化呢?其實原理很簡單,假設客戶端發送一個請求到服務端,在請求頭中設置了鍵值對,“Accept-Language”:“zh-CN”,根據這個信息,可以構建出一個代表這個區域的本地化對象Locale,根據配置文件的baseName和Locale對象就可以知道讀取哪個配置文件的屬性,將要顯示的文字格式化處理,最終返回給客戶端進行顯示。
在Springboot中,我們會使用到一個MessageSource
接口,用于訪問國際化信息,此接口定義了幾個重載的方法。code即國際化資源的屬性名(鍵);args即傳遞給格式化字符串中占位符的運行時參數值;local即本地化對象;resolvable封裝了國際化資源屬性名,參數,默認信息等。
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale)
String getMessage(String code, @Nullable Object[] args, Locale locale)
String getMessage(MessageSourceResolvable resolvable, Locale locale)
Springboot提供了國際化信息自動配置類MessageSourceAutoConfiguration,它可以生成MessageSource接口的實現類ResourceBundleMessageSource,注入到Spring容器中。MessageSource配置生效依靠ResourceBundleCondition條件,從環境變量中讀取spring.messages.basename的值(默認值messages),這個值就是MessageSource對應的資源文件名稱,資源文件擴展名是.properties,然后通過PathMatchingResourcePatternResolver從classpath*
:目錄下讀取對應的資源文件,如果能正常讀取到資源文件,則加載配置類。源碼如下:
package org.springframework.boot.autoconfigure.context; @Configuration @ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Conditional(ResourceBundleCondition.class) @EnableConfigurationProperties public class MessageSourceAutoConfiguration { private static final Resource[] NO_RESOURCES = {}; // 我們可以在application.properties文件中修改spring.messages前綴的默認值,比如修改basename的值 @Bean @ConfigurationProperties(prefix = "spring.messages") public MessageSourceProperties messageSourceProperties() { return new MessageSourceProperties(); } // 生成ResourceBundleMessageSource實例,注入容器中 @Bean public MessageSource messageSource(MessageSourceProperties properties) { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); if (StringUtils.hasText(properties.getBasename())) { messageSource.setBasenames(StringUtils .commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename()))); } if (properties.getEncoding() != null) { messageSource.setDefaultEncoding(properties.getEncoding().name()); } messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale()); Duration cacheDuration = properties.getCacheDuration(); if (cacheDuration != null) { messageSource.setCacheMillis(cacheDuration.toMillis()); } messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat()); messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage()); return messageSource; } protected static class ResourceBundleCondition extends SpringBootCondition { private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<>(); @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages"); ConditionOutcome outcome = cache.get(basename); if (outcome == null) { outcome = getMatchOutcomeForBasename(context, basename); cache.put(basename, outcome); } return outcome; } private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context, String basename) { ConditionMessage.Builder message = ConditionMessage.forCondition("ResourceBundle"); for (String name : StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(basename))) { for (Resource resource : getResources(context.getClassLoader(), name)) { if (resource.exists()) { return ConditionOutcome.match(message.found("bundle").items(resource)); } } } return ConditionOutcome.noMatch(message.didNotFind("bundle with basename " + basename).atAll()); } // 讀取classpath*:路徑下的配置文件 private Resource[] getResources(ClassLoader classLoader, String name) { String target = name.replace('.', '/'); try { return new PathMatchingResourcePatternResolver(classLoader) .getResources("classpath*:" + target + ".properties"); } catch (Exception ex) { return NO_RESOURCES; } } } }
以下這個類是Spring國際化處理的屬性配置類,我們可以在application.properties文件中自定義修改這些默認值,例如:spring.messages.basename=i18n
。
package org.springframework.boot.autoconfigure.context; /** * Configuration properties for Message Source. * * @author Stephane Nicoll * @author Kedar Joshi * @since 2.0.0 */ public class MessageSourceProperties { /** * Comma-separated list of basenames (essentially a fully-qualified classpath * location), each following the ResourceBundle convention with relaxed support for * slash based locations. If it doesn't contain a package qualifier (such as * "org.mypackage"), it will be resolved from the classpath root. */ private String basename = "messages"; /** * Message bundles encoding. */ private Charset encoding = StandardCharsets.UTF_8; /** * Loaded resource bundle files cache duration. When not set, bundles are cached * forever. If a duration suffix is not specified, seconds will be used. */ @DurationUnit(ChronoUnit.SECONDS) private Duration cacheDuration; /** * Whether to fall back to the system Locale if no files for a specific Locale have * been found. if this is turned off, the only fallback will be the default file (e.g. * "messages.properties" for basename "messages"). */ private boolean fallbackToSystemLocale = true; /** * Whether to always apply the MessageFormat rules, parsing even messages without * arguments. */ private boolean alwaysUseMessageFormat = false; /** * Whether to use the message code as the default message instead of throwing a * "NoSuchMessageException". Recommended during development only. */ private boolean useCodeAsDefaultMessage = false; // 省略get/set }
我們在類路徑下創建好國際化配置文件之后,就可以注入MessageSource實例,進行國際化處理了:
i18n.properties文件是默認文件,當找不到語言的配置的時候,使用該文件進行展示。
@Autowired private MessageSource messageSource; @GetMapping("test") public GeneralResult<String> test() { // 獲取客戶端的語言環境Locale對象,即取的請求頭Accept-Language鍵的值來判斷,我們也可以自定義請求頭鍵,來獲取語言標識 Locale locale = LocaleContextHolder.getLocale(); String userName = messageSource.getMessage("userName", null, locale); System.out.println(userName); return GeneralResult.genSuccessResult(userName); }
上面我們是利用Spirng自帶的LocaleContextHolder來獲取本地對象Locale,它是取的請求頭Accept-Language鍵的語言值來判斷生成相應Locale對象。我們也可以根據其他方式,例如請求頭中自定義鍵的值,來生成Locale對象,然后再通過messageSource.getMessage()方法來實現最終的國家化。
到此,關于“Spring Boot如何實現國際化”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。