Write world-class applications

From Chicago to Copenhagen, Colombia to Cameroon -- Java provides the tools for writing truly international applications

Take a quick look at the first date shown below. What date is this? If you guessed "October 5, 1997," you're wrong! Now look at the second date. What date is this? Are you confused yet?

  1. 10/05/97
  2. 16/10/97

Chances are, if you were born and reared in the United States your first guess was wrong, and you are now mildly puzzled by the appearance of the second date.

On the other hand, if you were born and reared in Europe you're probably wondering what all this is about (don't worry, I promise to pull it all together).

You see, the dates shown above are perfectly valid; I've simply shown them as they would appear in many European countries. The way in which the components of a date -- the year, month, and day -- are combined is largely a matter of local custom. In the United States, we format dates using the month/day/year style. In England, however, it's the day that comes first, followed by the month and year. It's a small difference, but please don't underestimate its importance to the end user.

It's surprising how few of our simplest assumptions about appearance are valid anyplace else. Date formats are perhaps the simplest. But what about time formats and monetary representations, the calendar we use to mark the progress of time, and the character set we use? It's interesting to note that ASCII (American Standard Code for Information Interchange), the character set that formed the basis for over twenty years of American computing, managed to leave out many of the characters necessary to display text in languages as common as French.

It's a small world after all

Since its release, Java has been billed as an international application development solution. When building Java's string- and character-handling toolkit, Sun chose to use Unicode (a character-encoding standard capable of representing 65536 characters from scripts around the world) rather than ASCII. Unfortunately, in early versions of the language, critical support for international computing was missing. (Last month's column, "

Use the two "R"s of Java 1.1 -- Readers and Writers

," provides more detail on this lack of support.)

This weakness was remedied with the release of JDK 1.1 whose class library included numerous changes necessary to improve international support:

  • Several existing classes were changed to provide better support for Unicode
  • Several classes (the new I/O classes) were added to existing packages
  • One new package (the java.text package that contains classes and interfaces for handling text in a locale-independent way) was added.

Data formats

As I mentioned in the introduction, even the simplest of formatting conventions do not stay the same as we move from country to country (or even as we move within a single country -- the French- and English-speaking parts of Canada, for example). Dates, times, and even numeric representations tend to vary -- often significantly.

Let's take a look at just how much they can vary.

Time

