Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Локализация приложений, Статья для FAQ 
:(
    Опции темы
LSD
Дата 13.11.2005, 19:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Локализация приложений.

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

Один из основных для локализации классов, это java.util.Locale. Этот класс описывает текущую локаль, локаль - это описание текущего региона, языка и других особенностей. Класс Locale предназначен только для идентификации локали, никаких данных для локализации он не содержит. Самый главный параметр это используемый язык, второй по значимости параметр это страна, и третий параметр это вариант. Вариант не имеет какого-то определенного смысла и предназначен для указания некой дополнительной информации, не описываемой первыми двумя параметрами, например диалекта. Как правило вариант не используется. Из всех параметров обязательным является только язык. Параметр страна является опциональным и предназначен для указания страны пользователя.
Язык описывается двухбуквенным кодом ISO 639, код записывается в нижнем регистре. Страна обозначается двухбуквенным кодом ISO-3166, код записывается в верхнем регистре. Получить список языков и стран можно с помощью Locale.getISOLanguages() и Locale.getISOCountries() соответсвенно. Хотя никаких ограничений на вариант не накладывается, для варианта лучше придерживаться правил формирования идентификаторов в Java, иначе некоторые механизмы могут не работать или работать неправильно.

Задача: создать локаль для описания русского языка на Украине:
Код
Locale locale = new Locale("ru","UA");


Локаль по умолчанию устанавливается исходя из установок ОС, получить ее можно Locale.getDefault(). Переопределить локаль по умолчанию можно из кода Locale.setDefault(locale), из командной строки запуска: -Duser.language=ru -Duser.country=RU.



Для преобразования чисел, дат и сообщений в строковое представление служал форматтеры (см. java.text.Format), они все поддерживают конструкторы с указанием локали, чтобы форматировать в соответсвии с текущей локалью. Эти классы можно использовать без доработки, они полностью локализованы. Единствеено но, может потребоваться написание своего ResourceBundle для поддержки "экзотического" языка или диалекта.



Основная нагрузка по локализации приложений ложится на класс ResourceBundle. Данный класс умеет загружать ресурсы для указанной локали. Для загрузки ресурсов используется ResourceBundle.getBundle(<name>, <locale>, <classLoader>), где:
  • name - имя загружаемого ResourceBundle, имя должно соответсвовать соглашениям об именах классов в Java
  • locale - локаль для которой загружать ресурсы, если отсутсвует используется локаль по умолчанию
  • classLoader - используемый для загрузки ресурсов, если отсутсвует используется ClassLoader вызывающего класса
ResourceBundle по сути похож на java.util.Map, позволяет по ключу получить значение, получить список всех ключей, а вот методов по добалению своих пар ключ-значение или редактированию существующих нет т.к. ResourceBundle неизменяемый. Загрузку ResourceBundle можно вызывать несколько раз, после первого раза все последующие вызовы будут возвращать указатель на ранее загруженный ResourceBundle.

Задача: загрузить ResourceBundle и отобразить локализованное сообщение об ошибке.
Код
ResourceBundle resourceBundle = ResourceBundle.getBundle("ru.vingrad.locale.TutorialAppBundle", getLocale());
JOptionPane.showMessageDialog(null,
                              resourceBundle.getString("Messages.fatalErrorMessage"),
                              resourceBundle.getString("Messages.errorDialogTitle"),
                              JOptionPane.ERROR_MESSAGE);


Ресурсы загружаемые ResourceBundle могут хранится либо в текстовых файлах, организованных наподобие properties, либо в виде классов унаследованных от ListResourceBundle (именно отсюда идет требование к именам ресурсов). Ресурсы реализованные в виде наследников ListResourceBundle быстрее загружаются, позволяют хранить не только строки но любые объекты, но для локализации приложения нужен исходный код классов и компилятор. В то время как ResourceBundle реализованные в виде файлов properties могут быть локализованы в любом текстовом редакторе (может понадобится утилита native2ascii).

Загрузка ресурсов происходит следующим образом: формируются потенциальные имена для ResourceBundle:
  1. <базовое_имя>_<язык1>_<страна1>_<вариант1>
  2. <базовое_имя>_<язык1>_<страна1>
  3. <базовое_имя>_<язык1>
  4. <базовое_имя>_<язык2>_<страна2>_<вариант2>
  5. <базовое_имя>_<язык2>_<страна2>
  6. <базовое_имя>_<язык2>
  7. <базовое_имя>
где язык1, страна1, вариант1 код языка, страны и вариант локали переданый в качестве параметра загрузки, а язык2, страна2, вариант2 аналогично но для умолчальной локали. Для каждого имени вначале проводится попытка загрузить класс с таким именем, а если он не найден то файл properties, если и он не найден, то пререходим к другому имени. Если все попытки загрузить ResourceBundle закончились неудачно, то выбрасывается исключение MissingResourceException. ResourceBundle без суффиксов (т.е. только <базовое_имя>), называется умолчальным ResourceBundle и должен всегда существовать, в качестве языка для него желательно выбирать английский.

Задача: реализовать приложение с поддержкой русского и английского языков, в качестве умолчального выбрать английский

Приложение:
Код
package ru.vingrad.locale;

import javax.swing.*;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Date;
import java.awt.*;
import java.text.MessageFormat;

public class LocalisedFrame extends JFrame
{
  public static final String BUNDLE_NAME               = "ru.vingrad.locale.TutorialAppBundle";
  public final static String FRAME_TITLE_KEY           = "LocalisedFrame.title";
  public final static String FILE_MENU_KEY             = "LocalisedFrame.menu.file";
  public final static String FILE_OPEN_KEY             = "LocalisedFrame.menu.file.open";
  public final static String FILE_OPEN_MNEMONIC_KEY    = "LocalisedFrame.menu.file.openMnemonic";
  public final static String FILE_EXIT_KEY             = "LocalisedFrame.menu.file.exit";
  public final static String FILE_EXIT_MNEMONIC_KEY    = "LocalisedFrame.menu.file.exitMnemonic";
  public final static String SATUS_MESSAGE_KEY         = "LocalisedFrame.status";

  private ResourceBundle resourceBundle;

  public LocalisedFrame(Locale locale)
  {
    if(locale != null)
      setLocale(locale);
    resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME, getLocale());

    setTitle(resourceBundle.getString(FRAME_TITLE_KEY));

    JMenuBar mainMenu = new JMenuBar();
    JMenu fileMenu = new JMenu(resourceBundle.getString(FILE_MENU_KEY));
    JMenuItem openItem = new JMenuItem(resourceBundle.getString(FILE_OPEN_KEY), resourceBundle.getString(FILE_OPEN_MNEMONIC_KEY).charAt(0));
    JMenuItem exitItem = new JMenuItem(resourceBundle.getString(FILE_EXIT_KEY), resourceBundle.getString(FILE_EXIT_MNEMONIC_KEY).charAt(0));

    fileMenu.add(openItem);
    fileMenu.add(exitItem);
    mainMenu.add(fileMenu);
    setJMenuBar(mainMenu);

    JTextArea textArea = new JTextArea(30, 60);
    JLabel statusBar = new JLabel();
    statusBar.setBorder(BorderFactory.createLoweredBevelBorder());
    MessageFormat messageFormat = new MessageFormat(resourceBundle.getString(SATUS_MESSAGE_KEY), getLocale());
    Runtime runtime = Runtime.getRuntime();
    statusBar.setText(messageFormat.format(new Object[]{new Date(), runtime.freeMemory(), runtime.totalMemory()}));

    getContentPane().add(textArea, BorderLayout.CENTER);
    getContentPane().add(statusBar, BorderLayout.SOUTH);

    pack();
    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    setLocationRelativeTo(null);
  }

  public static void main(String[] args)
  {
    Locale.setDefault(Locale.ENGLISH);
    LocalisedFrame frame = new LocalisedFrame(new Locale("fr"));
    frame.setVisible(true);
  }
}


