Подтвердить что ты не робот

Интернационализация Java (i18n) с надлежащим множественным числом

Я собирался использовать стандартную i18n-систему Java с классом ChoiceFormat для множественных чисел, но потом понял, что он не обрабатывает сложные множественные правила некоторых языков (например, польский). Если он обрабатывает только языки, похожие на английский, то это кажется немного бессмысленным.

Какие существуют варианты для получения правильных множественных форм? Каковы плюсы и минусы их использования?

4b9b3361

Ответ 1

Ну, вы уже правильно пометили вопрос, поэтому я предполагаю, что вы знаете кое-что о ICU.

В ICU у вас есть два варианта правильной обработки множественных форм:

  • PluralRules, который дает вам правила для данного Locale
  • PlurarFormat, который использует вышеупомянутые правила, чтобы разрешить форматирование

Какой из них использовать? Лично я предпочитаю использовать PluralRules напрямую, чтобы выбрать соответствующее сообщение из пакетов ресурсов.

ULocale uLocale = ULocale.forLanguageTag("pl-PL");
ResourceBundle resources = ResourceBundle.getBundle( "path.to.messages",
                               uLocale.toLocale());
PluralRules pluralRules = PluralRules.forLocale(uLocale);

double[] numbers = { 0, 1, 1.5, 2, 2.5, 3, 4, 5, 5.5, 11, 12, 23 };
for (double number : numbers) { 
  String resourceKey = "some.message.plural_form." + pluralRules.select(number);
  String message = "!" + resourceKey + "!";
  try {
    message = resources.getString(resourceKey);
    System.out.println(format(message, uLocale, number));
   } catch (MissingResourceException e) { // Log this } 
}

Конечно, вам (или переводчику) нужно будет добавить правильные формы в файл свойств, в этом примере пусть скажет:

some.message.plural_form.one=Znaleziono {0} plik
some.message.plural_form.few=Znaleziono {0} pliki
some.message.plural_form.many=Znaleziono {0} plików
some.message.plural_form.other=Znaleziono {0} pliku

Для других языков (например, арабских) вам также может понадобиться использовать слова "нуль" и "два", см. множественные правила языка CLDR для деталей.

В качестве альтернативы вы можете использовать PluralFormat для выбора допустимой формы. Обычные примеры показывают прямую конкретизацию, которая, на мой взгляд, совершенно не имеет смысла. Легче использовать его с ICU MessageFormat:

String pattern = "Znaleziono {0,plural,one{# plik}" +
                 "few{# pliki}" +
                 "many{# plików}" +
                 "other{# pliku}}";
MessageFormat fmt = new MessageFormat(pattern, ULocale.forLanguageTag("pl-PL"));
StringBuffer result = new StringBuffer();
FieldPosition zero = new FieldPosition(0);
double[] theNumber = { number };
fmt.format(theNumber, result, zero);

Конечно, реалистично, что вы не будете жестко кодировать строку шаблона, но поместите в файл свойств что-то подобное:

some.message.pattern=Found {0,plural,one{# file}other{# files}}

Единственная проблема с этим подходом заключается в том, что переводчик должен знать формат заполнителя. Другая проблема, которую я попытался показать в приведенном выше коде, - это метод MessageFormat static format() (тот, который прост в использовании) всегда форматирует для локали по умолчанию. Это может быть реальной проблемой в веб-приложениях, где по умолчанию Locale обычно означает серверные. Таким образом, мне пришлось отформатировать для определенного Locale (числа с плавающей запятой, заметьте), и код выглядит довольно уродливым...

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

Ответ 2

ChoiceFormat, так как описанный здесь, кажется достаточно гибким, чтобы иметь дело с любым плюрализацией, которую вы можете бросить на нее.

EDIT: как отметил в своем комментарии д-р Харибо, ChoiceFormat недостаточно для польской плюрализации. Но followup из того же блога предлагает ICU4J, который обрабатывает более сложные правила плюрализации