Adding support for other languages
At this point, we've built myCRM's user interface and added support for multiple host pages.
In this post, we'll look at how to add support for other languages by translating myCRM into German.
- Select an internationalisation technique
- Internationalise myCRM by creating a translation for each supported language
- Localise myCRM by selecting the appropriate translation
1. Select an internationalisation technique
There are several ways of adding support for multiple languages to GWT/smartGWT applications. However, we're going to look at just one Static String Internationalisation. Static string internationalisation is a very efficient technique for translating both constant and parameterised strings. It is also the simplest technique to implement. Static string internationalisation uses standard Java properties files to store translated strings and parameterised messages, then implements strongly-typed Java interfaces to retrieve their values.
2. Internationalise myCRM by creating a translation for each supported language
The steps to follow to implement Static String Internationalisation are straightforward.
-
First, we'll implement two Java interfaces:
- one for string constants, the GWT Constants interface (myCRMConstants.java)
- one for parameterised messages, the GWT Messages interface (myCRMMessages.java)
These interfaces use annotations to specify the default translation.
-
Then, for each new language, we'll create two Java properties files:
- one for string constants (myCRMConstants_de.properties)
- one for parameterised messages (myCRMMessages_de.properties)
- Then, we'll replace all the strings hardcoded in myCRM's Java source code with method calls to the appropriate interface.
Tip: GWT provides a command-line tool, i18nCreator, that automates the creation of Java interfaces to access strings in properties files. We'll look at how to use it in a moment mal.
Implement the Constants Interface
First we'll create the Java interface (myCRMConstants) that accesses the properties files which hold each translation. This interface implements the GWT Constants interface and uses annotations to specify the default translation. The interface is bound automatically to any myCRMConstants*.properties files we create because of its name.
The myCRMConstants interface contains methods for each of the constants in the properties files. At runtime, when one of these methods is called, the return value comes from whichever properties file corresponds with the selected locale. If no locale is set, myCRM uses the default translation specified by the annotations in the myCRMConstants interface. For example, if the locale is set to German, the NewActivity method will return "Neues Activity"; if no locale is set, the NewActivity method will return "New Activity".
-
Create myCRMConstants
-
In the client subpackage, create an interface and name it myCRMConstants.
In Eclipse, in the Package Explorer pane, select the package
au.org.myCRM.client
From the Eclipse menu, selectFile > New > Interface
Eclipse opens a New Java Interface window. -
Fill in the New Java Interface window.
At Name entermyCRMConstants
Accept the defaults for the other fields.
Click Finish
Eclipse creates a stub for the myCRMConstants interface. -
Replace the stub with following code.
Notice the use of annotations to set default values.package au.org.myCRM.client; public interface myCRMConstants extends com.google.gwt.i18n.client.Constants { // Menus @DefaultStringValue("New Activity") String NewActivityMenuName(); @DefaultStringValue("Task, Fax, Phone Call, Email, Letter, Appointment") String NewActivityMenuItemNames(); @DefaultStringValue("New Record") String NewRecordMenuName(); @DefaultStringValue("Account, Contact, separator, Lead, Opportunity") String NewRecordMenuItemNames(); @DefaultStringValue("Go To") String GoToMenuName(); @DefaultStringValue("Sales") String SalesMenuItemName(); @DefaultStringValue("Leads, Opportunities, Accounts, Contacts") String SalesMenuItemNames(); @DefaultStringValue("Settings") String SettingsMenuItemName(); @DefaultStringValue("Administration, Templates, Data Management") String SettingsMenuItemNames(); @DefaultStringValue("Resource Centre") String ResourceCentreMenuItemName(); @DefaultStringValue("Highlights, Sales, Settings") String ResourceCentreMenuItemNames(); @DefaultStringValue("Tools") String ToolsMenuName(); @DefaultStringValue("Import Data, Duplicate Detection, Advanced Find, Options") String ToolsMenuItemNames(); @DefaultStringValue("Help") String HelpMenuName(); @DefaultStringValue("Help on this Page, Contents, myCRM Online, About myCRM") String HelpMenuItemNames(); // Navigation Pane Header @DefaultStringValue("Workplace") String Workplace(); @DefaultStringValue("Activites") String Activites(); // Navigation Pane @DefaultStringValue("Sales") String SalesStackSectionName(); @DefaultStringValue("Settings") String SettingsStackSectionName(); @DefaultStringValue("Resource Centre") String ResourceCentreStackSectionName(); // Toolbar @DefaultStringValue("New") String New(); // Form Toolbar @DefaultStringValue("Save and Close") String SaveAndClose(); @DefaultStringValue("Help") String Help(); // Form Tabs @DefaultStringValue("General") String GeneralTab(); @DefaultStringValue("Administration") String AdministrationTab(); @DefaultStringValue("Notes") String NotesTab(); }
-
Create myCRMConstants_de.properties
-
In the client subpackage, create a Java properties file.
In Eclipse, in the Package Explorer pane, select the package
au.org.myCRM.client
From the Eclipse menu, selectFile > New > File
At File name, entermyCRMConstants_de.properties -
Change the encoding of the file to UTF-8.
Select the file and then from the Eclipse menu, selectFile > Propertiesor right-click.
Eclipse opens the Properties window.
At Text file encoding, select Other UTF-8. Apply and Save the change. -
Add the mappings for the static text in the German user interface.
Copy and paste the following text into the myCRMConstants_de.properties file.NewActivityMenuName: Neues Activity NewActivityMenuItemNames: Aufgabe, Telefax, Telefon-Anruf, EMail, Buchstabe, Verabredung NewRecordMenuName: Neuer Rekord NewRecordMenuItemNames: Konto, Kontakt, separator, Blei, Gelegenheit GoToMenuName: Gehen Sie zu SalesMenuItemName: Verkäufe SalesMenuItemNames: Bleiarten, Gelegenheiten, Konten, Kontakte SettingsMenuItemName: Einstellungen SettingsMenuItemNames: Verwaltung, Schablonen, Datenverwaltung ResourceCentreMenuItemName: Hilfsmittel-Mitte ResourceCentreMenuItemNames: Höhepunkte, Verkäufe, Einstellungen ToolsMenuName: Werkzeuge ToolsMenuItemNames: Import-Daten, Doppelte Abfragung, Fortgeschrittene Entdeckung, Wahlen HelpMenuName: Hilfe HelpMenuItemNames: Helfen Sie auf dieser Seite, Inhalt, meinCRM Online, Über meinCRM Workplace: Arbeitsplatz Activites: Activites SalesStackSectionName: Verkäufe SettingsStackSectionName: Einstellungen ResourceCentreStackSectionName: Hilfsmittel-Mitte SaveAndClose: Außer und nah Help: Hilfe GeneralTab: Allgemein AdministrationTab: Verwaltung NotesTab: Anmerkungen
Replacing hardcoded strings with generated localised ones
The next step in internationalising myCRM is to replace all hardcoded strings within the source code with method calls to one of the two new interfaces.
Replacing strings set programmatically
Go through the myCRM class files and replace all the strings that are hardcoded text.
-
Create instances of the myCRMConstants and myCRMMessages interfaces.
In the myCRM class, add the following pair of instance fields.
private myCRMConstants constants = GWT.create(myCRMConstants.class);
private myCRMMessages messages = GWT.create(myCRMMessages.class);
Because these are interfaces, not classes, you can't instantiate them directly. Instead, you use the GWT.create(Class) method.
Then you'll be able to use these interfaces' accessor methods to retrieve the appropriate strings. -
Eclipse flags GWT and suggests you include the import declaration.
import com.google.gwt.core.client.GWT; -
Replace all the hardcoded strings with method calls to the constants class.
... // initialise the Navigation Pane NavigationPane navigationPane = new NavigationPane(); navigationPane.add(constants.SalesStackSectionName(), SalesNavigationPaneSectionData.getRecords(), new NavigationPaneClickHandler()); navigationPane.add(constants.SettingsStackSectionName(), SettingsNavigationPaneSectionData.getRecords(), new NavigationPaneClickHandler()); navigationPane.add(constants.ResourceCentreStackSectionName(), ResourceCentreNavigationPaneSectionData.getRecords(), new NavigationPaneClickHandler()); ...
3. Localising myCRM
At this point we've created two localised versions of myCRM's user interface. But how does GWT know which one to load at runtime? GWT uses client properties to produce customised JavaScript compilations of your GWT application using a mechanism called deferred binding. To pick the correct localised version of myCRM to serve at runtime, GWT evaluates the client property, locale.
Identifying myCRM's supported locales
We need to let the GWT compiler know that myCRM now supports the German (de) locale by extending the set of values of the client property, locale.
-
Identify German as a supported language.
Open myCRM.gwt.xml and add the following elements.... <inherits name="com.google.gwt.i18n.I18N"/> <!-- Add German language support --> <extend-property name="locale" values="de"/>Your module definition file should know look like the following:
<?xml version="1.0" encoding="UTF-8"?> <module rename-to='mycrm'> <!-- Inherit the core Web Toolkit stuff. --> <inherits name='com.google.gwt.user.User'/> <inherits name="com.google.gwt.i18n.I18N"/> <!-- Inherit the default GWT style sheet. You can change --> <!-- the theme of your GWT application by uncommenting --> <!-- any one of the following lines. --> <!-- <inherits name='com.google.gwt.user.theme.standard.Standard'/> --> <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> --> <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/> --> <!-- Other module inherits --> <!-- <inherits name="com.smartgwt.SmartGwt"/> --> <inherits name="com.smartgwt.SmartGwtNoTheme"/> <inherits name="com.smartclient.theme.enterpriseblue.EnterpriseBlue"/> <inherits name="com.smartclient.theme.enterpriseblue.EnterpriseBlueResources"/> <!-- Inherit the gwt-multipage module --> <inherits name='com.claudiushauptmann.gwt.multipage.gwt-multipage'/> <!-- Specify the app entry point class. --> <entry-point class='com.claudiushauptmann.gwt.multipage.client.EntrypointDispatcher'/> <!-- <entry-point class='au.org.myCRM.client.myCRM'/> --> <!-- Specify the paths for translatable code --> <source path='client'/> <source path='shared'/> <!-- In any real-world application, you will define at least --> <!-- one locale in addition to the default locale. --> <extend-property name="locale" values="en"/> <!-- Add German language support --> <extend-property name="locale" values="de"/> </module> -
Update the GWT Multpage regular expression in the annotations for each entry point class.
... @MultipageEntryPoint(urlPattern = "myCRM.html(\\\\?locale=(de|fr))?((&|\\\\?)gwt.codesvr=127.0.0.1:9997)?") public class myCRM implements EntryPoint { -
Refresh myCRM in development mode.
The English language version loads by default.
-
Load the German version.
Append?locale=deto the end of the URL.
-
Compile myCRM and open it in production mode.
The web browser displays myCRM's default interface. -
Test the German interface.
Append the locale to the URL?locale=de
The web browser displays myCRM's German interface. -
Look at the files generated.
You should see files for twice as many permutations as you did before you created the German-language interface.
Setting the locale
Now that myCRM is internationalised, how does GWT know which locale to load at runtime? The answer is that it uses the value of the client property, locale. You can set this client property two ways:
-
Add an HTML <meta> tag to the <head> of the application host page, containing the property name and value:
<meta name="gwt:property" content="locale=de"> -
Append the client property value to the query string of the URL:
http://www.myCRM.org.au/myCRM.html?locale=de
If you specify a client property (such as locale) in both a <meta> tag and the URL, the URL value takes precedence.
Tip: When the GWT complier runs, it goes through several phases. First, the complier identifies which combinations of files need to be built. Then, it generates any client-side code and then it produces the final output. A particular build combination is defined in the GWT module definition file using a <define-property> tag. This establishes a base set of values that are used for the build.
However, during day-to-day development, you may want to use the <set-property> tag in your module defintion file to restrict the compile to a single language or browser version in order to speed up the compile step.
... <!-- Set user agents --> <set-property name="user.agent" value="gecko1_8"/> ...
What's Next
At this point, we've added support for the German language to myCRM. Now we're ready to look at some best practices for architecting GWT and smartGWT applications.
