springboot实现国际化居然可以这么简单

国际化介绍

国际化(internationalization)是设计和制造容易适应不同区域要求的产品的一种方式。它要求从产品中抽离所有地域语言,国家/地区和文化相关的元素。换言之,应用程序的功能和代码设计考虑在不同地区运行的需要,其代码简化了不同本地版本的生产。开发这样的程序的过程,就称为国际化。

不知道你们在浏览网页的时候有没有发现很多网页都是支持多种语言的,比如:中文、英语等等,我在浏览清华大学官网的时候就看到了

中文版在这里插入图片描述
英文版
在这里插入图片描述如果是在我们的项目开发中也要实现这样的需求,你会怎么设计呢?你可以在自己的脑海中构思一下,下面看看我是怎么实现java版本的国际化问题。

我这里主要介绍前后端分离的实现方式,虽然前端我不怎么会,这里我主要介绍思想以及后端的实现,前端这部分,知道了方式,实现应该不是很难,那我们直接开始进入主题把。

实现国际化思路

静态语言

对于前端的静态展示信息而言我们是不是需要事先准备两套或者多套语言,当用户进行语言切换的时候,前端切换到对应的语言,但是这样会有一个弊端,假如我们现在系统只有中英文,前端也只配置了两套语言,这个时候,如果我们需要加入日语,那这个时候是不是就需要前端重新编码,重新升级?而且当语言太多的时候前端的缓存也会面临很大的压力,所以我不是很推荐这种实现方式,是否有一种可以不用升级就能实现多语言切换的问题呢?我们接着往下看。

我们将多语言这一块交给后端,由后端向前端提供语言信息,当然前端也可以配置默认的一套,防止用户第一次进入系统的时候白屏。

1.在管理平台中添加一个可以设置语言种类的功能,可以新增或者删除。
2.后端向前端提供一个语言的种类,比如现在支持多少语言的切换。
3.我们将所有的展示信息做成键值对的形式,比如中文版:code0001=首页;英文版:code0001=home,前端根据根据用户选择的语言种类获取到所有的键值对,但是这有一个前提,那就是前端需要维护一套固定的key,前端根据key就能找到对应的展示内容
4.前端向后端提供一套完整的key,后端拿着这套key值,可以随意添加任何语言种类信息。

这个时候你就会发现,前端只维护了一套默认的静态语言以及key,语言的种类、其他语言的信息都是由后端提供,假如现在需要修改某个key的值,我们可以在管理平台做实时修改,如果我现在想新增法语呢?第一步:在管理平台添加一种语言类型;第二步:我们可以使用指定格式的excel导入将数据添加到我们的数据库或者缓存中,格式可以是:key、语言。

这个时候我们发现整个系统的灵活性等到了极大的提升,不管是修改语言还是新增语种,都不需要升级前端或者后端的代码,直接在管理平台配置即可,细心的人可能已经想到了一个问题,那就是前端拉去了语言信息之后,管理平台修改了某个key的值,那什么时候会生效呢?如果你不做任何处理的话,应该是需要等到下一次拉取语言的时候了,这肯定是不行的,所以我们可以给语言增加一个版本号,当语言发生新增或者修改的时候我们同样去修改语言的版本号,后端可以通过消息推送的形式告诉前端语言已经做过修改,请更新修改过的最新语言,这里可以设计成给每个key都设置版本号,这样前端只需要拉取比当前版本号大的语言即可,不需要全部拉取。

前面讲的是前端页面的静态数据可以通过这种方式进行配置,那后端提示的信息是否也可以这样配置呢?按照刚才的逻辑来说也是可以的,这个时候就需要后端向前端提供一套后端提示语的key,后端返回前端key,前端根据后端返回的key值找到对应的提示信息,如果提示信息带参数的话,可以使用占位符的形式解决。

动态提示信息

