Asp.net專案基於jQuery.i18n.properties 實現前端頁面的資源國際化

駑馬農夫發表於2017-10-12
主題 概要
JS 前端頁面,多語言
編輯 時間
新建 20171011
序號 參考資料
1 https://github.com/jquery-i18n-properties/jquery-i18n-properties
2 http://blog.csdn.net/aixiaoyang168/article/details/49336709
3 http://www.cnblogs.com/landeanfen/p/7581609.html

任何技術的引進都是為了解決問題,要想給一個好好執行的網站,支援多語言,如果設計的時候沒進行相應設計,調整起來是相當麻煩的。

假設有一個導航欄控制元件:
這裡寫圖片描述

為了把它做成中英文兩套系統,使用者登入的時候,根據他習慣的語言進行載入。

權宜的做法是一個控制元件,寫兩份程式碼,if-else進行判斷。
這不但產生了大量的重複程式碼,而且以後要同時維護兩份程式碼,如果每個控制元件,每個頁面都這樣處理,是給自己找罪受,十分不可取的。

這裡也用網上廣泛使用的jQuery.i18n.properties進行處理。

引進jQuery.i18n.properties

jQuery.i18n.properties在github上的地址:
https://github.com/jquery-i18n-properties/jquery-i18n-properties

所謂引進,就是把它的jquery.i18n.properties.js檔案複製下來,並引用到我們的頁面內。

為此,我在我們的Scripts資料夾下,新建個i18n_properties目錄:
這裡寫圖片描述

用來存放js檔案和翻譯檔案:
這裡寫圖片描述

language.extensions.js是我們自己的js,zh-cn,eu-us分別是對應中文網站和英文網站。

Zh-ch下,現在只有兩個.properties,一個公共的翻譯項,一個是特定頁面的翻譯項,比如現在這個Navgrey.properties表示導航控制元件的翻譯項。

這裡寫圖片描述

在頁面中引進這兩個指令碼進行使用:
這裡寫圖片描述

使用jQuery.i18n.properties

在要進行多語言切換在頁面,新增一個隱藏域:

<input type="hidden" id="i18n_pagename" value="<%=_sysUser.CurrentLanguage%>"  name="Common-Navgrey"/>

其中,value表示使用者登入時喜好的語言,name是表示當前頁面用到的.properties檔案,比如,上面Common-Navgrey表示用到了Common.properties和Navgrey.properties兩個檔案。

Js中解析這個語言選項,和資原始檔陣列:

var optionEle = $("#i18n_pagename");
    if (optionEle.length < 1) {
        console.log("Unknown i18n_pagename");
        return false;
    };
    var sourceName = optionEle.attr('name');
    sourceName = sourceName.split('-');

    var optionLang = optionEle.attr('value');

自定義標記

為了讓js判斷以什麼方式處理翻譯,以及賦值什麼內容,需要給需要進行多語言處理的元素自定義兩個屬性作為標記。

假設為data-i18nclassname和data-i18npropname:

data-i18nclassname="I18nHtmlText" data-i18npropname='Common-Nav-H1-Home'

這裡使用到了html5支援的以data-開始的自定義屬性,data-i18nclassname指定以什麼方式處理, data-i18npropname指定要賦值的內容。

這可能不好理解,舉例說明:

還是以這個導航控制元件來說,假設有一級標題公告和新聞,注意它們後面有個倒三角圖示:
這裡寫圖片描述

可以看到,它其實是個a標籤,“公告”後面還接了一個span。
這裡寫圖片描述

同理,新聞標題,“新聞”後面也接了一個span。
這裡寫圖片描述

而公告下面的二級標題:
這裡寫圖片描述

是在文字的左側有個小三角。
這裡寫圖片描述

Span是在“未讀公告”的前面。

換句話說,data-i18nclassname指示了span是在前面還是後面,data-i18npropname指示了是表示新聞還是公告。

這個控制元件只有兩種型別的data-i18nclassname, ”I18nHtmlText”,”I18nHtmlTextLeftArr”。每種型別js中定義一種處理方式,用簡單工廠方法。

基類,傳入的是當前選擇的元素:

/*
I18n的型別基類,不同的型別有不同的處理方式
 */
