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的形式,靈活度達到了極大的提升,當然了,如果只有兩套,中英文,後續也沒有擴展的可能行了,那直接讓前端維護也是不錯的,畢竟幾乎不會發生什麼改動,如果語言再平凡變動的話,還是推薦動態配置。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章