Refactor `Localize` functions to infallibly return `String`

finchie
Jun 4, 2025, 1:23 PM
QJC4IQITOQP65AFLA5CMH2EXHB6B3SOLW2XBV72U5ZQU2KOR2EIAC

Dependencies

  • [2] 7X4MEZJU Use Fluent AST when reporting error spans
  • [3] MWN4CAOZ Update `LocalizationError` in `fluent_embed`
  • [4] U2PHMYPD Return `String` directly instead of writing to buffer in `Localize::localize`
  • [5] UN2XEIEU Migrate from `locale_select` to `env_preferences`
  • [6] 73C6NOJ7 Fix minimal `Localize` implementation on errors
  • [7] MABGENI7 Refactor `fluent_embed_derive` tests
  • [8] JUV7C6ET Create initial prototype of `fluent_embed_interaction`
  • [9] BAH2JCJP Add progress bar to `fluent_embed_interaction`
  • [10] 3NMKD6I5 Refactor `Localize` trait to use `std::io::Write`
  • [11] 7JPOCQEI Add explicit error handling for macro parsing
  • [12] IRW6JACS Implement `Localize` for `RelativeTime`
  • [13] NO3PDO7P Refactor `fluent_embed` to support structs
  • [14] LU6IFZFG Remove `std::io::Write` trait bound from `Localize`
  • [15] F5LG7WEN Emit compilation errors from Fluent source code
  • [16] YZ6PVVQC Add error handling for common unsupported Rust code
  • [17] ARB66QTX Add support for unit structs/variants
  • [18] HHJDRLLN Create `fluent_embed_runtime` crate
  • [19] 7M4UI3TW Update dependencies to latest versions
  • [20] 5TEX4MNU Split `fluent_embed` into `group` and `parse` modules
  • [21] BFL2Y7GN Add relative timestamps using `jiff` and `icu_relativetime`
  • [22] GJMBIJOE Migrate to latest env_preferences version
  • [23] C6W7N6N5 Implement `Localize` for `FixedDecimal` and primitive number types
  • [24] WWDZWJTR Implement `Localize` for string types
  • [25] CESJ4CTO Move macro-specific code into `macro_impl` module
  • [*] XGNME3WR Move `Group::derive_enum` to new `crate::parse_macro` module
  • [*] O77KA6C4 Create `fluent_embed` crate

