SpringBoot2.x —— 手动变更i18n语言

前言

在Spring中,官方文档中提供了一种国际化配置。

什么是国际化配置,我的感受是不同国家不同语言,针对不同国家(中国和外国),分别使用不同语言(汉语和英语)进行提示操作。

在SpringBoot中,针对国际化配置的实现,官方主要使用了一个类org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration.class来实现。

MessageSourceAutoConfiguration

源码中,对其有充分的说明。
SpringBoot2.x —— 手动变更i18n语言_第1张图片
application.properties配置文件中,以spring.messages开头的配置信息。

这个bean中的逻辑流程是什么呢?
SpringBoot2.x —— 手动变更i18n语言_第2张图片
默认返回一个org.springframework.boot.autoconfigure.context.MessageSourceProperties实例化对象信息,其中包含有basenameencoding等系列配置默认信息。

如何实现自动化的修改请求的语言种类,接下来采取修改本地org.springframework.web.servlet.LocaleResolver.resolveLocale(HttpServletRequest)的方式测试!

搭建测试项目

依赖引入

<parent>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-parentartifactId>
		<version>2.1.14.RELEASEversion>
	parent>

	<properties>
		<java.version>1.8java.version>
		<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
	properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starterartifactId>
		dependency>

		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>
		
		
		<dependency>
			<groupId>com.alibabagroupId>
			<artifactId>fastjsonartifactId>
			<version>1.2.44version>
		dependency>
		
		<dependency>
			<groupId>org.projectlombokgroupId>
			<artifactId>lombokartifactId>
			<version>1.18.6version>
			<scope>providedscope>
		dependency>

		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
			<scope>testscope>
		dependency>
	dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.bootgroupId>
				<artifactId>spring-boot-maven-pluginartifactId>
			plugin>
		plugins>
	build>

配置文件

server.port=80

spring.messages.basename=i18n/test/test

配置文件为了简化,暂时只配置了端口索引文件地址

测试接口

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
	
	@RequestMapping("/test1")
	public String test1() {
		return "66666";
	}
}

服务器是如何能主动区分出,客户端请求时,需要的语言信息的?

请求上述自定义接口,观察浏览器中的请求消息头信息。
SpringBoot2.x —— 手动变更i18n语言_第3张图片
测试使用的浏览器,默认的请求头语言为:Accept-Language: zh-CN,zh;q=0.9

由此可见,服务器通过请求头中的此项参数信息,获取客户端需要的国际化显示信息。


方式一

此项方法以定义数据获取处理工具类的形式。

修改测试请求接口,接受一个状态信息值,用来变更当前临时语言环境信息。

import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
	
	private Logger log = LoggerFactory.getLogger(TestController.class);
	
	private static final String PATH_PARAMETER_SPLIT = "_";
	
	@Autowired
	private MessageSource messageSource;
	
	public String getMessage(String key,HttpServletRequest request,String... strings ) {
		String language = request.getParameter("language");
		log.info("language:{}",language);
		//获取请求头默认的local对象
		Locale locale = request.getLocale();
		log.info("Add default.");
		if(!StringUtils.isEmpty(language)) {
			//按照指定的正则表达式,解析出相关的数据信息
			String[] split = language.split(PATH_PARAMETER_SPLIT);
			//解析出数据后,修改local对象
			locale = new Locale(split[0], split[1]);
			log.info("Add custom.");
		}
		return this.messageSource.getMessage(key, strings,locale);
	}
	
	/**
	 * en_US 
* zh_CN * @param request * @return */
@RequestMapping("/test1") public String test1(HttpServletRequest request) { //log.info("local:{}",Locale.getDefault().getLanguage()); return getMessage("test.name",request,null); } }

编写i18n文件信息

SpringBoot2.x —— 手动变更i18n语言_第4张图片
test_en_US.properties

test.name=bunana

test.properties(其实和test_zh_CN.properties是一样的)

test.name=香蕉

测试结果

SpringBoot2.x —— 手动变更i18n语言_第5张图片
在这里插入图片描述

代码下载

《码云代码下载地址》


方式二

采取配置文件的形式获取

编写一个配置文件,用户解析请求。

import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
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;

@Configuration
public class LocaleResolverConfig {
	private static Logger log = LoggerFactory.getLogger(LocaleResolverConfig.class);
	
	/**
	 * 解析器(只做数据的解析操作,但需要主动调用)
	 * @return
	 */
	@Bean
	public LocaleResolver localeResolver() {
		log.info("LocaleResolver success");
		return new LanguageLocaleResolverConfig();
	}
	
	/**
	 * 拦截器,针对请求过来时,
	 * @return
	 */
	@Bean
	public WebMvcConfigurer localeInterceptor() {
		return new WebMvcConfigurer() {
			@Override
			public void addInterceptors(InterceptorRegistry registry) {
				LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
				localeInterceptor.setParamName("l");
				//针对指定的请求做过滤操作、增加某些拦截操作
				registry.addInterceptor(localeInterceptor);
			}
		};
	}
}

class LanguageLocaleResolverConfig implements LocaleResolver {
	private static Logger log = LoggerFactory.getLogger(LanguageLocaleResolverConfig.class);

	private static final String PATH_PARAMETER_SPLIT = "_";

	@Override
	public Locale resolveLocale(HttpServletRequest request) {
		// 获取请求来的语言方式
		String language = request.getParameter("l");
		log.info("language  l:{}", language);
		// 获取请求头默认的local对象
		Locale locale = request.getLocale();
		log.info("Add default.");
		if (!StringUtils.isEmpty(language)) {
			// 按照指定的正则表达式,解析出相关的数据信息
			String[] split = language.split(PATH_PARAMETER_SPLIT);
			// 解析出数据后,修改local对象
			locale = new Locale(split[0], split[1]);
			log.info("Add custom.");
		}
		return locale;
	}

	@Override
	public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
		// TODO Auto-generated method stub

	}

}

