Springboot 项目国际化

springboot 项目国际化解决方案

一、快速上手springboot国际化

springboot 只是对spring+springmvc等的一个整合,因此在只使用了springMVC的项目中将springboot中相关的实现发生迁移至springMVC下也比较简单

1、编写国际化资源文件

要求:一定要指定properties文件编码格式为utf-8 否则国际化资源文件获取后出现乱码

resourecs目录下创建 static/i18n/国际化资源文件:
在这里插入图片描述

  • language.properties为默认国际化资源文件内容
  • language_en_US.properties: 英文环境下资源文件内容
  • language_zh_CN.properties:简体中文下资源文件内容

实例:

# language_zh_CN.properties配置

user.username=用户名
user.password=密码
user.login=登录
user.title=请登录

# language_en_US.properties配置

user.username=username
user.password=password
user.login=login
user.title=please login

可根据国际化要求继续创建更多语言的文件

2、springboot配置读取该资源文件

application.properties中配置:

spring.messages.basename: static/i18n/language,static/i18n/login

只需要指定到默认资源文件前缀即可。如果存在多组资源文件配置用逗号分开即可

3、以thymeleaf方式在html页面中使用为例

login.html编写如下

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
    <meta charset="UTF-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>登录</title>
</head>
<body>
    <h3 th:text="#{user.title}">请登录</h3>
    <h4 th:text="#{user.username}"></h4>
    <input  th:value="${name}" >
    <h4 th:text="#{user.password}"></h4>
    <input th:value="${password}">
    <button><span th:text="#{user.login}"></span> </button>
</body>
</html>

读取国际化配置采用#{msgKey}方式,加载后台viewModule中内容采用${key}

4.controlle实现

@Controller
@RequestMapping("login")
public class LoginController {
    
    @GetMapping("view")
    public String loginView(){
        return "login";
    }
}

5、启动浏览器访问即可

中文环境:

在这里插入图片描述

英文环境:

在这里插入图片描述

到此我们已经完成一个简单的springboot国际化解决方案了。但是当前模式下默认只是跟随浏览器语言环境。我们经常会看到如在这里插入图片描述的方式来选择语言进行多语言的切换。对于用户来说这也是一种更加友好的切换方式。这样我们就可以采取通过url上绑定语言参数的方式来实现多语言。点击下来选择语言后,默认未用户所有发起的url请求上添加语言参数发起请求。类似于:

http://localhost:8088/login/view?lang=en_US 的方式来进行语言选择。

二、通过绑定url请求参数的方式实现国际化

1、重新定义LocaleResolver bean的创建

/**
* 默认语言bean的创建
* @return
*/
@Bean
public LocaleResolver localeResolver(){
	SessionLocaleResolver localeResolver = new SessionLocaleResolver();
	localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
	return localeResolver;
}

注意:此处创建的localeResolver bean的id名必须为localeResolver。否则springboot请求时会抛出 【Cannot change HTTP accept header - use a different locale resolution strategy 】 的异常

2、注入参数拦截器

/**
* 默认语言拦截器。lang表示切换语言的参数名
* @return
*/
@Bean
public WebMvcConfigurer localeInterceptor() {
    return new WebMvcConfigurer() {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
            // 指定拦截参数名为lang
            localeInterceptor.setParamName("lang");
            registry.addInterceptor(localeInterceptor);
        }
    };
}

3、启动项目后指定不同语言访问

http://localhost:8088/login/view?lang=en_US  英文访问地址
http://localhost:8088/login/view?lang=zh_CN  中文访问地址

该方式实现,再去设置浏览器所属的语言时,将不会改变语言环境

三、后台消息等国际化

以上国际化方案主要都是针对页面的国际化方案,但在现在的项目中基本都是采用前后端分离的架构。那spring所支持的国际化对页面感觉就没有什么作用了?在各种前端框架中比如主流的vue和react等都支持一套自有的国际化方案。但前端获取的很多提示性消息等都需要国际化之后响应给前端。

后台相关消息主要有:

1、提示给前端的,操作提示类消息。比如:

  • 程序运行错误的500 通常会提示:系统异常,请联系管理员

  • 登录验证时提示,密码错误,注册时提示:用户已存在

2、后台程序处理的相关异常。

3、logger.info等输出的日志信息。

通常后台程序处理相关以及日志输出是没有必要做国际化的。因为将日志等国际化,反而增加了错误排查的难度。相对直接采用中文是更有利于排查错误问题的。

提示性消息异常处理方案:

