Интернационализация, i18n, l10n

Вопрос интернационализации пользовательского интерфейса - один из важных вопросов при разработке приложения. Для этого недостаточно использовать Unicode и перевести на нужный язык все сообщения пользовательского интерфейса. Интернационализация приложения означает нечто большее, чем поддержка Unicode. Дата, время, денежные суммы и даже числа могут по-разному представляться на различных языках.

Широкое распространение получили условные сокращения терминов интернационализации и локализации приложений i18n и l10n, в которых цифра означает количество символов между первой и последней позицией:

  • i18n - интернационализация (internationalization);
  • l10n - локализация (localization).

В отдельной литературе делают акцент на этих двух определениях, под которыми понимается :

  1. Интернационализация - это процесс разработки приложения такой структуры, при которой дополнение нового языка не требует перестройки и перекомпиляции (сборки) всего приложения.
  2. Локализация предполагает адаптацию интерфейса приложения под несколько языков. Добавление нового языка может внести определенные сложности в локализацию интерфейса.

Java - первый язык программирования, в котором изначально были предусмотрены средства интернационализации. Строки формируются из символов Unicode. Поддержка этого стандарта кодирования позволяет создавать Java-приложения, обрабатывающие тексты на любом из существующих в мире языков.

Большинство фреймворков, используемые в Java 2EE, поддерживает интернационализацию приложений с использованием Java-технологии, существенно снижая трудозатраты при разработке Web-приложения, "говорящего" на нескольких языках.

Региональные стандарты Locale

Приложение, которое адаптировано для международного рынка, легко определить по возможности выбора языка, для работы с ним. Но профессионально адаптированные приложения могут иметь разные региональные настройки даже для тех стран, в которых используется одинаковый язык. В любом случае команды меню, надписи на кнопках и программные сообщения должны быть переведены на местный язык, возможно с использованием специального национального алфавита. Но существует еще много других более тонких различий, которые касаются форматов представления вещественных чисел (разделители целой и дробной частей, разделителей групп тысяч) и денежных сумм (включение и местоположения денежного знака), а также формата даты (порядок следования и символы разделители дней, месяцев и лет).

Существует ряд классов, которые выполняют форматирование, принимая во внимание указанные выше различия. Для управления форматированием используется класс Locale.

Региональный стандарт Locale определяет язык. Кроме этого могут быть указаны географическое расположение и вариант языка. Например, в США используется следующий региональный стандарт:

language=English, location=United States

В Германии региональный стандарт имеет вид :

language=German, location=Germany

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

language=German, location=Switzerland

В данном случае текст, даты и числа будут форматироваться так же, как и для Германии, но денежные суммы будут отображаться в швейцарских франках, а не в евро. Если задавать только язык, например language=German, то особенности конкретной страны (например, формат представления денежных единиц) не будут учтены.

Вариант языка используется довольно редко. Например, в настоящее время в норвежском языке (производном от датского) определены два набора правил правописания (Bokmel) и новый (Nynorsk). В этом случае, для задания традиционных правил орфографии используется параметр, определяющий вариант:

language=Norwegian, location=Norway, variant=Bokmel

Для выражения языка и расположения в компактной и стандартной форме в Java используются коды, определенные Международной организацией по стандартизации (ISO). Язык обозначается двумя строчными буквами в соответствии со стандартом ISO-639, а страна ( расположение)- двумя прописными буквами согласно стандарту ISO-3166.

Чтобы задать региональный стандарт, необходимо объединить код языка, код страны и вариант (если он есть), а затем передать полученную строку в конструктор класса Locale.

  Locale german = new Locale ("de"); 
  Locale germanGermany = new Locale ("de", "DE"); 
  Locale germanSwitzerland = new Locale ("de", "CH"); 
  Locale norwegianNorwayBokmel = new Locale ("no", "NO", "B"); 

Для удобства пользователей в JDK предусмотрено несколько предопределенных объектов с региональными настройками, а для некоторых языков также имеются объекты, позволяющие указать язык без указания страны:

Предопределенные объекты с региональными установками Объекты, позволяющие указать язык без указания страны
Locale.CHINA Locale.CHINESE
Locale.FRANCE Locale.FRENCH
Locale.GERMANYLocale.GERMAN
Locale.ITALY Locale.ITALIAN
Locale.JAPAN Locale.JAPANESE
Locale.US Locale.ENGLISH

