Simple implementation that wraps a call to icu_decimal::FixedDecimalFormatter, along with implementations for primitive number types such as isize, u32, f64, etc.
C6W7N6N57UCNHEV55HEZ3G7WN2ZOBGMFBB5M5ZPDB2HNNHHTOPBQC 7FYXVNAB6JAP3CJKE4MY57UWYSUPEXFVER6K264BSKYHVU6V4SGQC 3NMKD6I57ONAGHEN4PZIAV2KPYESVR4JL3DTWSHXKCMVJBEQ4GIQC AAERM7PBDVDFDEEXZ7UJ5WWQF7SPEJQQYXRBZ63ETB5YAVIECHMAC XEEXWJLGVIPIGURSDU4ETZMGAIFTFDPECM4QWFOSRHU7GMGVOUVQC F5LG7WENUUDRSCTDMA4M6BAC5RWTGQO45C4ZEBZDX6FHCTTHBVGQC 2SITVDYW6KANM24QXRHVSBL6S77UHKJLOSOHSUZQBJFL5NAAGQYAC 5TEX4MNUC4LDDRMNEOVCFNUUEZAGUXMKO3OIEQFXWRQKXSHY2NRQC NO3PDO7PY7J3WPADNCS5VD6HKFY63E23I3SDR4DHXNVQJTG27RAAC QFPQZR4K4UZ7R2GQZJG4NYBGVQJVL2ANIKGGTOHAMIRIBQHPSQGAC O77KA6C4UJGZXVGPEA7WCRQH6XYQJPWETSPDXI3VOKOSRQND7JEQC KZLFC7OWYNK3G5YNHRANUK3VUVCM6W6J34N7UABYA24XMZWAVVHQC 6ABVDTXZOHVUDZDKDQS256F74LFIMM5DO3OZWHKRXZBUTPII4WAQC HHJDRLLNN36UNIA7STAXEEVBCEMPJNB7SJQOS3TJLLYN4AEZ4MHQC BFL2Y7GN6NBXXNAUSD4M6T6CIVQ2OLERPE2CAFSLRF377WFFTVCQC VNSHGQYNPGKGGPYNVP4Z2RWD7JCSDJVYAADD6UXWBYL6ZRXKLE4AC UKFEFT6LSI4K7X6UHQFZYD52DILKXMZMYSO2UYS2FCHNPXIF4BEQC ROSR4HD5ENPQU3HH5IVYSOA5YM72W77CHVQARSD3T67BUNYG7KZQC // Only 1 is singular1 => format!("You have {unread_emails} unread email."),// Everything else is plural0 | 2.. => format!("You have {unread_emails} unread emails."),
0..=999 => unread_emails.to_string(),&u64::MAX => "18,446,744,073,709,551,615".to_string(),_ => unreachable!(),
// Both 0 & 1 are singular0 | 1 => format!("Vous avez {unread_emails} e-mail non lu."),// Everything else is plural2.. => format!("Vous avez {unread_emails} e-mails non lus."),
0..=999 => unread_emails.to_string(),&u64::MAX => "18 446 744 073 709 551 615".to_string(),_ => unreachable!(),
}}fn expected_message(&self, locale: &str) -> String {match self {Self::Emails { unread_emails } => {let expected_plural = self.expected_plural(locale);match locale {"en-US" => match unread_emails {// Only 1 is singular1 => "You have 1 unread email.".to_string(),// Everything else is plural0 | 2.. => format!("You have {expected_plural} unread emails."),},"fr" => match unread_emails {// Both 0 & 1 are singular0 | 1 => format!("Vous avez {expected_plural} e-mail non lu."),// Everything else is plural2.. => format!("Vous avez {expected_plural} e-mails non lus."),},_ => unreachable!(),}}
// FIXME: i128 is "good enough" for now but an incorrect representation!// e.g. decimals not supportedlet parsed_value = i128::from_str_radix(value, 10).unwrap();let number_literal = proc_macro2::Literal::i128_unsuffixed(parsed_value);parse_quote!(#number_literal)
let parsed_value: f64 = value.parse().unwrap();// Check validity at compile-time, so we avoid generating code that will break at runtimeassert!(FixedDecimal::try_from_f64(parsed_value, FloatPrecision::Floating).is_ok());let float_literal = proc_macro2::Literal::f64_suffixed(parsed_value);parse_quote!(::fluent_embed::FixedDecimal::try_from_f64(#float_literal, ::fluent_embed::FloatPrecision::Floating).unwrap())
ReferenceKind::EnumField => parse_quote!(*#ident),ReferenceKind::StructField => parse_quote!(message_context.#ident),
ReferenceKind::EnumField => parse_quote!(#ident),ReferenceKind::StructField => parse_quote!(self.#ident),
use crate::Localize;use duplicate::duplicate_item;use fixed_decimal::{FixedDecimal, FloatPrecision};use icu_decimal::{options::FixedDecimalFormatterOptions, FixedDecimalFormatter};use icu_locid::{langid, LanguageIdentifier};use icu_provider::DataLocale;use writeable::Writeable;// Implementations of `Localize` for all primitive number types// These just convert to a `FixedDecimal` and then use its implementation#[duplicate_item(type_name conversion;[u8] [FixedDecimal::from(*self)];[u16] [FixedDecimal::from(*self)];[u32] [FixedDecimal::from(*self)];[u64] [FixedDecimal::from(*self)];[u128] [FixedDecimal::from(*self)];[usize] [FixedDecimal::from(*self)];[i8] [FixedDecimal::from(*self)];[i16] [FixedDecimal::from(*self)];[i32] [FixedDecimal::from(*self)];[i64] [FixedDecimal::from(*self)];[i128] [FixedDecimal::from(*self)];[isize] [FixedDecimal::from(*self)];[f32] [FixedDecimal::try_from_f64(*self as f64, FloatPrecision::Floating).unwrap()];[f64] [FixedDecimal::try_from_f64(*self, FloatPrecision::Floating).unwrap()];)]impl<W: std::io::Write> Localize<W> for type_name {const CANONICAL_LOCALE: LanguageIdentifier = langid!("en-US");fn available_locales(&self) -> Vec<LanguageIdentifier> {// TODO: keep track of all locales with Fluent data, and return only thosevec![<Self as Localize<W>>::CANONICAL_LOCALE]}fn message_for_locale(&self,writer: &mut W,locale: &LanguageIdentifier,) -> Result<(), crate::LocalizationError> {let fixed_decimal = conversion;fixed_decimal.message_for_locale(writer, locale)}}impl<W: std::io::Write> Localize<W> for FixedDecimal {const CANONICAL_LOCALE: LanguageIdentifier = langid!("en-US");fn available_locales(&self) -> Vec<LanguageIdentifier> {// TODO: keep track of all locales with Fluent data, and return only thosevec![<Self as Localize<W>>::CANONICAL_LOCALE]}fn message_for_locale(&self,writer: &mut W,locale: &LanguageIdentifier,) -> Result<(), crate::LocalizationError> {let data_locale = DataLocale::from(locale);let formatter =FixedDecimalFormatter::try_new(&data_locale, FixedDecimalFormatterOptions::default()).unwrap();let formatted_decimal = formatter.format(self);writer.write_all(formatted_decimal.write_to_string().as_bytes())?;Ok(())}}