1、封装后台消息获取工具类

获取国际化消息主要采用 MessageSource接口中的getMessage方法来获取指定的消息

String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);

参数说明

参数说明
code消息key值,比如:message.tip
args消息中占位位置填充值,比如 message.tip=测试消息,参数{0}。那么传入args第0个消息填充花括号的0这个位置
defaultMessage默认消息,获取不到消息key时,直接取默认消息
locale指定语言环境(通过 LocaleContextHolder.getLocale()获取)

通过封装工具类后,再整个项目中直接使用LangeUtils.get()即可获取指定消息的国际化值

@Component
public class LanguageUtils {
    private static MessageSource messageSource;
    public LanguageUtils(MessageSource messageSource){
        LanguageUtils.messageSource = messageSource;
    }
    public static String get(String msgKey){
        return messageSource.getMessage(msgKey,null, LocaleContextHolder.getLocale());
    }
    public static String get(String msgKey,Object... objects){
        return messageSource.getMessage(msgKey,objects,LocaleContextHolder.getLocale());
    }
    public static String get(String msgKey,String defaultMsg,Object... objects){
        return messageSource.getMessage(msgKey,objects,defaultMsg,LocaleContextHolder.getLocale());
    }
}

2、定义异常消息枚举

也可以通过静态常量类的方式来解决msgkey问题,总之此目的为了尽可能减少魔法值的使用

public enum  ExceptionEnums {
    MESSAGE_SYS_EXCEPTION,
    MESSAGE_USER_EXIST,
    MESSAGE_SUCCESS
}

3、自定义提示性消息异常

通过自定义异常,来区别哪些异常是用于提示给前端,哪些是我们后台程序中需要处理的异常,方便在接口数据反馈时对异常进行捕获

public class TipException extends RuntimeException {
    public TipException(ExceptionEnums exceptionEnums){
        super(LanguageUtils.get(exceptionEnums.name()));
    }
    public TipException(String defaultMessage,ExceptionEnums exceptionEnums,Object... args){
        super(LanguageUtils.get(exceptionEnums.name(),defaultMessage,args));
    }
    public TipException(ExceptionEnums exceptionEnums,Object... args){
        super(LanguageUtils.get(exceptionEnums.name(),args));
    }
}

4、定义全局controller异常处理handle

定义全局controller异常捕获处理handle是后端常用的一种方案,避免我们在controller层写大量的try-catch异常捕获代码。而让程序员只关心service层的调用关系处理

@RestControllerAdvice
public class ExceptionHandle {
    private Logger logger = LoggerFactory.getLogger(getClass());
    
    @ExceptionHandler(TipException.class)
    public String handleTipException(TipException e){
        return e.getMessage();
    }
    
    @ExceptionHandler(Exception.class)
    public String handleException(Exception e){
        // 可打印出具体堆栈信息
        logger.error("程序运行异常:"+e.getMessage());
        return LanguageUtils.get(ExceptionEnums.MESSAGE_SYS_EXCEPTION);
    }
}

5、消息国际化资源配置

# en
MESSAGE_SYS_EXCEPTION=system exception
MESSAGE_USER_EXIST=user[{0}] exist
MESSAGE_SUCCESS=success

# zh
MESSAGE_SYS_EXCEPTION=系统异常
MESSAGE_USER_EXIST=用户[{0}]已经存在
MESSAGE_SUCCESS=成功

6、service层业务逻辑处理测试

@Service
public class LoginService {

    public void login(String username){
        if("1".equals(username)){
            throw new TipException(ExceptionEnums.MESSAGE_USER_EXIST,username);
        }else if("2".equals(username)){
            int i = 0;
            i = 2/i;
        }else if("3".equals(username)){
            // 登录成功
        }
    }
}

7、controller将异常捕获交由全局异常handle去处理

@Controller
@RequestMapping("login")
public class LoginController {
    @Autowired
    private LoginService loginService;

    @RequestMapping("login")
    @ResponseBody
    public String login(String username){
        loginService.login(username);
        return LanguageUtils.get(ExceptionEnums.MESSAGE_SUCCESS);
    }
}

通过以上处理,就完成一个完整的后台消息提示信息的国际化。当然以上针对返回值等通常还可以进行封装为返回状态码和msg一体的消息等。

以上只能实现整个项目中一些静态消息的国际化,而对于一些存储于数据库的动态数据国际化,则需要从数据库设计表层面去解决国际化问题。如:项目中通过后台维护的菜单,以及一些分类信息的描述。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值