The Problem with UiBinder i18n
Anyone who tried to internationalize a GWT application that uses UiBinder at some point got to the official documentation page:
http://www.gwtproject.org/doc/latest/DevGuideUiBinderI18n.html
In very short the official way of doing UiBinder i18n according to the documentation looks like this:
- Add three attributes to EVERY UiBinder ui.xml
- Mark every text you want to translate with a ui:msg or ui:attributes tag
- Run the compiler with the “-extras” flag to auto generate property files containing the key/message pairs used for translation
- Copy those generated property files from the extras folder into your project
- There is one property file per language per UiBinder xml file
- Keys are incomprehensible MD5 hashes
It took me a while to understand what is going on here. Not a very convenient and straight forward way in my opinion!
Even the official documentation has a hard time to take itself seriously:
(…)run the gwt compiler with -extra /tmp/gwt-extras; you’ll find the file in /tmp/gwt-extras/com.example.app.App/com.example.app.client.HelloBinderImplGenMessages.properties. No kidding. Hopefully this will be cleaned up in a future release of the toolkit.
What UiBinder i18n should be like
For me the basic straight forward way of i18n should be:
- Create key/value property files for every language
- Access the messages in UiBinder via the key
- The key is human readable
- One property file per language per module
What if I told you, you can actually do this easily with GWT UiBinder?
The magic key word here is a bit hidden in the developer guide: ui:baseMessagesInterface See the documentation:
ui:baseMessagesInterface
Sets the base interface to use for generated messages. The value must be the fully-qualified class name of an interface that extends Messages. You can then put whatever annotations you want there, making it easy to have company or project-wide settings that can be changed in just one place. You can still use the other attributes to override defaults inherited from that interface if necessary.
Example
What follows is a short example how this style of i18n would look like
WelcomePanel.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:baseMessagesInterface="com.sebastianmetzger.gwt.client.i18n.WelcomeMessages"> <ui:style> .wrapper { padding: 64px; } </ui:style> <g:HTMLPanel styleName="{style.wrapper}"> <h1><ui:msg key="heading">Not translated</ui:msg></h1> <p><ui:msg key="greeting">Not translated</ui:msg></p> </g:HTMLPanel> </ui:UiBinder>
Notice the ui:baseMessagesInterface attribute in the ui:UiBinder tag, which points at com.sebastianmetzger.gwt.client.i18n.WelcomeMessages
WelcomeMessages.java
package com.sebastianmetzger.gwt.client.i18n; import com.google.gwt.i18n.client.Messages; public interface WelcomeMessages extends Messages { // Only add Messages here, you need to access outside of UiBinder }
Note that it is not necessary to explicitly add the keys to the Java interface. They will be taken directly from the property files for UiBinder. You will need to put the messages into the interface, if you want to access the translations from anywhere else though.
WelcomeMessages_en.properties
heading = Welcome greeting = Can I offer you a juicy hamburger?
WelcomeMessages_de.properties
heading = Willkommen greeting = Möchten Sie eine leckere Schweinshaxe essen?
Note: The property files have to be in the same package as the corresponding Messages interface to be mapped automatically.
The Result
Dependent on the set locale the UiBinder template is now filled with the translated messages. Neat!
Get the Example from GitHub
I put the example project into a public github repo. Feel free to use it as a template!
Clone it
git clone https://github.com/Sturmination/gwt_uibinder_i18n
Build and run it (requires Maven)
gwt_uibinder_i18n/mvn clean jetty:run-exploded