一個(gè)簡(jiǎn)單的基于Spring @ControllerAdvice的Java后端的@ResponseBody i18n的實(shí)現(xiàn)

公司項(xiàng)目原先的架構(gòu)中耸峭,接口是通過一個(gè)通用的類轉(zhuǎn)換成 Json 后返回?cái)?shù)據(jù)的峡继,這個(gè)返回的類如下

public class ResultModel {
    private boolean success;
    private String message;
    private T data;
    private String status;

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }
}

然后原先是沒有i18n的需求岸梨,因此message都是直接寫死在代碼中。
如果要改成i18n篷扩,對(duì)于一個(gè)后端來說兄猩,我肯定需要知道客戶端是什么語言,然后才能返回特定語言的文字鉴未。
因此第一時(shí)間想到的是在請(qǐng)求頭添加語言信息枢冤,然后在 controller 方法調(diào)用結(jié)束后修改 resultModel 的message。
我需要在兩個(gè)時(shí)間點(diǎn)寫一些代碼:

  • request請(qǐng)求之后
  • controller方法調(diào)用返回之后铜秆,被寫到輸出流之前

第一時(shí)間想到的是 Spring 的 Interceptor 淹真,然后去翻了一遍官方文檔。很遺憾连茧, Interceptor的 postHandle方法并不能作用于用 @ResponseBody 修飾的方法核蘸,如圖:


image.png

根據(jù)官方文檔所說,我發(fā)現(xiàn)了可以用 ResponseBodyAdvice 實(shí)現(xiàn)我的需求啸驯。
然后就可以開始擼了客扎,我的思路是:

  1. 將i18n字符串資源文件存于 properties 內(nèi)。
  2. 用一個(gè)工具類管理各個(gè)語言的字符串資源
  3. web端保存一個(gè)指定語言的 cookie
  4. 在 ResponseBodyAdvice 中對(duì) ResultModel 的 message 嘗試替換罚斗,如果能在資源文件中找到徙鱼,那么就替換字符串

上代碼

ConvertResultMsgAdvice.java
@ControllerAdvice("com.hc.trr")
public class ConvertResultMsgAdvice implements ResponseBodyAdvice<ResultModel>{

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        //暫時(shí)全部放行
        return true;
    }

    @Override
    public ResultModel beforeBodyWrite(ResultModel body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        try {
            // 從request中拿到請(qǐng)求頭的多語言參數(shù)
            List<String> cookies = request.getHeaders().get("Cookie");
            if(cookies != null && cookies.size() > 0){
                String pattern = "^language=(^*.;*.);";
                Pattern r = Pattern.compile(pattern);
                for(String cookie: cookies){
                    Matcher m = r.matcher(cookie);
                    if(m.find()){
                        String language = m.group(1);
                        // 修改mgs參數(shù),返回
                        String message = result.getMessage();;

                        message = I18nResourcesUtil.getStringBaseOnLanguage(language, message);
                        body.setMessage(message);
                    }
                }
            }

        } catch (Exception e) {
            return body;
        }

        return body;
    }
}
I18nResourcesUtil.java
public class I18nResourcesUtil {
    //存放多語言字符串資源的map针姿,key為語言名稱袱吆,map為讀取到的properties
    private final static Map<String, Properties> languagesMap = new HashMap<>();

    // 加載多語言配置文件
    static{
        //獲取類加載路徑
        String stringsDir = new Object() {
            private String getPath() {
                return this.getClass().getResource("/strings").getPath();
            }
        }.getPath();
        File dir = new File(stringsDir);
        File[] stringFiles = dir.listFiles();
        try {
            if(stringFiles != null && stringFiles.length != 0){
                for(File file: stringFiles){
                    String language = file.getName().substring(0, file.getName().indexOf("."));
                    InputStream in = new BufferedInputStream(new FileInputStream(file.getAbsolutePath()));
                    Properties p = new Properties();
                    p.load(new InputStreamReader(in, "utf-8"));
                    languagesMap.put(language, p);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根據(jù)語言獲取i18n字符串
     * @param language
     * @param string
     * @return 如果沒找到語言對(duì)應(yīng)字符串厌衙,就去找默認(rèn)語言,如果還找不到杆故,就返回傳進(jìn)來的字符串
     */
    public static String getStringBaseOnLanguage(String language, String string){
        String i18nStr = string;
        Properties p = languagesMap.get(language);
        if(p != null){
            i18nStr = p.getProperty(string);
        }else{
            p = languagesMap.get("zh");
            if (p != null) {
                i18nStr = p.getProperty(string);
            }
        }
        return i18nStr;
    }
}
資源路徑
image.png

最后把原先 resultModel 中寫死的字符串迅箩,替換成properties中的key字符串就行了溉愁。

如果有更好的解決思路歡迎分享处铛,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拐揭,一起剝皮案震驚了整個(gè)濱河市撤蟆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌堂污,老刑警劉巖家肯,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異盟猖,居然都是意外死亡讨衣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門式镐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來反镇,“玉大人,你說我怎么就攤上這事娘汞〈醪瑁” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵你弦,是天一觀的道長惊豺。 經(jīng)常有香客問我,道長禽作,這世上最難降的妖魔是什么尸昧? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮旷偿,結(jié)果婚禮上烹俗,老公的妹妹穿的比我還像新娘。我一直安慰自己狸捅,他們只是感情好衷蜓,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著尘喝,像睡著了一般磁浇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上朽褪,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天置吓,我揣著相機(jī)與錄音无虚,去河邊找鬼。 笑死衍锚,一個(gè)胖子當(dāng)著我的面吹牛友题,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播戴质,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼度宦,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了告匠?” 一聲冷哼從身側(cè)響起戈抄,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎后专,沒想到半個(gè)月后划鸽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡戚哎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年裸诽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片型凳。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡丈冬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出啰脚,到底是詐尸還是另有隱情殷蛇,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布橄浓,位于F島的核電站粒梦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏荸实。R本人自食惡果不足惜匀们,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望准给。 院中可真熱鬧泄朴,春花似錦、人聲如沸露氮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽畔规。三九已至局扶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背三妈。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國打工畜埋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人畴蒲。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓悠鞍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親模燥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子咖祭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容