狂奔 lion

自强不息

富客户端技术中的JavaScript脚本国际化

当前的富客户端可以包含两部分:分别为JSP页面和通过富客户端js组件(如extjs)渲染的组件化窗口页。针对这两部分分别做如下处理:

对于JSP页面的部分采用JSTL标准库的fmt标签,如通过:

<fmt:message key="page.login.title"/>这样的形式进行展现,其中message对应的文本在服务端配置,并在web.xml中配置资源文件的位置,也可以采用spring标签,Structs标签等多种机制。不过为了以后的程序修改兼容性,建议采用JSTL进行JSP页面的国际化。

对于JavaScript,考虑到为提高效率,因为是静态资源,可以很方便的在一定周期内要在客户端浏览器进行缓存,不同的浏览器会有不同的缓存机理,在IE中,js文件通过定义一定的过期期限,C:"Documents and Settings"用户名"Local Settings"Temporary Internet Files下进行缓存,FirefoxC:"Documents and Settings"用户名"Local Settings"Application Data"Mozilla"Firefox"Profiles"XXXXXXX.default"Cache,为了缓存而不是每次下载为了实现富客户端而集成的很大的js,不能用动态的网页来生成(即把JavaScript包装为JSP页面,最简单的,把js扩展名改成jsp并利用jsp的机制做国际化)。因此,需要对JavaScript中国际化的内容通过变量单独加载,举例如下:

var Message = function(){

         this.title =’中文标题’;

         ……

};

var msg = new Message();

/********************************

或:

var msg = {

         title : ‘中文标题’;

};

*********************************/

new Ext.Window({  

     title : msg.title,          

     width : 265,              

     height : 140

});

其中msg对象的定义可以通过在另一个JavaScript文件中引用的本地化文件所定义,也可以通过AJAX返回JSON对象的形式来获取或者动态地进行服务端生成。

两种方法的优缺点定义如下:

方法

缺点

优点

前台主动定义

会产生大量零碎的文件(MJavaScript文件需要对象N个语言的资源文件,总数M×N,如果把这些资源文件都放在一个JavaScript文件中定义,则对客户端来说,要下载过多不必要的资源)

资源定义非常灵活,可以保证只定义自己需要的资源,并且可以做到随时更改其内容

后台自动获取

加重了服务器的负担,需要用到AJAX或者类似dwr的服务端动态加载。具有一定的复杂性

可以和后台及JSP页面共享同一个资源定义文件,并动态生成资源定义文件,减少了开发人员的负担

综上,可以采取如下国际化方法:

针对JavaScript文件的国际化,分成两部分来进行:

对于通用的文本定义,如:”确定返回等等,放在前台的资源文件中,随JavaScript文件一同加载,对于特殊的文本定义通过后台自动获取的形式来展现,这样就可以结合两种方法的优点。

后台实现的如下:

按照命名规约定义页面的文本元素,然后动态的生成一个如下的json对象:

{

messageName : messageValue

         ……

}

然后前台页面JavaScript在加载时获取这个json对象,并应用到页面文本元素的定义中,如利用Extjs的使用方法:

var msg = Ext.util.json.decode(jsonString); 或者服务器动态生成时就表述为var msg={…};的形式,并在头文件指向动态的地址(类似dwr动态生成的机制),然后就可以通过msg.XXX来获得文本定义了。文中涉及的代码如下:

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.ResourceBundle;

import net.sf.json.JSONObject;

/**
 * 根据层次结构获取到特定前缀的所有的资源名称,并把它们放在一个JSON对象中返回,对于相同类型的资源请求进行缓存,
 * 不再动态生成新的内容。这个对象要纳入到Spring的容器中进行管理,把bean的管理模式设置为单例模式就好,所以这里
 * 没有提供对于类的单例封装
 * 
 *  * 
@author 杨一
 
*/


