Refactor `Localize` for performance

finchie
Aug 27, 2025, 7:57 AM
RA3H7PWCI7WHONXB32IWYECQ3VKPKEXXYAJDIOHWNAGHDO7L5ZLQC

Dependencies

  • [2] 4MRF5E76 Generate simple locale matching code in `localize()`
  • [3] P6FW2GGO Remove unnecessary parameters in generated `localize()` function
  • [4] NO3PDO7P Refactor `fluent_embed` to support structs
  • [5] F5LG7WEN Emit compilation errors from Fluent source code
  • [6] HHJDRLLN Create `fluent_embed_runtime` crate
  • [7] BFL2Y7GN Add relative timestamps using `jiff` and `icu_relativetime`
  • [8] KZLFC7OW Rename `fluent_embed_runtime` to `fluent_embed`
  • [9] 7U2DXFMP Refactor `fluent_embed::Localize` to support overriding locales
  • [10] CESJ4CTO Move macro-specific code into `macro_impl` module
  • [11] IRW6JACS Implement `Localize` for `RelativeTime`
  • [12] 3NMKD6I5 Refactor `Localize` trait to use `std::io::Write`
  • [13] C6W7N6N5 Implement `Localize` for `FixedDecimal` and primitive number types
  • [14] WWDZWJTR Implement `Localize` for string types
  • [15] 7M4UI3TW Update dependencies to latest versions
  • [16] 7JPOCQEI Add explicit error handling for macro parsing
  • [17] MABGENI7 Refactor `fluent_embed_derive` tests
  • [18] LU6IFZFG Remove `std::io::Write` trait bound from `Localize`
  • [19] JUV7C6ET Create initial prototype of `fluent_embed_interaction`
  • [20] BAH2JCJP Add progress bar to `fluent_embed_interaction`
  • [21] U2PHMYPD Return `String` directly instead of writing to buffer in `Localize::localize`
  • [22] QJC4IQIT Refactor `Localize` functions to infallibly return `String`
  • [23] QM64L3XO Replace `duplicate` macro with `macro_rules!`
  • [24] NB7K77TZ Update formatting of `InteractionEnvironment::new()`
  • [25] 6XEMHUGS Use full `Locale` instead of `LanguageIdentifier` subset
  • [26] KFFAQIZU Rename `InteractionEnvironment::print_message` to `emit_message`
  • [27] AE3AZFVK Add `Styled<L: Localize>` struct to support localizing colors
  • [28] S26YOXQI Update `Cargo.toml` in workspace and packages
  • [29] RUCC2HKZ Rename from `fluent_embed` to `l10n_embed`
  • [30] USKESL6X Add examples for using common types in `l10n_embed`
  • [31] 7YOM2QEF Move interaction constructors from individual types to implementations on `InteractionEnvironment`
  • [32] EKXWNEPK Rename `Localize::message_for_locale` to `Localize::localize_for`
  • [33] PGBXJWIH Move `l10n_embed` re-exports into `macro_prelude` module
  • [34] IZ67IMRI Move prompt message to initial constructor
  • [35] BC22FLOQ Move interaction constructors back to individual types
  • [36] XSRT5QWX Return `InteractionError` instead of `std::io::Error` from `InteractionEnvironment::emit_message`
  • [37] YUW3BUXX Add examples for `l10n_embed_interaction`
  • [38] AS7RDZT7 Implement `Localize` for `std::path` and `camino`
  • [39] R2BAN2V6 Add support for unnamed fields in `l10n_embed_derive`
  • [40] XDJBTEXU Add support for integer selectors
  • [41] 2HHBS7VW Add rudimentary support for localizing lists
  • [42] XPGOKS6X Add rudimentary support for built-in `LEN()` function
  • [43] Q7LUHXXB Replace localization of `Vec<Localize>` with `List` type
  • [44] LYOV6ZIR Add `layout` module for localizing with separators and vertical/horizontal padding
  • [45] XGNME3WR Move `Group::derive_enum` to new `crate::parse_macro` module
  • [46] QSK7JRBA Add simple `attribute_path` function
  • [47] IXBE5Q6T Implement `InteractionEnvironment::print_message`
  • [48] MVTRHSJL Move from `fluent-syntax` PR to main branch
  • [49] NEBSVXIA Apply Clippy fixes
  • [50] CFJKYXUX Remove newtype wrapper `RelativeTime` for `jiff::Timestamp`
  • [51] 5I5NR4DQ Make `Localize::CANONICAL_LOCALE` a function instead of associated constant
  • [52] 7X4MEZJU Use Fluent AST when reporting error spans
  • [*] O77KA6C4 Create `fluent_embed` crate
  • [*] 5TEX4MNU Split `fluent_embed` into `group` and `parse` modules
  • [*] VZYZRAO4 Move `output-macros` crate into workspace
  • [*] UKFEFT6L Create basic `Output` proc-macro

