Chapter 21. Internationalization

Table of Contents

Locales
Character Sets
Internationalization and DB Objects
Internationalization in Controllers
Internationalization in JSP's
ISO Valid Values
Conclusion
Contributors

Note

If you find this EDG documentation helpful please consider DONATING! to keep the doc alive and current.

 Maintainer:David Lloyd

There are a number of facilities in Expresso that facilitate developing applications that are fully internationalized. Each Expresso application may have one or more MessagesBundle.properties files. Expresso itself has a collection of these files, one for each language supported: for example, the french translation file is MessagesBundle_fr.properties, following the standard Java conventions for local language files.In order to create an internationalized application, all messages and strings displayed to the user (or used in emails or log messages) should be stored in the local language files.

Locales

Every time a user logs in to Expresso or an Expresso application, a locale is established for the user. This locale is determined by first examining the user preference options for the user. If there are no preference values specified for the user, then the user's browser settings are used to determine a language preference. If still no language can be determined, then the defaults set for the application are used, if any. If no such default was specified, english will be used.

If the user changes language preference while logged in, they must log in again (either explicitly, via the Login controller, or implicitly via a cookie) in order for the change to take affect.

Character Sets

A specific character set can be specified for each language. In the local language file for each application and supported language, the "charset" key is specified with a character set as it's string - this character set is applied to each page that includes the <html:html> tag automatically. In this way, the character set is determined before all other strings on the page are displayed.

Internationalization and DB Objects

Every instance of the SecuredDBObject class has the capability to support internationalization easily as well. When the setDescription(String) method is called, the String specified is assumed to be a key for the local language file. If no such key is found, the string itself is used as the description - this means you can use the description directly instead of a key if you do not need to support internationalization.

The same is true of the descriptions for each field, as specified with the "addField" method within the "setupFields" method of each DB Object. The field description is assumed to be a key within the language file, and if the key is not found, the string specified is used directly.

Additional strings and messages can be internationalized within a SecuredDBObject. You can use the getString methods within the SecuredDBObject to access strings, with or without arguments.

Internationalization in Controllers

