Интернационализация. Пакеты ресурсов.

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

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

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

Поиск ресурсов

Для локализации приложений создаются так называемые пакеты ресурсов (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 автоматически находят ресурсы, наилучшим образом соответствующие конкретному региональному стандарту. Для включения в существующую программу новых локальных настроек необходимо всего лишь дополнить соответствующие пакеты ресурсов.
    Создавая приложения, не обязательно помещать все ресурсы в один пакет. Можно создать один пакет для надписей на кнопках, другой - для сообщений об ошибках и т.д.

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

    Для интернационализации строк необходимо все строки поместить в файл свойств, например 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  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 getKeys ()
      Возвращает объект Enumeration, содержащий ключи текущего пакета ресурсов. При этом в объект Enumeration также помещаются ключи из родительских пакетов ресурсов.
    • Object handleGetObject (String key)
      При реализации собственного механизма поиска ресурсов, данный метод следует переопределить так, чтобы он возвращал значение, соответствующее указанному ключу.