Negotiates the languages between the list of requested locales against a list of available locales.
It accepts three arguments:
requestedLocales: an Array of strings with BCP47 locale IDs sorted according to user preferences.
availableLocales: an Array of strings with BCP47 locale IDs of locale for which resources are available. Unsorted.
options: An object with the following, optional keys:
strategy: 'filtering' (default) | 'matching' | 'lookup'
defaultLocale:
a string with BCP47 locale ID to be used
as a last resort locale.
It returns an Array of strings with BCP47 locale IDs sorted according to the user preferences.
The exact list will be selected differently depending on the strategy:
'filtering': (default) In the filtering strategy, the algorithm will attempt to match as many keys in the available locales in order of the requested locales.
'matching': In the matching strategy, the algorithm will attempt to find the best possible match for each element of the requestedLocales list.
'lookup': In the lookup strategy, the algorithm will attempt to find a single best available locale based on the requested locales list.
This strategy requires defaultLocale option to be set.
Negotiates the languages between the list of requested locales against a list of available locales.
The algorithm is based on the BCP4647 3.3.2 Extended Filtering algorithm, with several modifications:
This change allows us to match a more specific request against more generic available locale.
For example, if the available locale list provides locale
en
, and the requested locale isen-US
, we treat the available locale as a locale that matches all possible english requests.This means that we expect available locale ID to be as precize as the matches they want to cover.
For example, if there is only
sr
available, it's ok to list it in available locales. But once the available locales has both, Cyrl and Latn variants, the locale IDs should besr-Cyrl
andsr-Latn
to avoid anysr-*
request to match against wholesr
range.What it does ([requested] * [available] = [supported]):
['en-US'] * ['en'] = ['en']
The most obvious likely subtag that can be computed is a duplication of the language field onto region field (
fr
=>fr-FR
).On top of that, likely subtags may use a list of mappings, that allow the algorithm to handle non-obvious matches. For example, making sure that we match
en
toen-US
orsr
tosr-Cyrl
, whilesr-RU
tosr-Latn-RU
.This list can be taken directly from CLDR Supplemental Data.
What it does ([requested] * [available] = [supported]):
['fr'] * ['fr-FR'] = ['fr-FR'] ['en'] * ['en-US'] = ['en-US'] ['sr'] * ['sr-Latn', 'sr-Cyrl'] = ['sr-Cyrl']
Lastly, the last form of check is against the requested locale ID but with the variant/region field replaced with a
*
range.The rationale here laid out in LDML 4.4 Language Matching: "(...) normally the fall-off between the user's languages is substantially greated than regional variants."
In other words, if we can't match for the given region, maybe we can match for the same language/script but other region, and it will in most cases be preferred over falling back on the next language.
What it does ([requested] * [available] = [supported]):
['en-AU'] * ['en-US'] = ['en-US'] ['sr-RU'] * ['sr-Latn-RO'] = ['sr-Latn-RO'] // sr-RU -> sr-Latn-RU
It works similarly to getParentLocales algo, except that we stop after matching against variant/region ranges and don't try to match ignoring script ranges. That means that
sr-Cyrl
will never match againstsr-Latn
.