Overview

Today, few of the existing applications adopt themselves to the user's preferred language and customs. The problem is of especial interest for applications distributed over the Internet, where the language of the end user is not known. The best solution would be for the software to automatically detect the user's language and tailor the user interface accordingly. Another critical case is a multi-user environment with its need for software that supports several languages and enables the end user to choose between them. The fact that an application is available in several languages does not always solve the problem. For example, in order to make it speak both English and French, the customer usually needs to purchase and install a full English and a full French versions of the application. For many applications even this solution is not possible as the two versions cannot co-exist. 

Java InfoTorg application automatically detects and acts according to the user's preferred language and customs. If no support for the required language is available, the user is prompted to choose between the available ones. Moreover, the language of the application can be changed at the run-time and all the visual information and sounds will change accordingly. The application's internationalisation architecture is extensible - adding support for a new language does not require a recompilation of the existing code. The work of the translator is taken into account. No knowledge of the programming language or any special environment is required, the translator  works only with text files that contain lists of text equivalencies of the following type: 
 

    Text in English = Text in any language 
     
Many of the internationalisation features of Java InfoTorg are due to the Internationalisation support in Java. The following paragraphs describe and illustrate some of its concepts. Resources and locales are introduced along with the Java Internationalisation API that provides mechanism to access internationalized resources. The Internationalisation architecture of Java Infotorg is explained. 

Go to index 

Internationalisation support in Java

Java designers thought of internationalisation from the beginning of the Java design. Java uses the Unicode character encoding that covers most of the written languages of the world. It provides solutions for code internationalisation, which means separating language dependent code from language independent code. Internationalisation implies writing programs so that user texts, images and sounds of the GUI are not hardcoded directly in the program. Facilities for this programming technique have been integrated into Java API beginning from the version 1.1. You can learn more about it reading Writing Global Programs in Java Tutorial

What is a resource?

Internationalisation means separating language dependent code from language independent code. When an internationalized application is translated into a new language, only the language dependent code is changed. Internationalisation implies writing programs so that texts, images and sounds of the GUI are not hard coded directly into the program. They are put in a separate file called resource file. Its location does not have to be that of the program's. A resource is thus a container for strings, images, sounds or other objects that are accessed by application code from an external source. The Java 1.1 API provides a mechanism to access resource files in a location independent manner. Any kind of resource files can be accessed by means of two methods in the class Class: getResource() and getResourceAsStream(). Both use the same search strategy to locate resources. In an application context, this search strategy implies looking up a resource on the local file system. In an applet context, it also includes looking it up on the Web server from which the applet was loaded. 
 

Getting a resource as URL

The method getResource(String name) of the class Class returns an URL for the resource. An URL provides access to image and audio files. To create an image from the file "infostore.gif", you can use the following code : 
 
    String resourcename = "infostore.gif"; 
    URL resourceasURL = getClass().getResource(resourcename); 
    Toolkit toolkit = java.awt.Toolkit.getDefaultToolkit(); 

    Image resource = toolkit.getImage(resourceasURL);

Getting a resource as InputStream

The method getResourceAsStream(String name) of the class Class returns an InputStream object. It can be used to initialize a Properties object that  provides access to text strings. Here is an example : 
 
    String resourcename = "Infotorg.properties"; 
    Properties resource = new Properties(); 
    InputStream resourceAsStream = getClass().getResourceAsStream(resourcename); 

    resource.load(resourceAsStream); 
     

The resource location mechanism itself does not support internationalisation, but is used by the Java Internationalisation API to access different resources according to the locale of the application. 

Go to index 
 

What is a locale?

Internationalisation in Java uses the locale concept. The same concept is also used in operating systems such as Unix and Windows. A locale represents a geographical or political region that shares the same language customs. The following are all examples of locale-sensitive data: 

          labels for GUI elements, 
          date and time displays, 
          numbers, including percentages and money, 
          error messages, 
          sounds, 
          images. 

