i18n using GWT’s UiBinder

Following my last article on how to efficiently include SEO with GWT, here’s a guide on how to easily add internationalization (i18n) to any UiBinder template and make your application available in different locales. Be sure to read Renaud’s beginner’s tutorial before if you want more details on UiBinders.

The first thing you will need to do is make sure you have Python installed, then grab a copy of Philippe Beaudoin’s mergelocales.py Python script and save it in the root of your project. This script does all the heavy lifting of processing the GWT’s compiler auxiliary extra files for us, and generates clean .properties files.

Preparing your UiBinders

Let’s start with two simple widgets hardcoded in English :

First, we need to add a few attributes to the ui:UiBinder tag to tell GWT’s compiler to generate extras for this UiBinder file:

Second, we need to wrap the translatable text with `ui:msg` tags:

Note the description attribute values. These need to be unique across your application. They are considered to be the unique identifier of the texts you want to translate. It is a good practice to prefix the value with the section of the application the text relates to.

Generating the extras

Now that our UiBinders are ready for translation, we need to generate the auxiliary extra files. This is done by passing the -extras and -extra <FOLDER> parameters to the GWT compiler. If you’re using Maven and the gwt-maven-plugin, you can simply add the extraParam and extra configuration tags:

The <extraParam> parameter tells the GWT compiler that we want to generate the extra auxiliary files, and the <extra> parameter instructs the GWT compiler to generate these files in a folder named extras (or the name of your choice).

In order to generate the extra files, simply compile your application (e.g. mvn clean gwt:compile). The generated extra files will be found at the root of your project. The extras folder will contain a subfolder named by your module: {projectRoot}/extras/{gwtModuleName}. In that folder, you should find one generated properties file per UiBinder file. Since in this example we have 2 UiBinder files, we’ll have 2 .properties file.

You might notice that every translatable text snippet (for instance, Hello, world!, This is an awesome i18n tutorial and Welcome to the i18n sample) is duplicated across the generated properties files; and that the descriptions are scattered across the many generated properties files. As your number of UiBinder files grows, you can imagine the complexity of managing all those snippets for each language of your application. This is where the mergelocales.py script comes into play.

Merging .properties files with mergelocales.py

Merging properties files

The idea is to only have one .properties file per language available in your application so that you have fewer files to maintain. The mergelocales.py script does all that for you. It will look for all the generated .properties files in the extras folder and merge them into a single language-specific properties file.

Let’s say we would like our application to also be available in French. We need to create two empty files named LocalizableResource.properties and LocalizableResource_fr.properties. The LocalizableResource.properties will contain the default (English) texts, whereas the LocalizableResource_fr.properties, will contain the French texts. Those files should be located in src/main/resources/com/google/gwt/i18n/client.

Next, we need to populate those files using the script : python mergelocales.py extras/<moduleName> src/main/resources/com/google/gwt/i18n/client/.

The first parameter (extras/<moduleName) tells the script where to look for the .properties files generated by GWT. The second parameter (src/main/resources/com/google/gwt/i18n/client/) tells the script where to write the centralized translations files. For our example, this would output:

You will notice that both files contain almost the same text, except for the #TODO: TRANSLATE comments found in the French properties file. Now, I can easily translate the LocalizableResource_fr.properties file:

That’s it! We now have a translated application. The last thing we need to do is to provide those translations to our users.

Handling the Locale

The GWT compiler needs to generate a permutation for each combination of browser and locale supported by our application. To tell the compiler which locale is supported, you need to add the following lines to your GWT’s module xml file (.gwt.xml).

This activates English and French permutations, and instructs the compiler that the default locale should be English. Without the 2 last lines, the default locale would be considered to be a different locale, even though all the texts are English, making the number of permutations that need to be compiled significantly higher.

You can now switch locale using a query parameter in your url. For example, to load our sample in French, we would simply need to use this URL : http://www.example.org/?locale=fr.

