前端系列——jquery.i18n.properties前端國際化解決方案「填坑日記」

 

正文前端

 前言:最近,新的平臺尚未開發完成,原來的老項目又提出了新的需求:系統國際化。若是是先後端徹底分離的開發模式,要作國際化,真的太簡單了,有現成的解決方案,基於Node構建的時下熱門的任何一種技術選型都有成熟的方案,好比:vue

  • vue + vue-i18nreact

  • angular + angular-translatejquery

  • react + react-intlgit

但如今的狀況是老的項目並無使用這類架構。提及國際化,博主幾年前就作過,在MVC裏面實現國際化有通用的解決方案,主要就是經過資源文件的方式定義多語言。最初接到這個任務,並無太多顧慮,畢竟這種東西有很成熟的解決方案,實現起來難點不會很大。可當真正動起來手來去實現的時候發現一些問題,這裏先介紹下咱們老平臺的架構:MVC+WebApi,MVC項目負責頁面渲染,webapi負責數據接口,是一種很傳統的架構方式。國際化主要在MVC端去作就行了,但是因爲MVC項目裏面使用了大量第三方bootstrap組件,幾乎95%的組件都是經過js去實現的,好比bootstrapTable,好比bootstrap-select,好比bootstrap-fileinput。若是按照傳統的方式,僅僅在MVC裏面去實現國際化,那麼大量的js代碼裏面的中文無法統一處理,而且不少第三方組件有本身的本地化local文件,和後端的國際化很難統一處理;可能有人又說,那就先後端分開國際化唄,這種方案博主真的想過,可是想到要維護兩套資源文件,果斷放棄。最後仍是決定直接維護一套,作前端國際化好了。因而在網上搜索基於jquery的國際化,千篇一概,幾乎都說的是jquery + jquery.i18n.properties這種方案,既然你們都這麼選型,那博主也按照這種思路去作好了。程序員

在實現的過程當中,有不少值得注意和分享的東西,在此寫一個填坑筆記,但願對你們有幫助!接下來,博主就一步一步帶領你們解決這個過程當中遇到的一些坑,若是有這個需求的童鞋能夠關注下,可能這些問題你也會遇到。github

本文原創地址:http://www.cnblogs.com/landeanfen/p/7581609.htmlweb

1、jquery.i18n.properties通用解決方案

關於jquery.i18n.properties的使用,網上資料不少,比較完整的使用能夠參考 這篇 ,有比較詳細的使用說明。這裏博主簡單概述下過程。bootstrap

一、須要引用的js文件

先在你的項目文件裏面添加以下目錄結構

首先頁面引用的js文件以下

   <script src="~/Scripts/jquery-1.9.1.min.js"></script>
    <script src="~/Content/i18n/jquery.i18n.properties.js"></script>
    <script src="~/Content/i18n/language.extensions.js"></script>

其中jquery-1.9.1.min.js和jquery.i18n.properties.js文件是開源組件,直接去網上找到便可

第三個文件language.extensions.js是咱們自定義的js文件,若是你將國際化的代碼直接寫在html頁面裏面,這個文件就是不用的。

二、html文件和國際化組件的初始化

這裏直接引用上面示例文章裏面的代碼,首先須要一個切換中英文的標籤,好比是一個select

        <select id="language">
                <option value="zh-CN">中文簡體</option>
                <option value="en">English</option>
            </select>

而後是一些查看效果的html標籤

<div>
    <input type="search" class="i18n-input" selectname="searchPlaceholder" selectattr="placeholder">
</div>

最後就是咱們須要封裝的language.extensions.js文件的內容了,裏面作了如下幾件事:

  • 初始化頁面的時候去當前域的cookie裏面取當前瀏覽器保存語言的cookie,根據取到的當前語言版本去初始化國際化組件,而後初始化select組件的選中值
  • 註冊select組件的change事件,根據當前選中的語言,更新cookie裏面的語言信息,而後刷新頁面。

這個文件的內容這裏就不展現了,能夠參考上面的使用示例文章

三、資源文件準備

根據上面的目錄能夠看出,咱們打算將不一樣的語言的資源文件放到不一樣語言的文件夾裏面,這裏暫時不分文件,全部的語言資源放到一個文件common.properties裏面,好比內容以下:

en/common.properties

複製代碼
searchPlaceholder=Please input serach information
signOut=Login Out

station=Station
partno=Part No
description=Description
query=Query

pleaseSelect=Please Select

add=Add
edit=Edit
delete=Delete
複製代碼