Умольчальный ResourceBundle (он же английский)
Код
package ru.vingrad.locale;

import static ru.vingrad.locale.LocalisedFrame.*;

import java.util.ListResourceBundle;

public class TutorialAppBundle extends ListResourceBundle
{
  private final static Object[][] content = new Object[][]
  {
    {FRAME_TITLE_KEY,          "Localised application"},
    {FILE_MENU_KEY,            "File"},
    {FILE_OPEN_KEY,            "Open"},
    {FILE_OPEN_MNEMONIC_KEY,   "O"},
    {FILE_EXIT_KEY,            "Exit"},
    {FILE_EXIT_MNEMONIC_KEY,   "x"},
    {SATUS_MESSAGE_KEY,        "Application started at {0,date,long} {0,time,long},    memory free {1,number} from {2,number}"}
  };

  protected Object[][] getContents()
  {
    return content;
  }
}


ResourceBundle для русского языка
Код
package ru.vingrad.locale;

import static ru.vingrad.locale.LocalisedFrame.*;

import java.util.ListResourceBundle;

public class TutorialAppBundle_ru extends ListResourceBundle
{
  private final static Object[][] content = new Object[][]
  {
    {FRAME_TITLE_KEY,          "Локализуемое приложение"},
    {FILE_MENU_KEY,            "Файл"},
    {FILE_OPEN_KEY,            "Открыть"},
    {FILE_OPEN_MNEMONIC_KEY,   "О"},
    {FILE_EXIT_KEY,            "Выход"},
    {FILE_EXIT_MNEMONIC_KEY,   "В"},
    {SATUS_MESSAGE_KEY,        "Приложение запущено {0,date,long} {0,time,long},    памяти свободно {1,number} из {2,number}"}
  };

  protected Object[][] getContents()
  {
    return content;
  }
}



