Friday, February 14, 2014

GWT - i18n made easy

Let's start by creating the good old java properties file, for now let's say we want to support English and Chinese.


Right click the String.properties file, then "Open"


However this properties file can not be directly accessed by the client code, as there is no file system when the complied javascript is running in the browser. GWT makes use of the deferred binding to solve this problem.

We need some interface to represent the keys, and let the GWT compiler/runtime decide how to access the properties file and to decide which language to load. Since the properties to interface translation is pretty mechanical, we could let the tool to do that for us. .
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>gwt-maven-plugin</artifactId>
    <version>${gwt.version}</version>
    <configuration>
        <runTarget>index.html</runTarget>
        <skip>${gwt.skip}</skip>
        <module>${gwt.module}</module>
        <i18nConstantsBundles>
            <i18nConstantsBundle>com.verydapeng.i18n.client.Strings</i18nConstantsBundle>
        </i18nConstantsBundles>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>i18n</goal>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>
notice we have added a new <i18nConstantsBundles> tag to tell gwt-maven-plugin which properties file to translate. We also need to add a new maven goal <goal>i18n</goal>.

add a custom goal to netbeans gwt:i18n, and run it. we shall see the generated interface.

we also need to modify the way to fire up the dev mode


to access the interface, call the might GWT.create

Strings strings = GWT.<Strings>create(Strings.class);

GWT.log(strings.username());
GWT.log(strings.password());
The bad news is every time a new property is added, you need to run the gwt:i18n goal again (changing the value of a property does not need to do so). The good news is the dev mode can pick up the new property on the fly. Just press F5.

using it inside uibinder will be even more fun
<!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:with field='strings' type='com.verydapeng.i18n.client.Strings'/>
    
    <g:HTMLPanel>
        <table>
            <tr>
                <td><ui:text from='{strings.username}' /></td>
                <td><g:TextBox /></td>
            </tr>
            <tr>
                <td><ui:text from='{strings.password}' /></td>
                <td><g:PasswordTextBox /></td>
            </tr>
        </table>
    </g:HTMLPanel>

</ui:UiBinder>
and here is the result
now we need to switch to Chinese mode. add the following code to the prod.gwt.xml to tell gwt compiler that we would like to use Chinese.

<extend-property name="locale" values="zh"/>
now restart the GWT dev mode and change the url from
  http://127.0.0.1:8888/index.html?gwt.codesvr=127.0.0.1:9997
to
  http://127.0.0.1:8888/index.html?gwt.codesvr=127.0.0.1:9997&locale=zh
and voila, the Chinese characters come out.

There is more than one way to inform the compiled GWT codes which locale to pickup. One way is to use the locale url parameter as shown above.

You can also use a meta tag to do this.
<head>
    <meta name="gwt:property" content="locale=zh">
</head>
If neither the url parameter nor the meta is desirable, then we can also make use of cookie. Just tell the GWT compiler which cookie name is designated for locale tracking.
<set-configuration-property name="locale.cookie" value="lang"/>
each methods has is own pro and cons, do evaluate all of them and pick the best match.

Code samples are available from https://github.com/verydapeng/gwt-tutorials under the i18n folder

To read more about GWT locale handling, go to http://www.gwtproject.org/doc/latest/DevGuideI18nLocale.html