后端项目集成i18n国际化多语言支持

本文介绍了如何在SpringBoot项目中实现后端响应的国际化处理,包括配置语言包、设置默认语言、创建拦截器以及使用@RestControllerAdvice统一处理响应。通过这种方式,可以根据前端传入的语言参数返回对应的语言信息,同时支持多种语言包和占位符替换,实现了灵活的多语言响应。
摘要由CSDN通过智能技术生成

框架:springboot 2.3.3
项目交付以后本来后端响应的消息统一响应的是code没有附带消息,消息前端通过code进行匹配多种语言,然后前端三哥突然打算让我直接返回对应的语言信息,好吧,只能临时更改了

恰巧前一天琢磨了一下@RestControllerAdvice的用法,今天恰巧排上了用场

配置类

有了依赖以后我们创建一个配置类,具体功能在里面解释

@Configuration
public class LocaleConfig {

	/**
 	*	默认解析器 其中locale表示默认语言,当请求中未包含语种信息,则设置默认语种
 	*	当前默认为CHINA,zh_CN
 	*/
	@Bean
	public SessionLocaleResolver localeResolver() {
		SessionLocaleResolver localeResolver = new SessionLocaleResolver();
		// 这里配置了在没有指定语言的默认情况下匹配的语言种类,你也可以追进去看看每个语言的tag是什么
		// tag就是你语言的代号,下面lang值对应的参数,这是一个全局变量。
		localeResolver.setDefaultLocale(Locale.ENGLISH);
		return localeResolver;
	}

	/**
	   *  默认拦截器 其中lang表示切换语言的参数名
	   *  拦截请求,获取请求参数lang种包含的语种信息并重新注册语种信息
	 */
	@Bean
	public WebMvcConfigurer localeInterceptor() {
		return new WebMvcConfigurer() {
			@Override
			public void addInterceptors(InterceptorRegistry registry) {
				LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
				// 这里指定一个从前端form-data或者url参数获取语言tag的key值,比如这里是lang
				// 前端就需要传参?lang=zh_CN。zh是语言tag,对应全局变量中的语言,CN是国家代号,这里似乎没用上最好还是设置上吧
				localeInterceptor.setParamName("lang");
				registry.addInterceptor(localeInterceptor);
			}
		};
	}
}

配置语言包

在resources下的language包里增加如下文件
语言包
yml配置文件添加内容

spring:
  messages:
    #指定了语言包所在位置
    basename: language.message
    cache-duration: 3600
    encoding: UTF-8

首先创建一个首要语言包message.properties,不需要什么内容,可以为空,后续我们通过拓展包对语种进行归类
然后我们分别创建其他语言的语言包,注意格式message****
语言包内容[同一个语义对应的同一个key]
英语
日语

使用

@Autowired
private MessageSource messageSource;
@GetMapping("/info")
public Result<User> getInfo(){
    User user = new User();
    return new Result<>(true,CodeEnum.SUCCESS.getCode(),
            messageSource.getMessage(
                    "success", // 这里是语言包中properties对应的key值
                    null,// 占位参数,一般不设置
                    LocaleContextHolder.getLocale() // 区域选择,这里选择了当前语言环境,也就是根据前端参数设置的语言
            ),user);
}

解决当前问题

当前情况是我已经全部统一通过Result返回了,并且只返回了code并没有msg,我现在在Result里新增一个成员属性msg
在这里插入图片描述
现在我们返回的结果就是

{
	flag:true
	code:20
	msg:null
	data:object
}

我们创建一个接口层增强器@RestControllerAdvice统一处理响应[也可以在拦截器中处理]