Помимо вызова конструктора или выбора предопределенных объектов, существует еще два пути получения объектов с региональными настройками. Статический метод getDefault() класса Locale позволяет определить региональную настройку, которая используется в операционной системе по-умолчанию. Изменить настройку по-умолчанию можно вызвав метод setDefault (). Однако следует помнить, что данный метод воздействует только на Java-программу, а не на операционную систему в целом.

Региональные настройки, getAvailableLocales

Метод getLocale() возвращает региональные настройки того компьютера, на котором он запущен. И наконец, все зависимые от региональных настроек вспомогательные классы могут возвращать массив поддерживаемых региональных стандартов. Например, приведенный ниже метод возвращает все региональные настройки, поддерживаемые классом DateFormat.

Locale [] supportedLocales = DateFormat.getAvailableLocales();

Какие действия можно выполнять на основе полученных региональных настроек? Выбор невелик. Единственными полезными методами класса Locale являются методы определения кодов языка и страны. Наиболее важными из них является метод getDisplayName(), возвращающий строку с описанием региональной настройки, которая содержит не какие-то двухбуквенные загадочные коды, а вполне понятные пользователю обозначения

 German (Switzerland)

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

 Locale loc = new Locale ("de", "CH"); 
 System.out.println (loc.getDisplayName (Locale.GERMAN)); 

В результате выполнения этого кода описание региональной настройки будет выведено на указанном в ней языке :

Deutsch (Schweiz)

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

Форматирование числовых значений NumberFormat

Ранее упоминалось, что в разных странах и регионах используются различные способы представления чисел и денежных сумм. В пакете java.text содержатся классы, позволяющие форматировать числа и выполнять разбор их строкового представления. Для форматирования чисел в соответствии с конкретным региональным стандартом необходимо выполнить ряд действий:

  1. Получить объект регионального стандарта, как было описано в предыдущем разделе.
  2. Использовать фабричный метод для получения объекта форматирования.
  3. Применить полученный объект форматирования для формирования числа или разбора его строкового представления.

В качестве фабричных методов (factory method) используются статические методы getNumberInstance (), getCurrencyInstance (), getPercentInstance () класса NumberFormat. Они получают в качестве параметра объект Locale и возвращают объекты, предназначенные для форматирования чисел, денежных сумм и значений, выраженных в процентах. Например, для отображения денежной суммы в формате, принятом в Германии, можно использовать приведенный ниже фрагмент кода:

 Locale loc = new Locale ("de", "DE");
 NumberFormat currFmt = NumberFormat.getCurrencyInstance (loc);
 double amt = 123456.78;
 System.out.println (currFmt.format (amt));

В результате выполнения этого кода будет получена следующая строка:


123.456,78 €
  

Для обозначения евро здесь используется знак €, который располагается в конце строки. Кроме этого, следует обратить внимание на символы, применяемые для обозначения дробной части и разделения десятичных разрядов.

Для преобразования строки, записанной в соответствии с определенным региональным стандартом, в число предусмотрен метод parse (), который выполняет синтаксический анализ строки с автоматическим использованием заданного по умолчанию регионального стандарта. В приведенном ниже примере показан способ преобразования строки, введеной пользователем в поле редактирования, в число. Метод parse () способен преобразовывать числа, в которых в качестве разделителя используется точка и запятая.

 TextField inputField; 
 . . . 
 NumberFormat fmt = NumberFormat.getNumberInstance (); 
 // Получить объект форматирования для используемого 
 // по умолчанию регионального стандарта 
 Number input = fmt.parse (inputField.getText ().trim ()); 
 double x = input.doubleValue (); 

Метод parse () возвращает результат абстрактного типа Number. На самом деле возвращаемый объект является экземпляром класса Long или Double, в зависимости от того, представляет исходная строка целое число или число с плавающей точкой. Если это не важно, то для получения числового значения достаточно использовать метод doubleValue() класса Number.

Для объектов типа Number не поддерживается автоматическое приведение к простым типам.
Необходимо явным образом вызывать метод doubleValue () или intValue ()
.

