Google Code offered in: English - Español - 日本語 - 한국어 - Português - Pусский - 中文(简体) - 中文(繁體)
At this point, you've created the initial implementation of the StockWatcher application.
In this tutorial, you'll learn how to prepare an application to support other languages and data formats by translating the StockWatcher user interface into German.
Specifically, you will:
This tutorial builds on the GWT concepts and the StockWatcher application created in the Getting Started tutorial. If you have not completed the Getting Started tutorial and are familiar with basic GWT concepts, you can import the StockWatcher project as coded to this point.
File menu, select the Import... menu option.Next button.Finish button.
If you are using ant, edit the gwt.sdk property in StockWatcher/build.xml to point to where you unzipped GWT.
If you look at the current English-language interface for StockWatcher, you will see that there are two types of text that can be localized: constants and messages.
When internationalizing a GWT application, you have several techniques to choose from. Because StockWatcher has only a few constants and parameterized messages in its user interface, you'll use Static String Internationalization.
Static String Internationalization
Static string initialization requires very little overhead at runtime and therefore is a very efficient technique for translating both constant and parameterized strings.
It is also the simplest technique to implement.
Static string internationalization uses standard Java properties files to store translated strings and parameterized messages, then implements strongly-typed Java interfaces to retrieve their values.
Dynamic String Internationalization
Dynamic string internationalization is slower than static string internationalization, but is very flexible.
Applications using this technique look up localized strings in the module's host page; therefore, they do not need to be recompiled when you add a new locale.
If you need to integrate a GWT application with an existing server-side localization system, dynamic string internationalization is the option to consider.
Localizable Interface
The most powerful technique is to implement the Localizable interface. Implementing Localizable allows you to go beyond simple string substitution and create localized versions of custom types. It's an advanced internationalization technique that you probably won't have to use very often.
The process you'll follow for Static String Internationalization is straightforward.
Tip: GWT provides a command-line tool, i18nCreator, that automates the creation of Java interfaces to access strings in properties files. This tool comes in handy if you have existing localized properties files you'd like to reuse.
First create the Java interface (StockWatcherConstants) that accesses the properties files which hold each translation. The interface uses annotations to specify the default translation. This interface implements the GWT Constants interface. This interface is bound automatically to any StockWatcherConstants*.properties files you create because of its name.
The StockWatcherConstants 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 that corresponds with the locale. (We'll show you how to set the locale in a minute.) If no locale is set, StockWatcher uses the default translation specified by the annotations. For example, if the locale is set to German, the stockWatcher method will return AktieWatcher; if no locale is set, the stockWatcher method will return StockWatcher.
com.google.gwt.sample.stockwatcher.client
File > New > InterfaceStockWatcherConstantsFinish
package com.google.gwt.sample.stockwatcher.client;
import com.google.gwt.i18n.client.Constants;
public interface StockWatcherConstants extends Constants {
@DefaultStringValue("StockWatcher")
String stockWatcher();
@DefaultStringValue("Symbol")
String symbol();
@DefaultStringValue("Price")
String price();
@DefaultStringValue("Change")
String change();
@DefaultStringValue("Remove")
String remove();
@DefaultStringValue("Add")
String add();
}Implementation Note: GWT provides another interface (ConstantsWithLookup) which is similar to Constants except that it also contains methods for looking up a localized string dynamically by name at runtime.
When you internationalize your application's interface, keep in mind that the languages you support may contain characters not in the ASCII character set. Therefore, both in the HTML host page (StockWatcher.html), and the Java properties files that contain the translations, you must set the encoding to UTF-8.
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
StockWatcher/src/com/google/gwt/sample/stockwatcher/clientStockWatcherConstants_de.properties
File > Properties or right-click.UTF-8 conflicts with the encoding defined in the content type (ISO-8859-1). Do you wish to set it anyways? You can ignore the warning and apply the change.stockWatcher = Aktienbeobachter symbol = Symbol price = Kurs change = Änderung remove = Entfernen add = Hinzufügen
If you've never dealt with internationalization before, you may be wondering why the _de suffix is appended to German properties file. The suffix _de is the standard language tag for the German language (Deutsch). Languages tags are abbreviations that indicate a document or application's locale. In addition to specifying the language, they can also contain a subtag indicating the region of a locale. For example, the language tag for French-speaking Canada is fr_CA.
In GWT, properties files indicate the locale with a language code suffix (just like Java resource bundles). The exception is the properties file for the default locale. When no locale is explicitly set at runtime, the properties file with no language code suffix is used. For StockWatcher, you've specified the default translation with annotations instead of using a default properties file.
First create the Java interface (StockWatcherMessages). It accesses the properties files which hold the translations of each parameterized message. This interface implements the GWT Messages interface. Unlike the StockWatcherConstants interface, the methods in this interface contain parameters. When you call these methods, the arguments you pass will replace the placeholders you left in the strings in the properties files. This interface is bound automatically to any StockWatcherMessages*.properties files you create because of its name.
Parameterized messages are not limited to pop-up alerts and error messages. Any place in the application where you set text on a Label widget has the potential to be a parameterized message. For example, in StockWatcher the timestamp is a parameterized message; not only do you pass in the value of the date, but the date format can vary by locale.
package com.google.gwt.sample.stockwatcher.client;
import com.google.gwt.i18n.client.Messages;
import java.util.Date;
public interface StockWatcherMessages extends Messages {
@DefaultMessage("''{0}'' is not a valid symbol.")
String invalidSymbol(String symbol);
@DefaultMessage("Last update: {0,date,medium} {0,time,medium}")
String lastUpdate(Date timestamp);
}
Notice that the message strings all have an {0} embedded in them. These are placeholders that will be replaced at runtime by arguments passed to our StockWatcherMessages interface methods.
If you have a string that needs more than one argument, number the placeholders sequentially.
For example: myString = First parm is {0}, second parm is {1}, third parm is {2}.
If your messages contains single quotes ('), as many of those in StockWatcher do, you'll need to replace them with two consecutive single quotes in the Java properties files. In general, the formatting rules for GWT messages are the same that apply to Java's MessageFormat class.
StockWatcher/src/com/google/gwt/sample/stockwatcher/clientStockWatcherMessages_de.properties
lastUpdate = Letzte Aktualisierung: {0,date,medium} {0,time,medium}
invalidSymbol = ''{0}'' ist kein gültiges Aktiensymbol.
The next step in internationalizing StockWatcher is to replace all hardcoded strings within the source code with method calls to one of the two new interfaces.
Currently the StockWatcher application has one string that isn't generated programmatically: the title, StockWatcher. It's an HTML <h1> heading in the host page (StockWatcher.html).
In the Getting Started tutorial we wanted to show you that is possible to mix static HTML elements with those generated by StockWatcher on the same page. It was also a fast and easy way of putting static text around the stock table. However, now that you are internationalizing StockWatcher, you can see that this is not the most flexible strategy.
An easy way to generate this heading is by replacing the text inside the <h1> element with a GWT Label widget and calling its setText(String) method. Remember, GWT widgets cannot be embedded directly into the HTML host page; so first wrap it with a Root panel.
<body>
<img src="images/GoogleCode.png"/>
<h1>StockWatcher</h1>
<h1 id="appTitle"></h1>Now, you should able to set all of StockWatcher's localized strings at runtime.
Go through the StockWatcher class and replace all the strings that are hardcoded text.
private ArrayList<String> stocks = new ArrayList<String>();
private StockWatcherConstants constants = GWT.create(StockWatcherConstants.class);
private StockWatcherMessages messages = GWT.create(StockWatcherMessages.class);
import com.google.gwt.core.client.GWT;
public void onModuleLoad() {
// Set the window title, the header text, and the Add button text.
Window.setTitle(constants.stockWatcher());
RootPanel.get("appTitle").add(new Label(constants.stockWatcher()));
addStockButton = new Button(constants.add());
// Create table for stock data.
stocksFlexTable.setText(0, 0, constants.symbol());
stocksFlexTable.setText(0, 1, constants.price());
stocksFlexTable.setText(0, 2, constants.change());
stocksFlexTable.setText(0, 3, constants.remove());
...
private void addStock() {
final String symbol = newSymbolTextBox.getText().toUpperCase().trim();
newSymbolTextBox.setFocus(true);
// Stock code must be between 1 and 10 chars that are numbers, letters, or dots.
if (!symbol.matches("^[0-9a-zA-Z\\.]{1,10}$")) {
Window.alert("'" + symbol + "' is not a valid symbol.");
Window.alert(messages.invalidSymbol(symbol));
newSymbolTextBox.selectAll();
return;
}
...
private void updateTable(StockPrice[] prices) {
for (int i = 0; i < prices.length; i++) {
updateTable(prices[i]);
}
// Display timestamp showing last refresh.
lastUpdatedLabel.setText("Last update : "
+ DateTimeFormat.getMediumDateTimeFormat().format(new Date()));
lastUpdatedLabel.setText(messages.lastUpdate(new Date()));
}At this point you have created two localized versions of StockWatcher's user interface. But how does GWT know which one to load at runtime? GWT uses client properties to produce customized JavaScript compilations of your GWT application using a mechanism called deferred binding. To pick the correct localized version of StockWatcher to serve at runtime, GWT evaluates the client property, locale.
You saw in the Getting Started tutorial that GWT uses deferred binding to generate different permutations of your application, each one targeting a different web browser. At runtime, the GWT bootstrap code delivers the appropriate permutation depending on which browser the end-user is using. These browser-specific compilations are created because user agent is a GWT client property. In the same way, GWT represents the locale as a client property. This means that the GWT compiler will generate custom versions of internationalized applications representing each supported locale.
When there are multiple client properties, GWT generates a unique compilation for every combination of possible client property values. So, for example, if GWT supports 5 web browsers and you translate an application into 4 different languages, the GWT compiler produces a total of 20 different versions of your application. However, each user of your application is served only the code in the permutation matching his or her particular combination of web browser and locale.
You tell the GWT compiler that StockWatcher now supports the German (de) locale by extending the set of values of the client property, locale.
<entry-point class='com.google.gwt.sample.stockwatcher.client.StockWatcher'/>
<extend-property name="locale" values="de"/>
</module>?locale=de
http://localhost:8888/StockWatcher.html?locale=de
?locale=deAt runtime, how do you determine the user's locale? You might do as many websites do, and present the user with a list of languages or locales to choose from manually. You could also have the web server check the Accept-Language field in the browser's HTTP request to determine the correct locale. If you do this, however, be sure to provide a way for the user to override the value in the Accept-Language field and select their language preference.
Now that StockWatcher is internationalized, 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 a this client property two ways:
<meta name="gwt:property" content="locale=de">http://www.example.org/myapp.html?locale=deIf you specify a client property (such as locale) in both a <meta> tag and the URL, the URL value takes precedence.
The locale settings for a GWT module apply only for that particular instance of that particular module. This means that if your application contains links to other GWT host pages or non-GWT web pages on your site, the locale setting does not carry over to those pages. Thus, if you want to preserve the user-specified locale, you'll either need to pass the locale in the query string of all links in your GWT application, or you'll need to store the locale setting somewhere on the server, which can then insert the appropriate <meta> tag into the host pages of any loaded GWT modules.
At this point you've used Static String Internationalization to generate a German-language permutation of your GWT application. You've implemented localization so that the appropriate permutation loads based on the user's locale.
To learn more about all three internationalization techniques, the GWT I18N module, or how to use GWT i18nCreator to reuse existing localized properties files, see the Developer's Guide, Internationalization.