文章目录

  • 一、什么是国际化?
  • 二、前端联合后端实现国际化。
  • 三、服务端国际化(Springboot内置国际化)


一、什么是国际化?

  • 国际化就是在不修改内部代码的情况下,根据不同语言及地区显示相应的语言界面。
  • i18n的由来→internationalization,数一下,首字母i和末位字母之间有18个字母。类似的命名还有很多,例如k8s。
  • Springboot已经对i18n国际化做了自动配置,自动配置类。

org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration

如果想自定义国际化,通过@Autowired配置使用MessageSource进行自定义。

二、前端联合后端实现国际化。

一图解释什么是国际化:

spring boot 后端 国际化 springboot国际化配置_properties文件

原理:由前端页面某个元素(中文/English)返回给后端一个标识,后端根据标识获取地域信息,Springboot框架内置的MessageSource会获取该标识(图中只有两种标识,因此只有两种语种)来进行语种转换。当然转换的前提是要提前通过messages.properties文件来配置相应语言。

  • 前端代码如下:(l='zh_CN’和l='en_US’就是标识)
<!DOCTYPE html>
  <html lang="en" xmlns:th="http://www.thymeleaf.org">
  	<head>
  		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  		<meta name="description" content="">
  		<meta name="author" content="">
  		<title>Signin Template for Bootstrap</title>
  		<!-- Bootstrap core CSS -->
  		<link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.2.1/css/bootstrap.css}" rel="stylesheet">
  		<!-- Custom styles for this template -->
  		<link href="asserts/css/signin.css" rel="stylesheet">
  	</head>
  
  	<body class="text-center">
  		<form class="form-signin" action="dashboard.html">
  			<img class="mb-4" src="asserts/img/bootstrap-solid.svg" alt="" width="72" height="72">
  			<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.btn}">Please sign in</h1>
  			<label class="sr-only">Username</label>
  			<input type="text" class="form-control" th:placeholder="#{login.username}" placeholder="Username" required="" autofocus="">
  			<label class="sr-only">Password</label>
  			<input type="password" class="form-control" th:placeholder="#{login.password}" placeholder="Password" required="">
  			<div class="checkbox mb-3">
  				<label>
            <input type="checkbox" value="remember-me"> [[#{login.remember}]]
          </label>
  			</div>
  			<button class="btn btn-lg btn-primary btn-block" th:text="#{login.sign}" type="submit">Sign in</button>
  			<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
  			<a class="btn btn-sm" th:href="@{/login.html(l='zh_CN')}">中文</a>
  			<a class="btn btn-sm" th:href="@{/login.html(l='en_US')}">English</a>
  		</form>
  	</body>
  </html>
  • 后端代码:
package com.example.bootstudy.component;
  
  import org.springframework.util.StringUtils;
  import org.springframework.web.servlet.LocaleResolver;
  
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import java.util.Locale;
  
  /**
   * ----------------------------------------------------------------
   * @description:   自定义Local地域的解析
   * @author: Create by Liu Wen at 2020-07-08 17:34
   * ----------------------------------------------------------------
   **/
  public class MyLocaleResolver implements LocaleResolver {
  
      @Override
      public Locale resolveLocale(HttpServletRequest httpServletRequest) {
          String l = httpServletRequest.getParameter("l");
          Locale locale = Locale.getDefault();
          if(!StringUtils.isEmpty(l)){
              String[] atts = l.split("_");
              locale = new Locale(atts[0],atts[1]);
          }
          return locale;
      }
  
      @Override
      public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
  
      }
  }
package com.example.bootstudy.config;

import com.example.bootstudy.component.MyLocaleResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * ----------------------------------------------------------------
 * @description:   将自定义的MyLocaleResolver配置到Springboot框架中
 * @author: Create by Liu Wen at 2020-07-08 17:34
 * ----------------------------------------------------------------
 **/
@Configuration
public class MymvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("login");
        registry.addViewController("/login.html").setViewName("login");
    }

    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }
}
  • login.properties文件配置
#login_en_US.properties文件
      
  login.btn=\u8BF7\u767B\u5F55
  login.password=\u5BC6\u7801
  login.remember=\u8BB0\u4F4F\u6211
  login.sign=\u767B\u5F55
  login.username=\u7528\u6237\u540D
      
  #login_zh_CN.properties文件
  login.btn=Please sign in
  login.password=Password
  login.remember=Remember me
  login.sign=Sign in
  login.username=Username
  • application.yml配置
spring:
    messages:
      basename: i18n/login

代码结构如图:

spring boot 后端 国际化 springboot国际化配置_Source_02

三、服务端国际化(Springboot内置国际化)

服务端的国际化一般用于信息提示。不集成前端。

可以直接使用默认的Springboot内置国际化,也可以自定义。代码如下:

  • messages.properties文件配置