The complete source code for this article is avaiable on GitHub. For a more complete example, you can take a look at our website’s source code, also available on GitHub. You can also find more information on Locales or UiBinder on GWT’s documentation site.

Subscribe to our newsletter to receive our upcoming posts.

Thanks to Simon-Pierre Gingras (@spgingras, +SPGingras) for the original version of this example.

2

Better SEO with GWT

When we started working on our new website, we quickly faced challenges often encountered with single-page application frameworks: enabling search engine optimization (SEO) and crawlability. The problem with these kinds of applications is that you only have one .html host file, which has two important consequences:

  1. Having only one title tag makes it hard for people to use the browser history for navigation
  2. It becomes impossible to define meta properties with relevant data for different site sections

Thankfully, each of these problems can be solved.¨

Changing the title

This is actually a simple fix. We can use GWT’s Document class to change the title when our section is attached to the DOM:

Adding and updating meta properties

The hard way

We could create MetaElements and inject them into the head section manually when our section is attached to the DOM :

Using GWT-SEO

This seems a bit cumbersome and isn’t quite what webmasters are used to, so perhaps we can do better. This is where GWT-SEO comes into play. It allows us to define meta properties in the UiBinder’s ui.xml file as a Widget, almost like we would define them in an html file. The library will then create and inject the defined meta elements in the head section when your View is attached to the DOM, removing the need to register your own AttachHandler.

Now that’s better! Notice that the title is also handled, which means no more document.setTitle() is needed either!

Internationalization (i18n)

Moreover, since we’re within a UiBinder, we can therefore take advantage of the power of ui:msg to internationalize our properties (more details on using GWT i18n with UiBinder in an upcoming post!). In the meantime, you can take a look at our website for a live example of GWT-SEO using i18n, and especially at the sources of the website to see more in details how it was done, or at the source of my bee page for a specific example.

Dynamic data

More often than not, when implementing a single-page application, the content of the section will depend on some data fetched from an AJAX request (e.g. : a product page in an online store). GWT-SEO can also help you with that, although it requires some Java code in your request’s callback. Let’s say we have a Presenter that retrieves a Product when the product’s dynamic page is loaded, and passes it to its View through the setProduct method.

The advantage of using GWT-SEO, versus manually creating MetaElements to inject dynamic properties, is that it prevents duplicates by replacing existing properties with new ones, by leveraging the injection of derived tags (title and og:title, description and og:description, etc.).

But what good would all that do if crawlers like Google or Facebook can’t retrieve the fully rendered version of the page?

Making the application crawlable

Making your application crawlable could be the topic of a whole other blogpost, but I will discuss it briefly here.

Most crawlers nowadays implement a simple directive that allows us to serve different content when a hashbang (#!) is encountered in an URL. So the first step is to make sure your section URLs contain hashbangs. For example, a crawler fetching http://www.arcbees.com/#!/support would replace the hashbang with the escaped_fragment query parameter, in this case : http://www.arcbees.com/?_escaped_fragment_=/support (take a look at the rendered DOM).

Rendering for crawlers

Now that we have a way to identify crawlers’ requests, we need to serve a rendered version when we detect the _escaped_fragment_ query parameter to ensure the crawler doesn’t need to run any JavaScript code to access the content. To achieve this, we need something like HtmlUnit or PhantomJS to retrieve our webpage and process the JavaScript until we have a fully rendered page. Or better yet, use GWTP Crawler Service, which handles everything for us, from detecting crawlers’ requests to generating a fully rendered page!

Conclusion

Writing the meta properties for all the sections of your single-page application will allow you to be better ranked on search engines, and give you a chance to stand out against your competitors. It will also make your pages easier to share across social medias, which is, in this 2.0 era, mandatory for any web page. SEO and crawlability are easy things to overlook when developing your application, but to ignore them is to miss out on a substantial number of potential visitors!

 

8