国际化就是在不修改内部代码的情况下,根据不同语言及地区显示相应的语言界面。
i18n的由来→internationalization,数一下,首字母i和末位字母之间有18个字母。类似的命名还有很多,例如k8s。
Springboot已经对i18n国际化做了自动配置,自动配置类。
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration
如果想自定义国际化,通过@Autowired
配置使用MessageSource
进行自定义。
一图解释什么是国际化:
原理:由前端页面某个元素(中文/English)返回给后端一个标识,后端根据标识获取地域信息,Springboot框架内置的MessageSource会获取该标识(图中只有两种标识,因此只有两种语种)来进行语种转换。当然转换的前提是要提前通过messages.properties文件来配置相应语言。
前端代码如下:(l='zh_CN’和l='en_US’就是标识)
<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 Bootstraptitle>
<link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.2.1/css/bootstrap.css}" rel="stylesheet">
<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 inh1>
<label class="sr-only">Usernamelabel>
<input type="text" class="form-control" th:placeholder="#{login.username}" placeholder="Username" required="" autofocus="">
<label class="sr-only">Passwordlabel>
<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 inbutton>
<p class="mt-5 mb-3 text-muted">© 2017-2018p>
<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')}">Englisha>
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
代码结构如图:
服务端的国际化一般用于信息提示。不集成前端。
可以直接使用默认的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中
寻找键,当都找不到的时候,会返回空字符串。