这个时候可能你就会有疑问了?如果后端的提示消息过多会不会导致前端缓存的炸裂?有可能的,所以在这里我在介绍一种后端直接根据前端的语言类型返回指定的提示消息,不过这种相对于上面那种灵活度较低,理由:由于后端的语言配置一般都是写在配置文件的,项目启动的时候jvm会将配置文件加载到内存中,也就是说当你发现一个语言的提示消息不是很恰当想修改的时候就需要修改配置文件以及重启服务器,当然了,这两种实现方式都有着自己的长处以及弊端,你们可以结合项目的实际情况分析。

我现在来介绍一下后端通过配置文件的方式来实现国际化,实现其实很简单,我们一起来看看吧。

1.新建springboot项目。
2.新建国家化的语言配置文件
在resources目录下新建文件夹夹:i18n(可以随意命名,但是推荐使用i18n),然后新建各种语言的配置文件:
在这里插入图片描述
解释一下:
messages_en_US.properties:英文语言的配置。
messages_fr_FR.properties:发文语言的配置。
messages_zh_CN.properties:中文语言的配置。
messages.properties:在中、英、法配置文件中都没有找到的时候就会提示这个配置文件中的提示信息。
我们新建一个提示信息,打开其中一个配置文件:
在这里插入图片描述
切换到resource bundle模式下,我们就可以同时编辑多个语言的提示信息,非常的方便。

3.新建国家化处理工具类

package com.ymy.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.ResourceBundleMessageSource;

import java.util.Locale;

@Slf4j
public class I18nUtil {


    /**
     * 通过code 返回对应的提示信息
     * @param code
     * @return
     */
    public static String getMessage(String code) {
        return getMessage(code, null);
    }

    /**
     * 返回带参数的提示信息
     * @param code
     * @param args
     * @return
     */
    public static String getMessage(String code, Object[] args) {
        return getMessage(code, args, "");
    }


    /**
     * 根据语种查询信息
     * @param code code
     * @param args  参数
     * @param defaultMessage  默认的提示消息
     * @return
     */
    public static String getMessage(String code, Object[] args, String defaultMessage) {
        //这里使用比较方便的方法,不依赖request.
        Locale locale = LocaleContextHolder.getLocale();
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("i18/messages");
        String content;
        try{
            content = messageSource.getMessage(code, args, locale);
        }catch (Exception e){
            log.info("获取提示消息失败: ->",e);
            content = defaultMessage;
        }
        return content;

    }


}

不知道你有没有发现messageSource.setBasename(“i18n/messages”);这行代码,很重要,i18n表示你们文件目录,messages表示你配置文件的前缀,比如我的:messages_en_US.properties、messages_zh_CN.properties、messages_fr_FR.properties、messages.properties。

4.新建测试代码

package com.ymy.controller;

import com.ymy.vo.ResultVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class TestController {

    @GetMapping(value = "/test")
    public ResultVo test(){
        ResultVo resultVo = ResultVo.faild("code-0001");

        return resultVo;
    }


}

这里我们看到引用了ResultVo,源码如下:

package com.ymy.vo;

import com.ymy.utils.I18nUtil;
import lombok.Getter;

@Getter
public class ResultVo<T> {

    private  String  code;

    private String msg;

    private T data;

    private String  createTime ;

    private  ResultVo(String code){
        this.code = code;
        setCode(code);
    }

    public void setCode(String code) {
        String message = null;
        try {
            message =I18nUtil.getMessage(code);
        }catch (Exception e){
            message = code;
        }
        this.code = code;
        this.msg = message;
    }

    /**
     * 默认成功返回
     * @param <T>
     * @return
     */
    public static<T> ResultVo<T> OK(){
        return new ResultVo<T>("SUCCESS");
    }

    /**
     * 返回只带code的信息
     * @param code
     * @param <T>
     * @return
     */
    public static<T> ResultVo<T> faild(String code){
        return new ResultVo<T>(code);
    }


}

如果ResultVo中的@Getter报错的话,可以在pom.xml中引入lombok的依赖:

<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

5.测试

