Internationalize your software, Part 2

Learn how to develop software for the global marketplace

Last month, I introduced a three-part series that explores the topic of developing Java-based software for an international audience. After a quick introduction to the concepts of internationalization and localization, Part 1 of this series explored character sets and standards for defining characters, introduced the concept of a locale along with Java's Locale class; and introduced the concept of a resource bundle along with Java's ResourceBundle, PropertyResourceBundle and ListResourceBundle classes.

Read the whole "Internationalize Your Software" series:

Part 2 expands upon the material presented last month, including:

  • Internationalization and localization, part 2
  • Characters and character definition standards, part 2
  • Locales, part 2
  • Resource bundles, part 2

A complete list of Java's internationalization and localization classes is presented along with the concept of an "umbrella class." We examine how to use Java classes for determining character properties, sorting characters, and detecting textual boundaries -- from an international perspective. We also look at how to use the host computer's operating system (using Windows 95 as an example) to set the default locale.

Finally, Part 2 concludes by expanding on last month's discussion of resource bundles. More specifically, we look at how to access objects that are contained within a resource bundle. We find out how to use property resource bundles with applets -- without running into security violations. And we explore how to use a list resource bundle to hold nontextual data -- such as locale-specific images.

Some notes on this part of the series: In last month's segment, I mentioned that formatters would be covered in the second part of this series. However, due to reorganization, coverage of formatters will be deferred to Part 3.

Java applets are used to illustrate Java's internationalization and localization features. These applets were compiled with the JDK 1.1.6 compiler and tested with the JDK 1.1.6 appletviewer and Netscape Navigator 4.06 programs. Netscape was running version 1.1.5 of the Java runtime environment during testing.

Internationalization and localization, part 2

Last month, we defined internationalization and localization. A checklist of elements, that should be verified when localizing software, was presented. This month, we're going to examine a complete list of classes (as of JDK version 1.1.6) that composes Java's internationalization and localization resources. We'll also examine umbrella classes.

Internationalization and localization classes

We've already been introduced to Java's ListResourceBundle, Locale, PropertyResourceBundle, and ResourceBundle classes. However, these classes are just the tip of the iceberg. There are many more classes that are part of Java's internationalization and localization framework.

The code below lists all of the internationalization and localization classes that are part of JDK 1.1.6. Classes are listed with full package names. Base classes are listed above and to the left of child classes. Abstract classes are listed in italics.

java.lang.Object
     java.text.BreakIterator
          java.text.SimpleTextBoundary
     java.util.Calendar
          java.util.GregorianCalendar
     java.lang.Character
     java.text.CollationElementIterator
     java.text.CollationKey
     java.text.Collator
          java.text.RuleBasedCollator
     java.text.CollationRules
     java.text.CompactByteArray
     java.text.CompactCharArray
     java.text.CompactIntArray
     java.text.CompactShortArray
     java.text.CompactStringArray
     java.util.Date
     java.text.DateFormatSymbols
     java.text.DecimalFormatSymbols
     java.text.DecompositionIterator
     java.text.DigitList
     java.text.EntryPair
     java.text.FieldPosition
     java.text.Format
          java.text.DateFormat
               java.text.SimpleDateFormat
          java.text.MessageFormat
          java.text.NumberFormat
               java.text.ChoiceFormat
               java.text.DecimalFormat
     java.util.Locale
     java.text.MergeCollation
     java.text.ParsePosition
     java.text.PatternEntry
     java.io.Reader
          java.io.InputStreamReader
     java.util.ResourceBundle
          java.util.ListResourceBundle
          java.util.PropertyResourceBundle
     java.text.SpecialMapping
     java.text.StringCharacterIterator (implements java.text.CharacterIterator)
     java.text.TextBoundaryData
          java.text.CharacterBreakData
          java.text.LineBreakData
          java.text.SentenceBreakData
          java.text.WordBoundaryData
     java.util.TimeZone
          java.util.SimpleTimeZone
     java.lang.Throwable
          java.lang.Exception
               java.text.ParseException
               java.lang.RunTimeException
                    java.lang.MissingResourceException
     java.text.UnicodeClassMapping
     java.text.Utility
     java.text.WordBreakTable
     java.io.Writer
          java.io.OutputStreamWriter

