Java 8 - поведение поиска в локали

Представленный в Java 8, Locale.lookup(), основанный на RFC 4647, позволяет пользователю найти наилучшее соответствие для списка Locale согласно списку приоритетов LocaleRange. Теперь я не понимаю каждый угловой случай для этого метода. Ниже приводится один конкретный случай, который я хотел бы получить для объяснения:

// Create a collection of Locale objects to search
Collection<Locale> locales = new ArrayList<>();
locales.add(Locale.forLanguageTag("en-GB"));
locales.add(Locale.forLanguageTag("en"));

// Express the user preferences with a Language Priority List
String ranges = "en-US;q=1.0,en-GB;q=1.0";
List<Locale.LanguageRange> languageRanges = Locale.LanguageRange.parse(ranges);

// Find the BEST match, and return just one result
Locale result = Locale.lookup(languageRanges,locales);
System.out.println(result.toString());

Отпечатает en, где я бы предположительно ожидал en-GB.

Обратите внимание, что:

  • если у вас есть диапазон "en-GB;q=1.0,en-US;q=1.0" (с отменой GB и US), это напечатает en-GB,
  • и если у вас есть диапазон "en-US;q=0.9,en-GB;q=1.0" (у GB более высокий приоритет, чем у США), это напечатает en-GB.

Может ли кто-нибудь объяснить обоснование этого поведения?

Ответы

Ответ 1

Если вы предоставляете языковые альтернативы с одинаковым приоритетом, порядок списка становится значительным. Это становится очевидным при проверке разобранного списка "en-US;q=1.0,en-GB;q=1.0". Он содержит две записи, представляющие "en-US;q=1.0", а затем "en-GB;q=1.0"

См. https://www.ietf.org/rfc/rfc4647.txt

3.4. Поиск

Поиск используется для выбора единственного языкового тега, который наилучшим образом соответствует    список приоритетов языка для данного запроса. При выполнении    поиск, каждый языковой диапазон в списке приоритетов языка    в свою очередь, в соответствии с приоритетом....    Первый совпадающий тег найден, согласно    приоритет пользователя считается самым близким совпадением и является элементом    вернулся. Например, если языковой диапазон "de-ch", поиск    операция может создавать контент с тегами "de" или "de-CH", но никогда    содержимое с тегом "de-CH-1996". Если языковой тег не соответствует    запрос, возвращается значение "по умолчанию".

...

В схеме поиска языковой диапазон постепенно усекается    от конца до тех пор, пока не будет найден соответствующий языковой тег....

Последнее предложение описывает то, что уже сказано в примере в первом абзаце, то есть диапазон языков de-CH может соответствовать либо de-CH, либо de. Этот поиск с отступлением выполняется для каждого элемента списка, останавливаясь на первом, для которого найдено совпадение.

Другими словами, указание "en-US;q=1.0,en-GB;q=1.0" похоже на указание "en-US,en,en-GB,en".


Возможно, что вы хотите фильтровать, см.

3.3. Фильтрация

Фильтрация используется для выбора набора языковых тегов, который соответствует    данный список приоритетов языка....

При фильтрации каждый диапазон языков представляет собой наименее конкретный    языковой тег (т.е. тег языка с наименьшим числом    subtags), что является приемлемым совпадением.

Таким образом, учитывая ваш исходный список выбираемых локалей

List<Locale> filtered = Locale.filter(
    Locale.LanguageRange.parse("en-US;q=1.0,en-GB;q=1.0"), locales);
System.out.println("filtered: "+filtered);

создает [en_GB].

тогда

Collection<Locale> locales = Arrays.asList(Locale.forLanguageTag("en"),
    Locale.forLanguageTag("en-GB"), Locale.forLanguageTag("en-US"));
List<Locale> filtered = Locale.filter(
    Locale.LanguageRange.parse("en-US;q=1.0,en-GB;q=1.0"), locales);
System.out.println("filtered: "+filtered);

производит [en_US, en_GB] (обратите внимание на приоритетный порядок и отсутствие резервной копии en). Поэтому в зависимости от контекста вы можете сначала попытаться выбрать из отфильтрованного списка и прибегать к поиску, когда отфильтрованный список пуст.

По крайней мере, поведение реализации Javas соответствует спецификации. Как вы уже отметили, изменение приоритета или изменение порядка (когда приоритет равен), изменяет результат в соответствии со спецификацией.

Ответ 2

Чтобы получить этот результат, выполните следующие действия:

  • соответствует en-US соответствует en-GB? → нет
  • выполняет en-US совпадение en? → нет
  • truncate en-US to en
  • выполняет en совпадение en-GB? → нет
  • выполняет en совпадение en? → да, найден соответствующий тег соответствия, верните его

Он работает в соответствии с RFC 4647:

3.4. Поиск

...

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

...

В схеме поиска диапазон языков постепенно усекается с конца, пока не будет найден соответствующий языковой тег.

Ядро алгоритма поиска реализовано в sun.util.locale.LocaleMatcher#lookupTag. Вы можете проверить исходный код

Ответ 3

Разбор заданного диапазона для генерации a Language Priority List:

  • для "en-US;q=1.0,en-GB;q=1.0" список приоритетов [en-us;=1.0,en-gb;=1.0]

  • для "en-GB;q=1.0,en-US;q=1.0" список приоритетов [en-gb;=1.0,en-us;=1.0]

  • для "en-US;q=0.9,en-GB;q=1.0" список приоритетов [en-gb;=1.0,en-us;=0.9]

Затем метод поиска следует за этим списком приоритетов, пока не найдет соответствующий языковой стандарт (согласно RFC 4647):

  • для en-us;=1.0,en-gb;=1.0, алгоритм берется первым en-us;=1.0, для которого наилучшим подходящим языком является en
  • для en-gb;=1.0,en-us;=1.0, алгоритм берется первым en-gb;=1.0, для которого наилучшая соответствующая локаль en-GB
  • для en-gb;=1.0,en-us;=0.9, алгоритм берется первым en-gb;=1.0, для которого наилучшим подходящим языком является en-GB