为了账号安全,请及时绑定邮箱和手机立即绑定

Spring Boot国际化

标签:
Java SpringBoot

spring-boot-starter-web里面有国际化需要的API,因此我们不需要引入其他的API了。

1.定义国际化的配置文件

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;

import java.util.Locale;

/**
 * @author fangxi
 */
@Configuration
public class I18nConfiguration {

    /**
     * Accept-Language Header 区域解析器
     */
    @Bean
    public LocaleResolver localeResolver() {
        AcceptHeaderLocaleResolver acceptHeaderLocaleResolver = new AcceptHeaderLocaleResolver();
        acceptHeaderLocaleResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
        return acceptHeaderLocaleResolver;
    }

    /**
     * 加载国际化配置文件资源
     */
    @Bean
    public ReloadableResourceBundleMessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        //设置国际化配置文件存放目录
        messageSource.setBasename("classpath:i18n/messages");
        //设置加载资源的缓存失效时间,-1表示永久有效,默认为-1
        messageSource.setCacheSeconds(-1);
        //设定 Resource Bundle 编码方式,默认为UTF-8
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }
}

AcceptHeaderLocaleResolver:Spring内置的国际化处理,获取Http请求头中的Accept-Language属性,判断当前环境是哪种语言。

2.在resources目录下,创建i18n目录和两个国际化的文件,要以message开头

messages_en_US.properties,英文提示

delete.success=delete success.
save.success=Save Success.
update.success=Update Success.

messages_zh_CN.properties,中文提示

delete.success=删除成功
save.success=保存成功
update.success=更新成功

3.定义一个工具类,根据当前语言环境和国际化提示中的key获取对应的提示

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.stereotype.Component;

import java.util.Locale;

/**
 * @author fangxi
 */
@Component
public class I18nUtils {

    private static ReloadableResourceBundleMessageSource messageSource;

    @Autowired
    I18nUtils(ReloadableResourceBundleMessageSource messageSource) {
        I18nUtils.messageSource = messageSource;
    }

    public static String toLocale(String message) {
        Locale locale = LocaleContextHolder.getLocale();
        return messageSource.getMessage(message, null, locale);
    }
}

4.为了统一管理key,我们用枚举保存。

import lombok.AllArgsConstructor;

import java.util.stream.Stream;

/**
 * @author fangxi
 */
@AllArgsConstructor
public enum I18nEnum {

    SAVE_SUCCESS("save.success"),
    UPDATE_SUCCESS("update.success"),
    DELETE_SUCCESS("delete.success"),
    ;

    private final String message;

    public String message() {
        return this.message;
    }

    public static boolean exists(String s) {
        return Stream.of(I18nEnum.values())
                .map(I18nEnum::message)
                .anyMatch(s::equals);
    }

}

注:用枚举是因为我们在全局异常处理的时候,要获遍历枚举里面的常量,从而判断是否要国际化。

5.使用

@PostMapping
public ResponseEntity<String> save(@RequestBody @Validated UserRequestVO requestVO) {
    userService.save(requestVO);
    return ResponseEntity.status(HttpStatus.CREATED)
        .body(I18nUtils.toLocale(I18nEnum.SAVE_SUCCESS.message()));
}

这样一来,会就完成了提示消息国际化功能。

上面的做法不能让入参校验进行国际化,比如@NotNull(message = "请传入xxxx")。因此,我们还需要改造,定义两组提示信息

messages_zh_CN.properties

paging.tip=请传入分页参数

messages_en_US.properties

paging.tip=Please pass in paging parameters

改造注解上的提示信息:@NotNull(message = "paging.tip")

import lombok.Data;

import javax.validation.constraints.NotNull;

/**
 * @author fangxi
 */
@Data
public class PageRequestVO {

    @NotNull(message = "paging.tip")
    private Integer page;

    @NotNull(message = "paging.tip")
    private Integer size;

}

现在一般提交数据的方式用两种:表单提交和JSON格式

如果是表单提交,校验不通过的话,会抛出BindException。JSON格式的提交,校验不通过则会抛出MethodArgumentNotValidException。所以,我们要拦截这两种异常处理

import com.isecstar.print.common.enums.I18nEnum;
import com.isecstar.print.common.utils.I18nUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author fangxi
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, Object> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException methodArgumentNotValidException, HttpServletRequest request) {
        return getError(methodArgumentNotValidException, request);
    }

    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, Object> bindException(BindException bindException, HttpServletRequest request) {
        return getError(bindException, request);
    }

    private Map<String, Object> getError(Exception e, HttpServletRequest request) {
        log.error("", e);
        Map<String, Object> error = new HashMap<>(5);
        String message = e.getMessage();
        error.put("message", I18nEnum.exists(message) ? I18nUtils.toLocale(message) : message);
        error.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
        if (e instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException methodArgumentNotValidException = (MethodArgumentNotValidException) e;
            BindingResult bindingResult = methodArgumentNotValidException.getBindingResult();
            // messageList就是写在@NotNull(message = "")message的信息, 这里获取到的是 paging.tip
            Set<String> messageList = bindingResult.getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.toSet());
            List<String> globalizationMessage = messageList.stream().map(I18nUtils::toLocale).collect(Collectors.toList());
            error.put("message", globalizationMessage);
            error.put("httpStatus", HttpStatus.BAD_REQUEST);
            error.put("code", HttpStatus.BAD_REQUEST.value());
        } else if (e instanceof BindException) {
            BindException bindException = (BindException) e;
            // messageList就是写在@NotNull(message = "")message的信息, 这里获取到的是 paging.tip
            Set<String> messageList = bindException.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.toSet());
            List<String> globalizationMessage = messageList.stream().map(I18nUtils::toLocale).collect(Collectors.toList());
            error.put("message", globalizationMessage);
            error.put("httpStatus", HttpStatus.BAD_REQUEST);
            error.put("code", HttpStatus.BAD_REQUEST.value());
        }
        error.put("path", request.getRequestURI());
        error.put("timestamp", LocalDateTime.now());
        return error;
    }
}

这样一来,我们就可以在校验中,对外国际化之后的异常信息。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
1
获赞与收藏
5

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消