博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

jQuery mobile国际化实现

Posted on 2013-06-13 19:42  三块石头  阅读(2013)  评论(0)    收藏  举报

最近写了一个jQuery mobile国际化插件,用于解决其国际化的问题。

我们知道jQuery mobile采取的是DOM增强,想要达到国际化的效果,我们有如下两种思路:

  • 服务端国际化,类似于struts等国际化,页面完全由后端生成
  • 前端国际化,即前端检测浏览器语言然后自动下载国际化信息并执行

第一种思路在Web应用上使用较多,但是如果页面被打到PhoneGap等容器里面,就无效了。所以我们主要思考第二种方案。由于jQuery mobile是DOM增强,所以我们必须在其增强之前替换国际化信息,否则设置页面DOM一变化,我们就没有机会设置国际化信息了。

在jQuery mobile中,是以Page为单位来渲染的,所以我们可以在每一个Page渲染前进行国际化处理。Page渲染增强前会触发pagebeforecreate事件,我们只需要监听该事件,然后进行国际化即可。如下面的代码所示:

(function( $, window, undefined ) {
        
    var language =  window.navigator.language.replace("-","_"),

        defaultFolder = "i18n",

        mobile = $.mobile,

        version = mobile.version;
    
        
    mobile.i18n = {
        
        /**
         * 获得国际化信息
         * @param {String} key 国际化信息字符串,如"user.name"
         * @param {Object} context 国际化信息上下文,默认为window.i18n
         * @method getText
         */
        getText: function( key, context ){
            if( !key || typeof( key ) !== "string" ) return;
            var parts = key.split("."),
                obj = context || window.i18n || window;
            if ( typeof( obj ) !== "object" ) return;
            for( var i=0, p ; p = parts[i]; i++){
                obj =  ( p in obj ) ? obj[p] : undefined;
                if ( obj === undefined ) return;
            }
    
            return obj;
        },
        
        
        /**
         * 处理某个DOM元素中的国际化标签
         * @param {DOM} 
         * @return {jQuery} 封装后的jQuery对象
         */
        applyI18n: function( ele ){
            var $eles = $( ele ),
                getText = this.getText;
            if( $eles.length ===0 ) return;

            var applyContext = function() {
                var inputs = "input,textarea,select",
                    $this = $( this ),
                    key = $this.attr( "data-i18n" ),
                    value = getText( key ),
                    reg = new RegExp( "\\b" + this.nodeName.toLowerCase() + "\\b" );
                inputs.match( reg ) ? $this.val( value ) : $this.text( value );
            };
            
            var apply2Ele = function( $ele ) {
                $ele.children().length === 0 ? 
                    $ele.each( applyContext ) : 
                    $ele.find( "[data-i18n]" ).each( applyContext );
            };

            return $eles.each( function(){
                var $ele = $(this),
                    isScriptEle = $ele[0].tagName.toLowerCase() === "script",
                    scriptType  = $ele[0].type,
                    $div = $("<div></div>");
                if( scriptType === "" || scriptType === "text/javascript" ) return;
                apply2Ele( isScriptEle ? $div.html( $ele.html() ) : $ele );
                isScriptEle && $ele.html( $div.html() );
            });
        },
        
        /**
         * 获得当前浏览器的语言
         * @return {String} 返回当前语言类型,如"zh-CN"、"en-US"等
         */
        getLanguage: function() {
            return language.replace("_","-");
        }
        
    };
    
    
    var loadJSON = function( folder ) { 
        var url = folder + "/" + language + ".json?_=" + new Date().getTime();
        $.ajax({
            url: url,
            async: false,
            dataType: "json",
            success: function( msg ){
                window.i18n = msg;
            },
            error: function(){
                console.error("error: Could not find file " +  url )
            }
        });
        
    }, i18n = mobile.i18n;
        
    
    // 检测是否开启国际化,用于可以在$(document).bind("mobileinit",function(){})中进行设置
    if( mobile.i18nEnabled ) {
        
        var path = $("script").filter(function(){ return this.src.match(/jquery\.mobile/)})[0].src,
            reg = new RegExp("(.*?)\/\\w+\/jquery\.mobile-" + version +"(?:\.min)?\.js$");
        
        path = path.match(reg)[1];
        
        loadJSON( path + "/" + ( mobile.i18nFolder || defaultFolder ) ) ;
        
        // 页面渲染前进行国际化信息替换
        $(document).bind("pagebeforecreate", function( evt ) { 
            var $page = $( evt.target );
            i18n.applyI18n( $().add( $page ).add( $page.find( "script" ) ) );
        });
   }        

})( jQuery, this );

