formatjs_icu_messageformat is the low-level Rust runtime for ICU
MessageFormat. It parses each message once and accepts a locale for every
formatting call, so one compiled message can serve requests in many locales.
Installation#
[dependencies]
formatjs_icu_messageformat = "0.1"
Format a message#
use formatjs_icu_messageformat::{IcuMessageFormat, Value, Values};
use std::collections::HashMap;
fn main() -> Result<(), formatjs_icu_messageformat::Error> {
let message = IcuMessageFormat::try_new(
"Hello, {name}. You have {count, plural, one {# task} other {# tasks}}.",
)?;
let values: Values = HashMap::from([
("name".to_owned(), Value::from("Ada")),
("count".to_owned(), Value::from(2_i64)),
]);
assert_eq!(
message.format_to_string("en-US", &values)?,
"Hello, Ada. You have 2 tasks."
);
Ok(())
}
Supply locale per request#
Locale is not part of IcuMessageFormat::try_new. Keep the compiled message in
shared application state, then pass the request locale to format,
format_to_parts, or format_to_string:
let english = message.format_to_string("en-US", &values)?;
let french = message.format_to_string("fr-FR", &values)?;
IcuMessageFormat is Send + Sync, so it can be shared through Arc in a
multi-request server.
Supported message features#
- arguments,
select,plural,selectordinal, plural offsets, and# - locale-aware numbers, dates, times, and plural rules through ICU4X
- named number, date, and time formats
- ICU number and date/time skeletons
- rich-text tags through
Value::Tag - pre-parsed ASTs through
IcuMessageFormat::from_ast
Use format_to_string for text-only output. Use format or
format_to_parts when rich values must survive formatting.
Custom formatters#
Options::formatters accepts an implementation of the Formatters trait.
Use it when application-specific number or date/time behavior is required.
ICU4X currently does not provide full ECMA-402 parity for currency, unit, and
time-zone formatting.
See the complete formatjs_icu_messageformat API.
Weekday-only skeletons (EEE, EEEE, EEEEE) render stand-alone abbreviated,
wide, and narrow names without adding date fields. Python bindings share this
behavior. Mixed date/time skeleton matching remains limited to the existing
ICU4X fieldsets.
External ICU data#
The default compiled_data feature preserves the existing constructors. Disable
it to supply ICU4X data yourself:
[dependencies]
formatjs_icu_messageformat = { version = "0.2", default-features = false }
icu_provider_blob = { version = "2.1", features = ["alloc"] }
use formatjs_icu_messageformat::{IcuMessageFormat, Options, ProviderFormatters};
use icu_provider_blob::BlobDataProvider;
use std::sync::Arc;
let provider = BlobDataProvider::try_new_from_blob(bytes.into_boxed_slice())?;
let options = Options::with_formatters(Arc::new(ProviderFormatters::new(provider)));
let message = IcuMessageFormat::try_new_with_options("{count, number}", options)?;
Use the same options with from_ast_with_options for precompiled messages.
Options::default_parser_options() returns the parser settings these options
use, for parsing or precompiling messages without building formatters.
Generate a compatible blob with ICU4X icu4x-datagen, including the locales and
markers needed for numbers, cardinal/ordinal plurals, calendars, dates, and times.
The provider must support the requested locales or implement locale fallback
(for example, through ICU4X's LocaleFallbackProvider). Missing data returns a
formatter error; it does not silently use compiled data. Deserialization support
must match the buffer format; icu_provider_blob enables Postcard support.
Data errors name the formatter and locale and keep the ICU4X error as
source(). Call Formatters::check_locale at startup for each locale you
format with; it builds every formatter and returns the first such error. Behind
LocaleFallbackProvider, a locale missing from the provider resolves to root
(und) data and passes this check; formatjs_intl's
IntlContext::check_catalog also rejects that case.
Without compiled_data, DefaultFormatters, Options::default(), and the
constructors that use default options are unavailable. Explicit-options APIs
remain available. Other dependencies can re-enable ICU compiled data through
Cargo feature unification; check the final application's dependency graph.