P.S. Как обычно замечания и предложения приветствуются smile
Если кто переведет текст на еще какой нибудь язык, я покажу как создавать ResourceBundle в виде properties.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
carper
Дата 16.11.2005, 10:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 227
Регистрация: 2.3.2005

Репутация: 1
Всего: 8



LSD
Цитата

Единствеено но, может потребоваться написание своего ResourceBundle для поддержки "экзотического" языка или диалекта.


Во-первых, большое спасибо за статью.

Во-вторых, не совсем понятно как свой ResourceBundle может помочь с экзотическими форматами, скажем, даты. Мне казалось, что в таких случаях надо создавать свой форматтер?

Да, что касается файлов properties. Что-то их необходимость, с учетом их 7-и значной кодировки и извращений при добавлении символов других языков, представляется более чем сомнительной.
Тем более, что не вижу какие препятствия и сложности могут возникнуть у переводчика при правке наследника ListResourceBundle? Его дело перевести, а вызов javac ..., особенно с учетом того, что переводить не глядя на получившийся результат нельзя, т.е. все равно переводчику придется устанавливать JAVA, ну не понятно как можно не откомпилировать один класс?
В крайнем случае скройте эту часть работы от переводчика. smile
PM MAIL   Вверх
LSD
Дата 16.11.2005, 11:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Цитата(carper @ 16.11.2005, 10:27)
Во-вторых, не совсем понятно как свой ResourceBundle может помочь с экзотическими форматами, скажем, даты. Мне казалось, что в таких случаях надо создавать свой форматтер?

Я говорил не о формате даты, а о языке. Например хочешь ты писать дату на латыни.

Цитата(carper @ 16.11.2005, 10:27)
Да, что касается файлов properties. Что-то их необходимость, с учетом их 7-и значной кодировки и извращений при добавлении символов других языков, представляется более чем сомнительной.

Вообщем да, например Sun полностью перешла на ListResourceBundle.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
carper
Дата 16.11.2005, 11:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 227
Регистрация: 2.3.2005

Репутация: 1
Всего: 8



LSD
Цитата(LSD @ 16.11.2005, 11:17)
Вообщем да, например Sun полностью перешла на ListResourceBundle.


Ну, тогда, только остается еще раз поблагодарить за понятную и полезную статью, думаю, что она и так вполне полноценна и без описания properties.

Да, почему-то на форумах SUN постоянно тусуется народ с одной и той же проблемой, а именно, у SUN как-то странно описано понятие базового имени класса-наследника ListResourceBundle и многие пытаются задавать base name не указывая имя пакета.

Может стоит как-то это акцентировать?

И еще, мне кажется использование ResourceBundle, благодаря возможности использовать объекты, дает довольно интересную (хотя и не полностью заменяет) альтернативу таким вещам как Enum для хранения стандартных настроек.
Или это не целесообразно, все же нет такого контроля?
PM MAIL   Вверх
LSD
Дата 16.11.2005, 12:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Цитата(carper @ 16.11.2005, 11:54)
И еще, мне кажется использование ResourceBundle, благодаря возможности использовать объекты, дает довольно интересную (хотя и не полностью заменяет) альтернативу таким вещам как Enum для хранения стандартных настроек.
Или это не целесообразно, все же нет такого контроля?

Enum это скорее альтернатива public static final константам.
Хранить объекты можно, только надо учитывать, что сколько локалей поддерживается столько объектов может быть порождено. Т.е. если у тебя одна картинка на все языки то класть ее в ResourceBundle не разумно, а вот если ты ее можешь менянять в зависимости от языка, то тогда вполне.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
PashaOvechkin
Дата 17.6.2008, 15:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 394
Регистрация: 1.4.2007
Где: Riga, Latvia

Репутация: 2
Всего: 4



Хорошая статья!  smile 
Но веселей становится когда нужно сменить язык в ран тайме.
PM MAIL Skype   Вверх
w1nd
Дата 18.6.2008, 00:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Вертилятор
***


Профиль
Группа: Завсегдатай
Сообщений: 1077
Регистрация: 22.3.2006
Где: Москва

Репутация: 20
Всего: 54



Цитата(carper @  16.11.2005,  10:27 Найти цитируемый пост)
Да, что касается файлов properties. Что-то их необходимость, с учетом их 7-и значной кодировки и извращений при добавлении символов других языков, представляется более чем сомнительной. Тем более, что не вижу какие препятствия и сложности могут возникнуть у переводчика при правке наследника ListResourceBundle? Его дело перевести, а вызов javac ..., особенно с учетом того, что переводить не глядя на получившийся результат нельзя, т.е. все равно переводчику придется устанавливать JAVA, ну не понятно как можно не откомпилировать один класс?

Всё равно файлы properties удобнее. Я сам долгое время пользовался ListResourceBundle, но отсутствие необходимости перекомпиляции (и - главное - пересборки) дорогого стоит, так что теперь у меня только .properties. И нельзя упереться в ограничение на размер .class-файла. 


--------------------
user posted imageuser posted image
PM MAIL ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0983 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.