前言:最近,新的平臺還沒有開發完成,原來的老專案又提出了新的需求:系統國際化。如果是前後端完全分離的開發模式,要做國際化,真的太簡單了,有現成的解決方案,基於Node構建的時下熱門的任何一種技術選型都有成熟的方案,比如:
vue
+vue-i18n
angular
+angular-translate
react
+react-intl
但現在的情況是老的專案並沒有使用這類架構。說起國際化,博主幾年前就做過,在MVC裡面實現國際化有通用的解決方案,主要就是通過資原始檔的方式定義多語言。最初接到這個任務,並沒有太多顧慮,畢竟這種東西有很成熟的解決方案,實現起來難點不會很大。可當真正動起來手來去實現的時候發現一些問題,這裡先介紹下我們老平臺的架構:MVC+WebApi,MVC專案負責頁面渲染,webapi負責資料介面,是一種很傳統的架構方式。國際化主要在MVC端去做就好了,可是由於MVC專案裡面使用了大量第三方bootstrap元件,幾乎95%的元件都是通過js去實現的,比如bootstrapTable,比如bootstrap-select,比如bootstrap-fileinput。如果按照傳統的方式,僅僅在MVC裡面去實現國際化,那麼大量的js程式碼裡面的中文沒法統一處理,並且很多第三方元件有自己的本地化local檔案,和後端的國際化很難統一處理;可能有人又說,那就前後端分開國際化唄,這種方案博主真的想過,但是想到要維護兩套資原始檔,果斷放棄。最後還是決定直接維護一套,做前端國際化好了。於是在網上搜尋基於jquery的國際化,千篇一律,幾乎都說的是jquery + jquery.i18n.properties這種方案,既然大家都這麼選型,那博主也按照這種思路去做好了。
在實現的過程中,有很多值得注意和分享的東西,在此寫一個填坑筆記,希望對大家有幫助!接下來,博主就一步一步帶領大家解決這個過程中遇到的一些坑,如果有這個需求的童鞋可以關注下,可能這些問題你也會遇到。
本文原創地址:http://www.cnblogs.com/landeanfen/p/7581609.html
一、jquery.i18n.properties通用解決方案
關於jquery.i18n.properties的使用,網上資料很多,比較完整的使用可以參考 這篇 ,有比較詳細的使用說明。這裡博主簡單概述下過程。
1、需要引用的js檔案
首先頁面引用的js檔案如下
1 2 3 |
<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頁面裡面,這個檔案就是不用的。
2、html檔案和國際化元件的初始化
這裡直接引用上面示例文章裡面的程式碼,首先需要一個切換中英文的標籤,比如是一個select
1 2 3 4 |
<select id="language"> <option value="zh-CN">中文簡體option> <option value="en">Englishoption> <select> |
然後是一些檢視效果的html標籤
1 2 3 |
<div> <input type="search" class="i18n-input" selectname="searchPlaceholder" selectattr="placeholder"> <div> |
最後就是我們需要封裝的language.extensions.js檔案的內容了,裡面做了以下幾件事:
- 初始化頁面的時候去當前域的cookie裡面取當前瀏覽器儲存語言的cookie,根據取到的當前語言版本去初始化國際化元件,然後初始化select元件的選中值
- 註冊select元件的change事件,根據當前選中的語言,更新cookie裡面的語言資訊,然後重新整理頁面。
這個檔案的內容這裡就不展示了,可以參考上面的使用示例文章
3、資原始檔準備
根據上面的目錄可以看出,我們打算將不同的語言的資原始檔放到不同語言的資料夾裡面,這裡暫時不分檔案,所有的語言資源放到一個檔案common.properties裡面,比如內容如下:
en/common.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 |
searchPlaceholder=請輸入關鍵字 signOut=退出 station=站點 partno=零件號 description=描述 query=查詢 pleaseSelect=請選擇 add=新增 edit=編輯 delete=刪除 |
貌似大功告成!當你down原始碼直接在google瀏覽器裡面執行的時候你會發現一個跨域的問題。
要求你在一種webServer裡面去訪問.properties檔案,這個問題你只需要使用任何一種webserver執行即可,比如IIS、Apache、Node的web伺服器等。博主的程式碼是在Visual Studio裡面跑的,所以是基於IIS的,當你把程式碼搬到VS裡面跑的時候,第一個問題來了。
二、坑一:配置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#程式設計師肯定是不陌生的,無非就兩個原因:
- url不正確,這個目錄下面確實沒有找到這個資原始檔
- 檔案的型別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的,可能沒有這個問題,但我想思路或許相通,供參考!
三、坑二:使用html的data屬性初始化國際化內容
一般情況下,我們標籤裡面的內容如果要做國際化,需要使用 $(‘#id’).text($.i18n.prop(‘proName’)); 來給標籤賦值,現在問題來了,我們開發一個介面,有很多地方都需要去做國際化,我們總不能這樣每一個頁面每一個標籤通過這種方式去賦值吧,這樣工作量不是一點大,於是乎博主想,有沒有一種比較好的通用的解決方案去給這些需要做國際化的標籤統一賦值呢。html的data屬性似乎是一個不錯的選擇!它具有可讀性強、可維護性強、相容jquery的data()方法等優點。比如我們修改國際化元件的方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
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頁面的使用
1 |
<input class="typeahead" type="text" id="menu_search" data-i18n-placeholder = "searchPlaceholder"/> |
1 |
<span data-i18n-text="setting">span> |
這樣不用寫一句標籤的賦值程式碼,即可對標籤進行國際化。
四、坑三:第三方元件的國際化(一)
對於第三方元件,我們自定義的程式碼裡面的中文要做國際化,我只需要使用$.i18n.prop(‘key’)即可,比如bootstrapTable:
1 2 3 4 5 |
{ field: 'AuditEventType', title: '業務型別', width: '12%' } |
直接使用
1 2 3 4 5 |
{ field: 'AuditEventType', title: $.i18n.prop('bllType'), width: '12%' } |
即可。這個解決思路很簡單,沒啥好說的,可是有一些第三方元件,自己有國際化的功能,初始化的時候需要指定國際化的型別,形如:
1 2 3 4 |
$(".date").datetimepicker({ format: 'YYYY-MM-DD',//日期格式化,只顯示日期 locale: 'zh-CN' //中文化 }); |
目前想到的解決方案,就是根據cookie裡面儲存的當前語言來顯式賦值
1 2 3 4 5 6 |
//獲取cookie裡面的語言 var userLanguage = getlanguageCookie("userLanguage"); $(".date").datetimepicker({ format: 'YYYY-MM-DD',//日期格式化,只顯示日期 locale: userLanguage =='zh-CN'?'zh-CN':'en-US' //國際化 }); |
如果是多種語言,這裡可以在前端自己去處理。
五、坑四:第三方元件的國際化(二)
上面介紹了第三方元件初始化時候指定國際化,除此之外,還有另外一種情況,就是很多元件有自己的本地化(關於國際化和本地化的區別,請自行谷歌)檔案,它的國際化是通過引用不同的本地化js檔案來實現的,比如博主常用的bootstrapTable元件,它的目錄:
還有其他元件也是這樣,比如:
那麼針對這種情況,我們的國際化該如何實現了,這裡博主提供的思路是動態引用js,通過當前cookie裡面儲存的語言的型別來引用對應語言的js檔案,比如針對bootstrapTable,我們這樣去動態引用js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//元件根據國際化動態引入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); } |
如果要想程式碼寫得更加優雅,可以自己去實現前端的抽象工廠,這裡只是提供一種實現思路。
六、總結
排除了以上幾步的困難,我們的國際化在專案裡面基本就能正常執行起來了,至於WebApi裡面返回訊息的中文,如果你也想做國際化,我們可以通過將返回訊息封裝,統一返回前端處理。本篇文章的“填坑方式”或許不是最好的,但至少給大家提供了一種實現思路,如果大家有更好的實現方式,歡迎留言交流。
本文原創出處:http://www.cnblogs.com/landeanfen/
歡迎各位轉載,但是未經作者本人同意,轉載文章之後必須在文章頁面明顯位置給出作者和原文連線,否則保留追究法律責任的權利