论坛首页 AJAX版 EXT

浅谈基于ExtJS的AJAX程序中i18n的实现

浏览 1204 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
最后更新时间:2008-05-26

前两天在我的Gajax框架中实现了i18n
先来几张图

1. 中文界面

中文界面

2. 英文界面

英文界面

3. 界面语言设置窗口

界面语言设置窗口

实现步骤如下:

1. 写一个LocaleServlet

package org.gajaxframework.i18n;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.gajaxframework.context.GajaxContext;

/**
 * Gajax Locale Servlet
 * 
 * @author Sam Chen
 * @version 1.0 05/24/2008 11:54
 */
public class LocaleServlet extends HttpServlet {
	
	private static String DEFAULT_LANGUAGE = "EN";
	private static String localeConfigLocation = "/WEB-INF/locale";
	
	private static Map<String, String> LOCALES = new HashMap<String, String>();
	
	private static Log log = LogFactory.getLog(LocaleServlet.class);

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
//	    String language = (String) request.getParameter("language");
	    String pathInfo = request.getPathInfo();
	    String language = null == pathInfo ? "" : pathInfo.substring(pathInfo.lastIndexOf("/") + 1);
		
	    if (null == language || "".equals(language.trim())) {
	    	// FIXME: get language setting from the user's customized settings
	    	language = "EN";
	    }
	    
	    language = (null == language ? DEFAULT_LANGUAGE : language.toUpperCase());

	    String resource = LOCALES.get(language);
	    if (null == resource) {
	    	log.warn("No locale resource found for language '" + language + "'. Defaults to '" + DEFAULT_LANGUAGE + "'");
	    	resource = LOCALES.get(DEFAULT_LANGUAGE);
	    } else {
	    	log.info("Locale resource found for language '" + language + "'.");
	    }
	    
	    printResource(response, resource);
	    
	}
	
	/**
	 * print locale resource to the client
	 * 
	 * @param response
	 */
	private void printResource(HttpServletResponse response, String resource) throws IOException {
		response.setContentType("text/html;charset=utf-8");
		PrintWriter writer = response.getWriter();
		writer.print(resource);
		writer.flush();
		writer.close();
	}

	// ====================== initialization ======================
	@Override
	public void init(ServletConfig config) throws ServletException {
		
		String customizedDEFAULT_LANGUAGE = config.getInitParameter("DEFAULT_LANGUAGE");
		String customizedlocaleConfigLocation = config.getInitParameter("localeConfigLocation");

		// override the default(hard-coded) configurations if there're customized configurations
		if (null != customizedDEFAULT_LANGUAGE) {
			DEFAULT_LANGUAGE = customizedDEFAULT_LANGUAGE;
		}
		if (null != customizedlocaleConfigLocation) {
			localeConfigLocation = customizedlocaleConfigLocation;
		}
		
		loadLocales(localeConfigLocation);
	}

	/**
	 * load all the locales and put 'em to the map
	 * @param localeConfigLocation
	 */
	private void loadLocales(String localeConfigLocation) {
		localeConfigLocation = GajaxContext.REAL_PATH + localeConfigLocation;
		File localeConfigFolder = new File(localeConfigLocation);
		File[] localeFiles = localeConfigFolder.listFiles();
		
		for (int i = 0, l = localeFiles.length; i < l; i++) {
			File localeFile = localeFiles[i];
			String fileName = localeFile.getName();
			if (!fileName.endsWith(".properties") || fileName.endsWith(".original.properties")) {
				continue;
			}
			String language = fileName.substring(fileName.indexOf("_") + 1, fileName.indexOf(".properties"));
			
			InputStream is = null;
			try {
				is = new FileInputStream(localeFile);
				Properties properties = new Properties();
				properties.load(is);
				String json = conver2Json(properties);
				LOCALES.put(language.toUpperCase(), json);
				log.info("Locale resource for language '" + language + "' loaded:\n" + json);
			} catch (FileNotFoundException fnfe) {
				;
			} catch (IOException ioe) {
				;
			} finally {
				try {
					is.close();
				} catch (Exception x) {}
			}
		}
	

	}
	
	/**
	 * convert key-value pairs to JSON format
	 * @param properties
	 * @return
	 */
	private String conver2Json(Properties properties) {
		StringBuilder sb = new StringBuilder();
		sb.append("GajaxLocale={\n");
		sb.append("    'PROJECT_NAME':'MyDesktop',\n");
		sb.append("    'PROJECT_VERSION':'1.0 Beta',\n");
		sb.append("    'SERVLET_CONTEXT_NAME':").append("'").append(GajaxContext.SERVLET_CONTEXT_NAME).append("',\n");
		
		List<String> keys = new ArrayList<String>();
		for (Enumeration<Object> em = properties.keys(); em.hasMoreElements();) {
			String key = (String) em.nextElement();
			keys.add(key);
		}
		Collections.sort(keys, new Comparator<String>() {
			public int compare(String k1, String k2) {
				return k1.compareTo(k2);
			}
		});
		
		for (int i = 0, l = keys.size(); i < l; i++) {
			String key = keys.get(i);
			String value = properties.getProperty(key);
			String suffix = i == l - 1 ? "};" : ",";
			
			if (null == value || "".equals(value.trim()) || "null".equalsIgnoreCase(value.trim())) {
				sb.append("    '").append(key).append("'").append(":").append("null").append(suffix).append("\n");
			} else {
				sb.append("    '").append(key).append("'").append(":").append("'").append(value).append("'").append(suffix).append("\n");
			}
		}
		
		return sb.toString();
	}
	
}

  为了得到中文Locale资源文件对应的UTF-8编码的文件,写个bat来调用%JAVA_HOME%/bin/native2ascii.exe

