PDA

View Full Version : i18n in ExtJS-based Apps



sam.ds.chen
25 May 2008, 7:07 PM
The other day I came up with an i18n solution, and now I wanna share it with you all, and any comment is appreciated.
I'm using mxracer's desktop (Many thanks to mxracer;))

As I'm not good at describing things by words, let's take a look at some screenshots first.

1. English UI
http://extjs.com/forum/attachment.php?attachmentid=7023&stc=1&d=1211768621

2. Chinese UI
http://extjs.com/forum/attachment.php?attachmentid=7024&stc=1&d=1211768637

3. Language Preference Setting
http://extjs.com/forum/attachment.php?attachmentid=7025&stc=1&d=1211768653

We've implemented the i18n support this way:
1. Write a locale servlet



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();
}

}


I also wrote a bat to invoke %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


and register the servlet in the web.xml file



<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. Add a script tag in the html file



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

It gotta come first to ensure that the locale resources are available when we use it in the javascripts that follow.

3. We still need a helper, that is, the locale.js file



/**
* 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. So the html file is somethin' like this



<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>

sshinde
20 Nov 2011, 10:19 PM
Simply awesome!