Я хочу заверить вас, что если вам нужны постоянные изменения в локализованных текстах, например, они, как правило, отличаются от развертывания к развертыванию, база данных - это путь.Ну, не только база данных, вам нужно как-то кэшировать ваши строки.И, конечно, вы бы не хотели полностью перестраивать свой уровень доступа к ресурсам, я полагаю.
Для этого я могу предложить расширить класс ResourceBundle
для автоматической загрузки строк из базы данных и сохранения их в WeakHashMap
,Я выбираю WeakHashMap
из-за его особенностей - он удаляет ключ с карты, когда он больше не нужен, уменьшая объем памяти.В любом случае вам нужно создать класс доступа.Поскольку вы упомянули J2EE, довольно древнюю технологию, я приведу вам пример, совместимый с Java SE 1.4 (он может быть легко переработан для более новой Java, просто поместите @Override при необходимости и добавьте некоторое обобщение строк в Enumeration):
public class WeakResourceBundle extends ResourceBundle {
private Map cache = new WeakHashMap();
protected Locale locale = Locale.US; // default fall-back locale
// required - Base is abstract
// @Override
protected Object handleGetObject(String key) {
if (cache.containsKey(key))
return cache.get(key);
String value = loadFromDatabase(key, locale);
cache.put(key, value);
return value;
}
// required - Base is abstract
// @Override
public Enumeration getKeys() {
return loadKeysFromDatabase();
}
// optional but I believe needed
// @Override
public Locale getLocale() {
return locale;
}
// dummy testing method, you need to provide your own
// should throw MissingResourceException if key does not exist
private String loadFromDatabase(String key, Locale aLocale) {
System.out.println("Loading key: " + key
+ " from database for locale:"
+ aLocale );
return "dummy_" + aLocale.getDisplayLanguage(aLocale);
}
// dummy testing method, you need to provide your own
private Enumeration loadKeysFromDatabase() {
return Collections.enumeration(new ArrayList());
}
}
Из-за каких-то странных правил загрузки ResourceBundle вам действительно нужно расширить класс WeakResourceBundle
, чтобы создать один класс для поддерживаемых языков:
// Empty Base class for Invariant Language (usually English-US) resources
// Do not need to modify anything here since I already set fall-back language
package com.example.i18n;
public class MyBundle extends WeakResourceBundle {
}
Каждый поддерживаемый язык (я знаю это)отстой):
// Example class for Polish ResourceBundles
package com.example.i18n;
import java.util.Locale;
public class MyBundle_pl extends WeakResourceBundle {
public MyBundle_pl() {
super();
locale = new Locale("pl");
}
}
Теперь, если вам нужно создать экземпляр вашего ResourceBundle
, вам нужно всего лишь позвонить:
// You probably need to get Locale from web browser
Locale polishLocale = new Locale("pl", "PL");
ResourceBundle myBundle = ResourceBundle.getBundle(
"com.example.i18n.MyBundle", polishLocale);
И получить доступ к ключу:
String someValue = myBundle.getString("some.key");
Возможные ошибки:
ResourceBundle
требует Полное имя класса (следовательно, имя пакета). - Если вы опустите параметр
Locale
, по умолчанию (что означает сервер)Locale
будет использоваться.Обязательно всегда передайте Locale
, тогда как экземпляр ResourceBundle
. myBundle.getString()
может выдать MissingResourceException
, если вы последуете моему предложению.Вам нужно будет использовать блок try-catch, чтобы избежать проблем.Вместо этого вы можете принять решение о возврате некоторой фиктивной строки из слоя доступа к базе данных в случае отсутствия ключа (например, return "!" + key + "!"
), но в любом случае это, вероятно, должно быть зарегистрировано как ошибка. - Вы всегда должны пытаться создать Localeобъекты, передающие как язык, так и код страны.Это потому, что такие языки, как китайский упрощенный (zh_CN) и китайский традиционный (zh_TW), например, являются совершенно разными языками (по крайней мере, с точки зрения письменности), и вам необходимо будет поддерживать их два вида.Для других стран ResourceBundle фактически автоматически загрузит правильный языковой ресурс (обратите внимание, что я создал
MyBundle_pl.java
, а не MyBundle_pl_PL.java
, и он все еще работает. Кроме того, ResourceBundle автоматически переключится на Enlish-US (MyBundle.java
), если там есть).не является классом ресурсов для данного языка (именно поэтому я использовал такую странную иерархию классов).
РЕДАКТИРОВАТЬ
Некоторые случайные мысли о том, какчтобы сделать его более потрясающим .
Статическими фабриками (избегайте прямого использования ResourceBundle)
Вместо того, чтобы напрямую создавать экземпляры пакетов с помощью ResourceBundle, вы можете добавитьстатические фабричные методы:
public static ResourceBundle getInstance(Locale aLocale) {
return ResourceBundle.getBundle("com.example.i18n.MyBundle", aLocale);
}
Если вы решите изменить имя класса WeakResourceBundle
на что-то более подходящее (я решил использовать LocalizationProvider
), теперь вы можете легко создавать экземпляры своих пакетов изкод потребления:
ResourceBundle myBundle = LocalizationProvider.getInstance(polishLocale);
автоматически сгенерированные классы ресурсов
локализованные MyBundle
классы могут быть легко сгенерированы с помощью сценария сборки.ript может быть либо файлом конфигурации, либо базой данных - ему каким-то образом необходимо знать, какой язык используется в системе.В любом случае, классы имеют очень похожий код, поэтому их автоматическая генерация действительно имеет смысл.
Автоопределение локали
Поскольку именно вы реализуете класс,у вас есть полный контроль над его поведением.Поэтому (зная архитектуру вашего приложения) вы можете включить здесь определение локали и изменить getInstance()
для автоматической загрузки соответствующих языковых ресурсов.
Реализация дополнительных методов, связанных с локализацией
В локализованном приложении есть типичные задачи, которые необходимо выполнить - форматирование и разбор дат, чисел, валют и т. Д. Являются обычными примерами. Имея локаль конечного пользователя, вы можете просто обернуть такие методы в LocalizationProvider.
Ну и дела, я действительно люблю моя работа:)