Spring Boot 实现国际化消息提示

2022-06-21 11:06:31

前言

本文介绍使用Spring Boot实现国际化信息(i18n)提示。

Spring Boot 官方文档中关于国际化的文档资料:boot-features-internationalization

默认国际化配置

SpringBoot提供了自动配置类org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration

可以看到自动配置类中提供的可配置参数为spring.messages,其中典型配置为:

#默认配置国际化文件路径
spring.messages.basename=messages

默认将读取resources名为messages的Resource Bundle文件。

如果要使用该自动化配置的国际化资源,注入org.springframework.context.MessageSource实例即可。

自定义国际化配置

如果需要更灵活的国际化配置,可以自行指定对应的国际化资源配置,自行读取。

这里以一个动态响应错误信息的国际化示例为例。

创建ResultCodeMessages国际化文件

resources目录下创建i18n文件夹,用于存放国际化文件。

i18n目录下创建名为ResultCodeMessages的Resource Bundle文件。

ResultCodeMessages.properties

00=success
97=request param error
98=not found
99=fail. arg : {0} , {1}

ResultCodeMessages_en.properties

#同ResultCodeMessages.properties,这里默认Locale为ENGLISH
00=success
97=request param error
98=not found
99=fail. arg : {0} , {1}

ResultCodeMessages_zh_CN.properties

00=成功
97=请求入参校验出错
98=未找到
99=失败。错了,说点啥吧:{0},{1}

创建标准响应结果类和响应码枚举类

响应码枚举类

publicenum ResultCodeEnum{//成功SUCCESS("00"),//失败FAIL("99"),//未找到NOT_FOUND("98"),//请求入参校验出错REQUEST_PARAM_ERROR("97");privatefinal String code;public StringgetCode(){return code;}ResultCodeEnum(String code){this.code= code;}publicstatic ResultCodeEnumgetEnum(String code){for(ResultCodeEnum resultCodeEnum: ResultCodeEnum.values()){if(resultCodeEnum.getCode().equals(code)){return resultCodeEnum;}}return null;}}

标准响应结果类

@Data@JsonInclude(JsonInclude.Include.NON_NULL)publicclassApiResult<T>implementsSerializable{private String code;private String message;@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")private LocalDateTime timestamp;private T data;privateApiResult(String code, String message, T data){this.code= code;this.message= message;this.timestamp= LocalDateTime.now();this.data= data;}publicstatic<T> ApiResult<T>success(T data){returnnewApiResult<>(ResultCodeEnum.SUCCESS.getCode(), null, data);}publicstatic ApiResult<Void>fail(String code, String message){returnnewApiResult<>(code, message, null);}}

自定义国际化拦截器

Spring Boot中国际化拦截器默认为org.springframework.web.servlet.i18n.LocaleChangeInterceptor,默认国际化参数为URL参数locale

这里自定义修改国际化拦截器,默认国际化参数为URL参数(可以修改为从请求头获取),这里不做实质上的修改

publicclassCustomizeLocaleChangeInterceptorextendsLocaleChangeInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws ServletException{//从URL参数中获取
        String newLocale= request.getParameter(getParamName());//可修改成从请求头获取//String newLocale = request.getHeader(getParamName());if(newLocale!= null){if(checkHttpMethod(request.getMethod())){
                LocaleResolver localeResolver= RequestContextUtils.getLocaleResolver(request);if(localeResolver== null){thrownewIllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");}try{
                    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.returntrue;}privatebooleancheckHttpMethod(String currentMethod){
        String[] configuredMethods=getHttpMethods();if(ObjectUtils.isEmpty(configuredMethods)){returntrue;}for(String configuredMethod: configuredMethods){if(configuredMethod.equalsIgnoreCase(currentMethod)){returntrue;}}returnfalse;}}

注册自定义国际化拦截器

@ConfigurationpublicclassWebConfigurationimplementsWebMvcConfigurer{@Resource(name="customizeLocaleChangeInterceptor")
    LocaleChangeInterceptor localeChangeInterceptor;@OverridepublicvoidaddInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(localeChangeInterceptor);}}

国际化配置

i18n配置

@ConfigurationpublicclassLocaleConfig{/**
     * 默认本地化解析器
     */@Beanpublic LocaleResolverlocaleResolver(){
        SessionLocaleResolver localeResolver=newSessionLocaleResolver();//指定默认语言
        localeResolver.setDefaultLocale(Locale.ENGLISH);return localeResolver;}/**
     * 默认本地化拦截器
     */@Bean("customizeLocaleChangeInterceptor")public LocaleChangeInterceptorlocaleChangeInterceptor(){
        CustomizeLocaleChangeInterceptor localeChangeInterceptor=newCustomizeLocaleChangeInterceptor();//自定义国际化参数
        localeChangeInterceptor.setParamName("language");return localeChangeInterceptor;}// result code : 响应码信息国际化配置@Bean("resultCodeMessageResource")public MessageSourceresultCodeMessageResource(){
        ResourceBundleMessageSource resourceBundleMessageSource=newResourceBundleMessageSource();//指定国际化的Resource Bundle地址
        resourceBundleMessageSource.setBasename("i18n/ResultCodeMessages");//指定国际化的默认编码
        resourceBundleMessageSource.setDefaultEncoding("UTF-8");return resourceBundleMessageSource;}@Bean("resultCodeLocaleMessage")public LocaleMessageresultCodeLocaleMessage(){returnnewLocaleMessage(resultCodeMessageResource());}}