Если число представлено в некорректном формате, генерируется исключение ParseException. Например, не допускается наличие символа пробела в начале строки, преобразуемой в число. (Для их удаления следует использовать метод trim ()). Любые символы, которые располагаются в строке после числа, лишь игнорируются и исключение в этом случае не возникает.

Очевидно, что классы, возвращаемые методами getXxxInstance (), являются экземплярами не абстрактного класса NumberFormat, а одного из его подклассов. Фабричным методам известно лишь то, как найти объект, представляющий определенный региональный стандарт.

Для получения списка поддерживаемых региональных стандартов можно использовать статистический метод getAvailableLocales, возвращающий массив региональных стандартов, для которых существуют объекты форматирования.

Методы пакета java.text.NumberFormat

МетодОписание
static Locale[] getAvailableLocales () Возвращает массив объектов Locale, для которых доступны объекты форматирования
static NumberFormat getNumberFormatInstance()
static NumberFormat getNumberFormatInstance(Locale l)
static NumberFormat getNumberCurrency ()
static NumberFormat getNumberCurrencyInstance (Locale l)
static NumberFormat getNumberPercent ()
static NumberFormat getNumberPercentInstance (Locale l)
Возвращает объект форматирования чисел, денежных сумм или величин, представленных в процентах, для текущего или заданного регионального стандарта
String format (double x)
String format (long x)
Возвращает строку, полученную в результате форматирования заданного числа с плавающей точкой или целого числа.
Number parse (String s) Возвращает число, полученное после преобразования строки. Это число может иметь тип Long или Double. Строка не должна начинаться с пробелов. Любые символы в строке после числа игнорируются. Если преобразование закончилось неудачей, то метод генерирует исключение ParseException
void setParseIntegerOnly (boolean b)
boolean isParseIntegerOnly ()
Устанавливает или возвращает признак того, что данный объект форматирования предназначен для преобразования только целочисленных значений.
void setGroupingUsed (boolean b)
boolean isGroupingUsed ()
Устанавливает или возвращает флаг, указывающий на то, что данный объект форматирования распознает символы разделения групп десятичных разрядов (например, 100, 000)
void setMinimumIntegerDigits (int n)
void setMaximumIntegerDigits (int n)
void setMinimumFractionDigits (int n)
void setMaximumFractionDigits (int n)
int getMinimumIntegerDigits ()
int getMaximumIntegerDigits ()
int getMinimumFractionDigits ()
int getMaximumFractionDigits ()
Устанавливает или возвращает максимальное либо минимальное количество цифр в целой или дробной части числа

Денежные суммы

Для форматирования денежных сумм используется метод getCurrencyInstance() класса NumberFormat.Однако этот метод не обеспечивает достаточной гибкости - он возвращает форматированную строку для одной валюты. Допустим, Вы выписываете счет для иностранного потребителя, в котором одни суммы представлены в долларах, а другие в евро. Использование двух приведенных ниже объектов форматирования не является решением задачи.

 NumberFormat dollarFormatter = NumberFormat.getCurrencyInstance (Locale.US);
 NumberFormat euroFormatter = NumberFormat.getCurrencyInstance (Locale.GERMANY);

Счет, содержащий такие значения, как $100,000 и 100.000€, будет выглядеть достаточно странно, поскольку символы разделителей групп разрядов отличаются.

Для управления форматированием денежных сумм следует использовать класс Currency.Для получения объекта Currency необходимо передать статическому методу Currency.getInstance () идентификатор валюты. Затем необходимо вызвать метод setCurrency () каждого объекта форматирования. Ниже показано, как настроить объект форматирования евро для американсого потребителя.

 NumberFormat euroFormatter = NumberFormat.getCurrencyInstance (Locale.US);
 euroFormatter.setCurrency (Currency.getInstance ("EUR"));

Идентификаторы валют определены стандартом ISO 4217. Некоторые из них приведены в таблице.

Наименование валютыОбозначение
Доллар США USD
Евро EUR
Английский фунт GBR
Японская йена JPY
Индийская рупия INR
Российская рупияRUB

Методы пакета java.util.Currency

