Skip to main content

Internationalization of Hudson --- Help wanted!

Posted by kohsuke on December 28, 2007 at 9:45 PM PST

I just posted a new version of Hudson (1.164), which includes the first cut of i18n and localization to Japanese by using it. Localization is an area where the barrier of entry for contributions is low (and there's almost infinite amount of work), so I'm writing this entry to explain how it works in the hope of soliciting contributions.

Here's the way it works. In view scripts of Hudson, which is written in Jelly, message resource references can appear like this:

<a href="configure">${%Configure Hudson}</a>

The text between '${%' and '}' is the message resource key name, and its translations can be defined in the corresponding property file (for example, if the view script is foo.jelly):

Configure\ Hudson=Hudson???

You might have noticed that I chose the key name that can be used as the English message, and this is by design. When property files don't define the message for a particular key, the key itself is used as the message. This eliminates the need of introducing identifiers for every single message, and thus it reduces cognitive overhead for developers.

For messages that have parameters, a reference in view scripts would be something like ${%title(expression1,expression2,...)} and its definition in the property file would be something like title=abc{0}def{1}>. In this case, a property file is required, even for the default locale. This also makes it easier to work on property files, as they read more like translations where English comes left and translation comes right.

On top of this, I wrote a Maven plugin that scans all view scripts and generate empty resource property files for the locale of your choice. So let's say if you are interested in working on French translation, you can just run the following command, and it'll generate all * files with all the place-holder entries:

$ cd path/to/hudson/workspace
$ mvn stapler:i18n -Dlocale=fr

From here, you'd just open * files, work on translations, and send modified files to me, and that's it! I'm still working on putting '${%...}' around all the messages, but the stapler:i18n mojo works incrementally, so when there are more messages, you rerun the mojo and it'll just add missing entries to the existing files without clobbering what you already wrote.

For messages used inside Java code, I wrote a little tool called localizer that reads property files and generates Java methods to format messages. In this scheme, I first write message resource property files (let's say like this:

foo=error at {0} with {1}

When you build Hudson, Maven automatically generates the following class, thanks to the localizer maven plugin:

public class Messages {

    private final static ResourceBundleHolder holder = new ResourceBundleHolder(Messages.class);

     * error at {0} with {1}
    public static String foo(Object arg1, Object arg2) {
        return holder.format("foo",arg1,arg2);

     * error at {0} with {1}
    public static Localizable _foo(Object arg1, Object arg2) {
        return new Localizable(holder, "foo", arg1, arg2);

In this way, you can get auto-completion and type-safe access to message properties, and javadoc popup in your IDE can tell you which message you are going to get. The method signature also enforces the right number of arguments. I've been doing something very similar in Metro and I liked it, so this is a re-implementation of that with proper Maven/Ant integration.

If you are interested in helping us localize Hudson, please let us know.

Related Topics >>