国际化资源工具类

publicclassLocaleMessage{privatefinal MessageSource messageSource;publicLocaleMessage(MessageSource messageSource){this.messageSource= messageSource;}public StringgetMessage(String code){returngetMessage(code,newObject[]{});}public StringgetMessage(String code, String defaultMessage){returngetMessage(code,newObject[]{}, defaultMessage);}public StringgetMessage(String code, String defaultMessage, Locale locale){returngetMessage(code,newObject[]{}, defaultMessage, locale);}public StringgetMessage(String code, Locale locale){returngetMessage(code,newObject[]{},"", locale);}public StringgetMessage(String code, Object[] args){returngetMessage(code, args,"");}public StringgetMessage(String code, Object[] args, Locale locale){returngetMessage(code, args,"", locale);}public StringgetMessage(String code, Object[] args, String defaultMessage){//根据应用部署的服务器系统来决定国际化returngetMessage(code, args, defaultMessage, LocaleContextHolder.getLocale());}public StringgetMessage(String code, Object[] args, String defaultMessage, Locale locale){return messageSource.getMessage(code, args== null?newObject[]{}: args, defaultMessage, locale);}}

全局统一异常处理

业务异常类

publicclassBusinessExceptionextendsRuntimeException{privatefinal String resultCode;privatefinal Object[] args;public StringgetResultCode(){return resultCode;}public Object[]getArgs(){return args;}publicBusinessException(ResultCodeEnum resultCodeEnum, Object[] args){this.resultCode= resultCodeEnum.getCode();this.args= args;}publicBusinessException(String code, Object[] args){this.resultCode= code;this.args= args;}publicstaticvoidthrowMessage(String errorCode, Object[] args){thrownewBusinessException(errorCode, args);}publicstaticvoidthrowMessage(ResultCodeEnum resultCodeEnum, Object[] args){thrownewBusinessException(resultCodeEnum.getCode(), args);}}

统一异常处理

@Slf4j@RestControllerAdviceclassGlobalExceptionHandler{@Resource(name="resultCodeLocaleMessage")
    LocaleMessage resultCodeLocaleMessage;@ExceptionHandler(BindException.class)public ApiResult<Void>handlerBindExceptionHandler(HttpServletRequest req,final BindException e){
        log.error(req.getServletPath()+" Bind Exception", e);
        String message= e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(";"));return ApiResult.fail(ResultCodeEnum.REQUEST_PARAM_ERROR.getCode(), message);}@ExceptionHandler(MethodArgumentNotValidException.class)public ApiResult<Void>handlerMethodArgumentNotValidException(HttpServletRequest req,final MethodArgumentNotValidException e){
        log.error(req.getServletPath()+" MethodArgumentNotValid Exception", e);
        String message= e.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(";"));return ApiResult.fail(ResultCodeEnum.REQUEST_PARAM_ERROR.getCode(), message);}@ExceptionHandler(BusinessException.class)public ApiResult<Void>handlerBusinessException(HttpServletRequest req,final BusinessException e){
        log.error(req.getServletPath()+" Business Exception", e);return ApiResult.fail(e.getResultCode(), resultCodeLocaleMessage.getMessage(e.getResultCode(), e.getArgs(), RequestContextUtils.getLocale(req)));}}

请求示例

@GetMapping("/success")public ApiResult<String>success(){return ApiResult.success("success");}@GetMapping("/fail")publicvoidfail(@RequestParam(value="code", required=false, defaultValue="99") String code,@RequestParam(value="content", required=false, defaultValue="错了吧,说点啥吧") List<String> contentList){
        BusinessException.throwMessage(code, contentList.toArray());}

http测试示例

### 测试成功响应
GET http://localhost:8080/success HTTP/1.1

### 测试中文,响应码99
GET http://localhost:8080/fail?language=zh_CN&code=99&content=我没错&content=是系统的错 HTTP/1.1

### 测试英文,响应码99
GET http://localhost:8080/fail?language=en&code=99&content=I'm not wrong&content=It's the system's fault HTTP/1.1

示例

项目Demo:internationalization

效果图:

example

example

example

参考

  • 作者:飞翔的大白菜丶
  • 原文链接:https://blog.csdn.net/wangzhihao1994/article/details/108406710
    更新时间:2022-06-21 11:06:31