МетодОписание
static Currency getInstance (String currencyCode)
static Currency getInstance (Locale locale)
Возвращает экземпляр класса Currency, соответствующий заданному коду ISO 4217 или стране, указанной посредством объекта Locale
String toString ()
String getCurrencyCode ()
Возвращает код ISO 4217 для данной валюты
String getSymbol ()
String getSymbol (Locale locale)
Возвращает символ, обозначающий данную валюту в соответствии или заданными региональными настройками. Например, в зависимости от объекта Locale, доллар США (USD) может обозначаться как $ или US$
int getDefaultFractionDigits () Возвращает число цифр в дробной части для данной валюты, принятое по умолчанию

Форматирование даты и времени DateFormat

При форматировании даты и времени в соответствии с региональными стандартами следует иметь в виду четыре особенности:

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

Для учета перечисленных возможносте в Java имеется класс DateFormat, который используется почти также, как и класс NumberFormat. В первую очередь следует сформировать объект регионального стандарта. Для получения массива региональных стандартов, поддерживающих формат даты, можно использовать предлагаемый по умолчанию статический метод getAvailableLocales (). Далее необходимо вызвать один из трех фабричных методов:

 fmt = DateFormat.getDateInstance (dateStyle, loc);
 fmt = DateFormat.getTimeInstance (timeStyle, loc);
 fmt = DateFormat.getDateTimeInstance (dateStyle, timeStyle, loc);

Для указания нужного стиля предусмотрен параметр, в качестве которого задается одна из следующих констант:

 DateFormat.DEFAULT; 
 DateFormat.FULL    // Wednesday, Septemer 15 2004, 8:15:03 pm PDT для регионального стандарта США 
 DateFormat.LONG    // Septemer 15, 2004 8:15:03 pm PDT для регионального стандарта США 
 DateFormat.MEDIUM  // Sep 15, 2004 8:15:03 pm для регионального стандарта США 
 DateFormat.SHORT   // 9/15/04 8:15 pm для регионального стандарта США 

Представленные выше фабричные методы возвращают объект, который можно использовать для форматирования даты.

 Date date = new Date (); 
 String s = fmt.format (date); 

Для преобразования строки в дату используется метод parse(), который работает аналогично одноименному методу класса NumberFormat. Например, приведенный ниже код преобразует строку, введенную пользователем в поле редактирования; при этом учитываются региональный настройки по умолчанию:

 TextField inputField; 
 . . . 
 DateFormat fmt = DateFormat.getDateInstance (DateFormat.MEDIUM); 
 Date input = fmt.parse (inputField.getText ().trim ()); 

В случае некорректного ввода даты попытка преобразования приведет к генерации исключения ParseException (). Следует отметить, что в начале строки, подлежащей преобразованию в дату также не допускаются пробелы. Для их удаления следует вызвать метод trim (). Любые символы, котрые располагаются после даты, игнорируются. К сожалению, пользователь должен вводить дату в конкретном формате. Например, если установлен тип представления даты MEDIUM в региональном стандарте США, то предполагается, что введенная строка должна иметь вид Sep 18, 1997. Но если пользователь введет строку Sep 18 1997 (без запятой) или 9/18/97 (в кратком формате), то это приведет к ошибке преобразования.

Для интерпретации неточно указанных дат предусмотрен флаг lenient. Если данный флаг установлен, то неверно заданная дата February 30, 1999 будет автоматически преобразована в дату March 2, 1999. Такое поведение вряд ли можно считать безопасным, поэтому данный флаг следует отключить. В этом случае, при попытке пользователя ввести некорректное сочетание дня, месяца и года во время преобразования строки в дату будет сгенерировано исключение IllegalArgumetException.

Пакеты ресурсов resources

При локализации приложений необходимо переводить огромное количество сообщений, надписей интерфейса и т.п. Для упрощения задачи рекомендуется собрать все локализуемые строки в отдельном месте, которое называется ресурсом (resource). В этом случае достаточно отредактировать файлы ресурсов, не трогая исходный код программы.

В Java для определения строковых ресурсов используются файлы свойств, а для ресурсов других типов создаются классы ресурсов.

Технология использования ресурсов в Java отличается от технологии использования ресурсов в операционныхсистемах Windows и Macintosh. В выполняемой программе системы Windows такие ресурсы, как меню, диалоговые окна, пиктограммы и сообщения, хранятся отдельно от программы. Поэтому специальный редактор ресурсов позволяет просматривать и модифицировать их без изменения программного кода.