Change contents

  • replacement in fluent_embed_interaction/src/prompt/select.rs at line 3
    [5.258][5.258:307]()
    use fluent_embed::{LocalizationError, Localize};
    [5.258]
    [5.307]
    use fluent_embed::Localize;
  • replacement in fluent_embed_interaction/src/prompt/select.rs at line 17
    [5.534][5.534:629]()
    pub fn with_items<L: Localize>(mut self, items: &[L]) -> Result<Self, LocalizationError> {
    [5.534]
    [5.629]
    pub fn with_items<L: Localize>(mut self, items: &[L]) -> Self {
  • replacement in fluent_embed_interaction/src/prompt/select.rs at line 21
    [4.37][5.1013:1080](),[5.1013][5.1013:1080]()
    .collect::<Result<Vec<String>, LocalizationError>>()?;
    [4.37]
    [5.1080]
    .collect::<Vec<String>>();
  • replacement in fluent_embed_interaction/src/prompt/select.rs at line 24
    [5.1125][5.1125:1142]()
    Ok(self)
    [5.1125]
    [5.1142]
    self
  • replacement in fluent_embed_interaction/src/prompt/password.rs at line 3
    [5.2163][5.2163:2212]()
    use fluent_embed::{LocalizationError, Localize};
    [5.2163]
    [5.2212]
    use fluent_embed::Localize;
  • replacement in fluent_embed_interaction/src/prompt/password.rs at line 26
    [5.2710][5.2710:2753](),[5.2753][4.38:158]()
    ) -> Result<Self, LocalizationError> {
    let confirmation_text = confirmation.localize()?;
    let mismatch_error_text = mismatch_error.localize()?;
    [5.2710]
    [5.2895]
    ) -> Self {
    let confirmation_text = confirmation.localize();
    let mismatch_error_text = mismatch_error.localize();
  • replacement in fluent_embed_interaction/src/prompt/password.rs at line 35
    [5.3200][5.3200:3217]()
    Ok(self)
    [5.3200]
    [5.3217]
    self
  • replacement in fluent_embed_interaction/src/prompt/macros.rs at line 25
    [5.4938][5.4938:5087](),[5.5087][4.159:216]()
    pub fn with_prompt<L: Localize>(
    mut self,
    prompt: L,
    ) -> Result<Self, LocalizationError> {
    let localized_text = prompt.localize()?;
    [5.4938]
    [5.5244]
    pub fn with_prompt<L: fluent_embed::Localize>(mut self, prompt: L) -> Self {
    let localized_text = prompt.localize();
  • replacement in fluent_embed_interaction/src/prompt/macros.rs at line 29
    [5.5297][5.5297:5322]()
    Ok(self)
    [5.5297]
    [5.5322]
    self
  • replacement in fluent_embed_interaction/src/prompt/macros.rs at line 38
    [5.5441][5.5441:5532]()
    pub fn with_validator<L: Localize, V: Fn(&String) -> Result<(), L> + 'static>(
    [5.5441]
    [5.5532]
    pub fn with_validator<
    L: fluent_embed::Localize,
    V: Fn(&String) -> Result<(), L> + 'static,
    >(
  • replacement in fluent_embed_interaction/src/prompt/macros.rs at line 50
    [5.5892][4.217:278]()
    Err(message.localize().unwrap())
    [5.5892]
    [5.6188]
    Err(message.localize())
  • replacement in fluent_embed_interaction/src/prompt/input.rs at line 3
    [5.6639][5.6639:6688]()
    use fluent_embed::{LocalizationError, Localize};
    [5.6639]
    [5.6688]
    use fluent_embed::Localize;
  • replacement in fluent_embed_interaction/src/prompt/input.rs at line 17
    [5.6941][5.6941:7037](),[5.7037][4.279:329]()
    pub fn with_default<L: Localize>(mut self, default: L) -> Result<Self, LocalizationError> {
    let localized_text = default.localize()?;
    [5.6941]
    [5.7171]
    pub fn with_default<L: Localize>(mut self, default: L) -> Self {
    let localized_text = default.localize();
  • replacement in fluent_embed_interaction/src/prompt/input.rs at line 21
    [5.7217][5.7217:7234]()
    Ok(self)
    [5.7217]
    [5.7234]
    self
  • edit in fluent_embed_interaction/src/prompt/confirm.rs at line 3
    [5.8289][5.8289:8338]()
    use fluent_embed::{LocalizationError, Localize};
  • replacement in fluent_embed_interaction/src/progress.rs at line 2
    [5.549][5.549:598]()
    use fluent_embed::{LocalizationError, Localize};
    [5.549]
    [5.598]
    use fluent_embed::Localize;
  • replacement in fluent_embed_interaction/src/progress.rs at line 25
    [5.1231][5.1231:1327](),[5.1327][4.330:380]()
    pub fn with_message<L: Localize>(mut self, message: L) -> Result<Self, LocalizationError> {
    let localized_text = message.localize()?;
    [5.1231]
    [5.1461]
    pub fn with_message<L: Localize>(mut self, message: L) -> Self {
    let localized_text = message.localize();
  • replacement in fluent_embed_interaction/src/progress.rs at line 29
    [5.1538][5.1538:1555]()
    Ok(self)
    [5.1538]
    [5.1555]
    self
  • replacement in fluent_embed_derive/tests/common/mod.rs at line 9
    [5.3249][5.3249:3399]()
    let mut buffer = Vec::new();
    message.message_for_locale(&mut buffer, &locale).unwrap();
    let result = String::from_utf8(buffer).unwrap();
    [5.3249]
    [5.3399]
    let result = message.message_for_locale(&locale);
  • replacement in fluent_embed_derive/src/macro_impl/mod.rs at line 87
    [5.564][5.564:580]()
    }))
    [5.564]
    [5.580]
    }));
  • replacement in fluent_embed_derive/src/macro_impl/mod.rs at line 100
    [5.790][5.790:806]()
    }))
    [5.790]
    [5.806]
    }));
  • replacement in fluent_embed_derive/src/macro_impl/mod.rs at line 121
    [5.1022][5.1022:1042]()
    }))
    [5.1022]
    [5.1042]
    }));
  • replacement in fluent_embed_derive/src/macro_impl/mod.rs at line 178
    [5.1868][5.283:337]()
    fn message_for_locale<W: std::io::Write>(
    [5.1868]
    [5.454]
    fn message_for_locale(
  • edit in fluent_embed_derive/src/macro_impl/mod.rs at line 180
    [5.477][5.477:509]()
    writer: &mut W,
  • replacement in fluent_embed_derive/src/macro_impl/mod.rs at line 181
    [5.426][5.581:646](),[5.581][5.581:646]()
    ) -> Result<(), ::fluent_embed::LocalizationError> {
    [5.426]
    [5.646]
    ) -> String {
  • edit in fluent_embed_derive/src/macro_impl/derive.rs at line 41
    [5.510]
    [5.903]
    let mut buffer = String::new();
  • replacement in fluent_embed_derive/src/macro_impl/derive.rs at line 53
    [5.768][5.768:795]()
    return Ok(());
    [5.768]
    [5.795]
    return buffer;
  • replacement in fluent_embed_derive/src/macro_impl/derive.rs at line 58
    [5.814][5.814:829]()
    Ok(())
    [5.814]
    [5.511]
    buffer
  • replacement in fluent_embed_derive/src/macro_impl/derive.rs at line 111
    [5.2575][5.2575:2591]()
    }))
    [5.2575]
    [5.2591]
    }));
  • replacement in fluent_embed_derive/src/macro_impl/derive.rs at line 167
    [5.2906][5.2906:2926]()
    }))
    [5.2906]
    [5.2926]
    }));
  • replacement in fluent_embed_derive/src/macro_impl/derive.rs at line 184
    [5.3146][5.3146:3166]()
    }))
    [5.3146]
    [5.3166]
    }));
  • replacement in fluent_embed_derive/src/lib.rs at line 53
    [5.2886][5.406:476]()
    fn message_for_locale<W: std::io::Write>(
    [5.2886]
    [5.2937]
    fn message_for_locale(
  • edit in fluent_embed_derive/src/lib.rs at line 55
    [5.2976][5.2976:3024]()
    writer: &mut W,
  • replacement in fluent_embed_derive/src/lib.rs at line 56
    [5.3113][5.3113:3194]()
    ) -> Result<(), ::fluent_embed::LocalizationError> {
    [5.3113]
    [5.3194]
    ) -> String {
  • replacement in fluent_embed_derive/src/fluent/ast.rs at line 20
    [2.898][5.1029:1219](),[5.1029][5.1029:1219]()
    let byte_string_literal = proc_macro2::Literal::byte_string(value.as_bytes());
    write_expressions.push(parse_quote!(writer.write_all(#byte_string_literal)?));
    [2.898]
    [5.1219]
    write_expressions.push(parse_quote!(buffer.push_str(#value)));
  • replacement in fluent_embed_derive/src/fluent/ast.rs at line 72
    [5.2973][5.1617:1703]()
    parse_quote!(#expression.message_for_locale(writer, locale)?)
    [5.2973]
    [5.3995]
    parse_quote!(buffer.push_str(&#expression.message_for_locale(locale)))
  • replacement in fluent_embed/src/time.rs at line 1
    [5.53][5.3724:3766]()
    use crate::{LocalizationError, Localize};
    [5.53]
    [5.21]
    use crate::Localize;
  • replacement in fluent_embed/src/time.rs at line 5
    [5.143][5.143:218]()
    options::Numeric, RelativeTimeFormatter, RelativeTimeFormatterOptions,
    [5.143]
    [5.218]
    RelativeTimeFormatter, RelativeTimeFormatterOptions, options::Numeric,
  • replacement in fluent_embed/src/time.rs at line 7
    [5.221][5.1523:1569](),[5.1569][5.296:350](),[5.296][5.296:350]()
    use icu_locale::{langid, LanguageIdentifier};
    use jiff::{tz::TimeZone, SpanRound, Timestamp, Unit};
    [5.221]
    [5.350]
    use icu_locale::{LanguageIdentifier, langid};
    use jiff::{SpanRound, Timestamp, Unit, tz::TimeZone};
  • replacement in fluent_embed/src/time.rs at line 23
    [5.125][5.546:592](),[5.592][5.4149:4266](),[5.4149][5.4149:4266]()
    fn message_for_locale<W: std::io::Write>(
    &self,
    writer: &mut W,
    locale: &LanguageIdentifier,
    ) -> Result<(), LocalizationError> {
    [5.125]
    [5.990]
    fn message_for_locale(&self, locale: &LanguageIdentifier) -> String {
  • replacement in fluent_embed/src/time.rs at line 108
    [5.4323][5.4323:4324](),[5.4324][5.2572:2638](),[5.2638][5.4396:4421](),[5.4396][5.4396:4421](),[5.4421][5.249:250](),[5.4358][5.249:250](),[5.250][5.4422:4437]()
    writer.write_all(formatted_text.to_string().as_bytes())?;
    writer.flush()?;
    Ok(())
    [5.4323]
    [5.394]
    formatted_text.to_string()
  • replacement in fluent_embed/src/string.rs at line 7
    [5.170][5.2639:2685]()
    use icu_locale::{langid, LanguageIdentifier};
    [5.170]
    [5.215]
    use icu_locale::{LanguageIdentifier, langid};
  • replacement in fluent_embed/src/string.rs at line 24
    [5.639][5.662:708](),[5.708][5.666:850](),[5.666][5.666:850]()
    fn message_for_locale<W: std::io::Write>(
    &self,
    writer: &mut W,
    _locale: &LanguageIdentifier,
    ) -> Result<(), crate::LocalizationError> {
    writer.write_all(self.as_bytes())?;
    Ok(())
    [5.639]
    [5.850]
    fn message_for_locale(&self, _locale: &LanguageIdentifier) -> String {
    self.to_string()
  • edit in fluent_embed/src/lib.rs at line 17
    [5.406][5.4438:4502](),[5.4561][5.4561:4642](),[5.4642][5.0:100](),[5.100][3.57:213](),[5.100][5.4642:4645](),[3.213][5.4642:4645](),[5.4642][5.4642:4645]()
    #[derive(thiserror::Error, Debug)]
    pub enum LocalizationError {
    #[error("unable to write localized output")]
    IO(#[from] std::io::Error),
    #[error("unable to retrieve system locales")]
    RetrievalError(env_preferences::LocaleError),
    #[error("unable to parse localized output as a UTF-8 string. This is a bug in `fluent_embed`.")]
    InvalidOutput(#[from] std::string::FromUtf8Error),
    }
  • replacement in fluent_embed/src/lib.rs at line 22
    [5.4813][5.731:777](),[5.777][5.4840:4956](),[5.4840][5.4840:4956]()
    fn message_for_locale<W: std::io::Write>(
    &self,
    writer: &mut W,
    locale: &LanguageIdentifier,
    ) -> Result<(), LocalizationError>;
    [5.4813]
    [5.4956]
    fn message_for_locale(&self, locale: &LanguageIdentifier) -> String;
  • replacement in fluent_embed/src/lib.rs at line 38
    [5.3447][4.381:443](),[4.443][5.321:460](),[5.871][5.321:460](),[5.5031][5.321:460]()
    fn localize(&self) -> Result<String, LocalizationError> {
    let system_locales = env_preferences::get_locales_lossy()
    .map_err(|error| LocalizationError::RetrievalError(error))?;
    [5.3447]
    [5.460]
    fn localize(&self) -> String {
    let system_locales =
    env_preferences::get_locales_lossy().unwrap_or(vec![Self::CANONICAL_LOCALE.into()]);
  • edit in fluent_embed/src/lib.rs at line 60
    [4.445][4.445:482]()
    let mut buffer = Vec::new();
  • edit in fluent_embed/src/lib.rs at line 61
    [5.3805][4.483:508]()
    &mut buffer,
  • replacement in fluent_embed/src/lib.rs at line 62
    [5.3914][4.509:606]()
    )?;
    let localized_text = String::from_utf8(buffer)?;
    Ok(localized_text)
    [5.3914]
    [5.5304]
    )
  • replacement in fluent_embed/src/decimal.rs at line 37
    [5.4036][5.941:987]()
    fn message_for_locale<W: std::io::Write>(
    [5.4036]
    [5.4063]
    fn message_for_locale(
  • edit in fluent_embed/src/decimal.rs at line 39
    [5.4078][5.4078:4102]()
    writer: &mut W,
  • replacement in fluent_embed/src/decimal.rs at line 40
    [5.4139][5.4139:4187]()
    ) -> Result<(), crate::LocalizationError> {
    [5.4139]
    [5.4187]
    ) -> String {
  • replacement in fluent_embed/src/decimal.rs at line 42
    [5.4227][5.4227:4284]()
    fixed_decimal.message_for_locale(writer, locale)
    [5.4227]
    [5.4284]
    fixed_decimal.message_for_locale(locale)
  • replacement in fluent_embed/src/decimal.rs at line 54
    [5.4621][5.1055:1101]()
    fn message_for_locale<W: std::io::Write>(
    [5.4621]
    [5.4648]
    fn message_for_locale(
  • edit in fluent_embed/src/decimal.rs at line 56
    [5.4663][5.4663:4687]()
    writer: &mut W,
  • replacement in fluent_embed/src/decimal.rs at line 57
    [5.4724][5.4724:4772]()
    ) -> Result<(), crate::LocalizationError> {
    [5.4724]
    [5.4824]
    ) -> String {
  • replacement in fluent_embed/src/decimal.rs at line 63
    [5.5030][5.5079:5148](),[5.5148][5.5105:5120](),[5.5105][5.5105:5120]()
    writer.write_all(formatted_decimal.to_string().as_bytes())?;
    Ok(())
    [5.5030]
    [5.5120]
    formatted_decimal.to_string()