Wiki

Clone wiki

javarosa / Internationalization

Internationalization Framework

Overview

JavaRosa supports localization of the interface through a device independent service provider. Dynamic locale switching is supported, as is providing arguments in both a stack and a map format.

Important Background

Terms: '''JavaRosaServiceProvider''', '''Localization''', '''Property Manager'''

See Also

[wiki:ExternalizedStringFormat External Language Resource Files]

Retrieving Localized Strings

The framework provides access to localized text as a mapping from keys to text for a current locale. The JavaRosaServiceProvider provides a few shortcuts to the localizer in order to keep localization code brief. For instance, the localized text for the key '''menu.Next''', in the current locale, can be retrieved with the call

JavaRosaServiceProvider.instance().localize("'''menu.Next'''")

If the string in question has arguments to handle, they can also be provided with the same overloaded helper method

JavaRosaServiceProvider.instance().localize("'''menu.Next'''", new String[]{"arg1","arg2")

More information about arguments and how they are defined and used is provided later in the article.

The Localizer

For content management, access to the localization service is provided by the JavaRosaServiceProvider via the method getLocaleManager(). The main operations for the localizer are

  • '''addAvailableLocale()''' - Adds a new locale.
  • '''setLocale(String)''' - Sets the current locale to be used. When using externalized resources as the data backing, this method will also retrieve the relevant resources and load them into memory
  • '''setDefaultLocale(String)''' - Sets the default locale. If no text is found for a key in the current locale, this locale is treated as a fallback and any text matching the missing key in that default locale will be returned.
  • '''registerLocaleResources(String,String)''' - Registers a resource file in the Jar to be associated with a locale. Each of the key/value pairs in that external file (which is described below) will be available for retrieval.
  • '''getText()''' - Retrieves a piece of localized text with a variable number of arguments.

Externalized File Format

The majority of JavaRosa projects externalize their text through the use of flat text files which are loaded as resources into the build jar. The format of the external file, as well as troubleshooting common troubles with them are provided in [wiki:ExternalizedStringFormat this wiki page].

Locale Arguments

Externalized strings support the use of arguments to allow for the text to be useful in situations where the string needs to contain dynamic data. In order to specify an argument in a string, it should be wrapped in the same format as Ant property references, a dollar sign with braces surrounding the argument name. An example is ${argument}. There are two ways to provide arguments while fetching localized text, by providing a stack of values or by providing a dictionary of arguments.

Although the ordering of parameters across locales can change, the number of parameters in a localized string should remain the same between locales.

Argument Stack

When arguments are provided in a stack, using the datatype String[], the localizer simply places the first element in the stack to the first instance of an argument, seeking linearly from left to right in the string. In this format, the name of the argument (What is provided inside of the ${} wrapper) is arbitrary, although this will change soon to allow for numeric specifications for argument ordering.

'''Example'''

sample.string=The quick ${0} fox jumped over the lazy ${1} dog.

...

JavaRosaServiceProvider.instance().localize("sample.string",new String[]{"red","brown"}) -> "The quick red fox jumped over the lazy brown dog."

Argument Map

For strings requiring more complex arguments, or require arguments to be parsed in multiple stages, the string can be localized by providing a map of arguments and their replacement text. When provided in this format, the localizer replaces instances of any argument which is located in the dictionary with the appropriate value from it. Any arguments in the string that are not located in the dictionary are left unparsed.

The arguments that are applicable for a given string can be retrieved from the localizer using the static getArgs(String) method.

For multiple-pass parsing, the localizer's static processArguments(String, Hashtable) method can be used to continue to apply arguments as they become available.

'''Example'''

sample.string=The quick ${fox.color} fox ${fox.verb} over the lazy ${dog.color} dog.

...

Hashtable args = new Hashtable();
args.put("fox.color","red");
args.put("dog.color","brown");
String partial = JavaRosaServiceProvider.instance().localize("sample.string",args); // -> "The quick red fox ${fox.verb} over the lazy brown dog."

Hashtable moreargs = new Hashtable();
moreargs.put("fox.verb","jumped");
String full = Localizer.processArguments(partial,moreargs); // "The quick red fox jumped over the lazy brown dog."

Customizable Localization

Although you can provide any number of different methods of selecting the current locale for the application, and carrying that selection forward, JavaRosa also provides a mechanism using the Property Manager. The core set of '''JavaRosaPropertyRules''' contains a static key ('''CURRENT_LOCALE''') for accessing the appropriate values from the localizer, and the Localizer will respond to the property manager setting that key. The easiest way to provide an initial language, and allow the settings screen to change the language in the future, is to provide the following line in the app's initialization process, replacing '''english''' with the locale of your choice.

		JavaRosaServiceProvider.instance().getLocaleManager().setLocale(
				PropertyUtils.initializeProperty(JavaRosaPropertyRules.CURRENT_LOCALE, "english"));

Localizable Listeners

Even though text localization is provided at runtime, changes to the application's locale will not automatically update by default unless a form is loaded again. To provide the capacity for a form to dynamically change its localization the Localizable interface can be implemented for that form and registered with the Localizer.

'''Example'''

public class ExampleView extends Form implements IView, Localizable {
    private static final StringItem username = new StringItem(JavaRosaServiceProvider.instance().localize("example.username.label"),
                                                              JavaRosaServiceProvider.instance().localize("example.username.text"));

    public ExampleView() {
        this.append(username);
        JavaRosaServiceProvider.instance().getLocaleManager().registerLocalizable(this);
    }
   
    ...

    public void localeChanged(String locale, Localizer localizer) {
        username.setLabel(localizer.getText("example.username.label"));
        username.setText(localizer.getText("example.username.text")); 
    }
}

Pitfalls

There are some common pitfalls to watch out for when using dynamic localization in this way.

  • '''Necessity''' - Since the workflow of most J2ME applications is fairly linear, it is generally rare for dynamic localization to be a necessity.
  • '''Registration Management''' - One a form is registered with the localizer as dynamically localizable, the registration must be maintained. When the form is destroyed, it must manually be removed from the localizer or errors will probably occur.

Updated