В Java технологии применяется концепция использования ресурсов, позволяющая размещать файлы данных, аудиофайлы и изображения в JAR-архивах. Метод getResource () класса Class находит файл, открывает его и возвращает URL, указывающий на ресурс. При размещении файлов в JAR-архивах задачу поиска файлов решает загрузчик классов. Данный механизм обеспечивает поддержку региональных стандартов.

Определение файла ресурсов ResourceBundle

Для локализации приложений создаются так называемые пакеты ресурсов (resource bundle). Каждый пакет представляет собой файл свойств или класс, который описывает элементы, специфические для конкретного регионального стандарта (например, сообщения, надписи и т.д.). В каждый пакет помещаются ресурсы для всех региональных стандартов, поддержка которых предполагается в программе.

Для именования пакетов ресурсов используются специальные соглашения. Например, ресурсы, специфические для Германии, помещаются в файл с именем имяПакета_de_DE, а ресурсы, общие для стран, в которых используется немецкий язык, размещаются в классе имяПакета_de. Общие правила таковы : ресурсы для конкретной страны именуются по принципу:

имяПакета_язык_СТРАНА

Имя файла ресурсов для конкретного языка формируется так :

имяПакета_язык

Ресурсы, применяемые по умолчанию, помещаются в файл, имя которого не содержит суффикса. Для загрузки пакета ресурсов используется метод getBundle().

ResourceBundle bundle = ResourceBundle.getBundle("ProgramResources", currentLocale)

Метод getBundle () пытается загрузить информацию из пакета ресурсов, которая соответствует языку, расположению и варианту текущего регионального стандарта.Если попытка загрузки окончилась неудачей, последовательно отбрасывается вариант, страна и язык. Затем осуществляется поиск ресурса, соответствующего текущему региональному стандарту, и происходит обращение к пакету ресурсов по умолчанию. Если и эта попытка завершается неудачей, генерируется исключение MissingResourceException. Таким образом, метод getBundle () пытается загрузить первый доступный ресурс из перечисленных пакетов :

  имяПакета_трс_язык_трс_СТРАНА_трс_вариант  
  имяПакета_трс_язык_трс_СТРАНА  
  имяПакета_трс_язык  
   
  имяПакета_рсу_язык_рсу_СТРАНА_рсу_вариант  
  имяПакета_рсу_язык_рсу_СТРАНА  
  имяПакета_рсу_язык  
   
  имяПакета  

Здесь используются сокращения :

  • трс - текущий региональный стандарт;
  • рсу - региональный стандарт по умолчанию.

Даже, если метод getBundle () находит пакет, например имяПакета_de_DE, он продолжает искать пакеты имяПакета_de, имяПакета. Если такие пакеты существуют, то они становятся родительскими по отношению к пакету имяПакета_de_DE в иерархии ресурсов. Родительские классы нужны в тех случаях, когда необходимый ресурс не найден в пакете имяПакета_de_DE, и выполняется поиск ресурса в пакетах имяПакета_de, имяПакета. Другими словами, поиск ресурса проверяется последовательно во всех пакетах до первого вхождения.

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

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

Файлы свойств properties

Для интернационализации строк необходимо все строки поместить в файл свойств, например MyPackage.properties. Файл свойств - это обычный текстовый файл, каждая строка которого содержит ключ и значение. Пример содержимого такого файла приведен ниже :

  colorName=black
  PageSize=210x297
  buttonName=Insert

Имя файла выбирается по принципу, описанному в предыдущем разделе.

MyPackage.properties
MyPackage_en.properties
MyPackage_de_DE.properties

Для загрузки пакета ресурсов из файла свойств применяется приведенное ниже выражение :

ResourceBundle bundle = ResourceBundle.getBundle("MyPackage", locale);

Поиск конкретной строки выполняется следующим образом :

String label = bundle.getString ("PageSize");

Файлы свойств могут содержать только ASCII-символы. Для размещения в них сомволов в кодировке Unicode следует использовать формат \uxxxx. Например, строка 'colorName=Зеленый' для кириллицы будет иметь вид

