Интернационализация. Форматирование сообщений.

В библиотеке Java содержится класс MessageFormat, который форматирует текст, содержащий фрагменты, представленные посредством переменных. Например,

  
  String template = "On {2}, a {0} destroyed {1} houses and caused {3} of damage.";

В данном примере номера в фигурных скобках используются как "заполнители" для реальных имен и значений. Статический метод MessageFormat.format () позволяет подставить значения переменных. В JDK 5.0 поддерживаются методы с переменным числом параметров: таким образом, подстановка может быть выполнена так, как показано ниже.

  String message;

     * * *

  message = MessageFormat.format (template,  "hurricane", 99, new GregorianCalendar (1999, 0, 1).getTime (), 10.0E7);

В более старых версиях JDK необходимо было помещать значения в массив Object []. В рассматриваемом примере переменная {0} замещается значением "hurricane", переменная {1} заменяется значением 99 и т.д.
Статический метод format () форматирует значения с учетом текущего регионального стандарта. Для того, чтобы использовать класс MessageFormat с произвольными региональными настройками, необходимо поступить следующим образом :

MessageFormat mf = new MessageFormat (pattern locale);
String msg = mf.format (new Object[ ] { значения });

Здесь вызывается метод format суперкласса Format. К сожалению, класс MessageFormat не предоставляет аналогичный метод, обеспечивающий работу с переменным числом параметров.
В результате обработки строки, рассматриваемой в качестве примера, будет получено следующее сообщение:


  On 1/1/99 12:00 АМ, a hurricane destroyed 99 houses and caused 100,000,000 of damage.
  

Результат можно преобразовать, если сумму ущерба представить в денежных единицах, а дату с учетом формата:


  String template = "On {2,date,long}, a {0} destroyed {1} houses and caused {3,number,currency} of damage.";
  

В результате будет получено сообщение:

On January 1, 1999, a hurricane destroyed 99 houses and caused $100,000,000 of damage.

В составе переменной допускается задавать тип и стиль, которые разделяются запятыми. Допустимыми значениями являются следующие типы : number, time, date, choice. Если указан тип number, то возможны следующие стили: integer, currency, percent. Кроме того, в качестве стиля может быть указан шаблон числового формата, например $,##0. Дополнительную информацию по данному вопросу можно найти в описании класса DecimalFormat.
Для типа time и date может быть указан один из следующих стилей : short, medium, long, full.

Аналогично числам, в качестве стиля может быть использован шаблон даты. Допустимые форматы подробно рассматриваются в описании класса SimpleDateFormat.
Форматы выбора (тип choice) имеют более сложную структуру и подробно рассматриваются далее.

Методы класса MessageFormat

Наименование методаОписание
MessageFormat (String pattern);
MessageFormat (String pattern, Locale locale);
Создает объект форматирования сообщения согласно указанному шаблону и региональному стандарту.
void applyPattern (String pattern) Задает шаблон для объекта форматирования.
void setLocale (Locale locale)
Locale getLocale ()
Устанавливает или возвращает региональный стандарт для переменных в составе сообщения. Он используется только для последующих шаблонов, заданных с помощью метода applyPattern ().
static String format (String pattern, Object ... args) Форматирует строку согласно шаблону pattern, заменяя в нем переменные {i} значениями объектов из массива args [i].
StringBuffer format (Object args, StringBuffer result,
FieldPosition pos)
Форматирует шаблон данного объекта MessageFormat. Параметр args должен представлять собой массив объектов. Форматируемая строка добавляется к значению result, которое затем возвращается. Если параметр pos эквивалентен new FieldPosition (MessageFormat.Field.ARGUMENT), его свойства beginIndex и endIndex устанавливаются в соответствии с расположением текста, который замещает переменную {1}. Если информация о расположении Вас не интересует, в качестве этого параметра следует задать значение null.

Класс java.text.Format имеет метод String format (Object object), который форматирует заданный объект в соответствии с правилами, определенными посредством текущего объекта форматирования. В процессе работы данный метод обращается к методу format (object, new StringBuffer (), new FieldPosition (1)).toString ().

Формат выбора choice

Использование формата выбора предполагает определение последовательности пар значений, каждая из которых содержит нижнюю границу и строку подстановки.
Нижняя граница и строка подстановки разделяются символами #, а для разделения пар значений используется символ |. Ниже приведен пример переменной с указанием формата выбора.

  {1, choice, 0#no houses | 1#one house | 2#{1} houses}

Результаты форматирования, в зависимости от значения {1}, представлены в следующей таблице.

{1} Результат
0 "no houses"
1 "one house"
3 3 houses
-1 "no houses"

Может возникнуть вопрос, а зачем в форматируемой строке дважды указывается переменная {1}? Когда для этой переменной применяется формат выбора и значение оказывается большим или равным 2, возвращается выражение "{1} houses". Оно форматируется снова и включается в результирующую строку.
Данный пример показывает, что разработчики формата выбора приняли не самое лучшее решение. Если есть два варианта форматируемых строк, то для их разделения достаточно двух граничных значений, но согласно формату нужно задать три таких значения. Наименьшее из них никогда не используется.
Синтаксис мог бы быть более понятным, если бы границы указывались между вариантами значений, например следующим образом:

no houses | 1|one house | 2{1} houses
// к сожалению данный формат не поддерживается

С помощью символа < можно указать, что предполагаемый вариант должен быть выбран, если нижняя граница строго меньше значения.

Завершая пример о последствиях стихийного бедствия, необходимо поместить строку с условиями выбора внутри исходной строки сообщения. В результате получится следующая конструкция:

  String pattern = "On {2, date, long}, {0} destroyed {1, choice, 0#no houses | 1#one house | 2#{1} houses} "
                       
+ "and caused {3, number, currency} of damage.";

В немецком варианте она будет выглядет иначе.


  String pattern = "{0} zerstцrte am {2, date, long} {1, choice, 0#kein Haus | 1#ein Haus | 2#{1} Hдuser} "
                       
+ "und richtete einen Shaden von {3, number, currency} an.";

Примечательно, что последовательность слов в английском и немецком вариантах разная, но методу format передается тот же самый массив объектов. Под требуемый порядок слов подстраивается только последовательность появления переменных.