Как избежать повторения в строках Java ResourceBundle?
У нас было много строк, которые содержали одну и ту же подстроку, от предложений о проверке журнала или о том, как связаться со службой поддержки, до брендинговых строк, содержащих название компании или продукта. Повторение вызывало у нас несколько проблем (в первую очередь опечатки или ошибки копирования / вставки), но оно также вызывает проблемы, поскольку увеличивает объем текста, который должен переводить наш переводчик.
Решение, которое я придумал, получилось примерно так:
public class ExpandingResourceBundleControl extends ResourceBundle.Control {
public static final ResourceBundle.Control EXPANDING =
new ExpandingResourceBundleControl();
private ExpandingResourceBundleControl() { }
@Override
public ResourceBundle newBundle(String baseName, Locale locale, String format,
ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException {
ResourceBundle inner = super.newBundle(baseName, locale, format, loader, reload);
return inner == null ? null : new ExpandingResourceBundle(inner, loader);
}
}
ExpandingResourceBundle
делегирует реальный пакет ресурсов, но выполняет преобразование {{this.kind.of.thing}} для поиска ключа в ресурсах.
Каждый раз, когда вы хотите получить один из них, вы должны идти:
ResourceBundle.getBundle("com/acme/app/Bundle", EXPANDING);
И это работает отлично - на некоторое время.
В конечном итоге происходит то, что какой-то новый код (в нашем случае автоматически сгенерированный код, который выплевывался из Matisse) ищет тот же пакет ресурсов без указания пользовательского элемента управления. Это кажется невоспроизводимым, если вы пишете простой модульный тест, который вызывает его с, а затем без, но это происходит, когда приложение выполняется по-настоящему. Как-то кеш внутри ResourceBundle
выбрасывает хорошее значение и заменяет его на сломанное. Мне еще предстоит выяснить, почему JAR-файлы Sun были скомпилированы без отладочной информации, поэтому отладка - это рутина.
Мои вопросы:
Есть ли какой-то способ глобальной настройки ResourceBundle.Control по умолчанию, о котором я могу не знать? Это решило бы все довольно элегантно.
Есть ли какой-то другой способ элегантной обработки такого рода вещей, возможно, вообще не затрагивая классы ResourceBundle?
2 ответа
Я думаю, что это фундаментальный недостаток в том, как ResourceBundles предназначены для функционирования: ключи, которые ссылаются на другие ключи, автоматически нарушают принцип СУХОЙ (не повторяться). Способ, которым я обошел это, был похож на ваш метод: создайте класс ReflectiveResourceBundle, который позволяет вам указывать ключи ресурсов в сообщениях с использованием нотации EL.
НЕПРАВИЛЬНЫЙ ПУТЬ:
my.name.first=Bob
my.name.last=Smith
my.name.full=Bob Smith
ПРАВИЛЬНЫЙ ПУТЬ:
my.name.first=Bob
my.name.last=Smith
my.name.full=${my.name.first} ${my.name.last}
Я загрузил код на GitHub, чтобы вы или кто-либо другой мог его скачать. Кроме того, я добавил пример кода для тех, кто использует Stripes Framework ( http://www.stripesframework.org/), чтобы вы могли быстро приступить к работе.
Хитрость в том, чтобы заставить его работать со стандартными тегами JSTL fmt, заключалась в том, чтобы установить перехватчик, который заменил ресурс HttpServletRequest нашим собственным. Код выглядит примерно так:
ResourceBundle bundle = MyStaticResourceHoldingTheBundle.getBundle();
Config.set(request, Config.FMT_LOCALIZATION_CONTEXT, new LocalizationContext(bundle, locale));
Посмотрите на пакет stripes.interceptor в ссылке выше для более подробной информации.
Если повторы строк локализованы в том смысле, что вы знаете, что определенная строка будет повторяться, но только внутри одного и того же проекта, так что совместное использование пакетов ресурсов не является кошмаром проектирования, тогда вы можете рассмотреть разбиение строк на несколько частей значения ключа. Отделите повторяющиеся части от тех, которые не повторяют, и повторно используйте повторяющиеся части. Например, допустим, у вас есть следующие две строки, которые нужно отобразить:
- "Робин в красной шапочке - маленькая воробьиная птица, родом из Австралии".
- "Робин с красной шапочкой находится в более сухих регионах на большей части континента".
Пакет ресурсов может быть следующим:
robin.name=The Red-capped Robin
robin.native=is a small passerine bird native to Australia.
robin.region=is found in dryer regions across much of the continent.
а затем объедините необходимые части, где это необходимо bundle.getString("robin.name")+bundle.getString(robin.native).
Однако следует быть осторожным с одной вещью: правила грамматики, такие как порядок предикатов субъекта и т. Д., Могут быть разными на всех языках. Так что вам нужно быть немного осторожнее при разбиении предложений.