1)部署时通过环境变量确定国际化语言,部署完成后不能改变。适合场景:软件部署后,只服务于同一语言环境的用户,部署后不需要改变。如,在国内部署,部署为中文,访问者均使用中文环境;国外部署,访问者均使用英文环境。
2)通过HTTP请求中Url中的特定参数来标识国际化语言,所有的GET/POST等请求,均需要在Url中携带该特定参数。如,http://127.0.0.1:8080/?lang=en_US
3)通过HTTP请求中携带的Cookie、Header或Session信息中的特定参数标识国际化语言,只需要在请求发送时在Cookie、Header或Session中携带同一的特定参数即可。如,所有的请求发送时,均使用统一的Ajax请求接口,统一添加Cookie信息即可。
优缺点:
第1)种方案,方案最简单易行,不需要拦截每个HTTP请求,没有性能损耗。但是,使用场景有限,只适用于同一语言环境内运行,服务端部署后不能提供跨语言的服务。
第2)种方案,修改Url中的参数即可实现中英文的随意切换,比较灵活,也适合多个应用之间协同提供服务时语言环境的传递。比如,A应用通过URL跳转到B应用,A应用的语言环境就可以通过URL参数的方式带到B应用。但是Url中总要携带语言参数,显得冗余。
第3)种方案,修改HTTP请求中的Cookie等信息即可,是目前比较常用的一种方案。前端Ajax请求时Cookie中携带统一的语言类型字段即可,后端也只需要从Cookie中解析出语言环境即可。
resources文件夹下创建i18n文件夹,创建默认的属性文件messages.properties及标识中英文的其他两个属性文件messages_zh_CN.properties,messages_en_US.properties即可。
本文示例创建两组属性文件,因为不同业务的属性文件通常分开管理,避免资源文件后期膨胀过大。
i18n下创建login文件夹,其中创建login.properties、login_zh_CN.properties、login_en_US.properties三个属性文件;创建errorMessage文件夹,属性文件同login。
本文使用application.yml。
spring:
messages:
encoding: UTF-8
# MessageSourceAutoConfiguration.class中加载,classpath*:{basename}.properties
basename: i18n/login/login,i18n/errorMessage/errorMessage
此处配置错误,获取属性值时,会抛出org.springframework.context.NoSuchMessageException错,具体到No message found under code '你的键名' for locale 'zh_CN'.
具体问题解决,参考Spring Boot 国际化功能实现出现 No message found under code ‘xx’ for locale 'xx’问题处理
application.yml
spring:
profiles:
active: dev
application:
name: demo
log:
path: ../log
level: DEBUG
messages:
encoding: UTF-8
# MessageSourceAutoConfiguration.class中加载,classpath*:{basename}.properties
basename: i18n/login/login,i18n/errorMessage/errorMessage
server:
port: 10095
servlet:
context-path: /demo
logback.xml
${LOG_PATTERN}
UTF-8
${LOG_HOME}/${LOG_FILE}.log
${LOG_HOME}/${LOG_FILE}.%d{yyyy-MM-dd}.%i.log
60
100MB
${LOG_PATTERN}
UTF-8
${LOG_HOME}/${LOG_FILE}Error.log
${LOG_HOME}/${LOG_FILE}Error.%d{yyyy-MM-dd}.%i.log
60
100MB
${LOG_PATTERN}
UTF-8
ERROR
ACCEPT
DENY
注意:logback按照时间和大小切分日志,需要使用ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy,参考:
SpringBoot配置Logback 日志按时间和大小分割失效问题
此处只实现第2)/3)种方案,第1)种方案,直接初始化一个Bean从环境变量中读取并初始化语言环境即可。
配置默认的语言环境和语言环境变化的拦截器。拦截器解析Url中携带的lang=zh_CN或lang=en_US实现中英文的切换(Get/Post同样处理,均解析Url中的参数)。
package com.springboot.demo.web.config;
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.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import java.util.Locale;
@Configuration
public class I18nConfig implements WebMvcConfigurer {
/**
* 国际化,设置默认的语言为中文
*
* @return LocaleResolver
*/
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return slr;
}
/**
* 国际化,设置url为识别参数,请求的url形式为url?lang=zh_CN 或 url?lang=en_US
*
* @return LocaleChangeInterceptor
*/
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}
新建资源文件服务类
package com.springboot.demo.web.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.Locale;
@Slf4j
@Component
public class I18nService {
@Resource
private MessageSource messageSource;
/**
* 获取国际化资源
* @param key 国际化资源的key
* @return 国际化内容
*/
public String getMessage(String key) {
String defaultValue = "";
if (StringUtils.isEmpty(key)) {
return defaultValue;
}
Locale locale = LocaleContextHolder.getLocale();
if (locale == null) {
locale = Locale.SIMPLIFIED_CHINESE;
}
try {
return messageSource.getMessage(key, null, locale);
} catch (Exception ex) {
log.warn("No message value for key: {}, locale: {}", key, locale, ex);
}
return defaultValue;
}
}
使用时注入该Bean即可
package com.springboot.demo.web.controller;
import com.springboot.demo.web.model.ResultInfo;
import com.springboot.demo.web.service.I18nService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@Slf4j
public class LoginController {
private static final String LOGIN_KEY = "login";
private static final String LOGIN_FAILED_KEY = "login.failed";
@Resource
private I18nService i18NService;
@GetMapping("/login")
public ResultInfo login() {
log.info("login.");
ResultInfo resultInfo = new ResultInfo();
resultInfo.setMsg(i18NService.getMessage(LOGIN_KEY));
return resultInfo;
}
@GetMapping("/login/failed")
public ResultInfo loginFailed() {
log.info("login failed.");
ResultInfo resultInfo = new ResultInfo();
resultInfo.setMsg(i18NService.getMessage(LOGIN_FAILED_KEY));
return resultInfo;
}
}
Session、Header中携带语言标识,同样处理方式。
新建拦截器,设置中英文
package com.springboot.demo.web.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Locale;
@Component
@Slf4j
public class I18nInterceptor implements HandlerInterceptor {
private static final String COOKIE_NAME_LANG = "lang";
private static final String ENGLISH_LANG = "en";
private static final String ENGLISH_COUNTRY = "US";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
// 1、获取请求的Cookies
Cookie[] cookies = request.getCookies();
// 2、遍历Cookie 查看是否有目标Cookie
String lang = "";
if (cookies != null) {
for (Cookie cookie : cookies) {
if (COOKIE_NAME_LANG.equals(cookie.getName())) {
lang = cookie.getValue();
break;
}
}
}
// 3、设置到语言环境中
LocaleContextHolder.setLocale(getLocale(lang));
return true;
}
private Locale getLocale(String lang) {
// 默认中国大陆-简体中文 lang=zh country=CN
Locale defaultLocale = Locale.SIMPLIFIED_CHINESE;
if (StringUtils.isEmpty(lang)) {
return defaultLocale;
}
// 英语采用 en_US
if (lang.toLowerCase(Locale.ENGLISH).contains(ENGLISH_LANG)) {
return new Locale(ENGLISH_LANG, ENGLISH_COUNTRY);
}
return defaultLocale;
}
}
拦截器注册到spring容器中
package com.springboot.demo.web.config;
import com.springboot.demo.web.interceptor.I18nInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
@Configuration
public class I18nConfig implements WebMvcConfigurer {
@Resource
private I18nInterceptor i18nInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(i18nInterceptor);
}
}
资源文件服务类I18nService和使用方式同Url实现方式。