中文:
在这里插入图片描述
如果中文乱码,推荐你将中文转换成unicode,在写往配置文件中。

英文:
在这里插入图片描述
法文:
在这里插入图片描述
是不是很简单?就是这么的丝滑,我们现在在升级一下,我们现在需要提示:对不起,张三,密码错误,剩余5次,这里面有两个动态信息,张三、5 ,这两个都是会实时发生改变的,那我们如何处理呢?
不知道你还记得国际化工具类中的这个方法吗?

/**
     * 返回带参数的提示信息
     * @param code
     * @param args
     * @return
     */
    public static String getMessage(String code, Object[] args) {
        return getMessage(code, args, "");
    }

我们一起改造一下代码:

controller:

package com.ymy.controller;

import com.ymy.vo.PwdVo;
import com.ymy.vo.ResultVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@RestController
@Slf4j
public class TestController {

    private static Integer count = 5;

    /**
     * 密码校验
     * @param pwdVo
     * @return
     */
    @RequestMapping(value = "checkPwd",method = RequestMethod.POST)
    public ResultVo checkPwd(@RequestBody PwdVo pwdVo){
        //这里我就不做判空处理了,因为判空处理还需要往配置文件中添加控的提示信息,这里主要展示带参数的提示信息
        if(!"123456".equals(pwdVo.getPwd())){
            count--;
            return ResultVo.faild("code-0002",pwdVo.getUserName(),count);
        }
        count = 5;
        return ResultVo.OK();

    }



    @GetMapping(value = "/test")
    public ResultVo test(){
        ResultVo resultVo = ResultVo.faild("code-0001");

        return resultVo;
    }

}

PwdVo:

package com.ymy.vo;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class PwdVo {

    /**
     * 用户名
     */
    private String userName;

    /**
     * 密码
     */
    private String pwd;
}

ReslutVo:

package com.ymy.vo;

import com.ymy.utils.I18nUtil;
import lombok.Getter;

@Getter
public class ResultVo<T> {

    private  String  code;

    private String msg;

    private T data;

    private String  createTime ;

//    private  ResultVo(String code){
//        this.code = code;
//        setCode(code);
//    }

    private  ResultVo(String code,Object ... args){
        this.code = code;
        setCode(code,args);
    }


    public void setCode(String code,Object ... args) {
        String message = null;
        try {
            message =I18nUtil.getMessage(code,args);
        }catch (Exception e){
            message = code;
        }
        this.code = code;
        this.msg = message;
    }

    /**
     * 默认成功返回
     * @param <T>
     * @return
     */
    public static<T> ResultVo<T> OK(){
        return new ResultVo<T>("code-0003");
    }

    /**
     * 返回只带code的信息
     * @param code
     * @param <T>
     * @return
     */
    public static<T> ResultVo<T> faild(String code){
        return new ResultVo<T>(code);
    }

    /**
     * 返回带参数的提示信息
     * @param code
     * @param <T>
     * @return
     */
    public static<T> ResultVo<T> faild(String code,Object ... arg){
        return new ResultVo<T>(code,arg);
    }


}

语言配置文件:
在这里插入图片描述

再次测试:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
带参数的也完美解决了,后端之所以可以灵活的变动语言是需要前端提供准确的Accept-Language配置,zh-CN,zh;q=0.9,例如这个值代表的就是中文,zh-cn表示简体中文;zh 表示中文;

关于各个国家的语言代码可以自行百度,上面有很多。

总结

springboot实现国际化还是很简单的,主要是思想,比如由之前的前端维护几套静态语言替换成后端维护,然后再通过excel的这种格式做成可上传修啊给i的形式,灵活度达到了极大的提升,当然了,如果只有两套,中英文,后续也没有扩展的可能行了,那直接让前端维护也是不错的,毕竟几乎不会发生什么改动,如果语言再平凡变动的话,还是推荐动态配置。

发布了41 篇原创文章 · 获赞 79 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_33220089/article/details/104837066