@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
    @Autowired
    private ObjectMapper objectMapper; // 用来向前端直接写出Json
    @Autowired
    private MessageSource messageSource;// 语言转换对象
    @Autowired
    private HttpServletResponse response;// 响应对象

    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        return true;
    }


    /**
     * 在响应前对结果进行处理。这里一般配合异常处理进行使用,否则出现异常的时候依然会被封装进来
     * @param o 原始响应体
     * @param methodParameter 请求方法参数
     * @param mediaType 
     * @param aClass
     * @param serverHttpRequest 请求
     * @param serverHttpResponse 响应
     * @return 处理过的响应体
     */
    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        int status = response.getStatus();
        // 这里为了避免出现失败的时候依然封装为成功
        if (status!=200)
            return Result.fail(status,null);
        if (o instanceof String) {
            // 如果是字符串就以Json写出
            return objectMapper.writeValueAsString(Result.success("成功", o));
        }
        if (o instanceof Result){
            // 如果是结果实体,就调用下面的方法取出code作为key匹配消息进行填充msg
            return languageFormat(o);
        }
        // 如果都不符合证明返回的是其他实体。我们将他封装进data进行响应
        return Result.success("成功", o);
    }

    /**
     * 语言转换方法
     * @param o
     * @return
     */
   public Object languageFormat(Object o){
       // 由于调用方法前使用了instanceof确定了具体类型,我们这里可以直接强转
       Result<?> result = (Result<?>) o;
       // 强转以后取出code
       String key = result.getCode().toString();
       // 设置msg,用code作为key获取参数
       result.setMsg(messageSource.getMessage(key,null, LocaleContextHolder.getLocale()));
       return result;
   }
}

前端请求以及响应
中文

小日子过的不错的日本朋友
注意这里的param必须和你的配置文件后缀一致

如果不想统一响应可以去除最后的那段,如果加了那个:当匹配不上的时候就包装起来响应,那我们不处理的话就只是检测到是Result就取出code并匹配填入

后续补充:

支持多个数据包匹配

在这里插入图片描述
我们只需要在配置文件中将这两个包都写入配置文件中即可

server:
  port: 8080
  servlet:
    context-path: /learn
spring:
  messages:
    #指定了语言包所在位置
    basename:
      language.message,
      language.scop
    cache-duration: 3600
    encoding: UTF-8

需要注意的一点就是,这个只是相当于扩容一样,只是方便管理,key还是会出现冲突的
由于是按照配置文件中配置的顺序进行匹配的,所以如果出现key值冲突,会优先显示最先匹配到的那个

关于占位符

也就是在提取的时候,传入第二个参数,自动替换占位符
占位符在模板中的写法

单个占位符:
{0}: 表示第一个参数。
{1}: 表示第二个参数。
依此类推。

带有格式化选项的占位符:
{0,number}: 表示第一个参数为数字类型。
{1,date}: 表示第二个参数为日期类型。
{2,time}: 表示第三个参数为时间类型。

例如可以写作
msg = 你好{0},感谢您的注册!
msg2 = 你好{0,string}感谢您的注册您是第{1,number}个注册的用户

使用占位符

param = [1,2]
messageSource.getMessage(
	result.getCode(),
	param,
	LocaleContextHolder.getLocale()
)

注意如果只提供了一个占位符,比如: 你好{0}
然后我们传递的参数又是一个数组,比如:[‘张三’,‘李四’,‘王五’],
由于是按顺序替换,所以最终只有张三会被替换进去,得到结果,“你好张三”
如果想都显示进去,可以手动转换成字符串传入

占位符指定数组第几个参数
msg=你好{0[2]}

这样的写法就能指定使用参数数组的第几个参数

实际使用记录

@SneakyThrows
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        // 这里为了避免出现失败的时候依然封装为成功,这里只会拦截未经处理的,我们手动抛出的非200异常并不会受影响,因为那个是在这个之后才处理;
        int status = response.getStatus();
        if (status != 200)
            return o;
        if (o instanceof String) {
            // 如果是字符串就用objectMapper以Json写出
            return objectMapper.writeValueAsString(
                    new Result<>(true, 20000, translationTools.get("20000"), o));
        }
        if (o instanceof Result) {
            // 如果是结果实体,就调用下面的方法取出code作为key匹配消息进行填充msg,并且原路返回
            return languageFormat(o);
        }
        // 避免swagger失效
        if (Arrays.asList(exclude).contains(methodParameter.getDeclaringClass().getSimpleName())) {
            return o;
        }
        log.info("有响应结果跳过了Result封装,请求方法为:{},class:{},o:{}", methodParameter.getDeclaringClass().getSimpleName(), aClass, o);
        // 如果都不符合证明返回的是其他实体。我们将他封装进data进行响应
        return new Result<>(true, 20000, translationTools.get("20000"), o);
    }

补充

可以自定义地区对象

import java.util.Locale;
new Locale("en_US")

String body = messageSource.getMessage(msgCode, null,new Locale("zh_CN"));
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值