2010-04-09 3 views
14

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

Мои вопросы:

  1. есть какой-то способ глобально установки ResourceBundle.Control по умолчанию, что я не мог бы быть в курсе? Это решило бы все довольно элегантно.

  2. Есть ли какой-либо другой способ управления этим видом элегантно, возможно, без вмешательства в классы ResourceBundle?

+0

Это то, что можно было бы легко обработать с помощью метапрограммирования, если бы у него была Java. –

ответ

6

Я думаю, что это фундаментальный недостаток в способе создания ResourceBundles: ключи, которые ссылаются на другие клавиши, автоматически нарушают принцип DRY (не повторяйте сам). Способ, которым я обходился, был похож на ваш метод: создать класс 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} 

Я uploaded the code to GitHub так что вы или кто-то может загрузить его. Кроме того, я добавил несколько примеров кода для тех, кто использует Stripes Framework (http://www.stripesframework.org/), чтобы быстро и быстро работать.

Уловкой для работы со стандартными JTL fmt taglibs было создание перехватчика, который заменил ресурс HttpServletRequest нашим собственным. Код выглядит примерно так:

ResourceBundle bundle = MyStaticResourceHoldingTheBundle.getBundle(); 
Config.set(request, Config.FMT_LOCALIZATION_CONTEXT, new LocalizationContext(bundle, locale)); 

Взгляните на пакет stripes.interceptor в ссылке выше для более подробной информации.

+0

Вы правы, это очень похоже на наше решение, и у него есть тот же недостаток, который заключается в том, что сгенерированный код с использованием ResourceBundle.getBundle() не получит обходного пути. Основное различие заключается в том, что в нашем случае мы используем существующий кэш ResourceBundle, тогда как в вашем случае вы храните кеш отдельно, предположительно для работы с кешем Sun, иногда сбивая кешированные пакеты. – Trejkaz

+0

Потому что я только что споткнулся по той же проблеме: Я в основном хотел повторно использовать все функции ResourceBundle, но добавил расширение ключа. Таким образом, я получил свойство PropertyResourceBundle и соответствующим образом обработал handleGetObject() (как это видно из источников Matt Brocks). Затем я получил из ResourceBundle.Control, overrode newBundle(), чтобы вернуть свой собственный PropertyResourceBundle (просто изменил исходный источник для этого метода) и передал его в ResourceBundle.getBundle(). Это позволяет повторно использовать все возможности ResourceBundle + добавить расширение ключа. – quaylar

+0

@Matt Brock, кажется, ссылка сломана. Вы можете поделиться страницей? –

0

Если строка повторы локализованы в том смысле, что вы знаете, определенная строка будет повторяться, но только в рамках того же проекта таким образом, что пучки совместное использование ресурсов не дизайн кошмар, то вы могли бы подумать о разделении строк вниз в несколько частей с ключом. Отделите части, которые повторяются от тех, которые не используют и повторно используют повторяющиеся части. Например, предположим, что у вас есть следующие две строки, необходимые для отображения: «Красный шапками Робин является небольшой воробьиных птиц из Австралии»

  1. «Красная шапками Робин находится в сушильные зоны на большей части континента ».

Расслоение ресурс может быть следующим:

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).

Одна вещь, вы должны быть осторожны, однако, что правила грамматики, как предмет предикатный заказ и т. д., возможно, не совпадают на всех языках. Поэтому вам нужно быть осторожным при расщеплении предложений.

+5

На самом деле это также плохая идея для разных контекстов (например, в меню или в строке состояния), пол субъекта и множественность (1, 2, 27 и т. Д.), Так как они также изменяются в зависимости от культуры. – devstuff

+0

Это на самом деле точная причина, по которой мы пошли с подходом к выполнению всех вставок со стороны ResourceBundle. В нашем текущем подходе этот пример можно было бы сделать, поместив {{robin.name}} в соответствующее место в значениях ниже. Если кто-то столкнулся с языком, на котором он не будет работать, он сможет полностью удалить эту переменную. Но ... наше решение, конечно, сложно сделать работу правильно. – Trejkaz