Manejar Plurales

En Inglés, las formas plural y singular de una palabra nomalmente son diferentes. Esto puede representar un problema cuando se construyen mensajes que se refieren a cantidades. Por ejemplo, si el mensaje informa del número de ficheros en un disco, son posibles las siguientes variaciones:
There are no files on XDisk.
There is one file on XDisk.
There are 2 files on XDisk.
La forma más rápida de resolver este problema es crear un patrón de MessageFormat como éste:
There are {0,number} file(s) on {1}.
Desafortunadamente, este patrón resulta gramáticamente incorrecto:
There are 1 file(s) on XDisk.
Podemos hacer algo mejor que esto utilizando la clase ChoiceFormat. En esta sección, veremos como trarar con plurales en un mensaje, pasando a través de un programa de ejemplo llamado ChoiceFormatDemo.java. Este programa también hace uso de la clase MessageFormat que se describió en la sección anterior, Tratar con Mensajes Concatenados.

1. Definir el Patrón del Mensaje

Primero, identifiquemos las variables de nuestro mensaje:
There | are no files | on | XDisk | .
There | is one file  | on | XDisk | .
There | are 2 files  | on | XDisk | .
      |______________|    |_______|
            ^                 ^
            |                 |
         variable          variable
Luego, reemplazamos las variables del mensaje con argumentos, creando un patrón que puede aplicarse a un objeto MessageFormat:
There {0} on {1}.

Es muy sencillo trabajar con el argumento para el nombre del disco, que está representado por {1}. Lo trataremos igual que cualquier otra variable String en un patrón MessageFormat. Este argumento corresponde con el elemento 1 del array de valores (Ver paso 7).

Tratar con el argumento {0} es más complejo por un par de razones:

2. Crear un ResourceBundle

Aislaremos el texto del mensaje en un ResourceBundle porque debe ser traducido:
ResourceBundle bundle =
   ResourceBundle.getBundle("ChoiceBundle",currentLocale);
Hemos decidido construir nuestro ResourceBundle con ficheros de propiedades. El fichero ChoiceBundle_en_US.properties contiene las siguientes líneas:
pattern = There {0} on {1}.
noFiles = are no files
oneFile = is one file
multipleFiles = are {2} files
El contenido de este fichero de propiedades muestra cómo se construirán y formatearán los mensajes. La primera línea contiene el patrón para MessageFormat, que explicamos en el paso anterior. Las otras líneas contienen frases que reemplazarán el argumento {0} en el patrón. La frase para la clave "multipleFiles" contiene el argumento {2}, que será reemplazado por un número

Aquí podemos ver la versión francesa del fichero de propiedades ChoiceBundle_fr_FR.properties:

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

3. Crear un formateador de Mensaje

En este paso, ejemplarizamos MessageFormat y seleccionamos su Localidad:
MessageFormat messageForm = new MessageFormat("");
messageForm.setLocale(currentLocale);

4. Crear un formateador de Choice

El objeto ChoiceFormat nos permite elegir, basándose en un número double, un String particular. El rango de números double y los objetos String con los que se mapean, se especifican en arrays:
double[] fileLimits = {0,1,2};

String [] fileStrings = {
   bundle.getString("noFiles"),
   bundle.getString("oneFile"),
   bundle.getString("multipleFiles")
};
ChoiceFormat mapea cada elemento del array double con el elemento del array String que tiene el mismo índice. En nuestro código de ejemplo, el 0 mapea el String devuelto por la llamada a bundle.getString("noFiles"). Por coincidencia, en nuestro ejemplo, el índice es el mismo que el valor en el array fileLimits. si hubieramos seleccionado fileLimits[0] a 7, ChoiceFormat mapearía el número 7 con fileStrings[0].

Especificamos los arrays double y String cuando ejemplarizamos ChoiceFormat:

ChoiceFormat choiceForm = new ChoiceFormat(fileLimits, fileStrings);

5. Aplicar el Patrón

¿Recuerdas el patrón que construimos en el paso 1? Ahora es el momento de recuperar el patrón del ResourceBundle y aplicarlo al objeto MessageFormat:
String pattern = bundle.getString("pattern");
messageForm.applyPattern(pattern);

6. Asignar lo formatos

En este paso, asignamos el objeto ChoiceFormat creado en el paso 4 al objeto MessageFormat:
Format[] formats = {choiceForm, null, NumberFormat.getInstance()};
messageForm.setFormats(formats);
El método setFormats asigna objetos Format a los argumentos del patrón del mensaje. Debemos llamar al método applyPattern antes del llamar al método setFormats. La siguiente tabla muestra cómo el array Format corresponde con los argumentos del patrón del mensaje:

Elemento del ArrayArgumento del Patrón
choiceForm{0}
null{1}
NumberFormat.getInstance(){2}

7. Seleccionar los Argumentos y el Formato del Mensaje

En tiempo de ejecución, asignamos las variables al array de argumentos que pasamos al objeto MessageFormat. Los elementos del array corresponden con los argumentos del patrón. Por ejemplo, messageArgument[1] mapea al argumento {1} del patrón, que es un String que contiene el nombre del disco. En el paso anterior, asignamos un objeto ChoiceFormat al argumento {0} del patrón. Por lo tanto, el número asignado a messageArgument[0] el String seleccionado por el objeto ChoiceFormat. Si messageArgument[0] es mayor o igual que 2, el String "are {2} files" reemplaza al argumento {0} del patrón. El número asignado a messageArgument[2] será substituido en lugar del argumento {2} del patrón. Intentaremos hacer esto con las siguientes líneas de código:
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. Ejecutar el Programa de Demostración

Ejecutemos el programa para la Localidad U.S. English:
% 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.
Compara los mensajes mostrados por el programa con las frases del ResourceBundle del paso 2. Observa que el objeto ChoiceFormat selecciona la frase corecta, que el objeto MessageFormat utiliza para construir el mensaje apropiado.

La versión francesa del mensaje también parece correcta:

% 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.

Ozito