You might find this class list handy when exploring the JDK. However, you should not use any class that isn't listed in the JDK documentation (some of these classes are for internal use only).

As a point of interest, these classes were originally coded by Taligent (an IBM subsidiary) and have been licensed to Sun. Although Taligent and IBM hold the original copyright to these classes, Sun also holds copyright to portions of this code.

Umbrella classes

An umbrella class is an abstract base class that describes either a common entity (such as a calendar) or a common operation (such as sorting). This class shields code from the many concrete (as opposed to abstract) subclass implementation differences by providing a common interface to these implementations. Umbrella classes contain static factory methods, giving code the ability to instantiate objects from concrete subclasses without needing to know any details about those subclasses.

When writing international code, it's important to be as locale-independent as possible. Therefore, it's a good idea to work directly with the umbrella classes, instead of instantiating objects from their concrete subclasses.

Java's internationalization and localization classes include the following umbrella classes:

  • BreakIterator
  • Calendar
  • Collator
  • DateFormat
  • NumberFormat
  • ResourceBundle
  • TimeZone

With the exception of TimeZone, the following code fragment shows how to call the static factory methods for these classes (I'll show you how to call TimeZone's factory methods in the third part of this series, where I'll discuss time zones in more detail).

// Obtain a Chinese break iterator for sentences.

BreakIterator bi = BreakIterator.getSentenceInstance (Locale.CHINESE);

// Obtain a German calendar.

Calendar cal = Calendar.getInstance (Locale.GERMAN);

// Obtain an American English collator.

Collator col = Collator.getInstance (new Locale ("en", "US"));

// Obtain an Italian date formatter.

DateFormat df = DateFormat.getDateInstance (DateFormat.SHORT, Locale.ITALIAN);

// Obtain a currency formatter for France.

NumberFormat nf = NumberFormat.getCurrencyInstance (Locale.FRANCE);

// Obtain an Arabic images resource bundle.

ResourceBundle rb = ResourceBundle.getBundle ("images", new Locale ("ar", ""));

The preceding code shows that each factory method takes a Locale object argument. This argument is either a directly instantiated Locale object or is an indirectly instantiated Locale object -- a language constant such as Locale.GERMAN. However, each umbrella class also contains one or more factory methods that don't take Locale object arguments. These other factory methods work with the default locale.

With the exceptions of ResourceBundle and TimeZone, all of the umbrella classes contain the getAvailableLocales () method, which returns an array of Locale objects for those locales that the umbrella class supports. Instead of assuming that you can pass any Locale object to an umbrella class, you should get into the habit of writing code that calls the getAvailableLocales () method and iterates through its list of supported locales, to see if a specific locale is supported. The following code fragment shows how to do this:

Locale l [] = Calendar.getAvailableLocales ();

Locale target = Locale.ITALIAN;

int i;

for (i = 0; i < l.length; i++) if (l [i].equals (target)) break;

if (i == l.length) { // target locale not found

... } else { // target locale found

... }

There may be times when you need access to functionality that is only found in a concrete subclass. For example, it's possible to create more flexible date formatters by working directly with DateFormat's SimpleDateFormat subclass. The following code fragment shows how to create a SimpleDateFormat object:

SimpleDateFormat formatter = new SimpleDateFormat ("yyyy.mm.dd e");

The argument being passed to SimpleDateFormat's constructor identifies a new date format. Check with the JDK documentation for more detailed information about date formats.

The umbrella classes provide the maximum degree of portability when writing international software because they hide the specifics of concrete subclasses. On the other hand, concrete subclasses contain useful functionality (as shown by SimpleDateFormat) which is not normally accessible through the umbrella classes -- there are limitations to portability.

Is it a good idea to focus exclusively on the umbrella classes? Yes and no. Yes, because future versions of Java may deprecate some of the concrete subclasses and it is doubtful that the umbrella classes would be deprecated. No, because you may need functionality that you cannot get through an umbrella class.

So what do you do? Try to isolate the code that works with concrete subclasses. Perhaps you could create a class library. If you ever need to change this code, this approach would minimize your work.

1 2 3 Page 1