#messages_zh_CN.properties文件
  MSGFM00100001=\u8282\u70B9(id:{0})\u4E0D\u5B58\u5728.
  MSGFM00100002=\u8282\u70B9(id:{0})\u4E0D\u662F\u6D41\u7A0B\uFF0C\u6240\u4EE5\u5E76\u6CA1\u6709\u6D41\u7A0B\u56FE.
  MSGFM00100003=\u9879\u76EE(id:{0})\u5DF2\u7ECF\u5B58\u5728\u8BE5\u8282\u70B9.
  MSGFM00100004=\u9879\u76EE(id:{0})\u7684\u6839\u8282\u70B9\u4E0D\u5B58\u5728.
  MSGFM00100005=\u8282\u70B9(id:{0})\u7684\u7236\u8282\u70B9\u4E0D\u5B58\u5728.
  
  #messages_en_US.properties文件
  MSGFM00100001=The node(id:{0}) does not exist.
  MSGFM00100002=The node(id:{0}) is not a "FLOW" node, so it can not have a graph.
  MSGFM00100003=The project(id:{0}) has already got a root node.
  MSGFM00100004=The root node of project(id:{0}) does not exist.
  MSGFM00100005=The parent node(id:{0}) does not exist.
  • 自定义MessageSource工具:
package com.sample.utils;
  
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.context.MessageSource;
  import org.springframework.context.i18n.LocaleContextHolder;
  import org.springframework.stereotype.Component;
  
  import java.util.Locale;
  
  /**
   * MessageSource工具
   * 依賴{@link MessageSource}
   *
   * @author Catscan
   * @date 2019-06-07
   */
  @Component
  public class MessageSourceUtil {
  
      @Autowired
      private MessageSource messageSource;
  
      public String getMessage(String code) {
          return getMessage(code, null);
      }
  
      public String getMessage(String code, Object[] args) {
          return getMessage(code, args, "");
      }
  
      public String getMessage(String code, Object[] args, String defaultMsg) {
          // 這裡可以使用一些比較trick的方法動態設定語言
          // 可以做全站全局、單個request,當然,方法需要根據不同情況修改
          Locale locale = LocaleContextHolder.getLocale();
          return messageSource.getMessage(code, args, defaultMsg, locale);
      }
  }
  • 测试内置的国际化与自定义的国际化
package com.sample.controller;
  
  import com.sample.utils.MessageSourceUtil;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.context.MessageSource;
  import org.springframework.http.ResponseEntity;
  import org.springframework.web.bind.annotation.RequestMapping;
  import org.springframework.web.bind.annotation.RestController;
  
  import java.util.HashMap;
  import java.util.Locale;
  import java.util.Map;
  
  /**
   * 測試用的Controller
   *
   * @author Catscan
   * @date 2019-06-07
   */
  @RestController
  @RequestMapping("/")
  public class MessageController {
      @Autowired
      private MessageSourceUtil messageSourceUtil;
  
      @Autowired
      private MessageSource messageSource;
  
      /**
       * 測試使用{@link MessageSource}
       *
       * @return {@link ResponseEntity}
       */
      @RequestMapping("/message")
      public ResponseEntity pingResource(){
          Map<String ,String> result = new HashMap<>(3);
  
          result.put("default",messageSource.getMessage("hello", null, "", null));
          result.put("en_US",messageSource.getMessage("hello", null, "", Locale.US));
          result.put("zh_CN",messageSource.getMessage("hello", null, "", Locale.SIMPLIFIED_CHINESE));
  
          return ResponseEntity.ok(result);
      }
  
  
      /**
       * 測試使用{@link MessageSourceUtil}
       *
       * @return {@link ResponseEntity}
       */
      @RequestMapping("/util")
      public ResponseEntity pingUtil(){
          return ResponseEntity.ok(messageSourceUtil.getMessage("hello"));
      }
  }

多语言环境是定义在java.util.Locale类中的,如:

...
static public final Locale SIMPLIFIED_CHINESE = createConstant("zh", "CN");
static public final Locale CHINA = SIMPLIFIED_CHINESE;
static public final Locale UK = createConstant("en", "GB");
static public final Locale US = createConstant("en", "US");
static public final Locale CANADA = createConstant("en", "CA");
...

分析:在MessageSource.getMessage(...)所有方法中,都有Locale参数,指定区域信息。
当调用时,要如果指定位置为:Locale.CHINA,这时ResourceBundelMessageSource会从messages_zh_CN.properties中寻找对应的键值。
当给出Locale参数值为null,空的区域信息;或者对应的properties文件中没有找到对应的键值对,那么ResourceBundelMessageSource默认会从messages.properties中寻找键,当都找不到的时候,会返回空字符串。