The display of the hours, minutes, and seconds in a day (the time, in other words) seems like it should be the most straightforward of tasks. Surprisingly, it is a haven for hidden traps. First, there's the question of the number of hours in a day. Most folks agree that there are 24. But how are they represented? Two half days of twelve hours (like here in the United States) or one day of 24 hours (what we commonly call "military time")? Then there's the choice of the character that separates hours from minutes. Here in the States, we use the colon (:); in Italy it's the period (.). More perplexingly, there's the question of time zone indication and daylight savings time (some locales have it, some don't).

Date

As I demonstrated in the introduction, the position of the days, months, and years in a formatted date change position as you move from locale to locale. And if that's not enough, you must also consider the separator character. Here in the United States, the slash (/) is the character of choice. In some countries (Italy, for instance) it's the dash (-). And let's not forget that the names of the days, as well as the names of the months, are language-dependent.

Number

Even the numeric representation has its share of tricks. First, there's the decimal separator. Here in the United States we use the period (.), but in many other countries, the comma (,) is preferred. To enhance readability, digits are usually grouped together (tens, hundreds, thousands). In the United States we group digits into segments of three, separated by commas. In other countries, the period (.) is the preferred separator. Even the character to indicate a numeric quantity represents money differs. Most of you are familiar with the dollar sign ($) used in the U.S. and several other countries; its alternatives are too numerous to mention.

Java -- the language of international appeal

Given the attachment with which each of us views our own customs and conventions, it seems desirable to build software that is sensitive to those customs -- especially those of any potential customers. As it turns out, Java makes it easier than you might imagine. Let's see how truly worldly Java is by stepping through the process of building such an application.

Step 1: A database record

Consider the class Record, shown below. Its instances represent records in a database application. The fields contain important information, which is entered and displayed as simple strings of characters. These strings should be appropriate for each user, based on his or her locale.

public class Record
{
    public Date date;
    public Date time;
    public Number number;
    public Number price;
}

A database record consists of three arbitrary pieces of data. Each piece of data is stored internally in a locale-independent format and must be localized at input and output. The Java class library provides two classes to assist in localization: NumberFormat and DateFormat. Both of these classes know the user's locale and how to perform the formatting. The Java runtime environment obtains the user's locale from the operating system when needed.

Step 2: Converting data to a localized string

Data stored in class Record is converted from its internal locale-independent format into a display format appropriate for the viewer's locale.

    NumberFormat nf = NumberFormat.getInstance();

The NumberFormat class provides static factory methods for creating instances of NumberFormat with specific characteristics. The getInstance() method returns the default number format for the current locale. Other factory methods include: getNumberInstance(), getCurrencyInstance(), or getPercentInstance(). They return formats specialized for specific formatting tasks.

    DateFormat df = DateFormat.getInstance();

Likewise, the DateFormat class provides static factory methods for creating instances of DateFormat with specific characteristics. In addition to the getInstance() method which returns the default date format for the current locale, the DateFormat class provides: getDateInstance(), getTimeInstance(), and getDateTimeInstance(). They return formats specialized for specific formatting tasks.

Once the format is created, it can be used to format data of the appropriate type.

    String str = nf.format(number);

The NumberFormat class's format() methods provide this functionality for numbers. The NumberFormat class provides several format methods that allow you to format long and double primitive data types, as well as Double, Float, and Number class instances.

    String str = df.format(date);

Likewise, the DateFormat class's format() methods provide similar functionality for dates.

Step 3: Converting data from a string

In addition to displaying localized data, the program must also be able to accept localized input from users.

    Number n = nf.parse(string);
    Date d = df.parse(date);

Once the format is created, it can be used to parse a string into data of the appropriate type.

The parse methods are guaranteed to parse any string formatted by the corresponding format method. They are also able to parse strings that are close -- but they are not omniscient. If the user-supplied string can't be parsed because it's in the wrong format, the parse() method will throw a ParseException.

Putting the tools to work

Java gives us the tools to add a wordly flavor to our apps, but it's up to us to use them properly. Here's a example of the methods and classes discussed above. All of the data members are private. Their values are accessed through complementary pairs of get/set methods. The set methods all throw ParseException if the user supplied string can't be parsed.

import java.text.DateFormat; import java.text.NumberFormat; import java.text.ParseException; import java.util.Date;

public class Record { private Date _date = null; private Date _time = null; private Number _number = null; private Number _price = null;

public void setDate(String str) throws ParseException { if (!str.equals("")) { // // here's a trick that I've found useful when parsing user // entered dates and times -- first attempt to parse the date // string in its fullest formatting style, if that fails try // again with a less detailed formatting style -- this allows // the user the most freedom in entering a date //

try { _date = DateFormat.getDateInstance(DateFormat.FULL).parse(str); } catch (ParseException pe1) { try { _date = DateFormat.getDateInstance(DateFormat.LONG).parse(str); } catch (ParseException pe2) { try { _date = DateFormat.getDateInstance(DateFormat.MEDIUM).parse(str); } catch (ParseException pe3) { _date = DateFormat.getDateInstance(DateFormat.SHORT).parse(str); } } } } else { _date = null; } }

public void setTime(String str) throws ParseException { if (!str.equals("")) { // // here's a trick that I've found useful when parsing user // entered dates and times -- first attempt to parse the time // string in its fullest formatting style, if that fails try // again with a less detailed formatting style -- this allows // the user the most freedom in entering a time //

try { _time = DateFormat.getTimeInstance(DateFormat.FULL).parse(str); } catch (ParseException pe1) { try { _time = DateFormat.getTimeInstance(DateFormat.LONG).parse(str); } catch (ParseException pe2) { try { _time = DateFormat.getTimeInstance(DateFormat.MEDIUM).parse(str); } catch (ParseException pe3) { _time = DateFormat.getTimeInstance(DateFormat.SHORT).parse(str); } } } } else { _time = null; } }

public void setNumber(String str) throws ParseException { if (!str.equals("")) _number = NumberFormat.getNumberInstance().parse(str); else _number = null; }

public void setPrice(String str) throws ParseException { if (!str.equals("")) _price = NumberFormat.getCurrencyInstance().parse(str); else _price = null; }

public String getDate() { if (_date != null) return DateFormat.getDateInstance(DateFormat.FULL).format(_date); else return ""; }

public String getTime() { if (_time != null) return DateFormat.getTimeInstance(DateFormat.FULL).format(_time); else return ""; }

public String getNumber() { if (_number != null) return NumberFormat.getNumberInstance().format(_number); else return ""; }

public String getPrice() { if (_price != null) return NumberFormat.getCurrencyInstance().format(_price); else return ""; } }

A record-browsing applet

The following example applet uses the

Record

class to implement a record browser. Via the user interface, records can be created, added, and deleted. The user can also use the next and previous buttons to move forward and backward through existing records. Each field accepts user input appropriate to the type of the field. Each field displays the contents of the record field correctly for value and locale.

You need a Java-enabled browser to see this applet.

Note: You must use a Java 1.1-compliant browser to access the features of this applet. The interface will display fine on older browsers, but you won't be able to create or browse records.

Conclusion

It might seem like date, time, and number formatting are minor user-interface details and of little consequence. Users of your application, however, may disagree -- especially when the user interface doesn't conform to their customs. Remember, the whole point of an effective user interface is to make the application as easy to use and as

friendly

1 2 Page 1
Page 1 of 2