<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          幾行SpringBoot代碼輕松實現(xiàn)國際化

          共 15423字,需瀏覽 31分鐘

           ·

          2021-05-01 21:19

          你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

          成功路上并不擁擠,因為堅持的人不多。

          編輯:業(yè)余草

          blog.csdn.net/chenlixiao007

          推薦:https://www.xttblog.com/?p=5182

          i18n 國際化

          在開發(fā)中,國際化(Internationalization),也叫本地化,指的是一個網(wǎng)站(或應(yīng)用)可以支持多種不同的語言,即可以根據(jù)用戶所在的語言類型和國家/地區(qū),顯示不同的文字。能夠讓不同國家,不同語種的用戶方便使用,提高用戶體驗性。

          實現(xiàn)國際化,比較簡單的實現(xiàn)方案就是根據(jù)不同的國家和語言開發(fā)不同的程序,分別用相應(yīng)的語言文字顯示,例如Oracle英文官網(wǎng)地址:https://www.oracle.com/index.html,中文官網(wǎng)地址:https://www.oracle.com/cn/index.html。

          一般比較大型的公司會使用這種根據(jù)不同的國家和語言開發(fā)不同的程序的形式實現(xiàn)國家化,其一人家公司有資源投入開發(fā),其二可以根據(jù)不同國家,不同語種用戶習慣開發(fā)更加符合當?shù)厝说牟季謽邮剑换サ取?/p>

          還有另外一種國家化實現(xiàn)方案,就是開發(fā)一套程序,可以根據(jù)用戶所在區(qū)域顯示不同的語言文字,但是網(wǎng)站/應(yīng)用的布局樣式等不會發(fā)生很大變化。這個方案也是我們要將的i18n國際化實現(xiàn),i18n其實就是英文單詞Internationalization(國際化)的縮寫,i和n代表單詞首尾字母,18代表中間的18個字母。

          i18n 實現(xiàn)

          在Java中,通過java.util.Locale類表示本地化對象,它通過語言類型和國家/地區(qū)等元素來確定創(chuàng)建一個本地化對象 。Locale對象表示具體的地理,時區(qū),語言,政治等。

          我們可以通過以下方法,獲取本地系統(tǒng)的語言,國家等信息;以及獲取代表指定地區(qū)的語言,國家信息Local對象。當然你也可以調(diào)用 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);
              }
          }

          // 輸出結(jié)果
          zh_CN
          zh
          CN
          zh_CN
          en_US
          en_US

          我們一般會將不同的語言的屬性值存放在不同的配置文件中,ResourceBundle類可以根據(jù)指定的baseName和Local對象,就可以找到相應(yīng)的配置文件,從而讀取到相應(yīng)的語言文字,從而構(gòu)建出ResourceBundle對象,然后我們可以通過ResourceBundle.getString(key)就可以取得key在不同地域的語言文字了。

          Properties配置文件命名規(guī)則:baseName_local.properties

          假如baseName為i18n,則相應(yīng)的配置文件應(yīng)該命名為如下:

          • 中文的配置文件:i18n_zh_CN.properties
          • 英文的配置文件:i18n_en_US.properties

          然后在兩個配置文件中,存放著鍵值對,對應(yīng)不同的語言文字

          # 在i18n_zh_CN.properties文件中
          userName=陳皮

          # 在i18n_en_US.properties文件中
          userName=Peel

          我們通過如下方式,就可以獲取相應(yīng)語言環(huán)境下的信息了,如下:

          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);

          // 輸出結(jié)果
          陳皮
          Peel

          對于不同地域語言環(huán)境的用戶,我們是如何處理國際化呢?其實原理很簡單,假設(shè)客戶端發(fā)送一個請求到服務(wù)端,在請求頭中設(shè)置了鍵值對,“Accept-Language”:“zh-CN”,根據(jù)這個信息,可以構(gòu)建出一個代表這個區(qū)域的本地化對象Locale,根據(jù)配置文件的baseName和Locale對象就可以知道讀取哪個配置文件的屬性,將要顯示的文字格式化處理,最終返回給客戶端進行顯示。

          Springboot 集成 i18n

          在Springboot中,我們會使用到一個MessageSource接口,用于訪問國際化信息,此接口定義了幾個重載的方法。code即國際化資源的屬性名(鍵);args即傳遞給格式化字符串中占位符的運行時參數(shù)值;local即本地化對象;resolvable封裝了國際化資源屬性名,參數(shù),默認信息等。

          • 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接口的實現(xiàn)類ResourceBundleMessageSource,注入到Spring容器中。MessageSource配置生效依靠ResourceBundleCondition條件,從環(huán)境變量中讀取spring.messages.basename的值(默認值messages),這個值就是MessageSource對應(yīng)的資源文件名稱,資源文件擴展名是.properties,然后通過PathMatchingResourcePatternResolver從classpath*:目錄下讀取對應(yīng)的資源文件,如果能正常讀取到資源文件,則加載配置類。源碼如下:

          package org.springframework.boot.autoconfigure.context;

          @Configuration
          @ConditionalOnMissingBean(value = MessageSource.classsearch = 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
          }

          我們在類路徑下創(chuàng)建好國際化配置文件之后,就可以注入MessageSource實例,進行國際化處理了:

          i18n.properties文件是默認文件,當找不到語言的配置的時候,使用該文件進行展示。

          @Autowired
          private MessageSource messageSource;

          @GetMapping("test")
          public GeneralResult<String> test() {
              // 獲取客戶端的語言環(huán)境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鍵的語言值來判斷生成相應(yīng)Locale對象。我們也可以根據(jù)其他方式,例如請求頭中自定義鍵的值,來生成Locale對象,然后再通過messageSource.getMessage()方法來實現(xiàn)最終的國家化。

          瀏覽 130
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  毛片国产| 免费网站观看www在线观看 | 亚洲性图一区二区三区 | 黄色丁五月 | 成人国产精品秘 久久久 |