@echo off
                                         
echo ==============================================
echo Native2Ascii Utility
echo Author Sam Chen, Senior Software Engineer, GRS
echo Version 1.0 05/24/2008 11:23
echo ==============================================

set CURRENT_DIR=%cd%
echo Current DIR is %CURRENT_DIR%
cd ../workspace/MyDesktop/WEB-INF/locale
set CURRENT_DIR=%cd%
echo Current DIR changed to %CURRENT_DIR%
native2ascii.exe -encoding UTF-8 ./Gajax_zh.original.properties ./Gajax_zh.properties
echo Command 'native2ascii.exe -encoding UTF-8 ./Gajax_zh.original.properties ./Gajax_zh.properties' executed successfully
@pause

在web.xml文件中配置这个serlet

<servlet>
        <servlet-name>localeServlet</servlet-name>
        <servlet-class>org.gajaxframework.i18n.LocaleServlet</servlet-class>
        <init-param>
            <param-name>DEFAULT_LANGUAGE</param-name>
            <param-value>EN</param-value>
        </init-param>
        <init-param>
            <param-name>localeConfigLocation</param-name>
            <param-value>/WEB-INF/locale</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
</servlet>
	
<servlet-mapping>
        <servlet-name>localeServlet</servlet-name>
        <url-pattern>/jslib/i18n/GajaxLocale/*</url-pattern>
</servlet-mapping>

2. html文件中用script标签下载locale资源

<!-- i18n resources -->
<script type="text/javascript" src="jslib/i18n/GajaxLocale/zh" charset="utf-8"></script>

注意这个script标签应该排在最前头,以保证locale资源对后续javascript可用

3. 再来个helper -- locale.js

/**
 * GRS.framework.data.Locale
 * @author Sam Chen
 * @version 1.0 11/21/2007 21:22
 * @version 1.0 05/24/2008 19:51 
 * (replaced hard-coded locale object with the JSON object downloaded from the server side)
 */
Ext.namespace("GRS.framework.data");
GRS.framework.data.Locale = function(M) {
   this.map = M || {}
};
Ext.extend(GRS.framework.data.Locale, Ext.util.Observable, {
   get : function(key) {
      var value = this.map[key] || (key + ' not found!'); 
      if(arguments.length > 1 && value.indexOf('{') >= 0) {
         value = new Ext.Template(value).apply(Array.prototype.slice.call(arguments, 1))
      }
      return value
   }
});

// GajaxLocale is a JSON object downloaded from the server side
var locale = new GRS.framework.data.Locale(GajaxLocale);

// shortcut for the method locale.get
$ = locale.get.createDelegate(locale);