The Java 1.1 API supports the concept of Locale through the Locale class. Internationalized programs use a Locale object to identify the user's locale settings and behave differently according to its value. A Locale object is frequently defined by a language, which is represented by its standard lowercase two-letter code, such as en for English or fr for French. Its specification can optionally include a country and a variant. Variants are vendor and browser specific. 
Here is one way of  initializing a Locale object: 

    String code = "fr"; 
    Locale localefrance = new Locale (code,"");
Here is another one. "CA" represents the country two-letter code for Canada. Adding this parameter will narrow the scope of the locale. 
    String code = "fr"; 
    String canadacountrycode = "CA"; 
    Locale localecanada = new Locale (code,canadacountrycode);
Internationalisation API uses the host system's locale as a default locale. It is possible to change the value of the default locale at run-time using the following code: 
    String code = "fr"; 
    String canadacountrycode = "CA"; 
    Locale localecanada = new Locale (code,canadacountrycode); 
    Locale.setDefault (localecanada);
Note that applets can be refused the write access to the default Locale as it is a system property. 

Go to index 
 

Resource bundling classes in Java 1.1 API

Once an application is internationalized, it needs a mechanism to access a particular resource that corresponds to a given locale. Java API provides resource bundling classes to manage resources. A resource bundle is a set of related classes that inherit from the abstract class ResourceBundle. The resources are seen as resource bundle families. For example, a resource bundle family that manages sounds of an application that supports British English, American English and French will be as follows: 
 
    SoundBundle_en_GB.class 
    SoundBundle_en_US.class 
    SoundBundle_fr.class 
     
ResourceBundles are created with the getBundle() method.  One of the getBundle() arguments is a Locale object. Which bundle is to be loaded  and which resources is to be retrieved depands on the value of the Locale object. Here is a way of creating a resource bundle for button labels using the current default locale: 
 
    String package = "mypackage"; 
    String relativebundlefamilyname = "LabelBundle"; 
    String bundlefamilyname = package + "." + relativebundlefamilyname; 
    Locale locale = Locale.getDefault(); 
    ResourceBundle buttonlabels = ResourceBundle.getBundle(bundlename,locale);
Loading Resource Bundles in Java Internationalisation tutorial will help you understand how getBundle() builds variations of the bundle name until it finds a class or a property file matching the bundle name. For further details, refer to Managing Locale-Sensitive Data in Java Internationalisation Tutorial

Go to index 
 

Java Infotorg's internationalisation architecture

Java Infotorg gets the most out of the internationalisation support in Java. The packages semagroup.infodata.client.ui.graphical.language and semagroup.infodata.client.ui.graphical represent the part of the program's structure that deals with the internationalisation.The package semagroup.infodata.client.ui.graphical.language manages internationalized resources of the application. The classes in semagroup.infodata.client.ui.graphical contains the implementation of the GUI. 

Before explaining the implementation in detail, here is a code extract that shows how a label is created: 
 

    String labeltextpassword = resource.getLabelTextPassword(); 
    Label label = new java.awt.Label(labeltextpassword);
where resource is a member of all the GUI classes in the package semagroup.infodata.client.ui.graphical. The way the resource is initialized is explained further. 

Note that there is no hardcoded text in the program and the label will have a different title according to the language chosen by the user. The number of languages supported by the application can grow dynamically without any need for recompiling. 

Go to index 
 

Implementing resources in Java Infotorg [ PENDING ]

Let us take the example of the login screen in Java Infotorg. 