colorName=\u0417\u0435\u043B\u0435\u043D\u044B\u0439

Классы, реализующие пакеты ресурсов

Для поддержки ресурсов, не являющихся строками, необходимо определить классы, являющиеся подклассами класса ResourceBundle. Выбор имен таких классов осуществляется в соответствии с соглашениями об именовании, например:

MyProgrammResource.java
MyProgrammResource_en.java
MyProgrammResource_de_DE.java

Для загрузки класса используется тот же метод getBundle (), что и для загрузки свойств.

ResourceBoundle boundle = ResourceBoundle.getBundle ("MyProgrammResource", locale);

Если два пакета ресурсов, один из которых реализован в виде класса, а другой в виде файла свойств имеют одинаковые имена, то при загрузке предпочтение отдается классу. В каждом классе, реализующем пакет ресурсов, поддерживается таблица поиска. Для получения значения используется строка-ключ.

Color backgroundColor = (Color) bundle.getObject("backgroundColor");
double[ ] paperSize   = (double[ ])bundle.getObject("defaultPaperSize");

Самый простой способ реализации пакета ресурсов - создание подкласса ListResourceBundle. Класс ListResourceBundle позволяет помещать все ресурсы в массив объектов и выполнять поиск. Подкласс класса ListResourceBundle должен иметь следующую структуру:

public class имяПакета_язык_СТРАНА extends ListResourceBundle  
{  
    private static final Objects[][] contents =  
    {  
          {ключ1, значение1},  
          {ключ2, значение2},  
          . . .  
    }  
    public Object[][] getContents () {
        return contents;
    }  
}  

Пример классов, созданных на базе ListResourceBundle, приведен ниже.

// Листинг примера использования ListResourceBundle
   
public class ProgramResources_de extends ListResourceBundle  
{  
     private static final Objects[] []   contents =  
     {  
          {"backgroundColor", Color.black},  
          {defaultPaperSize, new double[] {210, 297}}  
     }  
     public Object[][] getContents () {return contents; }  
}  
   
public class ProgramResources_en_US extends ListResourceBundle  
{  
     private static final Objects[][] contents =  
     {  
         {"backgroundColor", Color.blue},  
         {defaultPaperSize, new double[]  {216, 279}}  
     }  
     public Object[][] getContents () {return contents; }  
}  

Класс пакета ресурсов можно также создать как подкласс класса ResourceBundle. В этом случае необходимо реализовывать два метода, предназначенные для получения объекта Enumeration,содержащего ключи, и для извлечения значения, соответствующего конкретному ключу.

Enumeration <String> getKeys ();
Object handleGetObject (String key);

Метод getObject () класса ResourceBundle вызывает определяемый разработчиком метод handleGetObject ().

Методы пакета java.util.ResourceBundle

static ResourceBundle getBundle (String baseName, Locale loc)
static ResourceBundle getBundle (String baseName)
Загружает класс пакета ресурсов с заданным именем, а также его родительские классы для указанного регионального стандарта. Если классы пакетов расположены в Java-пакете, то должно быть указано полное имя, например, intl.ProgramResources. Классы пакетов ресурсов должны быть объявлены открытыми (public), чтобы метод getBundle() мог обращаться к ним.
Object getObject (String name) Извлекает объект из пакета ресурсов или его родительских пакетов.
String getString (String name) Извлекает объект из пакета ресурсов или его родительских пакетов и приводит к типу String.
String [] getStringArray (String name) Извлекает объект из пакета ресурсов или его родительских пакетов и представляет в виде массива строк.
Enumeration <String> getKeys () Возвращает объект Enumeration, содержащий ключи текущего пакета ресурсов. При этом в объект Enumeration также помещаются ключи из родительских пакетов ресурсов.
Object handleGetObject (String key) При реализации собственного механизма поиска ресурсов, данный метод следует переопределить так, чтобы он возвращал значение, соответствующее указанному ключу.

Форматирование сообщений MessageFormat

В библиотеке 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 | 21 houses}

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

{1}Результат
0"no houses"
1"one house"
33 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 | 21 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 | 21 Häuser} " + 
                   " und richtete einen Shaden von {3, number, currency} an.";

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

Наверх
  Рейтинг@Mail.ru