4. html文件如下

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>MyDesktop App</title>

    <link rel="stylesheet" type="text/css" href="jslib/ext-2.1/resources/css/ext-all.css" />
    <link rel="stylesheet" type="text/css" href="resources/css/icons/icons.css" />
    
    <!-- i18n resources -->
    <script type="text/javascript" src="jslib/i18n/GajaxLocale/zh" charset="utf-8"></script>
 	
    <script type="text/javascript" src="jslib/ext-2.1/adapter/ext/ext-base.js"></script>
    <script type="text/javascript" src="jslib/ext-2.1/ext-all.js"></script>
    
    <script type="text/javascript" src="jslib/grsframework/data/Locale.js"></script>

	<!-- DESKTOP -->
	<script type="text/javascript" src="jslib/desktop/core/StartMenu.js"></script>
	<script type="text/javascript" src="jslib/desktop/core/TaskBar.js"></script>
	<script type="text/javascript" src="jslib/desktop/core/Desktop.js"></script>
	<script type="text/javascript" src="jslib/desktop/core/App.js"></script>
	<script type="text/javascript" src="jslib/desktop/core/Module.js"></script>
	<script type="text/javascript" src="jslib/desktop/core/DesktopConfig.js"></script>

	<!-- DESKTOP HELPERS -->
	<script type="text/javascript" src="jslib/desktop/helpers/color-picker/color-picker.ux.js"></script>
	
	<link rel="stylesheet" type="text/css" href="jslib/desktop/helpers/color-picker/color-picker.ux.css" />
	<script type="text/javascript" src="jslib/desktop/helpers/preferences/Preferences.js"></script>
	<link rel="stylesheet" type="text/css" href="jslib/desktop/helpers/preferences/preferences.css" />
	
	<!-- MODULES -->
	<script type="text/javascript" src="jslib/modules/layout-window/js/layout-window.js"></script>
	<script type="text/javascript" src="jslib/modules/docs/js/docs.js"></script>
	<link rel="stylesheet" type="text/css" href="jslib/modules/docs/css/docs.css" />
	
	<!-- DESKTOP STYLES -->
	<link rel="stylesheet" type="text/css" href="jslib/ext-2.1/examples/desktop/css/desktop.css" />
	<link rel="stylesheet" type="text/css" href="resources/css/desktop-sam.css" />
</head>
<body id="desktop-body" scroll="no" background="resources/wallpapers/blue-swirl.jpg">
</body>
</html>

 谨发此文,以抛砖引玉。

   
最后更新时间:2008-05-26
EXT真是个好东西,可惜License问题是个问题。
   
0 请登录后投票
最后更新时间:2008-06-30
想问下楼主,如果没有server的支持,纯粹使用ext进行国际化,而又不整个刷新页面,有可能吗?该怎么做?
   
0 请登录后投票
最后更新时间:2008-07-03
引用

commond 2008-06-30
想问下楼主,如果没有server的支持,纯粹使用ext进行国际化,而又不整个刷新页面,有可能吗?该怎么做?


其实这篇文章已经给出了答案。
语言资源的来源对用户是透明的,他们不知道也无需知道它是服务器端动态输出的还是仅仅只是一个静态的js文件...

你不妨把这篇文章再认真看一遍,相信你会发现你问了个不该问的问题。
   
0 请登录后投票
最后更新时间:2008-07-04
楼主能把其他的代码共享吗
   
0 请登录后投票
最后更新时间:2008-07-07
引用
renhaimm
楼主能把其他的代码共享吗


并非原创,我只是改了点代码。如果你常去extjs论坛,你可以看出我用的是mxracer的desktop...代码(js)可以去extjs论坛下载。
   
0 请登录后投票
最后更新时间:2008-07-24
楼主能否将修改后的代码共享?
因为我不知道怎么修改那个“所有程序”显示在最下面?
   
0 请登录后投票
最后更新时间:2008-07-25
引用

sam1982 11 小时前
楼主能否将修改后的代码共享?
因为我不知道怎么修改那个“所有程序”显示在最下面?


