Handling Plurals

如果复数形式和单数形式都可能,则消息中的单词可能会有所不同。使用ChoiceFormat类,您可以将数字 Map 到单词或短语,从而可以构造语法正确的消息。

在英语中,单词的复数形式和单数形式通常是不同的。当您构造引用数量的消息时,这可能会带来问题。例如,如果您的消息报告磁盘上的文件数,则可能有以下变化:

There are no files on XDisk.
There is one file on XDisk.
There are 2 files on XDisk.

解决此问题的最快方法是创建一个MessageFormatPattern,如下所示:

There are {0,number} file(s) on {1}.

不幸的是,上述 Pattern 导致语法错误:

There are 1 file(s) on XDisk.

如果您使用ChoiceFormat类,则可以做得更好。在本节中,您将通过逐步执行名为ChoiceFormatDemo的示例程序来学习如何处理消息中的复数形式。该程序还使用MessageFormat类,在上一节处理复合消息中进行了讨论。

1.定义消息 Pattern

首先,确定消息中的变量:

三行文本,每行中的变量突出显示。

接下来,将消息中的变量替换为参数,创建可应用于MessageFormat对象的 Pattern:

There {0} on {1}.

磁盘名称的参数由{1}表示,很容易处理。您只需要像MessageFormatPattern 中的任何其他String变量一样对待它即可。此参数与参数值数组中索引 1 处的元素匹配。 (请参阅step 7。)

处理参数{0}比较复杂,原因有两个:

  • 该参数替换的短语随文件数量的不同而不同。要在运行时构造此短语,您需要将文件数 Map 到特定的String。例如,数字 1 将 Map 到包含短语is one fileStringChoiceFormat类允许您执行必要的 Map。

  • 如果磁盘包含多个文件,则该短语包括一个整数。 MessageFormat类可让您在短语中插入数字。

2.创建一个 ResourceBundle

因为必须翻译消息文本,所以将其用ResourceBundle隔开:

ResourceBundle bundle = ResourceBundle.getBundle(
    "ChoiceBundle", currentLocale);

该示例程序使用属性文件支持ResourceBundleChoiceBundle_en_US.properties包含以下几行:

pattern = There {0} on {1}.
noFiles = are no files
oneFile = is one file
multipleFiles = are {2} files

此属性文件的内容显示了如何构造和格式化消息。第一行包含MessageFormat的 Pattern。 (请参见step 1。)其他各行包含将替换 Pattern 中参数{0}的短语。 multipleFiles键的短语包含参数{2},该参数将替换为数字。

这是属性文件ChoiceBundle_fr_FR.properties的法语版本

pattern = Il {0} sur {1}.
noFiles = n'y a pas de fichiers
oneFile = y a un fichier
multipleFiles = y a {2} fichiers

3.创建邮件格式化程序

在此步骤中,实例化MessageFormat并设置其Locale

MessageFormat messageForm = new MessageFormat("");
messageForm.setLocale(currentLocale);

4.创建选择格式器

ChoiceFormat对象使您可以根据double数字选择特定的String。数组中指定double数字的范围以及它们所 Map 的String对象:

double[] fileLimits = {0,1,2};
String [] fileStrings = {
    bundle.getString("noFiles"),
    bundle.getString("oneFile"),
    bundle.getString("multipleFiles")
};

ChoiceFormatdouble数组中的每个元素 Map 到String数组中具有相同索引的元素。在示例代码中,0Map 到通过调用bundle.getString("noFiles")返回的String。碰巧的是,索引与fileLimits数组中的值相同。如果代码已将fileLimits[0]设置为 7,则ChoiceFormat会将数字 7Map 到fileStrings[0]

您在实例化ChoiceFormat时指定doubleString数组:

ChoiceFormat choiceForm = new ChoiceFormat(fileLimits, fileStrings);

5.应用图案

还记得您在步骤 1 中构建的 Pattern 吗?现在是时候从ResourceBundle检索 Pattern 并将其应用于MessageFormat对象了:

String pattern = bundle.getString("pattern");
messageForm.applyPattern(pattern);

6.分配格式

在此步骤中,您将在步骤 4 中创建的ChoiceFormat对象分配给MessageFormat对象:

Format[] formats = {choiceForm, null, NumberFormat.getInstance()};
messageForm.setFormats(formats);

setFormats方法将Format对象分配给消息 Pattern 中的参数。在调用setFormats方法之前,必须先调用applyPattern方法。下表显示了Format数组的元素如何与消息 Pattern 中的参数相对应:

ChoiceFormatDemo 程序的格式数组

Array ElementPattern Argument
choiceForm{0}
null{1}
NumberFormat.getInstance(){2}

7.设置参数并设置消息格式

在运行时,程序将变量分配给传递给MessageFormat对象的参数数组。数组中的元素对应于 Pattern 中的参数。例如,messageArgument[1]Map 到 Pattern 参数{1},该参数是包含磁盘名称的String。在上一步中,程序将ChoiceFormat对象分配给 Pattern 的自变量{0}。因此,分配给messageArgument[0]的数字确定ChoiceFormat对象选择哪个String。如果messageArgument[0]大于或等于 2,则包含短语are {2} filesString替换 Pattern 中的参数{0}。分配给messageArgument[2]的数字将替换 Pattern 参数{2}。这是try此代码的代码:

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.运行演示程序

将程序显示的消息与步骤 2 的ResourceBundle中的短语进行比较。请注意,ChoiceFormat对象选择了正确的短语,MessageFormat对象用来构造正确的消息。 ChoiceFormatDemo程序的输出如下:

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.

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.