Controllers provide easy access to internationalization via getString methods - these methods can be used to specify a key into the appropriate local language file, and optionally to provide arguments to this string (as described in the Javadoc of the Messages class in the com.jcorporate.expresso.core.i18n package.

Internationalization in JSP's

Expresso provides an extended version of the Struts message tag in order to make it easier to specify strings to be used in JSP pages. The extended tag takes an additional optional attribute: schema. The schema attribute specifies the schema class file of the application whose message file is to be used to look up the specified key. For example, to access the string with the key "description" in the eForum language files:

<bean:message key="description" schema="com.jcorporate.eforum.ForumSchema"/>

Once the message tag has been used with the schema attribute once, subsequent uses of the tag on that same page will default to the same schema file. Make sure, however, that the first tag is not in a conditional section - otherwise it may not set the schema default properly for subsequence calls to the tag. You can of course always use the schema attribute explicitly each time, but this gets verbose in a hurry.

In addition to the message tag, the TableHead tag in the "expresso" tag library also directly supports internationalization: instead of the usual "value" attribute, you can use the "keys" attribute instead - in this case, each of the values specified in the string will be considered a key to be looked up in the language file: so, instead of value="one|two|three" simply resulting in the table headers of "one", "two", and "three", keys="one|two|three" will look up each of those values as keys, and use the resulting strings as the table headers.

The DBMaint tag will use the local language version for the description of the database object if the "label" attribute is omitted.

ISO Valid Values

Expresso supports internationalised valid values in the class ISOValidValue which is a subclass of ValidValue. Both classes belong to the com.jcorporate.expresso.core.dbobj package. The ValidValue class is a simple label value Java Bean, that is it has a label and description attribute. The ISOValidValue class has the same attributes but supports internationalisation of the label attribute. In particular this class has canonisation support. This class has method to convert the `description' field to an i18n value from a resource bundle. Both classes support accessor and mutator method as well as default constructors.

public ValidValue();		// Default constructor
	public ISOValidValue();

	public String setValue() ;	// Accessor
	public String setDescription();

	public void setValue( String v) ;	// Mutator
	public void setDescription( String d );

The ISOValidValue supports canonisation. In order to do conversion of the `description' field with a resource bundle the `Schema' class name and `Locale' must be supplied, as well as `prefix'. The prefix parameter avoids possible namespace conclusions with the description attribute which we would use as key into the resource bundle. The most obvious candidate for a prefix string is a fully qualified class name.

public void canonize( String schemaClass, Locale locale, String prefix )

This is method will attempt to convert the `description' attribute into a localised value string from a message resource bundle. If the `prefix' parameter is not null then the `description' attribute is prepended with a full stop "." to the original description string to make a new look up key. Otherwise if the `prefix' parameter is null then original description is the look up key. The key value is used to look up the description from a resource bundle.

The method is equivalent to functionally calling `Messages.getString( String schemaClass, Locale locale, key, new Object[0] )'. If the key is found in the resource bundle then the value string becomes the new description.

So we if have the following in enumerated values

SORT_BY_DATE
	SORT_BY_SCORE

and I have a Java class `org.acme.test.SortOrder' then I might have use the class name as a prefix which results in a look up like this:

org.acme.test.SortType.SORT_BY_DATE
	org.acme.test.SortType.SORT_BY_SCORE

All I have to do is place these keys inside a resource bundle to internationalise an application (e.g German).

org.acme.test.SortType.SORT_BY_DATE = Sortiert von Datum
	org.acme.test.SortType.SORT_BY_SCORE = Sortiert von Punkten

This is exactly what we mean by canonisation or conversion. There are more additional convenience `ValidValue' APIs

public void canonize( String schemaClass, Locale locale, String prefix )
	public void canonize( String schemaClass, RequestContext context, String prefix )

The ISOValidValue class also has constructors that will automatic canonise the description upon initialisation. These are convenience functions and because they are constructors there are thread safe.

public ISOValidValue( 
		String schemaClass, Locale locale, String prefix, 
		String label, String description )

	public ISOValidValue( 
		String schemaClass, RequestContext request, String prefix,
		String label, String description )

We can use the ISOValidValue object in our own getValidValues method in a DBObject to generate hard-coded look up values.

public Vector getValidValues( String field ) {
	if ( field.equals( "Fruits" )) {
	    Vector list = new Vector();
	    String schemaClass="com.my.own.schema";
	    String prefix = this.getClass().getName();
	    list.add( new ISOValidValue( schemaClass, requestContext, prefix, "1", "Apple" ));
	    list.add( new ISOValidValue( schemaClass, requestContext, prefix, "2", "Pear" ));
	    list.add( new ISOValidValue( schemaClass, requestContext, prefix, "3", "Orange" ));
	    list.add( new ISOValidValue( schemaClass, requestContext, prefix, "4", "Banana" ));
	    return list;
	}
	else
	    return super.getValidValues( field );
    }

Or we can utilise the automatic lookup feature in the DBObject class, which has an alternative method to retrieve valid values that uses the newer ISOValidValue classes. This method is called getISOValuesDefault().

public Vector getValidValues( String field ) {
	if ( field.equals( "Product_Id" )) {
	    Vector list = this.getISOValuesDefault( "Product_Id", "Product_Desc");
	    return list;
	}
	else
	    return super.getValidValues( field );
    }

In the DBObject.getISOValuesDefault method, valid values are automatically cached by key name. The cacheName is built using a cache key using only the fully qualified java class name of the DBObject. For internationalisation the cache name needs to have the locale specifically added to it. So that the valid values are cached for each language and territorum.

String cacheName = this.myClassName + 
			locale.getLanguageCode()+"_"+locale.toString()+
			".validValues";

So that we get some cache names like this:

`com.acme.test.SortType.en_gb.validValues'
	`com.acme.test.SortType.fr_fr.validValues'
	`com.acme.test.SortType.fr_ca.validValues'
	`com.acme.test.SortType.es_es.validValues'

In the DBObject.getISOValuesDefault method the fully qualified class name is used as the `prefix' to get the valid value as described above. Also the schema class name and the locale is retrieved from the DBObject, because it has these attributes already stored. So the canonisation for the product type might be

# (English)
	org.acme.test.Product.1 = 1300RPM Washing Machine
	org.acme.test.Product.2 = Portable Television

	# (German)
	org.acme.test.Product.1 = 1300RPM Waeshmaschine
	org.acme.test.Product.2 = portierbares Fehersehen

And that is all there is to internationalisation of valid values.

Conclusion

Contributors

The following persons have contributed their time to this chapter:

Note

Was this EDG documentation helpful? Do you wish to express your appreciation for the time expended over years developing the EDG doc? We now accept and appreciate monetary donations. Your support will keep the EDG doc alive and current. Please click the Donate button and enter ANY amount you think the EDG doc is worth. In appreciation of a $35+ donation, we'll give you a subscription service by emailing you notifications of doc updates; and donations $75+ will also receive an Expresso T-shirt. All online donation forms are SSL secured and payment can be made via Credit Card or your Paypal account. Thank you in advance.

Copyright © 2001-2004 Jcorporate Ltd. All rights reserved.