|
|
Message Formatting |
In English, the plural and singular forms of a word are usually different. This can present a problem when you are constructing messages that refer to quantities. For example, if your message reports the number of files on a disk, the following variations are possible:The fastest way to solve this problem is to create aThere are no files on XDisk. There is one file on XDisk. There are 2 files on XDisk.MessageFormatpattern like this:Unfortunately, the preceeding pattern results in incorrect grammar:There are {0,number} file(s) on {1}.We can do better than that, provided that we use the ChoiceFormatThere are 1 file(s) on XDisk.class. In this section, we'll show you how to deal with plurals in a message by stepping through a sample program called ChoiceFormatDemo.java. This program also makes use of the
MessageFormatclass, which we discussed in the previous section, Dealing with Concatenated Messages.1. Define the Message Pattern
First, let's identify the variables in our message:Next, we replace the variables in the message with arguments, creating a pattern that can be applied to aThere | are no files | on | XDisk | . There | is one file | on | XDisk | . There | are 2 files | on | XDisk | . |______________| |_______| ^ ^ | | variable variableMessageFormatobject:There {0} on {1}.The argument for the disk name, which is represented by {1}, is easy enough to deal with. We just treat it like any other
Stringvariable in aMessageFormatpattern. This argument matches the element at index 1 in the array of argument values. (See step 7.)Dealing with argument {0} is more complex, for a couple of reasons:
- First, the phrase that this argument replaces varies with the number of files. To construct this phrase at runtime, we need to map the number of files to a particular
String. For example, the number 1 will map to theString"is one file." TheChoiceFormatclass allows us to perform the necessary mapping.- Second, if the disk contains multiple files then the phrase includes an integer. The
MessageFormatclass lets us insert a number into a phrase.We'll show you how all of this is done in the steps that follow.
2. Create a ResourceBundle
We'll isolate the message text in aResourceBundlebecause it must be translated:We've decided to back ourResourceBundle bundle = ResourceBundle.getBundle("ChoiceBundle",currentLocale);ResourceBundlewith properties files. TheChoiceBundle_en_US.propertiesfile contains the following lines:The contents of this properties file shows how the message will be constructed and formatted. The first line contains the pattern forpattern = There {0} on {1}. noFiles = are no files oneFile = is one file multipleFiles = are {2} filesMessageFormat, which we discussed in the previous step. The other lines contain phrases that will replace argument {0} in the pattern. The phrase for the "multipleFiles" key contains the argument {2}, which will be replaced by number.The French version of the properties file,
ChoiceBundle_fr_FR.properties, is as follows:pattern = Il {0} sur {1}. noFiles = n' y a pas des fichiers oneFile = y a un fichier multipleFiles = y a {2} fichiers3. Create a Message Formatter
In this step we instantiateMessageFormatand set itsLocale:MessageFormat messageForm = new MessageFormat(""); messageForm.setLocale(currentLocale);4. Create a Choice Formatter
TheChoiceFormatobject allows us to choose, based on adoublenumber, a particularString. The range ofdoublenumbers, and theStringobjects to which they map, are specified in arrays:double[] fileLimits = {0,1,2}; String [] fileStrings = { bundle.getString("noFiles"), bundle.getString("oneFile"), bundle.getString("multipleFiles") };ChoiceFormatmaps each element in thedoublearray to the element in theStringarray that has the same index. In our sample code, the 0 maps to theStringreturned by callingbundle.getString("noFiles"). By coincidence, in our example the index is the same as the value in thefileLimitsarray. If we had setfileLimits[0]to 7, thenChoiceFormatwould map the number 7 tofileStrings[0].We specify the
doubleandStringarrays when instantiatingChoiceFormat:ChoiceFormat choiceForm = new ChoiceFormat(fileLimits, fileStrings);5. Apply the Pattern
Remember the pattern we constructed in step 1? It's time for us to retrieve the pattern from theResourceBundleand apply it to theMessageFormatobject:String pattern = bundle.getString("pattern"); messageForm.applyPattern(pattern);6. Assign the Formats
In this step, we assign theChoiceFormatobject created in step 4 to theMessageFormatobject:TheFormat[] formats = {choiceForm, null, NumberFormat.getInstance()}; messageForm.setFormats(formats);setFormatsmethod assignsFormatobjects to the arguments in the message pattern. You must invoke theapplyPatternmethod before you call thesetFormatsmethod. The following table shows how theFormatarray matches the arguments in the message pattern:
Array Element Pattern Argument choiceForm {0} null {1} NumberFormat.getInstance() {2} 7. Set the Arguments and Format the Message
At runtime, we assign the variables to the array of arguments that we pass to theMessageFormatobject. The elements in the array correspond to the arguments in the pattern. For example,messageArgument[1]maps to pattern argument {1}, which is aStringcontaining the name of the disk. In the previous step, we assigned aChoiceFormatobject to argument {0} of the pattern. Therefore, the number assigned tomessageArgument[0]determines whichStringtheChoiceFormatobject selects. IfmessageArgument[0]is greater than or equal to 2, then theString"are {2} files" replaces argument {0} in the pattern. The number assigned tomessageArgument[2]will be substitued in place of pattern argument {2}. We'll try this out with the following lines of code:Object[] messageArguments = {null, "XDisk", null}; for (int numFiles = 0; numFiles < 4; numFiles++) { messageArguments[0] = new Integer(numFiles); messageArguments[2] = new Integer(numFiles); String result = messageForm.format(messageArguments); System.out.println(result); }8. Run the Demo Program
Let's run the program for the U.S. EnglishLocale:Compare the messages displayed by the program with the phrases in the% java ChoiceFormatDemo en US currentLocale = en_US There are no files on XDisk. There is one file on XDisk. There are 2 files on XDisk. There are 3 files on XDisk.ResourceBundleof step 2. Notice that theChoiceFormatobject selects the correct phrase, which theMessageFormatobject uses to construct the proper message.The French version of the message looks okay, too:
% java ChoiceFormatDemo fr FR currentLocale = fr_FR Il n' y a pas des fichiers sur XDisk. Il y a un fichier sur XDisk. Il y a 2 fichiers sur XDisk. Il y a 3 fichiers sur XDisk.
|
|
Message Formatting |