zh-CN/common.properties

複製代碼
searchPlaceholder=請輸入關鍵字
signOut=退出

station=站點
partno=零件號
description=描述
query=查詢

pleaseSelect=請選擇

add=新增
edit=編輯
delete=刪除
複製代碼

貌似大功告成!當你down源碼直接在google瀏覽器裏面運行的時候你會發現一個跨域的問題。

要求你在一種webServer裏面去訪問.properties文件,這個問題你只須要使用任何一種webserver運行便可,好比IIS、Apache、Node的web服務器等。博主的代碼是在Visual Studio裏面跑的,因此是基於IIS的,當你把代碼搬到VS裏面跑的時候,第一個問題來了。

2、坑一:配置IIS對.properties文件的支持

若是本文僅僅是上面的內容,是沒啥意義的。接下來纔是本文要介紹的重點!

將上述代碼直接搬到VS裏面,運行的時候你會發現第一個問題:

爲何這裏會請求三個properties文件?由於jquery.i18n.properties.js組件支持三種類型的命名方式,這點不少文章都有介紹,組件代碼運行的時候會去請求三種規則的properties文件,只要找到任何一種規則的文件均可以讀取到裏面的內容。按照博主上文給出的文件目錄

根據這個目錄,那咱們經過 http://localhost:12770/Content/i18n/zh-CN/common.properties 這個url應該能訪問到zh-CN/common.properties這個文件,可實際狀況確實這樣:

對於這種IIS報錯404的問題,C#程序員確定是不陌生的,無非就兩個緣由:

  1. url不正確,這個目錄下面確實沒有找到這個資源文件
  2. 文件的類型iis默認不支持,直接拒絕請求

排除了第一個緣由,那麼只多是第二個緣由引發的了。那麼咱們如何處理呢,在網上搜索半天資料,找到一種解決方案:

這樣確實能繞過IIS的文件名驗證,可是改源碼不是一個好的解決方案,博主有一千個理由來講明改源碼的弊端。這種方式確定不是一個最好的解決方案,因而博主決定另闢蹊徑。

還記得當初博主學習less的時候,iis默認也是不支持.less文件的,因而咱們在web.config裏面加了以下一些配置:

這絕對屬於同類型的問題,加這個配置是顯式告訴IIS,咱們系統裏面某種後綴的文件須要哪一種Processer(處理器或處理組件)去處理,受此啓發,那麼咱們這裏的.properties文件的404問題是否是也能夠經過此種方式解決?若是須要經過這種思路去解決,首要問題是須要找到.properties文件的mimeType,博主思考,既然是在js裏面調用這個.properties文件,那麼咱們是否可使用JavaScript的處理機制來處理.properties文件呢,考慮到上面那種將全部.properties替換成.js的處理方式,彷佛.properties和.js有不少類似之處,因而咱們加上以下一條配置:

獲得結果:

試驗成功,就是這麼簡單。固然若是發佈到IIS,可能須要在IIS的MIME類型裏面添加.properties這種類型的映射。

好了,這個問題就這麼愉快的解決了。若是你的WebServer不是基於IIS的,可能沒有這個問題,但我想思路或許相通,供參考!

3、坑二:使用html的data屬性初始化國際化內容

通常狀況下,咱們標籤裏面的內容若是要作國際化,須要使用 $('#id').text($.i18n.prop('proName')); 來給標籤賦值,如今問題來了,咱們開發一個界面,有不少地方都須要去作國際化,咱們總不能這樣每個頁面每個標籤經過這種方式去賦值吧,這樣工做量不是一點大,因而乎博主想,有沒有一種比較好的通用的解決方案去給這些須要作國際化的標籤統一賦值呢。html的data屬性彷佛是一個不錯的選擇!它具備可讀性強、可維護性強、兼容jquery的data()方法等優勢。好比咱們修改國際化組件的方法以下:

複製代碼
jQuery.i18n.properties({
        name: 'common',
        path: '/Content/i18n/' + i18nLanguage + '/', //資源文件路徑
        mode: 'map', //用Map的方式使用資源文件中的值
        language: i18nLanguage,
        callback: function () {//加載成功後設置顯示內容
            console.log("i18n賦值中...");
            try {
                //初始化頁面元素
                $('[data-i18n-placeholder]').each(function () {
                    $(this).attr('placeholder', $.i18n.prop($(this).data('i18n-placeholder')));
                });
                $('[data-i18n-text]').each(function () {
                    //若是text裏面還有html須要過濾掉
                    var html = $(this).html();
                    var reg = /<(.*)>/;
                    if (reg.test(html)) {
                        var htmlValue = reg.exec(html)[0];
                        $(this).html(htmlValue + $.i18n.prop($(this).data('i18n-text')));
                    }
                    else {
                        $(this).text($.i18n.prop($(this).data('i18n-text')));
                    }
                });
                $('[data-i18n-value]').each(function () {
                    $(this).val($.i18n.prop($(this).data('i18n-value')));
                });
            }
            catch(ex){ }
            console.log("i18n寫入完畢");
        }
    });
複製代碼

 經過data屬性獲取標籤,而後對每一個標籤經過對應的data-i18n-屬性進行國際化賦值便可,這裏暫時定義了三種類型data-i18n-placeholder、data-i18n-text、data-i18n-value的屬性,若是有其餘需求,能夠增長其餘類型。

而後看下咱們html頁面的使用

<input class="typeahead" type="text" id="menu_search" data-i18n-placeholder = "searchPlaceholder"/>
<span data-i18n-text="setting"></span>

這樣不用寫一句標籤的賦值代碼,便可對標籤進行國際化。

4、坑三:第三方組件的國際化(一)

對於第三方組件,咱們自定義的代碼裏面的中文要作國際化,我只須要使用$.i18n.prop('key')便可,好比bootstrapTable:

複製代碼
{
        field: 'AuditEventType',
        title: '業務類型',
        width: '12%'
}
複製代碼

直接使用

{
        field: 'AuditEventType',
        title: $.i18n.prop('bllType'),
        width: '12%'
}

便可。這個解決思路很簡單,沒啥好說的,但是有一些第三方組件,本身有國際化的功能,初始化的時候須要指定國際化的類型,形如:

$(".date").datetimepicker({
     format: 'YYYY-MM-DD',//日期格式化,只顯示日期
     locale: 'zh-CN'      //中文化
});

目前想到的解決方案,就是根據cookie裏面存儲的當前語言來顯式賦值

複製代碼
//獲取cookie裏面的語言
var userLanguage = getlanguageCookie("userLanguage");
$(".date").datetimepicker({
     format: 'YYYY-MM-DD',//日期格式化,只顯示日期
     locale: userLanguage =='zh-CN'?'zh-CN':'en-US'     //國際化
});
複製代碼

若是是多種語言,這裏能夠在前端本身去處理。

5、坑四:第三方組件的國際化(二)

 上面介紹了第三方組件初始化時候指定國際化,除此以外,還有另一種狀況,就是不少組件有本身的本地化(關於國際化和本地化的區別,請自行谷歌)文件,它的國際化是經過引用不一樣的本地化js文件來實現的,好比博主經常使用的bootstrapTable組件,它的目錄:

還有其餘組件也是這樣,好比:

 

那麼針對這種狀況,咱們的國際化該如何實現了,這裏博主提供的思路是動態引用js,經過當前cookie裏面保存的語言的類型來引用對應語言的js文件,好比針對bootstrapTable,咱們這樣去動態引用js

複製代碼
     //組件根據國際化動態引入js
var userLanguage = getlanguageCookie("userLanguage");
        //若是cookie裏面沒有,則使用默認值
        if (!userLanguage) {
            userLanguage = 'zh-CN';
        }
        if (userLanguage == 'zh-CN') {
            var script = $('<script><\/script>');
            script.attr('src', '/Content/bootstrap-table/locale/bootstrap-table-zh-CN.js');
            $('body').append(script);
        }
        else if (userLanguage == 'en') {
            var script = $('<script><\/script>');
            script.attr('src', '/Content/bootstrap-table/locale/bootstrap-table-en-US.js');
            $('body').append(script);
        }
複製代碼

若是要想代碼寫得更加優雅,能夠本身去實現前端的抽象工廠,這裏只是提供一種實現思路。

6、總結

排除了以上幾步的困難,咱們的國際化在項目裏面基本就能正常運行起來了,至於WebApi裏面返回消息的中文,若是你也想作國際化,咱們能夠經過將返回消息封裝,統一返回前端處理。本篇文章的「填坑方式」或許不是最好的,但至少給你們提供了一種實現思路,若是你們有更好的實現方式,歡迎留言交流。若是你以爲本文可以幫助你,能夠右邊隨意 打賞 博主。

本文原創出處:http://www.cnblogs.com/landeanfen/

相關文章
相關標籤/搜索