function I18nClassBase(select) {
    this.select = select;
}
I18nClassBase.prototype.doI18n = function() {}

一級標題類,把小三角append到屬性值的後面:

/*
處理Html文字型別的i18n
 */
function I18nHtmlTextClass(select) {
    this.select = select;
}

I18nHtmlTextClass.prototype = new I18nClassBase(this.select);
I18nHtmlTextClass.prototype.doI18n = function() {
    var propNam = "";
    try {
        propName = $(this.select).data(I18nGlobalConst.I18nPropName());
        var propValue = $.i18n.prop(propName);
        var child = $(this.select).children();
        $(this.select).empty().append(propValue).append(child);
    } catch (ex) {
        console.log("I18n propName[" + propName + "] failed");
        console.log(ex);
    }
}

二級標題類,把屬性值append到小三角的後面:

function I18nHtmlTextLeftArrClass(select) {
    this.select = select;
}

I18nHtmlTextLeftArrClass.prototype = new I18nClassBase(this.select);
I18nHtmlTextLeftArrClass.prototype.doI18n = function() {
    var propNam = "";
    try {
        propName = $(this.select).data(I18nGlobalConst.I18nPropName());
        var propValue = $.i18n.prop(propName);
        var child = $(this.select).children();
        $(this.select).empty().append(child).append(propValue);
    } catch (ex) {
        console.log("I18n propName[" + propName + "] failed");
        console.log(ex);
    }
}

簡單工廠:

/*
  簡單工廠,例項每種I18n的處理型別
 */
function I18nFactory(className, select) {
    switch (className) {
        case I18nGlobalConst.I18nHtmlText():
            return new I18nHtmlTextClass(select);
        case I18nGlobalConst.I18nHtmlTextLeftArr():
            return new I18nHtmlTextLeftArrClass(select);
        default:
            return new I18nClassBase(select);
    }
}

其它語言轉來客串js的,有強迫症,把常量用閉包封裝到一個類中:

var I18nGlobalConst = (function() {
    var i18nHtmlText = 'I18nHtmlText';
    var i18nHtmlTextLeftArr = 'I18nHtmlTextLeftArr';
    var i18nPropName = 'i18npropname';
    var i18nPathRoot = '/Scripts/i18n_properties/';
    var constants = {};

    constants.I18nHtmlText = function() {
        return i18nHtmlText;
    }

    constants.I18nPropName = function() {
        return i18nPropName;
    }

    constants.I18nPathRoot = function() {
        return i18nPathRoot;
    }

    constants.I18nHtmlTextLeftArr = function() {
        return i18nHtmlTextLeftArr;
    }


    return constants;
})();

執行入口函式:

/**
 * 執行頁面i18n方法
 * @return
 */
var execI18n = function() {
    /*
    獲取一下資原始檔名
     */
    var optionEle = $("#i18n_pagename");
    if (optionEle.length < 1) {
        console.log("Unknown i18n_pagename");
        return false;
    };
    var sourceName = optionEle.attr('name');
    sourceName = sourceName.split('-');


    var optionLang = optionEle.attr('value');

    /* 需要引入 i18n 檔案*/
    if ($.i18n == undefined) {
        console.log("Please reference i18n js file")
        return false;
    };

    /*
    這裡需要進行i18n的翻譯
     */
    jQuery.i18n.properties({
        name: sourceName, //資原始檔名稱
        path: I18nGlobalConst.I18nPathRoot() + optionLang + '/', //資原始檔路徑
        mode: 'map', //用Map的方式使用資原始檔中的值
        //language: optionLang,
        callback: function() { //載入成功後設定顯示內容
            console.log('Init i18n');
            try {
                $('[data-i18nclassname]').each(function() {
                    var className = $(this).data('i18nclassname');
                    new I18nFactory(className, this).doI18n();
                });
            } catch (ex) {
                console.log(ex);
            }
            console.log('Finish init i18n');
        }
    });
}

可以看到,回撥的時候,如果有新的型別,比如三級標題,四級標題等加進來,這裡不用改一行程式碼,只要寫對應的子類就行了。

這裡寫圖片描述

載入完頁面後執行:

$(function() {
    execI18n();
});

完整程式碼:

/*
全域性常量
 */

