* Router Console translation infrastructure:

- Persistent lang setting with routerconsole.lang=xx
      - Loading any page with ?lang=xx changes the persistent setting
      - Add a custom Jetty handler to load foo_xx.jsp if it
        exists for language xx. This is for jsp files with lots
        of text in them. Otherwise use inline translate methods.
        Not for included jsps.
      - Add a script to create and update messages_xx.po translation
        files, and create ResourceBundles from them
      - Add class to translate strings from cached ResourceBundles
      - Add translate wrappers to HelperBase, FormHandler, and *Renderer,
        so calls can be made from both jsp and java files
      - Add two example translations on configupdate.jsp - one in
        the jsp itself and one in the helper.
      - This is for strings in routerconsole only. Will be expanded
        to other webapps and the router later.
This commit is contained in:
zzz
2009-10-18 14:06:07 +00:00
parent 10b84418c3
commit 4497463778
19 changed files with 311 additions and 6 deletions

View File

@ -22,4 +22,10 @@ public class CSSHelper extends HelperBase {
}
return url;
}
/** change default language for the router but don't save it */
public void setLang(String lang) {
if (lang != null && lang.length() > 0)
_context.router().setConfigSetting(Messages.PROP_LANG, lang);
}
}

View File

@ -87,7 +87,7 @@ public class ConfigUpdateHelper extends HelperBase {
buf.append("<select name=\"updatePolicy\">");
if ("notify".equals(policy))
buf.append("<option value=\"notify\" selected=\"true\">Notify only</option>");
buf.append("<option value=\"notify\" selected=\"true\">").append(_("Notify only")).append("</option>");
else
buf.append("<option value=\"notify\">Notify only</option>");

View File

@ -62,7 +62,8 @@ public class ContentHelper extends HelperBase {
/**
* Convert file.ext to file_lang.ext if it exists.
* Get lang from either the cgi lang param or from the default locale.
* Get lang from the cgi lang param, then properties, then from the default locale.
* _context must be set to check the property.
*/
private String filename() {
int lastdot = _page.lastIndexOf('.');
@ -70,9 +71,13 @@ public class ContentHelper extends HelperBase {
return _page;
String lang = _lang;
if (lang == null || lang.length() <= 0) {
lang = Locale.getDefault().getLanguage();
if (lang == null || lang.length() <= 0)
return _page;
if (_context != null)
lang = _context.getProperty(Messages.PROP_LANG);
if (lang == null || lang.length() <= 0) {
lang = Locale.getDefault().getLanguage();
if (lang == null || lang.length() <= 0)
return _page;
}
}
if (lang.equals("en"))
return _page;

View File

@ -190,4 +190,8 @@ public class FormHandler {
}
}
/** translate a string */
public String _(String s) {
return Messages.getString(s, _context);
}
}

View File

@ -29,4 +29,9 @@ public abstract class HelperBase {
//public RouterContext getContext() { return _context; }
public void setWriter(Writer out) { _out = out; }
/** translate a string */
public String _(String s) {
return Messages.getString(s, _context);
}
}

View File

@ -0,0 +1,69 @@
package net.i2p.router.web;
import java.io.IOException;
import java.util.Locale;
import java.util.Map;
import net.i2p.I2PAppContext;
import org.mortbay.http.HttpRequest;
import org.mortbay.http.HttpResponse;
import org.mortbay.jetty.servlet.WebApplicationHandler;
/**
* Convert foo.jsp to foo_xx.jsp for language xx.
* This is appropriate for jsps with large amounts of text.
* This does not work for included jsps (e.g. summary*)
*
* @author zzz
*/
public class LocaleWebAppHandler extends WebApplicationHandler
{
private I2PAppContext _context;
public LocaleWebAppHandler(I2PAppContext ctx) {
super();
_context = ctx;
}
/**
* Handle foo.jsp by converting to foo_xx.jsp
* for language xx, where xx is the language for the default locale,
* or as specified in the routerconsole.lang property.
* Unless language==="en".
*/
public void handle(String pathInContext,
String pathParams,
HttpRequest httpRequest,
HttpResponse httpResponse)
throws IOException
{
//System.err.println("Path: " + pathInContext);
String newPath = pathInContext;
if (pathInContext.endsWith(".jsp")) {
int len = pathInContext.length();
// ...but leave foo_xx.jsp alone
if (len < 8 || pathInContext.charAt(len - 7) != '_') {
String lang = _context.getProperty(Messages.PROP_LANG);
if (lang == null || lang.length() <= 0)
lang = Locale.getDefault().getLanguage();
if (lang != null && lang.length() > 0 && !lang.equals("en")) {
String testPath = pathInContext.substring(0, len - 4) + '_' + lang + ".jsp";
// Do we have a servlet for the new path that isn't the catchall *.jsp?
Map.Entry servlet = getHolderEntry(testPath);
if (servlet != null) {
String servletPath = (String) servlet.getKey();
if (servletPath != null && !servletPath.startsWith("*")) {
// success!!
//System.err.println("Servlet is: " + servletPath);
newPath = testPath;
}
}
}
}
}
//System.err.println("New path: " + newPath);
super.handle(newPath, pathParams, httpRequest, httpResponse);
//System.err.println("Was handled? " + httpRequest.isHandled());
}
}

View File

@ -0,0 +1,78 @@
package net.i2p.router.web;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.I2PAppContext;
import net.i2p.util.ConcurrentHashSet;
/**
* Translate strings efficiently.
* We don't include an English or default ResourceBundle, we simply check
* for "en" and return the original string.
* Support real-time language changing with the routerconsole.lang property.
*
* @author zzz, from a base generated by eclipse.
*/
public class Messages {
public static final String PROP_LANG = "routerconsole.lang";
private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
private static final String _localeLang = Locale.getDefault().getLanguage();
private static final Map<String, ResourceBundle> _bundles = new ConcurrentHashMap(2);
private static final Set<String> _missing = new ConcurrentHashSet(2);
/** current locale **/
public static String getString(String key) {
if (_localeLang.equals("en"))
return key;
ResourceBundle bundle = findBundle(_localeLang);
if (bundle == null)
return key;
try {
return bundle.getString(key);
} catch (MissingResourceException e) {
return key;
}
}
/** lang in routerconsole.lang property, else current locale */
public static String getString(String key, I2PAppContext ctx) {
String lang = getLanguage(ctx);
if (lang.equals("en"))
return key;
ResourceBundle bundle = findBundle(lang);
if (bundle == null)
return key;
try {
return bundle.getString(key);
} catch (MissingResourceException e) {
return key;
}
}
private static String getLanguage(I2PAppContext ctx) {
String lang = ctx.getProperty(PROP_LANG);
if (lang == null || lang.length() <= 0)
lang = _localeLang;
return lang;
}
/** cache both found and not found for speed */
private static ResourceBundle findBundle(String lang) {
ResourceBundle rv = _bundles.get(lang);
if (rv == null && !_missing.contains(lang)) {
try {
// Would it be faster to specify a class loader?
// No matter we only do this once per lang.
rv = ResourceBundle.getBundle(BUNDLE_NAME, new Locale(lang));
} catch (MissingResourceException e) {
_missing.add(lang);
}
}
return rv;
}
}

View File

@ -268,4 +268,8 @@ public class NetDbRenderer {
buf.append("</td></tr>\n");
}
/** translate a string */
private String _(String s) {
return Messages.getString(s, _context);
}
}

View File

@ -319,4 +319,9 @@ class ProfileOrganizerRenderer {
long c = r.getCurrentEventCount() + r.getLastEventCount();
return "" + c;
}
/** translate a string */
private String _(String s) {
return Messages.getString(s, _context);
}
}

View File

@ -111,6 +111,7 @@ public class RouterConsoleRunner {
File tmpdir = new File(workDir, ROUTERCONSOLE + "-" + _listenPort);
tmpdir.mkdir();
wac.setTempDirectory(tmpdir);
wac.addHandler(0, new LocaleWebAppHandler(I2PAppContext.getGlobalContext()));
initialize(wac);
File dir = new File(_webAppsDir);
String fileNames[] = dir.list(WarFilenameFilter.instance());

View File

@ -235,4 +235,9 @@ public class StatsGenerator {
private final static DecimalFormat _pct = new DecimalFormat("#0.00%");
private final static String pct(double num) { synchronized (_pct) { return _pct.format(num); } }
/** translate a string */
private String _(String s) {
return Messages.getString(s, _context);
}
}

View File

@ -310,4 +310,9 @@ public class TunnelRenderer {
private String netDbLink(Hash peer) {
return _context.commSystem().renderPeerHTML(peer);
}
/** translate a string */
private String _(String s) {
return Messages.getString(s, _context);
}
}