i18n Messages

  1. Overview
  2. GWT-specific formats
  3. Using Annotations
  4. Plural Forms
  5. Select Forms
  6. SafeHtml Messages

Overview

The Messages interface allows you to substitute parameters into messages and even to re-order those parameters for different locales as needed. The format of the messages in the properties files follows the specification in Java MessageFormat (note that the choice format type is not supported with some extensions). The interface you create will contain a Java method with parameters matching those specified in the format string.

Here is an example Messages property value:

permissionDenied = Error {0}: User {1} Permission denied.

The following code implements an alert dialog by substituting values into the message:

public interface ErrorMessages extends Messages {
   String permissionDenied(int errorCode, String username);
 }
 ErrorMessages msgs = GWT.create(ErrorMessages.class)

 void permissionDenied(int errorVal, String loginId) {
   Window.alert(msgs.permissionDenied(errorVal, loginId));
 }

Caution: Be advised that the rules for using quotes may be a bit confusing. Refer to the MessageFormat javadoc for more details.

More Advanced Formatting

As described in the MessageFormat javadoc, you can do more formatting with values besides just inserting the value into the string. If no format type is supplied, the value is just appended to the output string at the proper position. If you want it formatted as a number, you can use {0,number} which will use the locale’s default number format, or {0,number,currency} to use the locale’s default currency format (be careful about which currency you are using though), or create your own pattern like {0,number,#,###.0}. Dates can be formatted with {0,date,medium} etc., and likewise with times: {0,time,full}.

Note that supplying your own format pattern means you are now responsible for localizing that pattern — if you do {0,date,MM/DD/YY} for example, this pattern will be used for all locales and some of them will likely be confused by the month coming before the day.

The Benefits of Static String Internationalization

As you can see from the example above, static internationalization relies on a very tight binding between internationalized code and its localized resources. Using explicit method calls in this way has a number of advantages. The GWT compiler can optimize deeply, removing uncalled methods and inlining localized strings — making generated code as efficient as if the strings had been hard-coded. The value of compile-time checking becomes even more apparent when applied to messages that take multiple arguments. Creating a Java method for each message allows the compiler to check both the number and types of arguments supplied by the calling code against the message template defined in a properties file. For example, attempting to use the following interface and .properties files results in a compile-time error:

public interface ErrorMessages extends Messages {
  String permissionDenied(int errorCode, String username);
}
permissionDenied = Error {0}: User {1} does not have permission to access {2}

An error is returned because the message template in the properties file expects three arguments, while the permissionDenied method can only supply two.

GWT-specific formats

In addition to the formatting supported by MessageFormat, GWT supports a number of extensions.

{name,text}

A “static argument”, which is simply text, except that it appears in translation output as if it were a placeholder named name. text is always terminated by the next “}”. This is useful to keep non-translated code out of what the translator sees, for example HTML markup:

@DefaultMessage("Welcome back, {startBold,<b>}{0}{endBold,</b>}")
{0,list} or {0,list,format...}
Format a List or array using locale-specific punctuation. For example, in English, lists would be formatted like this:
# of Items Sample Output
0 (empty string)
1 a
2 a and b
3 a, b, and c

Note that only the locale-default separator and the logical conjuctive form is supported – there is currently no way to produce a list like “a; b; or c”.

