fluent_locale

Module negotiate

Source
Expand description

Language Negotiation is a process in which locales from different sources are filtered and sorted in an effort to produce the best possible selection of them.

There are multiple language negotiation strategies, most popular is described in RFC4647.

The algorithm is based on the BCP4647 3.3.2 Extended Filtering algorithm, with several modifications.

§Example:

use fluent_locale::negotiate_languages;
use fluent_locale::NegotiationStrategy;
use fluent_locale::convert_vec_str_to_langids_lossy;
use unic_langid::LanguageIdentifier;

let requested = convert_vec_str_to_langids_lossy(&["pl", "fr", "en-US"]);
let available = convert_vec_str_to_langids_lossy(&["it", "de", "fr", "en-GB", "en_US"]);
let default: LanguageIdentifier = "en-US".parse().expect("Parsing langid failed.");

let supported = negotiate_languages(
  &requested,
  &available,
  Some(&default),
  NegotiationStrategy::Filtering
);

let expected = convert_vec_str_to_langids_lossy(&["fr", "en-US", "en-GB"]);
assert_eq!(supported,
           expected.iter().map(|t| t.as_ref()).collect::<Vec<&LanguageIdentifier>>());

§The exact algorithm is custom, and consists of a 6 level strategy:

§1) Attempt to find an exact match for each requested locale in available locales.

Example:

// [requested] * [available] = [supported]

["en-US"] * ["en-US"] = ["en-US"]

§2) Attempt to match a requested locale to an available locale treated as a locale range.

Example:

// [requested] * [available] = [supported]

["en-US"] * ["en"] = ["en"]
              ^^
               |-- becomes "en-*-*-*"

§3) Maximize the requested locale to find the best match in available locales.

This part uses ICU’s likelySubtags or similar database.

Example:

// [requested] * [available] = [supported]

["en"] * ["en-GB", "en-US"] = ["en-US"]
  ^^       ^^^^^    ^^^^^
   |           |        |
   |           |----------- become "en-*-GB-*" and "en-*-US-*"
   |
   |-- ICU likelySubtags expands it to "en-Latn-US"

§4) Attempt to look up for a different variant of the same locale.

Example:

// [requested] * [available] = [supported]

["ja-JP-win"] * ["ja-JP-mac"] = ["ja-JP-mac"]
  ^^^^^^^^^       ^^^^^^^^^
          |               |-- become "ja-*-JP-mac"
          |
          |----------- replace variant with range: "ja-JP-*"

§5) Look up for a maximized version of the requested locale, stripped of the region code.

Example:

// [requested] * [available] = [supported]

["en-CA"] * ["en-ZA", "en-US"] = ["en-US", "en-ZA"]
  ^^^^^
      |       ^^^^^    ^^^^^
      |           |        |
      |           |----------- become "en-*-ZA-*" and "en-*-US-*"
      |
      |----------- strip region produces "en", then lookup likelySubtag: "en-Latn-US"

§6) Attempt to look up for a different region of the same locale.

Example:

// [requested] * [available] = [supported]

["en-GB"] * ["en-AU"] = ["en-AU"]
  ^^^^^       ^^^^^
      |           |-- become "en-*-AU-*"
      |
      |----- replace region with range: "en-*"

Enums§

Functions§