Internationalisation Guide

The codebase is fully internationalised everywhere that text is used. As this is a graphics library, that mostly constrains it to the rendering as messages used in exceptions when something goes wrong. This section is a guide on how to create a new internationalised translation of the codebase.


Code Outline

The codebase makes use of the standard internationalisation capabilities provided by the Java core APIs. Specifically it makes use of the classes in java.util and java.text packages to ensure the correct formating of numbers and error messages. In addition to using the Java APIs, we have created a wrapper that does most of the management that a normal application needs for loading and maintaining internationalisation preferences.

Class Structure

The main class in the codebase is the I18Manager in org.j3d.util. This provides the interface to the underlying system as well as all the management. The class provides two capabilties:

  1. Loading and selecting the appropriate internationalisation file
  2. Managing the user selected preference for internationalisation

This class takes care of loading the right internationalised file and then storing the selection in the user preferences so that the next time the application does not need to store those preferences itself. The user preference is stored under a root node and then creates a node under that named by the application name that you pass in to the initialisation method. This allows you to have a single instance of the library on your system, while having many different applications share it and not have their individual internationalisation settings mixed up.

Configuration files

The files that contain the internationalised text use the standard Java properties file format. That is, each line of:

name = value

is used to define one internationalised resource. There is no specific requirements for how the names are formatted. The code doesn't really care when you integrate it within your application. Within the code repository we use the convention of using the fully qualified class name, including case, followed by a dot, then the individual resource name. For example:


The value of this property is the message to be displayed in the appropriate language for the file. The file must contain every single internationalised resource used in the system. The implemenation does not chain load a set of resources for the individual libraries. That means, for you application-specific internationalisation resource file, you must also include all the properties defined for the main codebase, and any derived codebases that make use of this library too (eg Aviatrix3D).

For most messages, all you will need to do is translate the string to the new language and you're done. However, for some, you will need to take additional steps because the messages use more complex formating. If you come across a message string that looks like this:

The value {0} is outside the range [0,1]

then this is the more complex message style. When you see a number in curly brackets like this example, that is where a substitution will occur by the message processing system. Typically the substituion will be a number, as the above example implies, but occasionally it can be text. When you translate messages, the exact number of those bracketed numbers must be included in the message. The message generator for that particular value will be expecting exactly that number of items for internal substitutions. Adding more will not provide any additional benefit - only the exact number. Most of the time you will be able to work out what those represent from the context of the message string. If you can't figure it out, look at the fully qualified class name of the property name and look in the source of that class.

Application Usage

To use internationalisation in your application, you must start with initialising the application manager. As the entire application is using this manager for any string-based content, it should be the first thing that the application does.

Initialising the internationalisation manager requires giving it the name of the resource flle to load and an application name string. The name string can be any string you like, but it is used to save preferences specific to internationalisation. Ideally this should be the same name string you are using for your own preferences storage. The name of the configuration file follows the naming convention that Java uses for property files that needs to be found in the classpath. Period characters should be used instead of back slashes for characters and the ".properties" file extension is not included. For example:

I18nManager intl_mgr = I18nManager.getManager();
intl_mgr.setApplication(APP_NAME, "config.i18n.j3dResources");

would go looking for the file family named,, etc in the directory config/i18n in the classpath.

After this point the internationalisation system is ready for usage in your application. To make use of the internationalisation capabilities, all you have to do is call the getString() method in your application. The simplest form looks like this:

if(value < 0)
    I18nManager intl_mgr = I18nManager.getManager();
    String msg = intl_mgr.getString(VALUE_RANGE_PROP);
    throw new IllegalArgumentException(msg);

Once you have fetched the basic string like this, then you can do all the normal localisation and formating that Java provides you. One of the most typical uses is to format floating point numbers in the way for the specific locale. Most applications will not have a lot of translations available so it is a good idea to have the numbers formatted consistently with the text that was chosen. To do that, you can find the Locale that was actually loaded with the getFoundLocale() method. For example:

if((angle <= 0) || (angle > 180))
    I18nManager intl_mgr = I18nManager.getManager();
    String msg_pattern = intl_mgr.getString(INVALID_ANGLE_PROP);

    Locale lcl = intl_mgr.getFoundLocale();

    NumberFormat n_fmt = NumberFormat.getNumberInstance(lcl);

    Object[] msg_args = { new Double(angle) };
    Format[] fmts = { n_fmt };
    MessageFormat msg_fmt =
        new MessageFormat(msg_pattern, lcl);
    String msg = msg_fmt.format(msg_args);

    throw new IllegalArgumentException(msg);

That's the core of the internationalisation system in a nutshell.


Other References