Module icu_locale::preferences

Expand description

This API provides necessary functionality for building user preferences structs.

It includes the ability to merge information between the struct and a Locale, facilitating the resolution of attributes against default values.

Preferences struct serve as a composable argument to ICU4X constructors, allowing for ergonomic merging between information encoded in multiple sets of user inputs: Locale, application preferences and operating system preferences.

The crate is intended primarily to be used by components constructors to normalize the format of ingesting preferences across all of ICU4X.

§Preferences vs Options

ICU4X introduces a separation between two classes of parameters that are used to adjust the behavior of a component.

Preferences represent the user-driven preferences on how the given user wants the internationalization to behave. Those are items like language, script, calendar and numbering systems etc.

Options represent the developer-driven adjustments that affect how given information is presented based on the requirements of the application like available space or intended tone.

§Options Division

The Options themselves are also divided into options that are affecting data slicing, and ones that don’t. This is necessary to allow for DCE and FFI to produce minimal outputs avoiding loading unnecessary data that is never to be used by a given component. The result is that some option keys affect specialized constructors such as try_new_short, try_new_long, which result in data provider loading only data necessary to format short or long values respectively. For options that are not affecting data slicing, an Options struct is provided that the developer can fill with selected key values, or use the defaults.

§Preferences Merging

In traditional internatonalization APIs, the argument passed to constructors is a locale. ICU4X changes this paradigm by accepting a Preferences, which can be extracted from a Locale and combined with other Preferencess provided by the environment.

This approach makes it easy for developers to write code that takes just a locale, as in other systems, as well as handle more sophisticated cases where the application may receive, for example, a locale, a set of internationalization preferences specified within the application, and a third set extracted from the operating system’s preferences.

§ECMA-402 vs ICU4X

The result of the two paradigm shifts presented above is that the way constructors work is different.

§ECMA-402

let locale = new Locale("en-US-u-hc-h12");
let options = {
  hourCycle: "h24", // user preference
  timeStyle: "long", // developer option
};

let dtf = new DateTimeFormat(locale, options);

§ICU4X

let loc = locale!("en-US-u-hc-h12");
let prefs = DateTimeFormatterPreferences {
    hour_cycle: HourCycle::H24,
};
let options = DateTimeFormatterOptions {
    time_style: TimeStyle::Long,
};

let mut combined_prefs = DateTimeFormatterPreferences::from(loc);
combined_prefs.extend(prefs);

let dtf = DateTimeFormatter::try_new(combined_prefs, options);

This architecture allows for flexible composition of user and developer settings sourced from different locations in custom ways based on the needs of each deployment.

Below are some examples of how the Preferences model can be used in different setups.

§Examples

use icu::locale::preferences::{
  define_preferences,
  extensions::unicode::keywords::HourCycle,
};
use icu::locale::locale;

define_preferences!(
    // Name of the preferences struct
    ExampleComponentPreferences,
    {
        // A preference relevant to the component
        hour_cycle: HourCycle
    }
);

pub struct ExampleComponent {
    data: MyData,
}

impl ExampleComponent {
    pub fn new(prefs: ExampleComponentPreferences) -> Self {
        let locale = get_data_locale_from_prefs(prefs);
        let data = load_data(locale);

        Self { data }
    }
}

Now we can use that component in multiple different ways,

§Scenario 1: Use Locale as the only input

let loc = locale!("en-US-u-hc-h23");
let tf = ExampleComponent::new(loc.into());

§Scenario 2: Compose Preferences and Locale

let loc = locale!("en-US-u-hc-h23");
let app_prefs = ExampleComponentPreferences {
    hour_cycle: Some(HourCycle::H12),
    ..Default::default()
};

let mut combined_prefs = ExampleComponentPreferences::from(loc);
combined_prefs.extend(app_prefs);

// HourCycle is set from the prefs bag and override the value from the locale
assert_eq!(combined_prefs.hour_cycle, Some(HourCycle::H12));

let tf = ExampleComponent::new(combined_prefs);

§Scenario 3: Merge Preferences from Locale, OS, and Application

let loc = locale!("en-US-u-hc-h24");

// Simulate OS preferences
let os_prefs = ExampleComponentPreferences {
    hour_cycle: Some(HourCycle::H23),
    ..Default::default()
};

// Application does not specify hour_cycle
let app_prefs = ExampleComponentPreferences {
    hour_cycle: Some(HourCycle::H12),
    ..Default::default()
};

let mut combined_prefs = ExampleComponentPreferences::from(loc);
combined_prefs.extend(os_prefs);
combined_prefs.extend(app_prefs);

// HourCycle is set from the OS preferences since the application didn't specify it
assert_eq!(combined_prefs.hour_cycle, Some(HourCycle::H12));

let tf = ExampleComponent::new(combined_prefs);

§Scenario 4: Neither Application nor OS specify the preference

let loc = locale!("en-US-u-hc-h24");

// Simulate OS preferences
let os_prefs = ExampleComponentPreferences::default(); // OS does not specify hour_cycle
let app_prefs = ExampleComponentPreferences::default(); // Application does not specify hour_cycle

let mut combined_prefs = ExampleComponentPreferences::from(loc);
combined_prefs.extend(os_prefs);
combined_prefs.extend(app_prefs);

// HourCycle is taken from the locale
assert_eq!(combined_prefs.hour_cycle, Some(HourCycle::H24));

let tf = ExampleComponent::new(combined_prefs);

Modules§

  • A set of extensions which correspond to preferences.

Structs§

Traits§

  • A low-level trait implemented on each preference exposed in component preferences.