在代码的最后,我们可以看到监听了pagebeforecreate事件,当Page切换时我们会获得其DOM内容,然后查找是否存在"data-i18n"属性,有的话就会从国际化文件中获取内容。注意上面的代码会自动下载国际化文件,如"zh_CN.json"、"en_US.json"等,这些文件一般位于名为i18n的文件夹,并且该文件夹和jquery.mobile-X.X.X.js的父目录同级。使用样例如下所示:

 

<!DOCTYPE html>
<html >
    <head >
        <meta name="viewport"  content="width=device-width, minimum-scale=1, maximum-scale=1, user-scalable=no" />
        <meta charset='utf-8' />
        <meta name="apple-mobile-web-app-capable" content="yes" />
        <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
        <link rel="stylesheet" href="libs/jquery.mobile-1.3.1.min.css" />
    </head>
    <body  style='visibility:hidden;'>
        <div data-role="page" id='main' data-theme="b">
            <div data-role="header" data-theme="b">
                <h1 data-i18n="main.header"></h1>
            </div>
            <div data-role="content">
                <p><a href="#next" data-transition="slide" data-i18n="main.next"></a></p>
                <input data-i18n="main.header"  />
                <textarea  data-i18n="main.header" ></textarea>
                <select data-i18n="main.value" data-enhance = "false">
                    <option value="1">One</option>
                    <option value="2">Two</option>
                </select>
                <a data-role="button" data-i18n="main.btn" id="btn"></a>
            </div>
            
            <div data-role='footer'>
                <p style='margin-left:10px' data-i18n="main.footer"></p>
            </div>
            
            <script type="text/tpl" id="tpl" >
                <p data-i18n="main.next"></p>
            </script>
        </div>
        
        <div data-role="page" id='next' style="height:100%"  >
            <div data-role="header" >
                <a href="#" data-role="button" data-back="true" data-rel="back" data-i18n="page2.next.back"></a>
                <h1 data-i18n="page2.next.header"></h1>
            </div>
            <div data-role="content">
                <p data-i18n="page2.next.content"></p>
                <p><a data-rel="back" href="#"  data-i18n="page2.next.prev"></a></p>
            </div>
            
        </div>
        <script src="libs/jquery-2.0.2.min.js"></script>
        <script>
            $(document).bind("mobileinit", function(){
                // enable i18n
                $.mobile.i18nEnabled = true;
                //$.mobile.i18nFolder = "i18n"
                $.mobile.ignoreContentEnabled = true;

                $(document).delegate("#btn", "vclick", function(){

                    alert( $.trim( $("#tpl").html() ) );
                });
            });

            $(function(){
                $(document.body).css( "visibility", "visible") ;
            });
        
        </script>
        <script src="libs/jquery.mobile-1.3.1.min.js"></script>
        <script src="i18n.js"></script>
    </body>
</html>

zh_CN.json等国际化文件内容如下所示:

{
     "title": "国际化测试",
     "main": {
        "header": "滑动效果展现",
        "next": "下一页",
        "footer": "我是Footer",
        "value": "2",
        "btn": "获得javascript模板内容"
     },
     
     "page2": {
     
         "next": {
         
            "back": "返回",
            "header": "内容展现",
            "content": "我是内容",
            "prev": "上一页"
         }
     }

}

更详细请参考我的github工程  :-)