It is made of two objects: 
 

  • an object that provides the graphical user interface. It is of type  semagroup.infodata.client.ui.peer.ConnectDataInputPeer.
  • an object that provides the multimedia resources of the window that is to say audio, images and texts. It is of type semagroup.infodata.client.ui.graphical.language. ResourceGraphicalConnectDataInputPeer.

  • The resource handling object has as many methods as there are resources for the login screen. These methods have self explanatory names. getLabelTextPassword() retrieves the title of the label placed besides the field in which the user enters his password. getAudioWelcome() retrieves the audio message that welcomes the user to the application. 
    You might wonder why we do not mention languages or locales in this section. The Locale is set at the beginning of the application and is used when we create the Resource handling object. Afterwards you just need to invoke the specific methods of the Resource handling object. 

    This is how we load the resource bundle for the login screen. Note that the bundle name argument is dynamically built in the actual application. 
     

      String package = "semagroup.infodata.client.ui.graphical.language"; 
      String TEXTS = "Texts"; 
      String relativebundlename = "ResourceGraphicalConnectDataInputPeer"; 
      String bundlename = package + "." + TEXTS + "." + relativebundlename; 
      Locale locale = getLocale(); 
      ResourceBundle resource = ResourceBundle.getBundle(bundlename,locale); 
       
     To know more on the architecture and how to extend it along with the application, read the Java Infotorg Internationalisation architecture API reference. 

    This figure shows the different packages of the application mapped onto directories as well as the resource handling classes. 

     

    Go to index 
     

    Factoring resource implementation in Java Infotorg [ PENDING ]

    The section below describes the implementation choices made in Java Infotorg. 
    As mentioned earlier, internationalized resources are used by classes that implement the graphical user interface of the application. Each of these classes needs a handle to its resource handling class. 
    Java Infotorg includes a helper class named semagroup.infodata.client.ui.UserInterface that creates a GUI object and its corresponding  Resource handling object. 
    Thus the method that instantiates the  resource handling object is implemented  in this helper class. It is named getResourceFor(ApplicationInputPeer peer) and here is its implementation. The comments inside the piece of code use the login screen example. 
     
      String RESOURCE_PREFIX = "Resource"; 
      String LOCALE_PACKAGE = "language"; 
      ResourceGraphicalApplicationInputPeer resource = null; 
        
      // semagroup.infodata.util.ClassNameTokenizer is a helper class that 
      // provides methods to manipulate class and package names. 
      // For example, it supplies methods to build a resource handling class 
      // name knowing its corresponding GUI class name. In the chosen context, 
      // peer is an object of type 
      // semagroup.infodata.client.ui.graphical. GraphicalConnectDataInputPeer. 
      // The ClassNameTokenizer object initializes its 
      // packageName property with the string 
      // "semagroup.infodata.client.ui.graphical" and its relativeClassName 
      // property with "GraphicalConnectDataInputPeer". 
        
      ClassNameTokenizer classnametokenizer = new ClassNameTokenizer(peer); 
        
      // The strings LOCALE_PACKAGE and "." are appended to the packageName 
      // property. 
        
      classnametokenizer.extendPackageNameBy(LOCALE_PACKAGE); 
        
      // The string RESOURCE_PREFIX is prepended to the relativeClassName 
      // property. 
        
      classnametokenizer.extendClassNameBy(RESOURCE_PREFIX,true); 
        
      // The ClassNameTokenizer object getClassName() method returns the class 
      // name it has just built that is to say the string 
      // "semagroup.infodata.client.ui.graphical.language. ResourceGraphicalConnectDataInputPeer". 
        
      Class resourcetype = classnametokenizer.getClassName(); 
        
      // The following line instantiates an object of type 
      // semagroup.infodata.client.ui.graphical.language. ResourceGraphicalConnectDataInputPeer. 
      resource = (ResourceApplicationInputPeer)createObject(resourcetype); 
        
      // The resource variable is returned. 
      return resource;
     
    Note that the returned variable resource is of type ResourceApplicationInputPeer
    GraphicalConnectDataInputPeer needs to downcast it to its specific resource handling type ResourceGraphicalConnectDataInputPeer

    As we already mentioned, resources are not hardcoded in Java Infotorg, not only text but also images and audios. We store them in property files. Images and audios are stored as relative paths to image and audio files. 
    Although the java.util package provides two helper classes named ListResourceBundle and PropertyResourceBundle, none of them supports a complete externalization of all resources. For this reason they are not used in Java Infotorg. 

    Thus  resource handling classes need specific methods to build an image out of a path to an image file and an audio message out of a path to an audio file. 
    Again, to avoid repetitions, the choice was to implement basic methods like getString(), getImage() and getAudio() in the  base class of all Resource handling classes. 
    For instance, getAudioWelcome() is a method in ResourceGraphicalConnectDataInputPeer that enables to retrieve the audio welcome message in the login screen. Here is its implementation in ResourceGraphicalConnectDataInputPeerImpl
     

      String PROPERTY_KEY = "ConnectData.audio.welcome"; 
      return getAudio(PROPERTY_KEY); 
       
    The string "ConnectData.audio.welcome" is a key in the corresponding property files. The value of this key is a relative path to the required .au file. 

    If you want to have a look at the code that implements the architecture we have just described, here is the source code of  the classes and property files involved in the internationalisation of the login screen: 
     

  • GraphicalApplicationInputPeer.java
  • GraphicalConnectDataInputPeer.java
  • ResourceGraphicalApplicationInputPeer.java
  • ResourceGraphicalConnectDataInputPeer.java
  • ResourceGraphicalConnectDataInputPeerImpl.java
  • ResourceGraphicalConnectDataInputPeerImpl_en.properties
  • ResourceGraphicalConnectDataInputPeerImpl_fr.properties

  • ResourceGraphicalConnectDataInputPeerImpl.properties 
     
     ResourceGraphicalApplicationInputPeer gives the application access to internationalized resources. It provides methods to retrieve 
    strings, images and sounds that are language dependent. 

    This page describes Java Infotorg internationalisation architecture and explains how to extend it. 

    Understanding the current architecture 

    The application is a set of screens. Two objects describe a screen of the application: 
     

         a Peer object of type ApplicationInputPeer that provides the user interface 
         a Resource handling object of type ResourceApplicationInputPeer that provides the Peer object methods to retrieve specific 
         resources for the screen. 

    The Resource handling object uses property files to access language dependent resources. A property file stores key/value pairs. For each 
    resource handling object, there is one property file per language as well as one default property file. 

    Let us take the example of the application login screen which is called the ConnectData screen. The ConnectData screen is represented 
    by an object of type GraphicalConnectDataInputPeer and an object of type ResourceGraphicalConnectDataInputPeer. 
    Look at ResourceGraphicalConnectDataInputPeer getButtonTextCancel() method. It returns the text string that will be 
    displayed on the button used to cancel the current operation. 
    It calls getString(CANCEL), CANCEL being a static String variable initialized as follows : 

    private static String CANCEL = "ConnectData.text.cancel"; 

    ConnectData.text.cancel is they key you will find with a different value in each property file. Property file names are built by 
    appending to ResourceGraphicalConnectDataInputPeer: 

         a suffix corresponding to ResourceGraphicalConnectDataInputPeer implementation class suffix, "Impl" 
         "_" 
         a language dependent two-letter code. 
     

    Language two-letter codes that follow the "_" are standardized. You can find a full list of these codes on the following Unicode site. The 
    property file for French is ResourceGraphicalConnectDataInputPeerImpl_fr.properties. The default property file is 
    ResourceGraphicalConnectDataInputPeerImpl.properties. It is used if no other file is found. It is also used to store values that 
    you want to use whatever language has been set. The default value for the key ConnectData.text.cancel is "Cancel". As a matter of 
    fact ResourceGraphicalConnectDataInputPeerImpl.properties will contain the entry: 

    ConnectData.text.cancel=Cancel 

    Implementing resources for a screen 

    This part is meant to show how to provide resources for an existing screen. Let us suppose this screen is called ConnectData and its 
    resource handling type ResourceConnectDataInputPeer is already specified. We will give examples using French as the language. 
    Apply them for each language available in the application. 

    List of classes and files to provide 

    Classes 

         semagroup.infodata.client.ui.graphical.language. ResourceGraphicalConnectDataInputPeerImpl 

    Files 

         ResourceGraphicalConnectDataInputPeerImpl_fr.properties (one for each language) 
         ResourceGraphicalConnectDataInputPeerImpl.properties 
         Sound (.au) and Image (.gif) files used 

    Methods 

    ResourceGraphicalConnectDataInputPeerImpl implements the methods retrieving the ConnectData screen specific resources. 
    Those methods are specified in the ResourceGraphicalCon 



    Go to Index            Go to Table of Contents 

    Author : f92-bro@nada.kth.se 
    Date : October 3, 1997