public class HierarchicalMessage {
    
/**资源对象的基础名称*/
    
private String bundleName;
    
/**特定组件所使用的前缀*/
    
private String prefix;
    
/**缓存对象用的哈希表*/
    
private HashMap<String, JSONObject> cachingMap = new HashMap<String, JSONObject>();
    
    
/**设置或注入对象的基础名称*/
    
public void setBundleName(String bundleName) {
        
this.bundleName = bundleName;
    }


    
/**设置或注入所使用的前缀*/
    
public void setPrefix(String prefix) {
        
this.prefix = prefix;
    }


    
/**根据注入的结果返回语言包,(某些情况下,请求的资源是一定的)*/
    
public JSONObject getMessagesWithPrefix(Locale localeName){
        
return getMessagesWithPrefix(this.bundleName,this.prefix,localeName);
    }

    
    
/**
     * 根据层次结构获取到特定前缀的所有的资源名称,并把它们放在一个JSON对象中返回
     * 
*/

    
public JSONObject getMessagesWithPrefix(String bundleName, String prefix, Locale localeName){
        JSONObject toReturn;
        
//拼接的缓存字符串的格式为:i18n/messages$page.login$zh_CN
        String cachingString = new StringBuilder().append(bundleName).append("$").
                                append(prefix).append(
"$").append(localeName.toString()).toString();
        toReturn 
= cachingMap.get(cachingString);
        
if(toReturn != null){
            
return toReturn;
        }

        
        toReturn 
= new JSONObject();
        
        
//此处无需缓存,因为Java核心库会做这件工作
        ResourceBundle rb = ResourceBundle.getBundle(bundleName, localeName);
        
        Enumeration
<String> e = rb.getKeys();
        String keyRef 
= null;
        String componentPrefix 
= new StringBuilder().append(prefix).append(".").toString();
        
int shortNameStartIndex = prefix.length() + 1;
        
while(e.hasMoreElements()){
            keyRef 
= e.nextElement();
            
if(keyRef.startsWith(componentPrefix)){
                toReturn.put(keyRef.substring(shortNameStartIndex), rb.getString(keyRef));
            }

        }

        
        cachingMap.put(cachingString, toReturn);
        
        
return toReturn;
}

}

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import HierarchicalMessage;

public class I18nServlet extends HttpServlet {
    
/**
     * 动态生成一个用于国际化的JavaScript脚本
     * 
     * 
@param request the request send by the client to the server
     * 
@param response the response send by the server to the client
     * 
@throws ServletException if an error occurred
     * 
@throws IOException if an error occurred
     
*/

    
public void doGet(HttpServletRequest request, HttpServletResponse response)
            
throws ServletException, IOException {
        
        response.setContentType(
"application/json");
        response.setCharacterEncoding(
"UTF-8");
        
        PrintWriter out 
= response.getWriter();
        
        HierarchicalMessage hm 
= getHierarchicalMessage();
        hm.setPrefix(
new StringBuilder().append("page.").append(getBizId(request)).toString());
        
//此处从session中获取用户的登陆语言环境
        String json = hm.getMessagesWithPrefix(request.getLocale()).toString();
        
        out.print(
new StringBuilder().append("var msg=").append(json).append(";"));
        out.close();
    }


    
private HierarchicalMessage getHierarchicalMessage() {
        WebApplicationContext wc 
= WebApplicationContextUtils
                                     .getWebApplicationContext(getServletContext());
        HierarchicalMessage hm 
= (HierarchicalMessage)wc.getBean("msgBean");
        
return hm;
    }


    
private String getBizId(HttpServletRequest request) {
        StringBuffer urlString 
= request.getRequestURL();
        
int start = urlString.lastIndexOf("/"+ 1;
        
int end = urlString.lastIndexOf(".");
        
return urlString.substring(start, end);
    }


}


Spring配置:
    
<bean id="msgBean" class="HierarchicalMessage" scope="singleton">
        
<property name="bundleName">
            
<value>i18n/messages</value>
        
</property>
        
<property name="prefix">
            
<value>sys</value>
        
</property>
    
</bean>

Web.xml配置:
    
<servlet>
      
<servlet-name>I18nServlet</servlet-name>
      
<servlet-class>I18nServlet</servlet-class>
    
</servlet>
    
<servlet-mapping>
      
<servlet-name>I18nServlet</servlet-name>
      
<url-pattern>/i18n/*</url-pattern>
    
</servlet-mapping>

 

使用方法:

html页面中先于功能js导入对应的语言js,名称相同,路径在/i18n/xxx.js

同时在根classpath下的i18n/messages资源下定义page.xxx.xxx



 @2008 杨一. 版权所有. 保留所有权利

posted on 2008-12-23 12:07 杨一 阅读(2560) 评论(1)  编辑  收藏

评论

# re: 富客户端技术中的JavaScript脚本国际化 2008-12-23 15:11 1444

学习了
文章不错 看作者名字让我想起了电视里爬出来贞子0_0  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 
<2008年12月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

导航

公告

本人在blogjava上发表的文章及随笔除特别声明外均为原创或翻译,作品受知识产权法保护并被授权遵从 知识分享协议:署名-非商业性使用-相同方式共享 欢迎转载,请在转载时注明作者姓名(杨一)及出处(www.blogjava.net/yangyi)
/////////////////////////////////////////
我的访问者

常用链接

留言簿(5)

随笔分类(55)

随笔档案(55)

相册

Java

其他技术

生活

最新随笔

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜

自强不息


用心 - 珍惜时间,勇于创造