среда, 22 июня 2011 г.

Java i18n - никакого мошенничества, только ловкость рук

В очередной раз старушка джава подкидывает прикол. На этот раз история у нас с интернационализацией, или просто i18n.

Как мы все знаем, при использовании стандартного подхода, тексты сообщений для различных языков размещаются в .properties файлах. Для каждого языка создается файл с соответствующим суффиксом. Кроме того, заботливые разработчики джавы настоятельно рекомендуют в доках:
You should always create a default properties file.
И даже пример приводят, где английский вариант помещен в качестве дефолтного и, соответственно, варианта файла с суффиксом для английского языка нету. Вот, взято оттуда же:

The PropertiesDemo sample program ships with three properties files:

LabelsBundle.properties
LabelsBundle_de.properties
LabelsBundle_fr.properties

Отлично! - подумал я. - Всё понятно, предоставишь язык de - выберется LabelsBundle_de.properties, предоставишь язык en, LabelsBundle_en.properties найден не будет и выберется LabelsBundle.properties!

Переполненный оптимизмом и радостью я сделал свой вариант
Messages.properties
Messages_ru.properties
где в дефолтном файле поместил английские тексты.
Протестировал локально, все замечательно работает. Загрузил на сервак.
Каков же был размер кирпича, когда я увидел, что несмотря на самые отчаяннейшие попытки кликать по ссылке "en" я получал один и тот же - русский вариант перевода.

Расследовав дело я убедился в той свинье, которую подложили мне летающие в облаках разработчики джавы.
Читаем хелп по ResourceBundle.getBundle:

Conceptually, getBundle uses the following strategy for locating and instantiating resource bundles:

getBundle uses the base name, the specified locale, and the default locale (obtained from Locale.getDefault) to generate a sequence of candidate bundle names. If the specified locale's language, country, and variant are all empty strings, then the base name is the only candidate bundle name. Otherwise, the following sequence is generated from the attribute values of the specified locale (language1, country1, and variant1) and of the default locale (language2, country2, and variant2):

* baseName + "_" + language1 + "_" + country1 + "_" + variant1
* baseName + "_" + language1 + "_" + country1
* baseName + "_" + language1
* baseName + "_" + language2 + "_" + country2 + "_" + variant2
* baseName + "_" + language2 + "_" + country2
* baseName + "_" + language2
* baseName

Вот она роковая часть. Вызывая ResourceBundle.getBundle("Messages", new Locale("en")) и ожидая что подхватится безсуффиксный файл заместо отсутствующего английского, я не знал, что разработчики джавы слишком умные и прежде чем юзать безсуффиксный файл ищут вот это:

...the default locale (language2, country2, and variant2)

* baseName + "_" + language2 + "_" + country2 + "_" + variant2
* baseName + "_" + language2 + "_" + country2
* baseName + "_" + language2

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

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

1 комментарий:

  1. http://skipy.ru/technics/localization.html

    "Хочу обратить особое внимание на порядок поиска. Как видите, в списке фигурируют имена пакетов, соответствующих языку по умолчанию! Что означает следующее: если у вас есть, скажем, пакеты main_ru и main, язык по умолчанию русский, то при попытке загрузить пакет для английского языка будет загружен пакет для русского, main_ru! А вовсе не main, как можно было бы предположить."

    ОтветитьУдалить