思想:

增加请求拦截器。达到每次请求时,将指定属性值设置至上下文中。

请求测试类的编写

import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
	
	private Logger log = LoggerFactory.getLogger(TestController.class);
	
	private static final String PATH_PARAMETER_SPLIT = "_";
	
	@Autowired
	private MessageSource messageSource;
	
	public String getMessage(String key,HttpServletRequest request,String... strings ) {
//		String language = request.getParameter("language");
//		log.info("language:{}",language);
//		//获取请求头默认的local对象
//		Locale locale = request.getLocale();
//		//log.info("Add default.");
//		if(!StringUtils.isEmpty(language)) {
//			//按照指定的正则表达式,解析出相关的数据信息
//			String[] split = language.split(PATH_PARAMETER_SPLIT);
//			//解析出数据后,修改local对象
//			locale = new Locale(split[0], split[1]);
//			//log.info("Add custom.");
//		}
//		return this.messageSource.getMessage(key, strings,locale);
		return this.messageSource.getMessage(key, strings, LocaleContextHolder.getLocale());
	}
	
	/**
	 * en_US 
* zh_CN * @param request * @return */
@RequestMapping("/test1") public String test1(HttpServletRequest request) { //log.info("local:{}",Locale.getDefault().getLanguage()); return getMessage("test.name",request,null); } }

代码逻辑

首先观察拦截器做了什么操作。
SpringBoot2.x —— 手动变更i18n语言_第6张图片
在配置文件中,增加了一项拦截器。此项拦截器中的思路是什么?继续看源码
SpringBoot2.x —— 手动变更i18n语言_第7张图片
org.springframework.web.servlet.i18n.LocaleChangeInterceptor.LocaleChangeInterceptor()中,此项类的结构为:
在这里插入图片描述
和增加对指定请求拦截过滤操作方式类似!

paramName的默认值:SpringBoot2.x —— 手动变更i18n语言_第8张图片
设置 paranName 值:
SpringBoot2.x —— 手动变更i18n语言_第9张图片
这个类中还存在一个很重要的方法:

@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws ServletException {
		//获取请求对象中,指定 paranName 信息
		String newLocale = request.getParameter(getParamName());
		//当存在请求参数时
		if (newLocale != null) {
			if (checkHttpMethod(request.getMethod())) {
				//获取request请求域中的指定key对应的数据
				LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
				if (localeResolver == null) {
					throw new IllegalStateException(
							"No LocaleResolver found: not in a DispatcherServlet request?");
				}
				try {
					//将请求中指定的 paranName 值设置于 org.springframework.web.servlet.LocaleResolver 对象中。
					localeResolver.setLocale(request, response, parseLocaleValue(newLocale));
				}
				catch (IllegalArgumentException ex) {
					if (isIgnoreInvalidLocale()) {
						if (logger.isDebugEnabled()) {
							logger.debug("Ignoring invalid locale value [" + newLocale + "]: " + ex.getMessage());
						}
					}
					else {
						throw ex;
					}
				}
			}
		}
		// Proceed in any case.
		return true;
	}

以至于在使用时,可以通过LocaleContextHolder.getLocale()获取到对应的Local信息。

代码下载

《配置方式实现代码》

你可能感兴趣的:(springboot)