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.

  1. Select an internationalisation technique
  2. Internationalise myCRM by creating a translation for each supported language
  3. 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.

  1. 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.

  2. 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)
  3. 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
  1. 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, select File > New > Interface
         Eclipse opens a New Java Interface window.
  2. Fill in the New Java Interface window.
         At Name enter myCRMConstants
         Accept the defaults for the other fields.
         Click Finish
         Eclipse creates a stub for the myCRMConstants interface.
  3. 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
  1. 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, select File > New > File
         At File name, enter myCRMConstants_de.properties
  2. Change the encoding of the file to UTF-8.
         Select the file and then from the Eclipse menu, select File > Properties or right-click.
         Eclipse opens the Properties window.
         At Text file encoding, select Other UTF-8. Apply and Save the change.
  3. 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.

  1. 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.
  2. Eclipse flags GWT and suggests you include the import declaration.
         import com.google.gwt.core.client.GWT;
  3. 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.

  1. 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>
        
  2. 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 {
    
    
  3. Refresh myCRM in development mode.
         The English language version loads by default.

     

  4. Load the German version.
         Append ?locale=de to the end of the URL.

     

  5. Compile myCRM and open it in production mode.
         The web browser displays myCRM's default interface.
  6. Test the German interface.
         Append the locale to the URL ?locale=de
         The web browser displays myCRM's German interface.
  7. 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.

GWT and smartGWT Best Practices