var I18nGlobalConst = (function() {
    var i18nHtmlText = 'I18nHtmlText';
    var i18nHtmlTextLeftArr = 'I18nHtmlTextLeftArr';
    var i18nPropName = 'i18npropname';
    var i18nPathRoot = '/Scripts/i18n_properties/';
    var constants = {};

    constants.I18nHtmlText = function() {
        return i18nHtmlText;
    }

    constants.I18nPropName = function() {
        return i18nPropName;
    }

    constants.I18nPathRoot = function() {
        return i18nPathRoot;
    }

    constants.I18nHtmlTextLeftArr = function() {
        return i18nHtmlTextLeftArr;
    }


    return constants;
})();


/*
I18n的型別基類,不同的型別有不同的處理方式
 */
function I18nClassBase(select) {
    this.select = select;
}
I18nClassBase.prototype.doI18n = function() {}


/*
處理Html文字型別的i18n
 */
function I18nHtmlTextClass(select) {
    this.select = select;
}

I18nHtmlTextClass.prototype = new I18nClassBase(this.select);
I18nHtmlTextClass.prototype.doI18n = function() {
    var propNam = "";
    try {
        propName = $(this.select).data(I18nGlobalConst.I18nPropName());
        var propValue = $.i18n.prop(propName);
        var child = $(this.select).children();
        $(this.select).empty().append(propValue).append(child);
    } catch (ex) {
        console.log("I18n propName[" + propName + "] failed");
        console.log(ex);
    }
}



function I18nHtmlTextLeftArrClass(select) {
    this.select = select;
}

I18nHtmlTextLeftArrClass.prototype = new I18nClassBase(this.select);
I18nHtmlTextLeftArrClass.prototype.doI18n = function() {
    var propNam = "";
    try {
        propName = $(this.select).data(I18nGlobalConst.I18nPropName());
        var propValue = $.i18n.prop(propName);
        var child = $(this.select).children();
        $(this.select).empty().append(child).append(propValue);
    } catch (ex) {
        console.log("I18n propName[" + propName + "] failed");
        console.log(ex);
    }
}


/*
  簡單工廠,例項每種I18n的處理型別
 */
function I18nFactory(className, select) {
    switch (className) {
        case I18nGlobalConst.I18nHtmlText():
            return new I18nHtmlTextClass(select);
        case I18nGlobalConst.I18nHtmlTextLeftArr():
            return new I18nHtmlTextLeftArrClass(select);
        default:
            return new I18nClassBase(select);
    }
}


/**
 * 執行頁面i18n方法
 * @return
 */
var execI18n = function() {
    /*
    獲取一下資原始檔名
     */
    var optionEle = $("#i18n_pagename");
    if (optionEle.length < 1) {
        console.log("Unknown i18n_pagename");
        return false;
    };
    var sourceName = optionEle.attr('name');
    sourceName = sourceName.split('-');


    var optionLang = optionEle.attr('value');

    /* 需要引入 i18n 檔案*/
    if ($.i18n == undefined) {
        console.log("Please reference i18n js file")
        return false;
    };

    /*
    這裡需要進行i18n的翻譯
     */
    jQuery.i18n.properties({
        name: sourceName, //資原始檔名稱
        path: I18nGlobalConst.I18nPathRoot() + optionLang + '/', //資原始檔路徑
        mode: 'map', //用Map的方式使用資原始檔中的值
        //language: optionLang,
        callback: function() { //載入成功後設定顯示內容
            console.log('Init i18n');
            try {
                $('[data-i18nclassname]').each(function() {
                    var className = $(this).data('i18nclassname');
                    new I18nFactory(className, this).doI18n();
                });
            } catch (ex) {
                console.log(ex);
            }
            console.log('Finish init i18n');
        }
    });
}

$(function() {
    execI18n();
});

Properties檔案

按名值對這樣儲存,比如,英文:
這裡寫圖片描述

對應的中文:
這裡寫圖片描述

把值賦到data-i18npropname屬性中,data-i18npropname=’Common-Nav-H1-Notice’,根據當前登入的語言,就可顯示是“公告“還是”Notice”。

IIS填坑

參考http://www.cnblogs.com/landeanfen/p/7581609.html
主要在web.config中配置IIS對.properties的支援。

這裡寫圖片描述

相關文章