Internationalising your Grails + GWT applications

The recent 0.4 release of the Grails GWT plugin includes some enhancements in the area of internationalisation (i18n), so now seems like a good time to have a look at how you can add i18n to a Grails/GWT app. This article focuses on the specifics of getting GWT’s i18n support working with Grails, but the GWT website has a useful tutorial if you want more information.

Localising text

Grails and GWT take different approaches to i18n. Both systems are based on properties files with names that follow the usual Java i18n convention for resource bundles, but that’s as far as the similarities go. Grails expects to find the bundles in the grails-app/i18n directory, but GWT looks for them in the module directory structure. With static internationalisation, GWT also requires each property file to have an associated Java interface.

Ideally, you would be able to use the same properties files for both standard GSP pages and GWT code, but the plugin doesn’t support this yet. I don’t even know if it’s possible. Despite that, the plugin does make it easy to get started. Let’s say you have a GWT module org.example.Main that you want to internationalise. The first step is to create the resource bundles that will contain the localised text:

grails create-gwt-i18n org.example.Main
This simple command will generate two files for you:

  • src/gwt/org/example/client/MainConstants.properties
  • src/gwt/org/example/client/MainMessages.properties

The MainConstants.properties file is for basic localised text, whereas MainMessages.properties contains parameterised messages like “Today is {0,date,medium}” (where {0…} is a message parameter). Other than that, both properties files can be treated as standard Java resource bundles.

So how do you access these messages from your GWT code? By creating Java interfaces based on the entries in the properties files and loading the appropriate implementations for those interfaces using GWT’s deferred binding mechanism. It sounds complicated, but in practice it’s pretty straightforward. The plugin provides a Grails command that will perform the first step of creating the appropriate Java interfaces:

grails compile-i18n
Assuming that you have added some enties to the MainConstants.properties and MainMessages.properties files, this command will create

  • src/gwt/org/example/client/MainConstants.java
  • src/gwt/org/example/client/MainMessages.java

In fact, you don’t even need to run this command explicitly because the plugin will run it automatically before compiling the GWT modules.

Both the *Constants and *Messages interfaces contain methods named after the keys in the corresponding properties files. For example, an entry cancelLabel will result in a method cancelLabel(). In the Java world, developers commonly use ‘.’ as a separator in resource bundle keys. You can do this with GWT too, but you end up with underscores in the method names. For example, cancel.label becomes cancel_label(). In other words, the generated methods don’t follow the Java convention. Whether that’s an issue is really down to you.

Now that you have the i18n interfaces, you can start replacing hard-coded text in your GWT UI with localised text. You first need to load the appropriate i18n classes:

public class Main implements EntryPoint {    
    private MainConstants constants = GWT.create(MainConstants.class);
    private MainMessages messages = GWT.create(MainMessages.class);
    ...

You can now call the methods on the constants and messages objects to get the localised text:

public class Main implements EntryPoint {
        ...
        Button saveButton = new Button(constants.save_label());
        Button saveButton = new Button(constants.cancel_label());
        ...
        // Report an error
        RootPanel.get("errorMessage").add(new HTML(messages.msg_upload_fail(filename)));
}

You can also see in the above example how you pass parameters to a message as simple method arguments.

Adding locales

That’s almost all there is to it. Your application will now work with the default locale. To add extra locales, though, requires a few more steps. First off, you need to create the language-specific resource bundles with the translated text. Say we want to display the application in French. We create the files

  • src/gwt/org/example/client/MainConstants_fr.properties
  • src/gwt/org/example/client/MainMessages_fr.properties

and add the French versions of all the messages there. Next, we need to inform GWT of the new locale by adding the following line to the module file:

<module>
    ...
    <!-- Supported languages -->
    <extend-property name="locale" values="fr"/>
</module>

That’s it! Your users can now view the application in French.

Setting the locale

The application is now localised, but how do you pick which locale to display? The simplest approach is to add a “locale=…” query parameter to the application’s URL. GWT picks up that parameter and loads the appropriate resource bundles. Unfortunately, this doesn’t work so well with Grails because it uses a different query parameter: “lang=…”.

Another issue is that you have to add the “locale” query parameter to any internal links so that the correct locale is propagated to the different pages. This is a pretty ugly and error-prone approach.

Have no fear! There is a simple solution. Simply add the following meta tag either to your GSP pages or, even better, your layouts:

<%@ page import="org.springframework.web.servlet.support.RequestContextUtils" %>
<html>
<head>
    ...
    <meta name="gwt:property" content="locale=${RequestContextUtils.getLocale(request)}">
</head>
...
</html>

Not only will this ensure that GWT uses the same locale as Grails, but the locale will also be retained between pages and modules. To change the locale to French, all you have to do is load the application with the query parameter “lang=fr”. Next time you reload the page without the query parameter, it will still be in French! The same when you navigate to a different URL with the application. Now that’s what I call a result.

Have fun internationalising your Grails/GWT apps!

4 thoughts on “Internationalising your Grails + GWT applications

  1. Peter Post author

    That’s a good question and I don’t yet know the answer. I would prefer to put the resource bundles in the grails-app/i18n directory, but I have no idea whether it’s possible to get GWT to pick them up from there. If it could, would Grails support the package structure?

    I may look at this at some stage, but there are no guarantees.

  2. Will

    Thanks for the post.
    Any updates on how to share the localization messages or constants between the GWT and Grails?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>