Refactor `Localize` for performance
Dependencies
- [2]
4MRF5E76Generate simple locale matching code in `localize()` - [3]
P6FW2GGORemove unnecessary parameters in generated `localize()` function - [4]
NO3PDO7PRefactor `fluent_embed` to support structs - [5]
F5LG7WENEmit compilation errors from Fluent source code - [6]
HHJDRLLNCreate `fluent_embed_runtime` crate - [7]
BFL2Y7GNAdd relative timestamps using `jiff` and `icu_relativetime` - [8]
KZLFC7OWRename `fluent_embed_runtime` to `fluent_embed` - [9]
7U2DXFMPRefactor `fluent_embed::Localize` to support overriding locales - [10]
CESJ4CTOMove macro-specific code into `macro_impl` module - [11]
IRW6JACSImplement `Localize` for `RelativeTime` - [12]
3NMKD6I5Refactor `Localize` trait to use `std::io::Write` - [13]
C6W7N6N5Implement `Localize` for `FixedDecimal` and primitive number types - [14]
WWDZWJTRImplement `Localize` for string types - [15]
7M4UI3TWUpdate dependencies to latest versions - [16]
7JPOCQEIAdd explicit error handling for macro parsing - [17]
MABGENI7Refactor `fluent_embed_derive` tests - [18]
LU6IFZFGRemove `std::io::Write` trait bound from `Localize` - [19]
JUV7C6ETCreate initial prototype of `fluent_embed_interaction` - [20]
BAH2JCJPAdd progress bar to `fluent_embed_interaction` - [21]
U2PHMYPDReturn `String` directly instead of writing to buffer in `Localize::localize` - [22]
QJC4IQITRefactor `Localize` functions to infallibly return `String` - [23]
QM64L3XOReplace `duplicate` macro with `macro_rules!` - [24]
NB7K77TZUpdate formatting of `InteractionEnvironment::new()` - [25]
6XEMHUGSUse full `Locale` instead of `LanguageIdentifier` subset - [26]
KFFAQIZURename `InteractionEnvironment::print_message` to `emit_message` - [27]
AE3AZFVKAdd `Styled<L: Localize>` struct to support localizing colors - [28]
S26YOXQIUpdate `Cargo.toml` in workspace and packages - [29]
RUCC2HKZRename from `fluent_embed` to `l10n_embed` - [30]
USKESL6XAdd examples for using common types in `l10n_embed` - [31]
7YOM2QEFMove interaction constructors from individual types to implementations on `InteractionEnvironment` - [32]
EKXWNEPKRename `Localize::message_for_locale` to `Localize::localize_for` - [33]
PGBXJWIHMove `l10n_embed` re-exports into `macro_prelude` module - [34]
IZ67IMRIMove prompt message to initial constructor - [35]
BC22FLOQMove interaction constructors back to individual types - [36]
XSRT5QWXReturn `InteractionError` instead of `std::io::Error` from `InteractionEnvironment::emit_message` - [37]
YUW3BUXXAdd examples for `l10n_embed_interaction` - [38]
AS7RDZT7Implement `Localize` for `std::path` and `camino` - [39]
R2BAN2V6Add support for unnamed fields in `l10n_embed_derive` - [40]
XDJBTEXUAdd support for integer selectors - [41]
2HHBS7VWAdd rudimentary support for localizing lists - [42]
XPGOKS6XAdd rudimentary support for built-in `LEN()` function - [43]
Q7LUHXXBReplace localization of `Vec<Localize>` with `List` type - [44]
LYOV6ZIRAdd `layout` module for localizing with separators and vertical/horizontal padding - [45]
XGNME3WRMove `Group::derive_enum` to new `crate::parse_macro` module - [46]
QSK7JRBAAdd simple `attribute_path` function - [47]
IXBE5Q6TImplement `InteractionEnvironment::print_message` - [48]
MVTRHSJLMove from `fluent-syntax` PR to main branch - [49]
NEBSVXIAApply Clippy fixes - [50]
CFJKYXUXRemove newtype wrapper `RelativeTime` for `jiff::Timestamp` - [51]
5I5NR4DQMake `Localize::CANONICAL_LOCALE` a function instead of associated constant - [52]
7X4MEZJUUse Fluent AST when reporting error spans - [*]
O77KA6C4Create `fluent_embed` crate - [*]
5TEX4MNUSplit `fluent_embed` into `group` and `parse` modules - [*]
VZYZRAO4Move `output-macros` crate into workspace - [*]
UKFEFT6LCreate basic `Output` proc-macro
Change contents
- replacement in l10n_embed_interaction/src/prompt/select.rs at line 16
let localized_prompt = prompt.localize_for(&environment.locale);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
.map(|item| item.localize_for(&self.environment.locale)).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
match self.environment.context {match self.environment.interaction_context { - replacement in l10n_embed_interaction/src/prompt/password.rs at line 6
localized_prompt: String,prompt: String, - replacement in l10n_embed_interaction/src/prompt/password.rs at line 20
pub fn new<L: Localize>(environment: &'environment InteractionEnvironment,prompt: L,) -> Self {let localized_prompt = prompt.localize_for(&environment.locale);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
confirmation: L1,confirmation_prompt: L1, - replacement in l10n_embed_interaction/src/prompt/password.rs at line 37
let confirmation_text = confirmation.localize_for(&self.environment.locale);let mismatch_error_text = mismatch_error.localize_for(&self.environment.locale);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
localized_prompt: confirmation_text,mismatch_error: mismatch_error_text,prompt: localized_prompt,mismatch_error: localized_mismatch_error, - replacement in l10n_embed_interaction/src/prompt/password.rs at line 58
match self.environment.context {match self.environment.interaction_context { - replacement in l10n_embed_interaction/src/prompt/password.rs at line 64
prompt = prompt.with_confirmation(confirmation.localized_prompt,confirmation.mismatch_error,);prompt =prompt.with_confirmation(confirmation.prompt, confirmation.mismatch_error); - replacement in l10n_embed_interaction/src/prompt/macros.rs at line 23
let locale = self.environment.locale.clone();let localization_context =std::sync::Arc::clone(&self.environment.localization_context); - replacement in l10n_embed_interaction/src/prompt/macros.rs at line 29
Err(message) => {// Localize the error messageErr(message.localize_for(&locale))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
pub fn new<L: Localize>(environment: &'environment InteractionEnvironment,prompt: L,) -> Self {let localized_prompt = prompt.localize_for(&environment.locale);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);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
match self.environment.context {match self.environment.interaction_context { - replacement in l10n_embed_interaction/src/prompt/confirm.rs at line 16
let localized_prompt = prompt.localize_for(&environment.locale);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
match self.environment.context {match self.environment.interaction_context { - replacement in l10n_embed_interaction/src/progress.rs at line 23
let localized_text = message.localize_for(&environment.locale);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
use std::sync::Arc; - edit in l10n_embed_interaction/src/lib.rs at line 7
use icu_locale::Locale; - replacement in l10n_embed_interaction/src/lib.rs at line 8
use l10n_embed::Localize;use l10n_embed::{Context, Localize}; - replacement in l10n_embed_interaction/src/lib.rs at line 41
context: InteractionContext,locale: Locale,localization_context: Arc<Context>,interaction_context: InteractionContext, - replacement in l10n_embed_interaction/src/lib.rs at line 48
pub fn new(locale: Locale, interactive: bool) -> Self {pub fn new(localization_context: Arc<Context>, interactive: bool) -> Self { - replacement in l10n_embed_interaction/src/lib.rs at line 50
context: match interactive {localization_context,interaction_context: match interactive { - edit in l10n_embed_interaction/src/lib.rs at line 55
locale, - replacement in l10n_embed_interaction/src/lib.rs at line 60
self.progress_bars.println(message.localize_for(&self.locale))?;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
match self.environment.context {match self.environment.interaction_context { - edit in l10n_embed_interaction/examples/prompt/main.rs at line 2
use std::sync::Arc; - edit in l10n_embed_interaction/examples/prompt/main.rs at line 6
use l10n_embed::Context; - replacement in l10n_embed_interaction/examples/prompt/main.rs at line 50
let environment = InteractionEnvironment::new(locale!("en-US"), true);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
use std::sync::Arc; - edit in l10n_embed_interaction/examples/progress/main.rs at line 6
use l10n_embed::Context; - replacement in l10n_embed_interaction/examples/progress/main.rs at line 18
let environment = InteractionEnvironment::new(locale!("en-US"), true);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
use l10n_embed::Localize;use l10n_embed::{Context, Localize}; - replacement in l10n_embed_derive/tests/unnamed_fields.rs at line 24
fn localize_for(&self, locale: &Locale) -> String {format!("Localized with locale: {locale}")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
use l10n_embed::Localize;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());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
fn localize_for(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 {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 messageconst 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
#(if locale.normalizing_eq(#additional_locales) {#(if context.locale.normalizing_eq(#additional_locales) { - replacement in l10n_embed_derive/src/macro_impl/derive.rs at line 44
return buffer;return; - replacement in l10n_embed_derive/src/macro_impl/derive.rs at line 46
// Fall back to the canonical locale, if no other valid locale was matched// 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
buffer - replacement in l10n_embed_derive/src/macro_impl/derive.rs at line 70
Ok(quote!(#field_reference.localize_for(locale)))Ok(quote!(#field_reference.localize(context, buffer))) - replacement in l10n_embed_derive/src/lib.rs at line 46
fn localize_for(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 {context: &::l10n_embed::Context,buffer: &mut String,) { - replacement in l10n_embed_derive/src/fluent/ast.rs at line 57
quote!(plural_rules.category_for(#category_reference))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
parse_quote!(buffer.push_str(&#expression.localize_for(locale)))parse_quote!(#expression.localize(context, buffer)) - replacement in l10n_embed/src/time.rs at line 1
use crate::Localize;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;use fixed_decimal::Decimal; - edit in l10n_embed/src/time.rs at line 5
use writeable::Writeable; - edit in l10n_embed/src/time.rs at line 7
/// 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
fn localize_for(&self, locale: &Locale) -> String {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();let formatter = context.relative_time_formatter(selected_unit).unwrap(); - replacement in l10n_embed/src/time.rs at line 67
let decimal =Decimal::try_from_f64(selected_value as f64, FloatPrecision::Integer).unwrap();let decimal = Decimal::from(selected_value); - replacement in l10n_embed/src/time.rs at line 69
formatted_text.to_string()formatted_text.write_to(buffer).unwrap(); - replacement in l10n_embed/src/style.rs at line 1
use crate::Localize;use crate::{Context, Localize}; - replacement in l10n_embed/src/style.rs at line 21
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,fn localize(&self, context: &Context, buffer: &mut String) {// Prefix with the terminal color codesif 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 codesif 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
use crate::Localize; - replacement in l10n_embed/src/string.rs at line 5
use icu_locale::Locale;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()fn localize(&self, _context: &Context, buffer: &mut String) {buffer.push_str(self.as_ref()); - replacement in l10n_embed/src/path.rs at line 3
use crate::Localize;use crate::{Context, Localize}; - edit in l10n_embed/src/path.rs at line 6
use icu_locale::Locale; - replacement in l10n_embed/src/path.rs at line 10
fn localize_for(&self, _locale: &Locale) -> String {self.to_string_lossy().to_string()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
fn localize_for(&self, _locale: &Locale) -> String {self.to_string()fn localize(&self, _context: &Context, buffer: &mut String) {buffer.push_str(self.as_str()) - replacement in l10n_embed/src/list.rs at line 6
use crate::Localize;use crate::{Context, Localize}; - replacement in l10n_embed/src/list.rs at line 8
use icu_list::ListFormatter;use icu_list::options::ListFormatterOptions;use icu_locale::Locale;use writeable::Writeable; - replacement in l10n_embed/src/list.rs at line 32
fn localize_for(&self, locale: &Locale) -> String {let list_formatter = ListFormatter::try_new_and(locale.into(),ListFormatterOptions::default().with_length(self.length),).unwrap();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
let localized_messages = self.messages.iter().map(|message| message.localize_for(locale));buffer}); - replacement in l10n_embed/src/list.rs at line 42
list_formatter.format(localized_messages).to_string()let formatted_list = list_formatter.format(localized_messages);formatted_list.write_to(buffer).unwrap(); - edit in l10n_embed/src/lib.rs at line 8
use std::sync::OnceLock; - edit in l10n_embed/src/lib.rs at line 11
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
use icu_plurals::{PluralRuleType, PluralRules, PluralRulesOptions, PluralRulesPreferences}; - replacement in l10n_embed/src/lib.rs at line 38
fn localize_for(&self, locale: &Locale) -> String;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
use crate::Localize; - edit in l10n_embed/src/layout.rs at line 6
use crate::{Context, Localize}; - replacement in l10n_embed/src/layout.rs at line 25
fn localize_for(&self, locale: &Locale) -> String {let localized_items: Vec<String> = self.messages.iter().map(|item| item.localize_for(locale)).collect();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 lineif index > 0 {buffer.push_str(&separator)} - replacement in l10n_embed/src/layout.rs at line 34
localized_items.join(&"\n".repeat(SEPARATOR_COUNT))message.localize(context, buffer);} - replacement in l10n_embed/src/layout.rs at line 62
fn localize_for(&self, locale: &Locale) -> String {let mut output = String::new();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
let vertical_padding = "\n".repeat(VERTICAL_PADDING);let horizontal_padding = " ".repeat(HORIZONTAL_PADDING); - replacement in l10n_embed/src/layout.rs at line 70
output.push_str(&"\n".repeat(VERTICAL_PADDING));buffer.push_str(&vertical_padding); - edit in l10n_embed/src/layout.rs at line 72
let localized_message = self.message.localize_for(locale); - edit in l10n_embed/src/layout.rs at line 73
let horizontal_padding = " ".repeat(HORIZONTAL_PADDING); - replacement in l10n_embed/src/layout.rs at line 74
// Add a newline on every additional line// Add a newline before every additional line - replacement in l10n_embed/src/layout.rs at line 76
output.push('\n');buffer.push('\n'); - replacement in l10n_embed/src/layout.rs at line 79
output.push_str(&horizontal_padding);output.push_str(line);buffer.push_str(&horizontal_padding);buffer.push_str(line); - edit in l10n_embed/src/layout.rs at line 82
output - replacement in l10n_embed/src/decimal.rs at line 4
use crate::Localize;use crate::{Context, Localize}; - replacement in l10n_embed/src/decimal.rs at line 7
use icu_decimal::{DecimalFormatter, options::DecimalFormatterOptions};use icu_locale::Locale;use writeable::Writeable; - replacement in l10n_embed/src/decimal.rs at line 12
fn localize_for(&self, locale: &Locale) -> String {fn localize(&self, context: &Context, buffer: &mut String) { - replacement in l10n_embed/src/decimal.rs at line 14
fixed_decimal.localize_for(locale)fixed_decimal.localize(context, buffer); - replacement in l10n_embed/src/decimal.rs at line 23
fn localize_for(&self, locale: &Locale) -> String {fn localize(&self, context: &Context, buffer: &mut String) { - replacement in l10n_embed/src/decimal.rs at line 26
fixed_decimal.localize_for(locale)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();fn localize(&self, context: &Context, buffer: &mut String) {let formatter = context.decimal_formatter(); - replacement in l10n_embed/src/decimal.rs at line 53
formatted_decimal.to_string()formatted_decimal.write_to(buffer).unwrap(); - replacement in l10n_embed/examples/time.rs at line 3
use icu_locale::{Locale, locale};use icu_locale::locale; - replacement in l10n_embed/examples/time.rs at line 5
use l10n_embed::Localize;use l10n_embed::{Context, Localize}; - edit in l10n_embed/examples/time.rs at line 7
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));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
use icu_locale::{Locale, locale};use icu_locale::locale; - edit in l10n_embed/examples/style.rs at line 6
use l10n_embed::Localize; - edit in l10n_embed/examples/style.rs at line 7
use l10n_embed::{Context, Localize}; - edit in l10n_embed/examples/style.rs at line 9
const DEFAULT_LOCALE: Locale = locale!("en-US"); - replacement in l10n_embed/examples/style.rs at line 14
let five_million: Styled<u64> = Styled::new(5_000_000, STYLE_ENABLED)let bold_green: Styled<u64> = Styled::new(5_000_000, STYLE_ENABLED) - replacement in l10n_embed/examples/style.rs at line 17
let static_str: Styled<&'static str> = Styled::new("&str", STYLE_ENABLED)let italic_blue: Styled<&'static str> = Styled::new("&str", STYLE_ENABLED) - replacement in l10n_embed/examples/style.rs at line 20
let unix_epoch: Styled<Timestamp> = Styled::new(Timestamp::UNIX_EPOCH, STYLE_ENABLED)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));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
use icu_locale::{Locale, locale};use l10n_embed::Localize;use icu_locale::locale;use l10n_embed::{Context, Localize}; - edit in l10n_embed/examples/string.rs at line 8
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));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
use icu_locale::{Locale, locale};use l10n_embed::Localize;const DEFAULT_LOCALE: Locale = locale!("en-US");use icu_locale::locale;use l10n_embed::{Context, Localize}; - replacement in l10n_embed/examples/path.rs at line 13
println!("Current directory: {}",current_directory.localize_for(&DEFAULT_LOCALE));println!("Current directory (UTF-8): {}",current_directory_utf8.localize_for(&DEFAULT_LOCALE));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
use icu_locale::{Locale, locale};use l10n_embed::Localize;use icu_locale::locale; - replacement in l10n_embed/examples/list.rs at line 5
const DEFAULT_LOCALE: Locale = locale!("en-US");use l10n_embed::{Context, Localize}; - replacement in l10n_embed/examples/list.rs at line 16
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));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
use icu_locale::{Locale, locale};use l10n_embed::Localize;use icu_locale::locale; - replacement in l10n_embed/examples/layout.rs at line 5
const DEFAULT_LOCALE: Locale = locale!("en-US");use l10n_embed::{Context, Localize}; - replacement in l10n_embed/examples/layout.rs at line 23
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),),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
let context = Context::new(locale!("en-US"));let mut buffer = String::new(); - edit in l10n_embed/examples/layout.rs at line 37
buffer.clear();layout.localize(&context, &mut buffer); - replacement in l10n_embed/examples/layout.rs at line 41
println!("{layout}");println!("{buffer}"); - replacement in l10n_embed/examples/decimal.rs at line 4
use icu_locale::{Locale, locale};use l10n_embed::Localize;use icu_locale::locale;use l10n_embed::{Context, Localize}; - edit in l10n_embed/examples/decimal.rs at line 7
const DEFAULT_LOCALE: Locale = locale!("en-US"); - edit in l10n_embed/examples/decimal.rs at line 14
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));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
"writeable",