initStartMenu : function(mIds){   
    var allProgramsItems = [];   
    if(mIds){   
        for(var i = 0, il = mIds.length; i < il; i++){   
        var m = this.getModule(mIds[i]);   
          if(m){   
            if(m.appType == 'menu'){ // handle menu modules   
                var items = m.items;   
                for(var j = 0, jl = items.length; j < jl; j++){   
                    m.launcher.menu.items.push(this.getModule(items[j]).launcher);   
                }   
            }   
            if (i < 10) {   
                this.startMenu.add(m.launcher);   
            }   
            allProgramsItems.push(m.launcher);   
        }   
      }   
}   
var itemCount = this.startMenu.items.items.length;   
var brs = '';   
switch (itemCount) {   
    case 0:   
        brs = '<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>';   
        break;   
    case 1:   
        brs = '<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>';   
        break;   
    case 2:   
        brs = '<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>';   
        break;   
    case 3:   
        brs = '<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>';   
        break;   
    case 4:   
        brs = '<br><br><br><br><br><br><br><br><br><br><br><br><br><br>';   
        break;   
    case 5:   
        brs = '<br><br><br><br><br><br><br><br><br><br><br><br>';   
        break;   
    case 6:   
        brs = '<br><br><br><br><br><br><br><br><br>';   
        break;   
    case 7:   
        brs = '<br><br><br><br><br><br><br>';   
        break;   
    case 8:   
        brs = '<br><br><br><br><br>';   
        break;   
    case 9:   
        brs = '<br><br><br>';   
        break;   
    case 10:   
        brs = '';   
        break;     
    default:   
        brs = '';   
        break;   
}   
  
this.startMenu.add(brs);   
this.startMenu.add('<hr color="white" size="1">');   
var allProgramsItem = new Ext.menu.Item({   
    id: 'all-programs-item',   
    text: $('module_text_allprograms'),   
    tooltip: $('module_text_allprograms'),   
    iconCls: 'z-application_x-msdos-program-small',   
    handler: function(){   
        return false;   
    },   
    menu: {   
        items: allProgramsItems   
    }   
});   
this.startMenu.add(allProgramsItem);   
}  
   
0 请登录后投票
最后更新时间:2008-07-25
sam.ds.chen 写道
引用

commond 2008-06-30
想问下楼主,如果没有server的支持,纯粹使用ext进行国际化,而又不整个刷新页面,有可能吗?该怎么做?


其实这篇文章已经给出了答案。
语言资源的来源对用户是透明的,他们不知道也无需知道它是服务器端动态输出的还是仅仅只是一个静态的js文件...

你不妨把这篇文章再认真看一遍,相信你会发现你问了个不该问的问题。


请问楼主哦,贴子中的国际化需要刷新界面吗?如果不需要刷新页面的话 可以稍微细致的讲解一下如何实现不刷新切换语言的吗?或者实现的原理呢.
   
0 请登录后投票
最后更新时间:2008-07-25
sam.ds.chen 写道
引用

sam1982 11 小时前
楼主能否将修改后的代码共享?
因为我不知道怎么修改那个“所有程序”显示在最下面?


initStartMenu : function(mIds){   
    var allProgramsItems = [];   
    if(mIds){   
        for(var i = 0, il = mIds.length; i < il; i++){   
        var m = this.getModule(mIds[i]);   
          if(m){   
            if(m.appType == 'menu'){ // handle menu modules   
                var items = m.items;   
                for(var j = 0, jl = items.length; j < jl; j++){   
                    m.launcher.menu.items.push(this.getModule(items[j]).launcher);   
                }   
            }   
            if (i < 10) {   
                this.startMenu.add(m.launcher);   
            }   
            allProgramsItems.push(m.launcher);   
        }   
      }   
}   
var itemCount = this.startMenu.items.items.length;   
var brs = '';   
switch (itemCount) {   
    case 0:   
        brs = '<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>';   
        break;   
    case 1:   
        brs = '<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>';   
        break;   
    case 2:   
        brs = '<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>';   
        break;   
    case 3:   
        brs = '<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>';   
        break;   
    case 4:   
        brs = '<br><br><br><br><br><br><br><br><br><br><br><br><br><br>';   
        break;   
    case 5:   
        brs = '<br><br><br><br><br><br><br><br><br><br><br><br>';   
        break;   
    case 6:   
        brs = '<br><br><br><br><br><br><br><br><br>';   
        break;   
    case 7:   
        brs = '<br><br><br><br><br><br><br>';   
        break;   
    case 8:   
        brs = '<br><br><br><br><br>';   
        break;   
    case 9:   
        brs = '<br><br><br>';   
        break;   
    case 10:   
        brs = '';   
        break;     
    default:   
        brs = '';   
        break;   
}   
  
this.startMenu.add(brs);   
this.startMenu.add('<hr color="white" size="1">');   
var allProgramsItem = new Ext.menu.Item({   
    id: 'all-programs-item',   
    text: $('module_text_allprograms'),   
    tooltip: $('module_text_allprograms'),   
    iconCls: 'z-application_x-msdos-program-small',   
    handler: function(){   
        return false;   
    },   
    menu: {   
        items: allProgramsItems   
    }   
});   
this.startMenu.add(allProgramsItem);   
}  


很感谢楼主的帮助,我搞定了
   
0 请登录后投票
论坛首页 AJAX版 EXT

跳转论坛:
JavaEye推荐