How to implement i18n in GWT application?

François Esthète picture François Esthète · May 11, 2012 · Viewed 8k times · Source

I have a problem with internationalization. I'm trying to implement support two languages ​​in my GWT application. Unfortunately I never found a complete example how to do it with the help of UiBinder. That is what I did:

My module I18nexample.gwt.xml:

<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='i18nexample'>
<inherits name="com.google.gwt.user.User" />
<inherits name='com.google.gwt.user.theme.clean.Clean' />
<inherits name="com.google.gwt.i18n.I18N" />
<inherits name="com.google.gwt.i18n.CldrLocales" />
<entry-point class='com.myexample.i18nexample.client.ExampleI18N' />
<servlet path="/start" class="com.myexample.i18nexample.server.StartServiceImpl" />
    <extend-property name="locale" values="en, fr" />
    <set-property-fallback name="locale" value="en" />
</module>

My interface Message.java:

package com.myexample.i18nexample.client;

import com.google.gwt.i18n.client.Constants;

public interface Message extends Constants {

    String greeting();
}

The same package com.myexample.i18nexample.client has three properties file:

Message.properties:

greeting = hello

Message_en.properties:

greeting = hello

Message_fr.properties:

greeting = bonjour

My UiBinder file Greeting.ui.xml:

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder 
    xmlns:ui="urn:ui:com.google.gwt.uibinder"
    xmlns:g="urn:import:com.google.gwt.user.client.ui"
    ui:generateFormat="com.google.gwt.i18n.rebind.format.PropertiesFormat"
    ui:generateKeys="com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator"
    ui:generateLocales="default" >
    <ui:with type="com.myexample.i18nexample.client.Message" field="string" />
    <g:HTMLPanel>   
        <ui:msg key="greeting" description="greeting">Default greeting</ui:msg>     
    </g:HTMLPanel>
</ui:UiBinder> 

When the application starts, I always get the output in the browser:

Default greeting

Why? What am I doing wrong?

I tried to run the application from different URL:

http://127.0.0.1:8888/i18nexample.html?gwt.codesvr=127.0.0.1:9997

http://127.0.0.1:8888/i18nexample.html?locale=en&gwt.codesvr=127.0.0.1:9997

http://127.0.0.1:8888/i18nexample.html?locale=fr&gwt.codesvr=127.0.0.1:9997

The result does not change. Although I expected in last case a message bonjour.

If for example I use a g:Buttton instead of the message ui:msg:

<g:HTMLPanel>   
     <g:Button text="{string.greeting}" />      
</g:HTMLPanel>

Then I get as a result of the button with text "hello"

And if I enter the URL:

http://127.0.0.1:8888/i18nexample.html?locale=fr&gwt.codesvr=127.0.0.1:9997

The text on the button changes to "bonjour". Here everything works as expected. But why internationalization is not working in my first case?

And whether there is a difference between the following:

<ui:msg description="greeting">Default greeting</ui:msg>

<ui:msg description="greeting">hello</ui:msg>

<ui:msg description="greeting"></ui:msg>

Should there be different results in these cases? How to write properly?

Please explain to me the principles of internationalization in GWT and why my example does not work. Any suggestions would be greatly appreciated.

Answer

Thomas Broyer picture Thomas Broyer · May 11, 2012

First, the files should be named Message_fr.properties (resp. Message_en.properties), not Message.properties_fr (resp. Message.properties_en).

Then ui:msg et al. in UiBinder will generate an interface (extending com.google.gwt.i18n.client.Messages)), not use one that you defined. For that, you have to use {string.greeting} (where string is the ui:field you gave to your ui:with). The UiBinder generator will do a GWT.create() on the type class of your ui:with, which is what you'd have done in Java code:

Message string = GWT.create(Message.class);
String localizedGreeting = string.greeting();

In the implicit Messages interface (generated by UiBinder), the various ui:generateXxx attributes on the ui:UiBinder will be transformed into annotations on the interface (properties of the @Generate annotation, or the value of the @GenerateKeys annotation).
Then, one method will be generated for each ui:msg, where the attributes generate equivalent annotations (@Key, @Description) and the content of the ui:msg element is the value of the @DefaultMessage annotation. When you have or widgets inside the content, they'll be turned into arguments to the method and placeholders in the @DefaultMessage text (the values will be filled by UiBinder).

I'd suggest you make something working without UiBinder first, and understand how it works; then try the ui:msg in UiBinder, using -gen in DevMode or the compiler so you can see exactly what code does UiBinder generate (yes, it really only generates code that you could have written yourself by hand).

Also, you should add a <set-property name="locale" value="en, fr" /> or you'll still have the default locale around, despite the set-property-fallback (it'd just never be used)).