Locale-Sensitive Data |
A message is any textual information presented to the user. A message could be an error message, or the instructions in an alert box, or the label on a button or other GUI item. Typically, messages are short but they don't have to be.A lot of messages are straightforward text which can simply be stored in a resource bundle and used as-is by the program. Examples of these types of messages are the Locale and Today's Date labels in the
AroundTheWorld
applet. These messages are simple text messages used directly from a resource bundle. Other messages are constructed at runtime and cannot simply be used verbatim from a resource bundle.For example, consider this message from the
AroundTheWorld
program: Current Time (in San Francisco). This message has two parts: the base message and the city. Each part of the message is dependent on the user's locale but in different ways. The base message depends on the locale's language--that is, the base message changes when the language changes but not when the country changes. So, locales with the same language such asen_GB
and theen_US
can share the base message. However, the city depends on the locale's country--that is, the city changes when the country changes soen_GB
anden_US
cannot share the city portion of the message. Thus the message must be constructed at runtime and cannot be used directly from a resource bundle.Other messages must also be constructed at runtime but for reasons other than differences in locale. For example, the italic parts of the following message are dependent on the user's runtime:
You are running JDK 1.1 on Solaris.You might be tempted to construct messages like the two described above using String concatenation and partial text items from resource bundles:However, this approach introduces language dependencies into your program because the word order in different languages is different. So while this may work in English, it probably won't work in French, and German, and certainly won't work in Japanese, Russian and other languages that use a completely different writing system. Instead you should format messages using the JDK 1.1's MessageFormat class.String version = getVersion(); String system = getSystem(); String msg = myBundle.getString("Running") + version + myBundle.getString("On") + system ;Use
MessageFormat
to construct sequences of strings, numbers, dates, and other formats to create messages. This class facilitates localization because it prevents both hard-coding of message strings, and hard-coding of the concatenation sequence for portions of message strings. This means localizers can change the content, format, and order of any text as appropriate for any language.Here's how the
AroundTheWorld
applet usesMessageFormat
to format the Current Time label:The first line creates an array of objects and puts one item in the array: the string containing the city portion of the message. It gets the city name from theObject[] args = { labels.getString("RepCity") }; String result = MessageFormat.format(labels.getString("TimeLabel"), args); timeLabel.setText(result);labels
resource bundle. This array is used as an argument list toMessageFormat
'sformat
method.The second line of code formats the message with
MessageFormat.format
. Theformat
method requires two arguments: a pattern, and an array of arguments.AroundTheWorld
stores the pattern for this message in a resource bundle and retrieves it withlabels.getString("TimeLabel")
. If you look at the pattern in each of the resource bundles, you will notice the construct{0}
where the city name ought to be. This is a placeholder for the argument that will be filled in with a value from theargs
argument array. The number 0 is the argument number and is the index in the array where the value for this argument is stored.
MessageFormat.format( pattern , args)
formats the message and fills in the missing parts using the objects in theargs
array matching up the argument numbers and the array indices.And finally, the third line of code,
timeLabel.setText(result)
, sets the label in theLinguaPanel
with the formatted message.You will notice that
AroundTheWorld
did not use a factory method to create aMessageFormat
object like it did to create the number, and date and time formatters. Instead the program usedMessageFormat
's class methodformat
. This is because messages are all different and there is no notion of "the message that most programmers want". SoMessageFormat
cannot come up with a reasonble default. Thus to format messages, you either instantiate aMessageFormat
using its constructors and set it up, or you use the class methodMessageFormat.format
.The above example is fairly straightforward: The message contains one argument,
{0}
, which is filled in by the one object contained in a single-element array.MessageFormat
can handle complex messages that have multiple arguments and even recursive arguments. Also, the argument in the above example is a String that requires no special formatting itself.MessageFormat
can handle arguments of any type (numbers, dates) and use other formatters (NumberFormat
,DateFormat
) to format them.To get a better understanding of what
MessageFormat
can do, and how it does it, bring up the following demo applet provided by Taligent and try some of the suggestions in their guide.
The Result text box at the top of the demo program's window shows the result after
MessageFormat
formats the Pattern using the values in the Arguments list in place of the argument placeholders in the pattern.The following code segment is the Java code that produces the same results as the demo program when it is first invoked:
The first four lines create a Date object with March 3, 1996 as the date, and create the arguments array with the three argument values. Next, the code snippet sets up the pattern, formats the message and displays the result.Calendar calendar = Calendar.getInstance(); calendar.set(96,2,3); Date datearg = calendar.getTime(); Object[] formatargs = { new Integer(3), "MyDisk", datearg }; String pattern = "The disk {1} contained {0,choice, 0#no files|1#one file|1< {0,number,integer} files} on {2,date}."; String result = MessageFormat.format(pattern, formatargs); System.out.println(result);The most interesting part of this example is the message pattern. This pattern uses three different types of arguments: {0} is a choice, {1} is a string, and {2} is a date. Additionally, the choice argument is recursive, that is, it contains another argument that is a number.
Let's look at the pattern more closely and locate all of the argument placeholders. The first thing that you will notice is that the arguments do not appear in the pattern in the same order that they are listed in the argument list. This is why they are numbered based on their position in the argument array.
The following table shows the relationship between the argument value, the argument placeholder and the result. The table lists the arguments from simplest to most complex.
Argument Placeholder Result {1}
MyDisk
{2,date}
03-Mar-96
{0,choice, 0#no files|1#one file|1< {0,number,integer} files}
3 files
Argument {1} is simply a
string
.MessageFormat
formats this item itself and just replaces the argument placeholder with the argument value.Argument {2} is a date and
MessageFormat
uses aDateFormat
object to format this argument.MessageFormat
replaces the argument placeholder with theString
that results from callingDateFormat
'sformat
method. As specified in the demo program,MessageFormat
uses the defaultDateFormat
object that you get when you callDateFormat.getInstance
. However, you can change this by specifying a date pattern in the argument placeholder.
Try this: Change{2,date}
to{2,date,yy.MMM.dd}
. Notice that the format of the date changes. The syntax for the pattern in the date placeholder is specified by theSimpleDateFormat
class.
Argument {0} is a choice and has the most complicated looking pattern. Argument {0} is also recursive, it contains another argument placeholder:
{0,number,integer}
.MessageFormat
uses aChoiceFormat
object to format this argument. AChoiceFormat
allows you to attach a format to a range of numbers. When the argument value is in a certain range,ChoiceFormat
chooses the corresponding format as its result.The syntax for specifying the choices is defined by the
ChoiceFormat
class and requires that you specify a list of limits and a list of formats. The limits and formats in the pattern in the demo program are:
Pattern Meaning 0#no files
Result is no files
if value is equal to 01#one file
Result is one file
if value is equal to 11< {0,number,integer} files
Result is {0,number,integer} files
if value is greater than 1. Note that{0,number,integer}
is an argument that gets formatted byNumberFormat
You can change the results by modifying the limit or the format specifier.
Try this:Change "no files" in the pattern to "not a single file" then change the value of the first argument to 0.
- Change "1<" to "2#" and add " |10< many files" between "files" and the curly bracket that follows it, then enter a number larger than 10 for the value in the Argument 0 field.
Generally speaking, argument placeholders look like this:
Curly brackets delimit the placeholder from the rest of the pattern. number is the argument number. The curly brackets and the argument number are required. type is optional and indicates the data type of the argument. The data type can be one of:{ number, type, format }time
,date
,number
, orchoice
. format is also optional and is a string describing format of the argument. You can only specify a format if you specify a type because the format depends on the type.
MessageFormat
often uses other format classes to format these items: it uses aChoiceFormat
to format choices, aDateFormat
to format dates, and aNumberFormat
to format numbers. Check these classes on how to specify the format pattern in the argument placeholder.
This page incorporates material or code copyrighted by Taligent, Inc. For more information on international resources, see their International Fact Sheet.
Locale-Sensitive Data |