最近写了一个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工程 :-)
浙公网安备 33010602011771号