See the plurals documentation for how this interacts with plural support. The format following the list tag, if any, describes how each list element is formatted. Ie, {0,list} means every element is formatted as if by {0}, {0,list,number,#,##} as if by [0,number,#,##}, etc.
{0,localdatetime,skeleton}
Format a date/time in a locale-specific format using the supplied skeleton pattern. The order of the pattern characters doesn’t matter, and spaces or other separators don’t matter. The localized pattern will contain the same fields (but may change MMM into LLL for example) and the same count of each.
If one of the predefined formats are not sufficient, you will be much better off using a skeleton pattern so you will include the items you want but still get a localized format.
For example, if you used {0,date,MM/dd/yy} to format a date, you get exactly that pattern in every locale, which is going to cause confusion for those users who expect dd/MM/yy. Instead, you can use {0,localdatetime,MMddyy} and you will get properly localized patterns for each locale.
{0,localdatetime,predef:PREDEF_NAME}
Use a locale-specific predefined format – see DateTimeFormat.PredefinedFormat" for possible values, example: {0,localdatetime,predef:DATE_SHORT}.
extra formatter arguments
Some formatters accept additional arguments. These are added to the main format specification, separated by a colon – for example: {0,list,number:curcode=USD,currency} says to use the default currency format for list elements, but use USD (US Dollars) for the currency code. You can also supply a dynamic argument, such as {0,localdatetime:tz=$tz,predef:DATE_FULL}, which says the timezone to use is supplied by a parameter TimeZone tz supplied to the method. Where supported, multiple arguments can be supplied like {0,format:arg1=val:arg2=val}.

Currently supported arguments:

Format Argument Name Argument Type Description
number curcode String Currency code to use for currency formatting
date, time, or localdatetime tz TimeZone Time zone to use for date/time formatting

Using Annotations

The annotations discussed here are the ones specific to Messages — for shared annotations see the main Internationalization page.

Method Annotations

The following annotations apply to methods in a Messages subtype:

  • @DefaultMessage(String message) Specifies the message string to be used for the default locale for this method, with all of the options above. If an @AlternateMessage annotation is present, this is the default text used when more specific forms do not apply — for count messages in English, this would be the plural form instead of the singular form.

  • @AlternateMessage({String form, String message, …}) Specifies the text for alternate forms of the message. The supplied array of strings must be in pairs, with the first entry the name of an alternate form appropriate for the default locale, and the second being the message to use for that form. See the Plural Forms and Select Forms examples below.

Parameter Annotations

The following annotations apply to parameters of methods in a

Messages subtype:

  • @Example(String example) An example for this variable. Many translation tools will show this to the translator instead of the placeholder — i.e., Hello {0} with @Example("John") will show as Hello John with “John” highlighted to indicate it should not be translated.
  • @Optional Indicates that this parameter need not be present in all translations. If this annotation is not supplied, it is a compile-time error if the translated string being compiled does not include the parameter.
  • @PluralCount Indicates that this parameter is used to select which form of text to use (ie, 1 widget vs. 2 widgets).

    The argument annotated must be int, short, an array, or a list (in the latter cases the size of the list is used as the count).

Plural Forms

The Messages interface also supports the use of plural forms. In English, you want to adjust the word being counted based on whether the count is 1 or not. For example:

You have one tree.
You have 2 trees.

Other languages may have far more complex plural forms. Fortunately, GWT allows you to easily handle this problem as follows:

public interface MyMessages extends Messages {
  @DefaultMessage("You have {0} trees.")
  @AlternateMessage({"one", "You have one tree."})
  String treeCount(@PluralCount int count);
}

Then, myMessages.treeCount(1) returns "You have one tree." while myMessages.treeCount(2) returns "You have 2 trees."

See the details for using plural forms.

Select Forms

Similar to plural forms above, you might need to choose messages based on something besides a count. For example, you might know the gender of a person referenced in the message ("{0} gave you her credits"), or you might want to support abbreviated and full versions of a message based on user preference.

public enum Gender {
  MALE,
  FEMALE,
  UNKNOWN
}

public interface MyMessages extends Messages {
  @DefaultMessage("{0} gave you their credits.")
  @AlternateMessage({
      "MALE", "{0} gave you his credits.",
      "FEMALE", "{0} gave you her credits."
  })
  String gaveCredits(String name, @Select Gender gender);
}

The default message is used if no form matches, in this case if gender is null or UNKNOWN. @Select parameters may be integral primitives, Strings, booleans, or enums.

SafeHtml Messages

Sometimes, message formats you create include HTML markup, with the resulting messages intended for use in an HTML context, such as the content of an InlineHTML widget. If the message is parameterized and the value of a String-typed parameter is derived from untrusted input, the application is vulnerable to Cross-Site-Scripting (XSS) attacks.

To avoid XSS vulnerabilities due to the use of messages in HTML contexts, you can declare methods in your Messages interfaces with a return type of SafeHtml:

public interface ErrorMessages extends Messages {
   @DefaultMessage("A <strong>{0} error</strong> has occurred: {1}.")
   SafeHtml errorHtml(String error, SafeHtml details);
 }
 ErrorMessages msgs = GWT.create(ErrorMessages.class)

 void showError(String error, SafeHtml details) {
   errorBar.setHTML(msgs.errorHtml(error, details));
   errorBar.setVisible(true);
 }

For SafeHtml messages, the code generator generates code that is guaranteed to produce values that satisfy the SafeHtml type contract and are safe to use in an HTML context. Before a parameter’s value is substituted into a message, the parameter’s value is automatically HTML-escaped, unless the parameter’s declared type is SafeHtml. In the above example, the error parameter is HTML escaped before substitution into the template, while the details parameter is not. The details parameter can be substituted into the message without escaping because the SafeHtml type contract guarantees that its value is indeed safe to use as HTML without further escaping. For more information on how to create SafeHtml values, refer to the SafeHtml Developer’s Guide.

In message formats of SafeHtml messages, parameters are not allowed inside of an HTML tag. For example, the following is not a valid SafeHml message format, because the {0} parameter appears inside a tag’s attribute:

errorHtmlWithClass = A <span class="{0}">{1} error</span> has occurred.

For more information on working with SafeHtml values, see the SafeHtml Developer’s Guide.