Change contents

  • replacement in l10n_embed_interaction/src/prompt/select.rs at line 16
    [35.100][34.217:290](),[34.217][34.217:290]()
    let localized_prompt = prompt.localize_for(&environment.locale);
    [35.100]
    [34.290]
    let mut localized_prompt = String::new();
    prompt.localize(&environment.localization_context, &mut localized_prompt);
  • replacement in l10n_embed_interaction/src/prompt/select.rs at line 30
    [19.685][32.0:69]()
    .map(|item| item.localize_for(&self.environment.locale))
    [19.685]
    [22.98]
    .map(|item| {
    let mut localized_item = String::new();
    item.localize(&self.environment.localization_context, &mut localized_item);
    localized_item
    })
  • replacement in l10n_embed_interaction/src/prompt/select.rs at line 43
    [31.590][31.590:631]()
    match self.environment.context {
    [31.590]
    [19.1286]
    match self.environment.interaction_context {
  • replacement in l10n_embed_interaction/src/prompt/password.rs at line 6
    [19.2243][34.506:536]()
    localized_prompt: String,
    [19.2243]
    [19.2263]
    prompt: String,
  • replacement in l10n_embed_interaction/src/prompt/password.rs at line 20
    [31.829][35.101:130](),[35.130][34.611:778](),[34.611][34.611:778]()
    pub fn new<L: Localize>(
    environment: &'environment InteractionEnvironment,
    prompt: L,
    ) -> Self {
    let localized_prompt = prompt.localize_for(&environment.locale);
    [31.829]
    [34.778]
    pub fn new<L: Localize>(environment: &'environment InteractionEnvironment, prompt: L) -> Self {
    let mut localized_prompt = String::new();
    prompt.localize(&environment.localization_context, &mut localized_prompt);
  • replacement in l10n_embed_interaction/src/prompt/password.rs at line 34
    [19.2656][19.2656:2682]()
    confirmation: L1,
    [19.2656]
    [19.2682]
    confirmation_prompt: L1,
  • replacement in l10n_embed_interaction/src/prompt/password.rs at line 37
    [22.197][32.70:244]()
    let confirmation_text = confirmation.localize_for(&self.environment.locale);
    let mismatch_error_text = mismatch_error.localize_for(&self.environment.locale);
    [22.197]
    [19.2895]
    let mut localized_prompt = String::new();
    confirmation_prompt.localize(
    &self.environment.localization_context,
    &mut localized_prompt,
    );
    let mut localized_mismatch_error = String::new();
    mismatch_error.localize(
    &self.environment.localization_context,
    &mut localized_mismatch_error,
    );
  • replacement in l10n_embed_interaction/src/prompt/password.rs at line 50
    [19.3099][34.819:868](),[34.868][19.3138:3187](),[19.3138][19.3138:3187]()
    localized_prompt: confirmation_text,
    mismatch_error: mismatch_error_text,
    [19.3099]
    [19.3187]
    prompt: localized_prompt,
    mismatch_error: localized_mismatch_error,
  • replacement in l10n_embed_interaction/src/prompt/password.rs at line 58
    [31.1333][31.1333:1374]()
    match self.environment.context {
    [31.1333]
    [19.3385]
    match self.environment.interaction_context {
  • replacement in l10n_embed_interaction/src/prompt/password.rs at line 64
    [19.3585][34.997:1183]()
    prompt = prompt.with_confirmation(
    confirmation.localized_prompt,
    confirmation.mismatch_error,
    );
    [19.3585]
    [19.3714]
    prompt =
    prompt.with_confirmation(confirmation.prompt, confirmation.mismatch_error);
  • replacement in l10n_embed_interaction/src/prompt/macros.rs at line 23
    [19.5612][31.1729:1791]()
    let locale = self.environment.locale.clone();
    [19.5612]
    [19.5612]
    let localization_context =
    std::sync::Arc::clone(&self.environment.localization_context);
  • replacement in l10n_embed_interaction/src/prompt/macros.rs at line 29
    [19.5792][19.5792:5892](),[19.5892][32.330:393]()
    Err(message) => {
    // Localize the error message
    Err(message.localize_for(&locale))
    [19.5792]
    [19.6188]
    Err(error_message) => {
    let mut localized_error = String::new();
    error_message.localize(&localization_context, &mut localized_error);
    Err(localized_error)
  • replacement in l10n_embed_interaction/src/prompt/input.rs at line 15
    [31.2129][35.131:160](),[35.160][34.1367:1534](),[34.1367][34.1367:1534]()
    pub fn new<L: Localize>(
    environment: &'environment InteractionEnvironment,
    prompt: L,
    ) -> Self {
    let localized_prompt = prompt.localize_for(&environment.locale);
    [31.2129]
    [34.1534]
    pub fn new<L: Localize>(environment: &'environment InteractionEnvironment, prompt: L) -> Self {
    let mut localized_prompt = String::new();
    prompt.localize(&environment.localization_context, &mut localized_prompt);
  • replacement in l10n_embed_interaction/src/prompt/input.rs at line 28
    [22.802][32.394:471](),[21.329][19.7171:7216](),[32.471][19.7171:7216](),[22.851][19.7171:7216](),[31.2452][19.7171:7216](),[19.7171][19.7171:7216]()
    let localized_text = default.localize_for(&self.environment.locale);
    self.default = Some(localized_text);
    [22.802]
    [19.7216]
    let mut localized_default = String::new();
    default.localize(
    &self.environment.localization_context,
    &mut localized_default,
    );
    self.default = Some(localized_default);
  • replacement in l10n_embed_interaction/src/prompt/input.rs at line 40
    [31.2525][31.2525:2566]()
    match self.environment.context {
    [31.2525]
    [19.7402]
    match self.environment.interaction_context {
  • replacement in l10n_embed_interaction/src/prompt/confirm.rs at line 16
    [35.261][34.1947:2020](),[34.1947][34.1947:2020]()
    let localized_prompt = prompt.localize_for(&environment.locale);
    [35.261]
    [34.2020]
    let mut localized_prompt = String::new();
    prompt.localize(&environment.localization_context, &mut localized_prompt);
  • replacement in l10n_embed_interaction/src/prompt/confirm.rs at line 27
    [31.3040][31.3040:3081]()
    match self.environment.context {
    [31.3040]
    [19.8672]
    match self.environment.interaction_context {
  • replacement in l10n_embed_interaction/src/progress.rs at line 23
    [31.3311][32.472:544]()
    let localized_text = message.localize_for(&environment.locale);
    [31.3311]
    [31.3389]
    let mut localized_text = String::new();
    message.localize(&environment.localization_context, &mut localized_text);
  • edit in l10n_embed_interaction/src/lib.rs at line 4
    [19.9294]
    [19.9294]
    use std::sync::Arc;
  • edit in l10n_embed_interaction/src/lib.rs at line 7
    [19.9295][31.3548:3572]()
    use icu_locale::Locale;
  • replacement in l10n_embed_interaction/src/lib.rs at line 8
    [31.3602][29.272:298](),[19.9295][29.272:298]()
    use l10n_embed::Localize;
    [31.3602]
    [19.9320]
    use l10n_embed::{Context, Localize};
  • replacement in l10n_embed_interaction/src/lib.rs at line 41
    [19.10022][19.10022:10055](),[19.10055][31.3603:3623]()
    context: InteractionContext,
    locale: Locale,
    [19.10022]
    [20.1850]
    localization_context: Arc<Context>,
    interaction_context: InteractionContext,
  • replacement in l10n_embed_interaction/src/lib.rs at line 48
    [24.45][31.3624:3684]()
    pub fn new(locale: Locale, interactive: bool) -> Self {
    [24.45]
    [19.10132]
    pub fn new(localization_context: Arc<Context>, interactive: bool) -> Self {
  • replacement in l10n_embed_interaction/src/lib.rs at line 50
    [19.10147][24.90:131]()
    context: match interactive {
    [19.10147]
    [24.131]
    localization_context,
    interaction_context: match interactive {
  • edit in l10n_embed_interaction/src/lib.rs at line 55
    [24.261][31.3685:3705]()
    locale,
  • replacement in l10n_embed_interaction/src/lib.rs at line 60
    [36.230][31.3706:3733](),[26.88][31.3706:3733](),[31.3733][36.231:290]()
    self.progress_bars
    .println(message.localize_for(&self.locale))?;
    [36.230]
    [36.290]
    let mut localized_message = String::new();
    message.localize(&self.localization_context, &mut localized_message);
    self.progress_bars.println(localized_message)?;
  • replacement in l10n_embed_interaction/src/editor.rs at line 25
    [31.5125][31.5125:5166]()
    match self.environment.context {
    [31.5125]
    [19.10938]
    match self.environment.interaction_context {
  • edit in l10n_embed_interaction/examples/prompt/main.rs at line 2
    [37.126]
    [37.126]
    use std::sync::Arc;
  • edit in l10n_embed_interaction/examples/prompt/main.rs at line 6
    [37.151]
    [37.151]
    use l10n_embed::Context;
  • replacement in l10n_embed_interaction/examples/prompt/main.rs at line 50
    [37.1236][37.1236:1311]()
    let environment = InteractionEnvironment::new(locale!("en-US"), true);
    [37.1236]
    [37.1311]
    let context = Arc::new(Context::new(locale!("en-US")));
    let environment = InteractionEnvironment::new(context, true);
  • edit in l10n_embed_interaction/examples/progress/main.rs at line 2
    [37.5012]
    [37.5012]
    use std::sync::Arc;
  • edit in l10n_embed_interaction/examples/progress/main.rs at line 6
    [37.5037]
    [37.5037]
    use l10n_embed::Context;
  • replacement in l10n_embed_interaction/examples/progress/main.rs at line 18
    [37.5313][37.5313:5388]()
    let environment = InteractionEnvironment::new(locale!("en-US"), true);
    [37.5313]
    [37.5388]
    let context = Arc::new(Context::new(locale!("en-US")));
    let environment = InteractionEnvironment::new(context, true);
  • replacement in l10n_embed_derive/tests/unnamed_fields.rs at line 6
    [39.170][39.170:196]()
    use l10n_embed::Localize;
    [39.170]
    [39.196]
    use l10n_embed::{Context, Localize};
  • replacement in l10n_embed_derive/tests/unnamed_fields.rs at line 24
    [39.563][39.563:670]()
    fn localize_for(&self, locale: &Locale) -> String {
    format!("Localized with locale: {locale}")
    [39.563]
    [39.670]
    fn localize(&self, context: &Context, buffer: &mut String) {
    buffer.push_str("Localized with locale: ");
    buffer.push_str(&context.locale.to_string());
  • replacement in l10n_embed_derive/tests/common/mod.rs at line 2
    [25.1223][29.964:990]()
    use l10n_embed::Localize;
    [25.1223]
    [17.3118]
    use l10n_embed::{Context, Localize};
  • replacement in l10n_embed_derive/tests/common/mod.rs at line 5
    [25.1318][32.621:669](),[32.669][17.3399:3461](),[22.1082][17.3399:3461](),[17.3399][17.3399:3461]()
    let result = message.localize_for(&locale);
    pretty_assertions::assert_eq!(result, expected.as_ref());
    [25.1318]
    [17.3461]
    let context = Context::new(locale);
    let mut buffer = String::new();
    message.localize(&context, &mut buffer);
    pretty_assertions::assert_eq!(expected.as_ref(), buffer);
  • replacement in l10n_embed_derive/src/macro_impl/mod.rs at line 159
    [10.1868][32.670:699]()
    fn localize_for(
    [10.1868]
    [12.454]
    fn localize(
  • replacement in l10n_embed_derive/src/macro_impl/mod.rs at line 161
    [12.477][33.276:350](),[33.350][22.1177:1203](),[29.1455][22.1177:1203](),[25.1617][22.1177:1203](),[15.426][22.1177:1203]()
    locale: &::l10n_embed::macro_prelude::icu_locale::Locale,
    ) -> String {
    [12.477]
    [12.646]
    context: &::l10n_embed::Context,
    buffer: &mut String,
    ) {
  • edit in l10n_embed_derive/src/macro_impl/derive.rs at line 41
    [5.510][22.1204:1245](),[22.1245][4.903:993](),[3.626][4.903:993](),[4.993][6.2172:2231](),[6.2231][33.351:840](),[15.640][2.712:713](),[8.745][2.712:713](),[33.840][2.712:713](),[3.994][2.712:713](),[4.1180][2.712:713](),[9.1347][2.712:713](),[29.1780][2.712:713](),[6.2467][2.712:713](),[2.712][2.712:713](),[2.713][33.841:842]()
    let mut buffer = String::new();
    // TODO: handle different rule types according to fluent code (not just cardinal)
    // TODO: only generate this when needed in message
    const PLURAL_RULE_TYPE: ::l10n_embed::macro_prelude::icu_plurals::PluralRuleType =
    ::l10n_embed::macro_prelude::icu_plurals::PluralRuleType::Cardinal;
    let plural_options = ::l10n_embed::macro_prelude::icu_plurals::PluralRulesOptions::default()
    .with_type(PLURAL_RULE_TYPE);
    let plural_rules = ::l10n_embed::macro_prelude::icu_plurals::PluralRules::try_new(
    locale.into(),
    plural_options,
    )
    .unwrap();
  • replacement in l10n_embed_derive/src/macro_impl/derive.rs at line 42
    [6.2509][12.677:735]()
    #(if locale.normalizing_eq(#additional_locales) {
    [6.2509]
    [12.735]
    #(if context.locale.normalizing_eq(#additional_locales) {
  • replacement in l10n_embed_derive/src/macro_impl/derive.rs at line 44
    [12.768][22.1246:1273]()
    return buffer;
    [12.768]
    [12.795]
    return;
  • replacement in l10n_embed_derive/src/macro_impl/derive.rs at line 46
    [12.812][6.2510:2593](),[4.1275][6.2510:2593]()
    // Fall back to the canonical locale, if no other valid locale was matched
    [12.812]
    [4.1275]
    // Fall back to the canonical locale if no other valid locale was matched
  • edit in l10n_embed_derive/src/macro_impl/derive.rs at line 49
    [4.1302][12.813:814](),[12.814][22.1274:1289]()
    buffer
  • replacement in l10n_embed_derive/src/macro_impl/derive.rs at line 70
    [39.6217][39.6217:6271]()
    Ok(quote!(#field_reference.localize_for(locale)))
    [39.6217]
    [39.6271]
    Ok(quote!(#field_reference.localize(context, buffer)))
  • replacement in l10n_embed_derive/src/lib.rs at line 46
    [29.2087][32.700:745]()
    fn localize_for(
    [29.2087]
    [16.2937]
    fn localize(
  • replacement in l10n_embed_derive/src/lib.rs at line 48
    [16.2976][33.973:1063](),[33.1063][22.1404:1446](),[25.1873][22.1404:1446](),[29.2163][22.1404:1446](),[16.3113][22.1404:1446]()
    locale: &::l10n_embed::macro_prelude::icu_locale::Locale,
    ) -> String {
    [16.2976]
    [16.3194]
    context: &::l10n_embed::Context,
    buffer: &mut String,
    ) {
  • replacement in l10n_embed_derive/src/fluent/ast.rs at line 57
    [42.4528][40.4198:4285](),[40.4198][40.4198:4285]()
    quote!(plural_rules.category_for(#category_reference))
    [42.4528]
    [12.2520]
    quote! {
    {
    // TODO: handle different rule types according to fluent code (not just cardinal)
    let plural_rules = context.plural_rule(::l10n_embed::macro_prelude::icu_plurals::PluralRuleType::Cardinal).unwrap();
    plural_rules.category_for(#category_reference)
    }
    }
  • replacement in l10n_embed_derive/src/fluent/ast.rs at line 99
    [12.2973][32.746:835]()
    parse_quote!(buffer.push_str(&#expression.localize_for(locale)))
    [12.2973]
    [5.3995]
    parse_quote!(#expression.localize(context, buffer))
  • replacement in l10n_embed/src/time.rs at line 1
    [7.53][22.1623:1644]()
    use crate::Localize;
    [7.53]
    [11.21]
    use crate::{Context, Localize};
  • replacement in l10n_embed/src/time.rs at line 3
    [11.22][15.1476:1522](),[15.1522][7.105:143](),[7.105][7.105:143](),[7.143][22.1645:1720](),[22.1720][7.218:221](),[7.218][7.218:221](),[7.221][25.1893:1917]()
    use fixed_decimal::{Decimal, FloatPrecision};
    use icu_experimental::relativetime::{
    RelativeTimeFormatter, RelativeTimeFormatterOptions, options::Numeric,
    };
    use icu_locale::Locale;
    [11.22]
    [22.1767]
    use fixed_decimal::Decimal;
  • edit in l10n_embed/src/time.rs at line 5
    [22.1821]
    [7.350]
    use writeable::Writeable;
  • edit in l10n_embed/src/time.rs at line 7
    [7.351][7.351:550](),[7.748][7.748:749]()
    /// Allow the formatter to use non-numeric output (e.g. "tomorrow", "yesterday")
    const FORMATTER_OPTIONS: RelativeTimeFormatterOptions = RelativeTimeFormatterOptions {
    numeric: Numeric::Auto,
    };
  • replacement in l10n_embed/src/time.rs at line 8
    [18.507][32.836:892]()
    fn localize_for(&self, locale: &Locale) -> String {
    [18.507]
    [7.990]
    fn localize(&self, context: &Context, buffer: &mut String) {
  • replacement in l10n_embed/src/time.rs at line 65
    [11.248][7.3398:3444](),[7.3398][7.3398:3444](),[7.3444][15.1570:2478](),[15.2478][7.4124:4186](),[7.4124][7.4124:4186]()
    let formatter = match selected_unit {
    Unit::Year => {
    RelativeTimeFormatter::try_new_long_year(locale.into(), FORMATTER_OPTIONS)
    }
    Unit::Month => {
    RelativeTimeFormatter::try_new_long_month(locale.into(), FORMATTER_OPTIONS)
    }
    Unit::Week => {
    RelativeTimeFormatter::try_new_long_week(locale.into(), FORMATTER_OPTIONS)
    }
    Unit::Day => RelativeTimeFormatter::try_new_long_day(locale.into(), FORMATTER_OPTIONS),
    Unit::Hour => {
    RelativeTimeFormatter::try_new_long_hour(locale.into(), FORMATTER_OPTIONS)
    }
    Unit::Minute => {
    RelativeTimeFormatter::try_new_long_minute(locale.into(), FORMATTER_OPTIONS)
    }
    Unit::Second => {
    RelativeTimeFormatter::try_new_long_second(locale.into(), FORMATTER_OPTIONS)
    }
    _ => unreachable!(),
    }
    .unwrap();
    [7.3293]
    [7.4186]
    let formatter = context.relative_time_formatter(selected_unit).unwrap();
  • replacement in l10n_embed/src/time.rs at line 67
    [7.4187][7.4187:4209](),[7.4209][15.2479:2571]()
    let decimal =
    Decimal::try_from_f64(selected_value as f64, FloatPrecision::Integer).unwrap();
    [7.4187]
    [12.4267]
    let decimal = Decimal::from(selected_value);
  • replacement in l10n_embed/src/time.rs at line 69
    [12.4323][22.1897:1932]()
    formatted_text.to_string()
    [12.4323]
    [11.394]
    formatted_text.write_to(buffer).unwrap();
  • replacement in l10n_embed/src/style.rs at line 1
    [27.33][27.34:55]()
    use crate::Localize;
    [27.33]
    [27.55]
    use crate::{Context, Localize};
  • replacement in l10n_embed/src/style.rs at line 21
    [27.442][32.893:1018](),[32.1018][27.579:700](),[27.579][27.579:700]()
    fn localize_for(&self, locale: &icu_locale::Locale) -> String {
    let message = self.message.localize_for(locale);
    match self.style {
    Some(style) => format!("{style}{message}{style:#}"),
    None => message,
    [27.442]
    [27.700]
    fn localize(&self, context: &Context, buffer: &mut String) {
    // Prefix with the terminal color codes
    if let Some(style) = self.style {
    let prefix = style.render().to_string();
    buffer.push_str(&prefix);
    }
    self.message.localize(context, buffer);
    // Suffix with the terminal reset codes
    if let Some(style) = self.style {
    let suffix = style.render_reset().to_string();
    buffer.push_str(&suffix);
  • edit in l10n_embed/src/string.rs at line 3
    [14.95][14.95:116]()
    use crate::Localize;
  • replacement in l10n_embed/src/string.rs at line 5
    [14.139][25.1981:2005]()
    use icu_locale::Locale;
    [14.139]
    [14.215]
    use crate::{Context, Localize};
  • replacement in l10n_embed/src/string.rs at line 10
    [23.95][32.1019:1084](),[32.1084][23.474:510](),[25.2077][23.474:510](),[23.474][23.474:510]()
    fn localize_for(&self, _locale: &Locale) -> String {
    (*self).to_string()
    [23.95]
    [23.510]
    fn localize(&self, _context: &Context, buffer: &mut String) {
    buffer.push_str(self.as_ref());
  • replacement in l10n_embed/src/path.rs at line 3
    [38.66][38.66:87]()
    use crate::Localize;
    [38.66]
    [38.87]
    use crate::{Context, Localize};
  • edit in l10n_embed/src/path.rs at line 6
    [38.125][38.125:149]()
    use icu_locale::Locale;
  • replacement in l10n_embed/src/path.rs at line 10
    [38.243][38.243:359]()
    fn localize_for(&self, _locale: &Locale) -> String {
    self.to_string_lossy().to_string()
    [38.243]
    [38.359]
    fn localize(&self, _context: &Context, buffer: &mut String) {
    buffer.push_str(self.to_string_lossy().as_ref());
  • replacement in l10n_embed/src/path.rs at line 20
    [38.489][38.489:587]()
    fn localize_for(&self, _locale: &Locale) -> String {
    self.to_string()
    [38.489]
    [38.587]
    fn localize(&self, _context: &Context, buffer: &mut String) {
    buffer.push_str(self.as_str())
  • replacement in l10n_embed/src/list.rs at line 6
    [41.91][41.91:112]()
    use crate::Localize;
    [41.91]
    [41.112]
    use crate::{Context, Localize};
  • replacement in l10n_embed/src/list.rs at line 8
    [41.113][43.152:226](),[43.226][41.113:137](),[41.113][41.113:137]()
    use icu_list::ListFormatter;
    use icu_list::options::ListFormatterOptions;
    use icu_locale::Locale;
    [41.113]
    [41.137]
    use writeable::Writeable;
  • replacement in l10n_embed/src/list.rs at line 32
    [43.635][43.635:874]()
    fn localize_for(&self, locale: &Locale) -> String {
    let list_formatter = ListFormatter::try_new_and(
    locale.into(),
    ListFormatterOptions::default().with_length(self.length),
    )
    .unwrap();
    [43.635]
    [43.874]
    fn localize(&self, context: &Context, buffer: &mut String) {
    let list_formatter = context.list_formatter(self.length);
    let localized_messages = self.messages.iter().map(|message| {
    let mut buffer = String::new();
    message.localize(context, &mut buffer);
  • replacement in l10n_embed/src/list.rs at line 39
    [43.875][43.875:1013]()
    let localized_messages = self
    .messages
    .iter()
    .map(|message| message.localize_for(locale));
    [43.875]
    [43.1013]
    buffer
    });
  • replacement in l10n_embed/src/list.rs at line 42
    [43.1014][43.1014:1076]()
    list_formatter.format(localized_messages).to_string()
    [43.1014]
    [43.1076]
    let formatted_list = list_formatter.format(localized_messages);
    formatted_list.write_to(buffer).unwrap();
  • edit in l10n_embed/src/lib.rs at line 8
    [7.4376]
    [33.1488]
    use std::sync::OnceLock;
  • edit in l10n_embed/src/lib.rs at line 11
    [33.1489]
    [33.1489]
    use icu_decimal::options::DecimalFormatterOptions;
    use icu_decimal::{DecimalFormatter, DecimalFormatterPreferences};
    use icu_experimental::relativetime::{
    RelativeTimeFormatter, RelativeTimeFormatterOptions, RelativeTimeFormatterPreferences,
    };
    use icu_list::options::{ListFormatterOptions, ListLength};
    use icu_list::{ListFormatter, ListFormatterPreferences};
  • edit in l10n_embed/src/lib.rs at line 19
    [33.1523]
    [33.1523]
    use icu_plurals::{PluralRuleType, PluralRules, PluralRulesOptions, PluralRulesPreferences};
  • replacement in l10n_embed/src/lib.rs at line 38
    [32.1086][32.1086:1141]()
    fn localize_for(&self, locale: &Locale) -> String;
    [32.1086]
    [6.807]
    fn localize(&self, context: &Context, buffer: &mut String);
    }
    pub struct Context {
    pub locale: Locale,
    decimal_formatter: OnceLock<DecimalFormatter>,
    list_formatters: [OnceLock<ListFormatter>; 3],
    plural_rules: [OnceLock<PluralRules>; 2],
    relative_time_formatters: [OnceLock<RelativeTimeFormatter>; 7],
    }
    impl Context {
    pub fn new(locale: Locale) -> Self {
    Self {
    locale,
    decimal_formatter: OnceLock::new(),
    list_formatters: [const { OnceLock::new() }; 3],
    plural_rules: [const { OnceLock::new() }; 2],
    relative_time_formatters: [const { OnceLock::new() }; 7],
    }
    }
    pub fn decimal_formatter(&self) -> &DecimalFormatter {
    self.decimal_formatter.get_or_init(|| {
    DecimalFormatter::try_new(
    DecimalFormatterPreferences::from(&self.locale),
    DecimalFormatterOptions::default(),
    )
    .unwrap()
    })
    }
    pub fn list_formatter(&self, length: ListLength) -> &ListFormatter {
    let index = match length {
    ListLength::Wide => 0,
    ListLength::Short => 1,
    ListLength::Narrow => 2,
    _ => unimplemented!(),
    };
    self.list_formatters[index].get_or_init(|| {
    ListFormatter::try_new_and(
    ListFormatterPreferences::from(&self.locale),
    ListFormatterOptions::default().with_length(length),
    )
    .unwrap()
    })
    }
    pub fn plural_rule(&self, rule_type: PluralRuleType) -> Option<&PluralRules> {
    let index = match rule_type {
    PluralRuleType::Cardinal => 0,
    PluralRuleType::Ordinal => 1,
    _ => return None,
    };
    let plural_rules = self.plural_rules[index].get_or_init(|| {
    PluralRules::try_new(
    PluralRulesPreferences::from(&self.locale),
    PluralRulesOptions::default().with_type(rule_type),
    )
    .unwrap()
    });
    Some(plural_rules)
    }
    pub fn relative_time_formatter(&self, unit: jiff::Unit) -> Option<&RelativeTimeFormatter> {
    let index = match unit {
    jiff::Unit::Year => 0,
    jiff::Unit::Month => 1,
    jiff::Unit::Week => 2,
    jiff::Unit::Day => 3,
    jiff::Unit::Hour => 4,
    jiff::Unit::Minute => 5,
    jiff::Unit::Second => 6,
    _ => return None,
    };
    let formatter = self.relative_time_formatters[index].get_or_init(|| {
    let preferences = RelativeTimeFormatterPreferences::from(&self.locale);
    const OPTIONS: RelativeTimeFormatterOptions = RelativeTimeFormatterOptions {
    numeric: icu_experimental::relativetime::options::Numeric::Auto,
    };
    match unit {
    jiff::Unit::Year => RelativeTimeFormatter::try_new_long_year(preferences, OPTIONS),
    jiff::Unit::Month => {
    RelativeTimeFormatter::try_new_long_month(preferences, OPTIONS)
    }
    jiff::Unit::Week => RelativeTimeFormatter::try_new_long_week(preferences, OPTIONS),
    jiff::Unit::Day => RelativeTimeFormatter::try_new_long_day(preferences, OPTIONS),
    jiff::Unit::Hour => RelativeTimeFormatter::try_new_long_hour(preferences, OPTIONS),
    jiff::Unit::Minute => {
    RelativeTimeFormatter::try_new_long_minute(preferences, OPTIONS)
    }
    jiff::Unit::Second => {
    RelativeTimeFormatter::try_new_long_second(preferences, OPTIONS)
    }
    _ => unreachable!(),
    }
    .unwrap()
    });
    Some(formatter)
    }
  • edit in l10n_embed/src/layout.rs at line 5
    [44.930][44.930:951]()
    use crate::Localize;
  • edit in l10n_embed/src/layout.rs at line 6
    [44.976]
    [44.976]
    use crate::{Context, Localize};
  • replacement in l10n_embed/src/layout.rs at line 25
    [44.1464][44.1464:1685]()
    fn localize_for(&self, locale: &Locale) -> String {
    let localized_items: Vec<String> = self
    .messages
    .iter()
    .map(|item| item.localize_for(locale))
    .collect();
    [44.1464]
    [44.1685]
    fn localize(&self, context: &Context, buffer: &mut String) {
    let separator = "\n".repeat(SEPARATOR_COUNT);
    for (index, message) in self.messages.iter().enumerate() {
    // Add the newlines before every additional line
    if index > 0 {
    buffer.push_str(&separator)
    }
  • replacement in l10n_embed/src/layout.rs at line 34
    [44.1686][44.1686:1746]()
    localized_items.join(&"\n".repeat(SEPARATOR_COUNT))
    [44.1686]
    [44.1746]
    message.localize(context, buffer);
    }
  • replacement in l10n_embed/src/layout.rs at line 62
    [44.2414][44.2414:2510]()
    fn localize_for(&self, locale: &Locale) -> String {
    let mut output = String::new();
    [44.2414]
    [44.2510]
    fn localize(&self, context: &Context, buffer: &mut String) {
    let mut localized_message = String::new();
    self.message.localize(context, &mut localized_message);
  • edit in l10n_embed/src/layout.rs at line 66
    [44.2511]
    [44.2511]
    let vertical_padding = "\n".repeat(VERTICAL_PADDING);
    let horizontal_padding = " ".repeat(HORIZONTAL_PADDING);
  • replacement in l10n_embed/src/layout.rs at line 70
    [44.2583][44.2583:2640]()
    output.push_str(&"\n".repeat(VERTICAL_PADDING));
    [44.2583]
    [44.2640]
    buffer.push_str(&vertical_padding);
  • edit in l10n_embed/src/layout.rs at line 72
    [44.2641][44.2641:2709]()
    let localized_message = self.message.localize_for(locale);
  • edit in l10n_embed/src/layout.rs at line 73
    [44.2762][44.2762:2827]()
    let horizontal_padding = " ".repeat(HORIZONTAL_PADDING);
  • replacement in l10n_embed/src/layout.rs at line 74
    [44.2896][44.2896:2950]()
    // Add a newline on every additional line
    [44.2896]
    [44.2950]
    // Add a newline before every additional line
  • replacement in l10n_embed/src/layout.rs at line 76
    [44.2977][44.2977:3012]()
    output.push('\n');
    [44.2977]
    [44.3012]
    buffer.push('\n');
  • replacement in l10n_embed/src/layout.rs at line 79
    [44.3027][44.3027:3112]()
    output.push_str(&horizontal_padding);
    output.push_str(line);
    [44.3027]
    [44.3112]
    buffer.push_str(&horizontal_padding);
    buffer.push_str(line);
  • edit in l10n_embed/src/layout.rs at line 82
    [44.3122][44.3122:3138]()
    output
  • replacement in l10n_embed/src/decimal.rs at line 4
    [23.774][13.2538:2559](),[13.2537][13.2538:2559]()
    use crate::Localize;
    [23.774]
    [13.2559]
    use crate::{Context, Localize};
  • replacement in l10n_embed/src/decimal.rs at line 7
    [15.4120][23.775:846](),[23.846][25.2959:2983]()
    use icu_decimal::{DecimalFormatter, options::DecimalFormatterOptions};
    use icu_locale::Locale;
    [15.4120]
    [23.892]
    use writeable::Writeable;
  • replacement in l10n_embed/src/decimal.rs at line 12
    [23.991][32.1142:1206]()
    fn localize_for(&self, locale: &Locale) -> String {
    [23.991]
    [23.1369]
    fn localize(&self, context: &Context, buffer: &mut String) {
  • replacement in l10n_embed/src/decimal.rs at line 14
    [23.1427][32.1207:1258]()
    fixed_decimal.localize_for(locale)
    [23.1427]
    [23.1484]
    fixed_decimal.localize(context, buffer);
  • replacement in l10n_embed/src/decimal.rs at line 23
    [23.1614][32.1259:1323]()
    fn localize_for(&self, locale: &Locale) -> String {
    [23.1614]
    [23.1992]
    fn localize(&self, context: &Context, buffer: &mut String) {
  • replacement in l10n_embed/src/decimal.rs at line 26
    [23.2125][32.1324:1375]()
    fixed_decimal.localize_for(locale)
    [23.2125]
    [23.2182]
    fixed_decimal.localize(context, buffer);
  • replacement in l10n_embed/src/decimal.rs at line 49
    [18.1016][32.1376:1432](),[32.1432][13.4824:4848](),[22.2471][13.4824:4848](),[23.2571][13.4824:4848](),[25.3188][13.4824:4848](),[13.4824][13.4824:4848](),[13.4848][23.2572:2671]()
    fn localize_for(&self, locale: &Locale) -> String {
    let formatter =
    DecimalFormatter::try_new(locale.into(), DecimalFormatterOptions::default()).unwrap();
    [18.1016]
    [13.4973]
    fn localize(&self, context: &Context, buffer: &mut String) {
    let formatter = context.decimal_formatter();
  • replacement in l10n_embed/src/decimal.rs at line 53
    [13.5030][22.2472:2510]()
    formatted_decimal.to_string()
    [13.5030]
    [13.5120]
    formatted_decimal.write_to(buffer).unwrap();
  • replacement in l10n_embed/examples/time.rs at line 3
    [30.108][30.108:142]()
    use icu_locale::{Locale, locale};
    [30.108]
    [30.142]
    use icu_locale::locale;
  • replacement in l10n_embed/examples/time.rs at line 5
    [30.173][30.173:199]()
    use l10n_embed::Localize;
    [30.173]
    [30.199]
    use l10n_embed::{Context, Localize};
  • edit in l10n_embed/examples/time.rs at line 7
    [30.200][30.200:250]()
    const DEFAULT_LOCALE: Locale = locale!("en-US");
  • replacement in l10n_embed/examples/time.rs at line 16
    [30.661][30.661:703](),[30.703][32.1433:1489](),[32.1489][30.765:772](),[30.765][30.765:772](),[30.772][32.1490:1564](),[32.1564][30.772:786](),[30.772][30.772:786](),[30.888][30.888:922](),[30.922][32.1565:1616](),[32.1616][30.979:1041](),[30.979][30.979:1041](),[30.1041][32.1617:1669](),[32.1669][30.1099:1106](),[30.1099][30.1099:1106]()
    println!(
    "Current time: {}",
    current_timestamp.localize_for(&DEFAULT_LOCALE)
    );
    println!("Unix epoch: {}", unix_epoch.localize_for(&DEFAULT_LOCALE));
    println!(
    "Two hours from now: {}",
    in_two_hours.localize_for(&DEFAULT_LOCALE)
    );
    println!(
    "Since start of year (UTC): {}",
    start_of_year.localize_for(&DEFAULT_LOCALE)
    );
    [30.661]
    [30.1106]
    let timestamps: [(&str, Timestamp); 4] = [
    ("Current time", current_timestamp),
    ("Unix epoch", unix_epoch),
    ("Two hours from now", in_two_hours),
    ("Since start of year (UTC)", start_of_year),
    ];
    let context = Context::new(locale!("en-US"));
    let mut buffer = String::new();
    for (name, timestamp) in timestamps {
    buffer.clear();
    timestamp.localize(&context, &mut buffer);
    println!("{name}: {buffer}");
    }
  • replacement in l10n_embed/examples/style.rs at line 4
    [30.1229][30.1229:1263]()
    use icu_locale::{Locale, locale};
    [30.1229]
    [30.1263]
    use icu_locale::locale;
  • edit in l10n_embed/examples/style.rs at line 6
    [30.1284][30.1284:1310]()
    use l10n_embed::Localize;
  • edit in l10n_embed/examples/style.rs at line 7
    [30.1341]
    [30.1341]
    use l10n_embed::{Context, Localize};
  • edit in l10n_embed/examples/style.rs at line 9
    [30.1342][30.1342:1391]()
    const DEFAULT_LOCALE: Locale = locale!("en-US");
  • replacement in l10n_embed/examples/style.rs at line 14
    [30.1568][30.1568:1642]()
    let five_million: Styled<u64> = Styled::new(5_000_000, STYLE_ENABLED)
    [30.1568]
    [30.1642]
    let bold_green: Styled<u64> = Styled::new(5_000_000, STYLE_ENABLED)
  • replacement in l10n_embed/examples/style.rs at line 17
    [30.1692][30.1692:1770]()
    let static_str: Styled<&'static str> = Styled::new("&str", STYLE_ENABLED)
    [30.1692]
    [30.1770]
    let italic_blue: Styled<&'static str> = Styled::new("&str", STYLE_ENABLED)
  • replacement in l10n_embed/examples/style.rs at line 20
    [30.1821][30.1821:1911]()
    let unix_epoch: Styled<Timestamp> = Styled::new(Timestamp::UNIX_EPOCH, STYLE_ENABLED)
    [30.1821]
    [30.1911]
    let strikethrough_red: Styled<Timestamp> = Styled::new(Timestamp::UNIX_EPOCH, STYLE_ENABLED)
  • replacement in l10n_embed/examples/style.rs at line 25
    [30.2081][30.2081:2123](),[30.2123][32.1670:1721](),[32.1721][30.2180:2230](),[30.2180][30.2180:2230](),[30.2230][32.1722:1771](),[32.1771][30.2285:2292](),[30.2285][30.2285:2292](),[30.2292][32.1772:1846]()
    println!(
    "Five million: {}",
    five_million.localize_for(&DEFAULT_LOCALE)
    );
    println!(
    "Static string: {}",
    static_str.localize_for(&DEFAULT_LOCALE)
    );
    println!("Unix epoch: {}", unix_epoch.localize_for(&DEFAULT_LOCALE));
    [30.2081]
    [30.2394]
    let styles: [(&str, Box<dyn Localize>); 3] = [
    ("Bold green", Box::new(bold_green)),
    ("Italic blue", Box::new(italic_blue)),
    ("Strikethrough red", Box::new(strikethrough_red)),
    ];
    let context = Context::new(locale!("en-US"));
    let mut buffer = String::new();
    for (name, style) in styles {
    buffer.clear();
    style.localize(&context, &mut buffer);
    println!("{name}: {buffer}");
    }
  • replacement in l10n_embed/examples/string.rs at line 5
    [30.2500][30.2500:2560]()
    use icu_locale::{Locale, locale};
    use l10n_embed::Localize;
    [30.2500]
    [30.2560]
    use icu_locale::locale;
    use l10n_embed::{Context, Localize};
  • edit in l10n_embed/examples/string.rs at line 8
    [30.2561][30.2561:2611]()
    const DEFAULT_LOCALE: Locale = locale!("en-US");
  • replacement in l10n_embed/examples/string.rs at line 16
    [30.2931][30.2931:2974](),[30.2974][32.1847:1896](),[32.1896][30.3029:3087](),[30.3029][30.3029:3087](),[30.3087][32.1897:1957](),[32.1957][30.3153:3210](),[30.3153][30.3153:3210](),[30.3210][32.1958:2014](),[32.2014][30.3272:3279](),[30.3272][30.3272:3279]()
    println!(
    "Static string: {}",
    static_str.localize_for(&DEFAULT_LOCALE)
    );
    println!(
    "Heap allocated string: {}",
    heap_allocated_string.localize_for(&DEFAULT_LOCALE)
    );
    println!(
    "Copy on write string: {}",
    copy_on_write_str.localize_for(&DEFAULT_LOCALE)
    );
    [30.2931]
    [30.3279]
    let strings: [(&str, Box<dyn Localize>); 3] = [
    ("Static string", Box::new(static_str)),
    ("Heap allocated string", Box::new(heap_allocated_string)),
    ("Copy on write string", Box::new(copy_on_write_str)),
    ];
    let context = Context::new(locale!("en-US"));
    let mut buffer = String::new();
    for (name, string) in strings {
    buffer.clear();
    string.localize(&context, &mut buffer);
    println!("{name}: {buffer}");
    }
  • replacement in l10n_embed/examples/path.rs at line 4
    [38.847][38.847:957]()
    use icu_locale::{Locale, locale};
    use l10n_embed::Localize;
    const DEFAULT_LOCALE: Locale = locale!("en-US");
    [38.847]
    [38.957]
    use icu_locale::locale;
    use l10n_embed::{Context, Localize};
  • replacement in l10n_embed/examples/path.rs at line 13
    [38.1274][38.1274:1507]()
    println!(
    "Current directory: {}",
    current_directory.localize_for(&DEFAULT_LOCALE)
    );
    println!(
    "Current directory (UTF-8): {}",
    current_directory_utf8.localize_for(&DEFAULT_LOCALE)
    );
    [38.1274]
    [38.1507]
    let directories: [(&str, Box<dyn Localize>); 2] = [
    ("Current directory", Box::new(current_directory)),
    (
    "Current directory (UTF-8)",
    Box::new(current_directory_utf8),
    ),
    ];
    let context = Context::new(locale!("en-US"));
    let mut buffer = String::new();
    for (name, directory) in directories {
    buffer.clear();
    directory.localize(&context, &mut buffer);
    println!("{name}: {buffer}");
    }
  • replacement in l10n_embed/examples/list.rs at line 3
    [43.1188][43.1188:1248]()
    use icu_locale::{Locale, locale};
    use l10n_embed::Localize;
    [43.1188]
    [43.1248]
    use icu_locale::locale;
  • replacement in l10n_embed/examples/list.rs at line 5
    [43.1290][43.1290:1340]()
    const DEFAULT_LOCALE: Locale = locale!("en-US");
    [43.1290]
    [43.1340]
    use l10n_embed::{Context, Localize};
  • replacement in l10n_embed/examples/list.rs at line 16
    [43.1689][43.1689:1911]()
    println!("Narrow list: {}", narrow_list.localize_for(&DEFAULT_LOCALE));
    println!("Short list: {}", short_list.localize_for(&DEFAULT_LOCALE));
    println!("Wide list: {}", wide_list.localize_for(&DEFAULT_LOCALE));
    [43.1689]
    [43.1911]
    let lists: [(&str, Box<dyn Localize>); 3] = [
    ("Narrow list", Box::new(narrow_list)),
    ("Short list", Box::new(short_list)),
    ("Wide list", Box::new(wide_list)),
    ];
    let context = Context::new(locale!("en-US"));
    let mut buffer = String::new();
    for (name, list) in lists {
    buffer.clear();
    list.localize(&context, &mut buffer);
    println!("{name}: {buffer}");
    }
  • replacement in l10n_embed/examples/layout.rs at line 3
    [44.3240][44.3240:3300]()
    use icu_locale::{Locale, locale};
    use l10n_embed::Localize;
    [44.3240]
    [44.3300]
    use icu_locale::locale;
  • replacement in l10n_embed/examples/layout.rs at line 5
    [44.3341][44.3341:3391]()
    const DEFAULT_LOCALE: Locale = locale!("en-US");
    [44.3341]
    [44.3391]
    use l10n_embed::{Context, Localize};
  • replacement in l10n_embed/examples/layout.rs at line 23
    [44.4047][44.4047:4741]()
    let layouts = [
    (
    "Single newline",
    single_newline.localize_for(&DEFAULT_LOCALE),
    ),
    (
    "Double newlines",
    double_newlines.localize_for(&DEFAULT_LOCALE),
    ),
    (
    "Vertical padding",
    vertical_padding.localize_for(&DEFAULT_LOCALE),
    ),
    (
    "Horizontal padding",
    horizontal_padding.localize_for(&DEFAULT_LOCALE),
    ),
    (
    "Combined padding",
    combined_padding.localize_for(&DEFAULT_LOCALE),
    ),
    (
    "Padded newlines",
    padded_newlines.localize_for(&DEFAULT_LOCALE),
    ),
    [44.4047]
    [44.4741]
    let layouts: [(&str, Box<dyn Localize>); 6] = [
    ("Single newline", Box::new(single_newline)),
    ("Double newlines", Box::new(double_newlines)),
    ("Vertical padding", Box::new(vertical_padding)),
    ("Horizontal padding", Box::new(horizontal_padding)),
    ("Combined padding", Box::new(combined_padding)),
    ("Padded newlines", Box::new(padded_newlines)),
  • edit in l10n_embed/examples/layout.rs at line 32
    [44.4749]
    [44.4749]
    let context = Context::new(locale!("en-US"));
    let mut buffer = String::new();
  • edit in l10n_embed/examples/layout.rs at line 37
    [44.4848]
    [44.4848]
    buffer.clear();
    layout.localize(&context, &mut buffer);
  • replacement in l10n_embed/examples/layout.rs at line 41
    [44.4888][44.4888:4918]()
    println!("{layout}");
    [44.4888]
    [44.4918]
    println!("{buffer}");
  • replacement in l10n_embed/examples/decimal.rs at line 4
    [30.3406][30.3406:3466]()
    use icu_locale::{Locale, locale};
    use l10n_embed::Localize;
    [30.3406]
    [30.3466]
    use icu_locale::locale;
    use l10n_embed::{Context, Localize};
  • edit in l10n_embed/examples/decimal.rs at line 7
    [30.3467][30.3467:3517]()
    const DEFAULT_LOCALE: Locale = locale!("en-US");
  • edit in l10n_embed/examples/decimal.rs at line 14
    [30.3801]
    [30.3801]
    let context = Context::new(locale!("en-US"));
    let mut buffer = String::new();
  • replacement in l10n_embed/examples/decimal.rs at line 19
    [30.3862][32.2015:2077](),[32.2077][30.3930:3972](),[30.3930][30.3930:3972](),[30.3972][32.2078:2129](),[32.2129][30.4029:4080](),[30.4029][30.4029:4080](),[30.4080][32.2130:2183](),[32.2183][30.4139:4199](),[30.4139][30.4139:4199](),[30.4199][32.2184:2246](),[32.2246][30.4267:4274](),[30.4267][30.4267:4274]()
    println!("Zero: {}", zero.localize_for(&DEFAULT_LOCALE));
    println!(
    "Five million: {}",
    five_million.localize_for(&DEFAULT_LOCALE)
    );
    println!(
    "Zero point two: {}",
    zero_point_two.localize_for(&DEFAULT_LOCALE)
    );
    println!(
    "Negative two point five: {}",
    negative_two_point_five.localize_for(&DEFAULT_LOCALE)
    );
    [30.3862]
    [30.4274]
    let numbers: [(&str, Box<dyn Localize>); 4] = [
    ("Zero", Box::new(zero)),
    ("Five million", Box::new(five_million)),
    ("Zero point two", Box::new(zero_point_two)),
    ("Negative two point five", Box::new(negative_two_point_five)),
    ];
    for (name, number) in numbers {
    buffer.clear();
    number.localize(&context, &mut buffer);
    println!("{name}: {buffer}");
    }
  • edit in l10n_embed/Cargo.toml at line 27
    [15.5498]
    writeable.workspace = true
  • edit in Cargo.toml at line 53
    [28.3478]
    writeable = "0.6"
  • edit in Cargo.lock at line 744
    [29.3149]
    [20.2794]
    "writeable",