Как избежать повторения в строках 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) ищет тот же комплект ресурсов без указания настраиваемого элемента управления. Это кажется невоспроизводимым, если вы пишете простой unit test, который вызывает его, а затем без него, но это происходит, когда приложение выполняется для реального. Каким-то образом кеш внутри ResourceBundle извлекает хорошее значение и заменяет его сломанным. Я еще не понял, почему и файлы Sun jar были скомпилированы без отладочной информации, поэтому отладка это тяжелая работа.

Мои вопросы:

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

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

Ответ 1

Я думаю, что это фундаментальный недостаток в способе создания 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}

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

Трюк, чтобы заставить это работать со стандартными JTLFmt taglibs, состоял в том, чтобы настроить перехватчик, который заменил ресурс HttpServletRequest нашим собственным. Код выглядит примерно так:

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

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

Ответ 2

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

  • "Красная крыса Робин - маленькая птица-паук, родившаяся в Австралии".
  • "Робин с красной крышкой находится в сушильных районах на большей части континента".

Пакет ресурсов может быть следующим:

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

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