(Quick Reference)

10 Internationalization - Reference Documentation

Authors: Andres Almiray

Version: 1.2.0

10 Internationalization

This chapter describes Internationalization (I18N) features available to all applications.

10.1 The Messsage Source

Applications have the ability to resolve internationalizable messages by leveraging the behavior exposed by griffon.core.i18n.MessageSource which every single griffon.core.GriffonApplication implements. This interface exposes the following methods:
  • String getMessage(String key)
  • String getMessage(String key, Locale locale)
  • String getMessage(String key, Object[] args)
  • String getMessage(String key, Object[] args, Locale locale)
  • String getMessage(String key, List args)
  • String getMessage(String key, List args, Locale locale)
  • String getMessage(String key, Map args)
  • String getMessage(String key, Map args, Locale locale)

The first set throws NoSuchMessageException if a message could not be resolved given the key sent as argument. The following methods take and additional defaultMessage parameter that may be used if no configured message is found. If this optional parameter were to be null then the key is used as the message; in other words, these methods never throw NoSuchMessageException nor return null unless the passed in key is null.

  • String getMessage(String key, String defaultMessage)
  • String getMessage(String key, String defaultMessage, Locale locale)
  • String getMessage(String key, Object[] args, String defaultMessage)
  • String getMessage(String key, Object[] args, String defaultMessage, Locale locale)
  • String getMessage(String key, List args, String defaultMessage)
  • String getMessage(String key, List args, String defaultMessage, Locale locale)
  • String getMessage(String key, Map args, String defaultMessage)
  • String getMessage(String key, Map args, String defaultMessage, Locale locale)

The simplest way to resolve a message thus results like this

app.getMessage('some.key')

The set of methods that take a List as arguments are meant to be used from Groovy code whereas those that take an Object[] are meant for Java code; this leads to better idiomatic code as the following examples reveal

app.getMessage('groovy.message', ['apples', 'bananas'])
app.getMessage("java.message", new Object[]{"unicorns", "rainbows"});

Of course you may also use List versions in Java, like this

import static java.util.Arrays.asList;
…
app.getMessage("hybrid.message", asList("bells", "whistles"));

Message Formats

There are three types of message formats supported by default. Additional formats may be supported if the right plugins are installed. Resources may be configured using either properties files or Groovy scripts, please refer to the configuration section.

Standard Format

The first set of message formats are those supported by the JDK's MessageFormat facilities. These formats work with all versions of the getMessage() method that take a List or an Object[] as arguments. Examples follow. First the messages stored in a properties file

healthy.proverb = An {0} a day keeps the {1} away
yoda.says = {0} is the path to the dark side. {0} leads to {1}. {1} leads to {2}. {2} leads to suffering.

Then the code used to resolve them

// Groovy
String quote = app.getMessage('healthy.proverb', ['apple', 'doctor'])
assert quote == 'An apple a day keeps the doctor away'
// Java
String quote = app.getMessage("yoday.says", new Object[]{"Fear", "Anger", "Hate"});
assertEquals(quote, "Fear is the path to the dark side. Fear leads to Anger. Anger leads to Hate. Hate leads to suffering");

Map Format

The following format is non-standard (i.e, not supported by MessageFormat) and can only be resolved by Griffon. This format uses symbols instead of numbers as placeholders for arguments. Thus the previous messages can be rewritten as follows

healthy.proverb = An {:fruit} a day keeps the {:occupation} away
yoda.says = {:foo} is the path to the dark side. {:foo} leads to {:bar}. {:bar} leads to {:foobar}. {:foobar} leads to suffering.

Which may be resolved in this manner

// Groovy
String quote = app.getMessage('healthy.proverb', [fruit: 'apple', occupation: 'doctor'])
assert quote == 'An apple a day keeps the doctor away
// Java
import static griffon.util.CollectionUtils.map;
String quote = app.getMessage("yoday.says", map().e("foo", "Fear").e("bar", "Anger").e("foobar","Hate"));
assertEquals(quote, "Fear is the path to the dark side. Fear leads to Anger. Anger leads to Hate. Hate leads to suffering");

Groovy format

Groovy scripts have one advantage over properties files as you can embed custom logic that may conditionally resolve a message based on environmental values or generate a message on the fly. In order to accomplish this feat messages must be defined as closures and must return a String value; if they do not then their return value will be translated to a String. The following message uses the value of the current running environment to determine the text of a warning to be displayed on a label

import griffon.util.Environment

warning.label = { args -> if (Environment.current == Environment.PRODUCTION) { "The application has encountered an error: $args" } else { "Somebody setup us the bomb! $args" } }

10.2 Configuration

Messages may be configured in either properties files or Groovy scripts. Properties files have precedence over Groovy scripts should there be two files that match the same basename. The default configured basename is "messages", thus the application will search for the following resources in the classpath
  • messages.groovy
  • messages.properties

The default basename may be changed to some other value, or additional basenames may be specified too; it's just a matter of configuring a flag in Config.groovy


i18n.basenames = ['messages', 'menus', 'labels']

Both properties files and Groovy scripts are subject to the same locale aware loading mechanism described in Runtime Configuration, that is, the following resources will be searched for and loaded for a Locate set to de_CH_Basel

  • messages.groovy
  • messages.properties
  • messages_de.groovy
  • messages_de.properties
  • messages_de_CH.groovy
  • messages_de_CH.properties
  • messages_de_CH_Basel.groovy
  • messages_de_CH_Basel.properties

Properties files and Groovy scripts used for internationalization purposes are usually placed under griffon-app/i18n as these files are automatically processed with native2ascii when packaging is executed. The default messages.properties file is placed in this directory upon creating an application with create-app.

10.3 Injecting Message Source Behavior

Any component may gain the ability to resolve messages through the application's MessageSource. You only need annotate the class with griffon.transform.MessageSourceAware and it will automatically gain all methods exposed by MessageSource.

This feature is just a shortcut to avoid reaching for the application instance from objects that do not hold a reference to it.

Here's an example of a custom Java bean that's able to resolve messages

@griffon.transform.MessageSourceAware
class Bean {
    String name
}

This class could be used in the following way

class SampleService {
    String lookupValues(String arg) {
        Bean bean = new Bean(name: arg)
        bean.getMessage('some.message.key', [bean.name])
    }
}