diff --git a/components/experimental/src/dimension/currency/compact_formatter.rs b/components/experimental/src/dimension/currency/compact_formatter.rs index 20e76797d76..fefbfb64a79 100644 --- a/components/experimental/src/dimension/currency/compact_formatter.rs +++ b/components/experimental/src/dimension/currency/compact_formatter.rs @@ -30,6 +30,16 @@ impl CurrencyFormatter { ] ); + icu_provider::gen_buffer_data_constructors!( + (prefs: CurrencyFormatterPreferences, currency_code: &CurrencyCode) -> error: DataError, + functions: [ + try_new_compact_code: skip, + try_new_compact_code_with_buffer_provider, + try_new_compact_code_unstable, + Self + ] + ); + icu_provider::gen_buffer_data_constructors!( (prefs: CurrencyFormatterPreferences, currency_code: &CurrencyCode) -> error: DataError, functions: [ @@ -60,6 +70,16 @@ impl CurrencyFormatter { ] ); + icu_provider::gen_buffer_data_constructors!( + (prefs: CurrencyFormatterPreferences, currency_code: &CurrencyCode) -> error: DataError, + functions: [ + try_new_compact_long_code: skip, + try_new_compact_long_code_with_buffer_provider, + try_new_compact_long_code_unstable, + Self + ] + ); + icu_provider::gen_buffer_data_constructors!( (prefs: CurrencyFormatterPreferences, currency_code: &CurrencyCode) -> error: DataError, functions: [ @@ -106,6 +126,23 @@ impl CurrencyFormatter { ) } + /// Creates a new [`CurrencyFormatter`] for compact short number formatting using the 3-letter ISO currency code from compiled locale data. + /// + /// ✨ *Enabled with the `compiled_data` Cargo feature.* + /// + /// [📚 Help choosing a constructor](icu_provider::constructors) + #[cfg(feature = "compiled_data")] + pub fn try_new_compact_code( + prefs: CurrencyFormatterPreferences, + currency_code: &CurrencyCode, + ) -> Result { + Self::try_new_code_internal( + CompactDecimalFormatter::try_new_short((&prefs).into(), Default::default())?, + prefs, + *currency_code, + ) + } + /// Creates a new [`CurrencyFormatter`] for compact short number formatting with full currency display names from compiled locale data. /// /// ✨ *Enabled with the `compiled_data` Cargo feature.* @@ -159,6 +196,23 @@ impl CurrencyFormatter { ) } + /// Creates a new [`CurrencyFormatter`] for compact long number formatting using the 3-letter ISO currency code from compiled locale data. + /// + /// ✨ *Enabled with the `compiled_data` Cargo feature.* + /// + /// [📚 Help choosing a constructor](icu_provider::constructors) + #[cfg(feature = "compiled_data")] + pub fn try_new_compact_long_code( + prefs: CurrencyFormatterPreferences, + currency_code: &CurrencyCode, + ) -> Result { + Self::try_new_code_internal( + CompactDecimalFormatter::try_new_long((&prefs).into(), Default::default())?, + prefs, + *currency_code, + ) + } + /// Creates a new [`CurrencyFormatter`] for compact long number formatting with full currency display names from compiled locale data. /// /// ✨ *Enabled with the `compiled_data` Cargo feature.* @@ -341,4 +395,56 @@ impl CurrencyFormatter { *currency_code, ) } + + #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_new_compact_code)] + pub fn try_new_compact_code_unstable( + provider: &D, + prefs: CurrencyFormatterPreferences, + currency_code: &CurrencyCode, + ) -> Result + where + D: ?Sized + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider, + { + Self::try_new_code_internal_unstable( + provider, + CompactDecimalFormatter::try_new_short_unstable( + provider, + (&prefs).into(), + Default::default(), + )?, + prefs, + *currency_code, + ) + } + + #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_new_compact_long_code)] + pub fn try_new_compact_long_code_unstable( + provider: &D, + prefs: CurrencyFormatterPreferences, + currency_code: &CurrencyCode, + ) -> Result + where + D: ?Sized + + DataProvider + + DataProvider + + DataProvider + + DataProvider + + DataProvider, + { + Self::try_new_code_internal_unstable( + provider, + CompactDecimalFormatter::try_new_long_unstable( + provider, + (&prefs).into(), + Default::default(), + )?, + prefs, + *currency_code, + ) + } } diff --git a/components/experimental/src/dimension/currency/format.rs b/components/experimental/src/dimension/currency/format.rs index 0dc65833ac7..4534b2afea8 100644 --- a/components/experimental/src/dimension/currency/format.rs +++ b/components/experimental/src/dimension/currency/format.rs @@ -262,4 +262,36 @@ mod tests { CurrencyFormatter::try_new_symbol_narrow(prefs, ¤cy_code).unwrap(); assert_writeable_eq!(fmt_symbol_narrow.format_fixed_decimal(&value), "$12,345.67"); } + + #[test] + pub fn test_code() { + let prefs_en = locale!("en-US").into(); + let currency_usd = CurrencyCode(tinystr!(3, "USD")); + let value = "12345.67".parse().unwrap(); + + let fmt_code_en = CurrencyFormatter::try_new_code(prefs_en, ¤cy_usd).unwrap(); + assert_writeable_eq!( + fmt_code_en.format_fixed_decimal(&value), + "USD\u{a0}12,345.67" + ); + + let prefs_fr = locale!("fr-FR").into(); + let currency_eur = CurrencyCode(tinystr!(3, "EUR")); + let fmt_code_fr = CurrencyFormatter::try_new_code(prefs_fr, ¤cy_eur).unwrap(); + assert_writeable_eq!( + fmt_code_fr.format_fixed_decimal(&value), + "12\u{202f}345,67\u{a0}EUR" + ); + } + + #[test] + pub fn test_name_fallback_to_iso_name() { + let prefs_en = locale!("en-US").into(); + // Unknown currency code should gracefully fall back to IsoName instead of DataError(IdentifierNotFound) + let currency_xyz = CurrencyCode(tinystr!(3, "XYZ")); + let value = "12345.67".parse().unwrap(); + + let fmt_name = CurrencyFormatter::try_new_name(prefs_en, ¤cy_xyz).unwrap(); + assert_writeable_eq!(fmt_name.format_fixed_decimal(&value), "12,345.67 XYZ"); + } } diff --git a/components/experimental/src/dimension/currency/formatter.rs b/components/experimental/src/dimension/currency/formatter.rs index 312ebafeb6c..9c27425f8b5 100644 --- a/components/experimental/src/dimension/currency/formatter.rs +++ b/components/experimental/src/dimension/currency/formatter.rs @@ -49,6 +49,10 @@ pub(crate) enum CurrencyFormatterData { essential: DataPayload, currency: CurrencyCode, }, + IsoName { + patterns: DataPayload, + currency: CurrencyCode, + }, Symbol { essential: DataPayload, symbol: DataPayload, @@ -163,6 +167,57 @@ impl CurrencyFormatter { }) } + #[cfg(feature = "compiled_data")] + pub(crate) fn try_new_code_internal( + value_formatter: V, + prefs: CurrencyFormatterPreferences, + currency: CurrencyCode, + ) -> Result { + let locale = CurrencyEssentialsV1::make_locale(prefs.locale_preferences); + let decimal_prefs = DecimalFormatterPreferences::from(&prefs); + + let req_id = decimal_prefs.nu_id(&locale); + let default_id = DataIdentifierBorrowed::for_locale(&locale); + let ids = req_id.into_iter().chain(core::iter::once(default_id)); + let essential = + load_with_fallback::(&crate::provider::Baked, ids.clone())? + .payload; + + Ok(Self { + value_formatter, + currency_data: CurrencyFormatterData::Iso { + essential, + currency, + }, + }) + } + + pub(crate) fn try_new_code_internal_unstable( + provider: &D, + value_formatter: V, + prefs: CurrencyFormatterPreferences, + currency: CurrencyCode, + ) -> Result + where + D: ?Sized + DataProvider, + { + let locale = CurrencyEssentialsV1::make_locale(prefs.locale_preferences); + let decimal_prefs = DecimalFormatterPreferences::from(&prefs); + + let req_id = decimal_prefs.nu_id(&locale); + let default_id = DataIdentifierBorrowed::for_locale(&locale); + let ids = req_id.into_iter().chain(core::iter::once(default_id)); + let essential = load_with_fallback::(provider, ids.clone())?.payload; + + Ok(Self { + value_formatter, + currency_data: CurrencyFormatterData::Iso { + essential, + currency, + }, + }) + } + #[cfg(feature = "compiled_data")] pub(crate) fn try_new_name_internal( value_formatter: V, @@ -176,27 +231,34 @@ impl CurrencyFormatter { .into_error() .with_debug_context("failed to get data marker attribute from a `CurrencyCode`") })?; - let extended = crate::provider::Baked + let extended_opt = crate::provider::Baked .load(DataRequest { id: DataIdentifierBorrowed::for_marker_attributes_and_locale( marker_attributes, &locale, ), ..Default::default() - })? - .payload; + }) + .allow_identifier_not_found()? + .map(|res| res.payload); let patterns = crate::provider::Baked.load(Default::default())?.payload; - let plural_rules = PluralRules::try_new_cardinal((&prefs).into())?; + let currency_data = match extended_opt { + Some(extended) => { + let plural_rules = PluralRules::try_new_cardinal((&prefs).into())?; + CurrencyFormatterData::Name { + extended, + patterns, + plural_rules, + } + } + None => CurrencyFormatterData::IsoName { patterns, currency }, + }; Ok(Self { value_formatter, - currency_data: CurrencyFormatterData::Name { - extended, - patterns, - plural_rules, - }, + currency_data, }) } @@ -219,27 +281,35 @@ impl CurrencyFormatter { .into_error() .with_debug_context("failed to get data marker attribute from a `CurrencyCode`") })?; - let extended = provider + let extended_opt = provider .load(DataRequest { id: DataIdentifierBorrowed::for_marker_attributes_and_locale( marker_attributes, &locale, ), ..Default::default() - })? - .payload; + }) + .allow_identifier_not_found()? + .map(|res| res.payload); let patterns = provider.load(Default::default())?.payload; - let plural_rules = PluralRules::try_new_cardinal_unstable(provider, (&prefs).into())?; + let currency_data = match extended_opt { + Some(extended) => { + let plural_rules = + PluralRules::try_new_cardinal_unstable(provider, (&prefs).into())?; + CurrencyFormatterData::Name { + extended, + patterns, + plural_rules, + } + } + None => CurrencyFormatterData::IsoName { patterns, currency }, + }; Ok(Self { value_formatter, - currency_data: CurrencyFormatterData::Name { - extended, - patterns, - plural_rules, - }, + currency_data, }) } } @@ -350,6 +420,68 @@ impl CurrencyFormatter { ) } + icu_provider::gen_buffer_data_constructors!( + (prefs: CurrencyFormatterPreferences, currency_code: &CurrencyCode) -> error: DataError, + functions: [ + try_new_code: skip, + try_new_code_with_buffer_provider, + try_new_code_unstable, + Self + ] + ); + + /// Creates a new [`CurrencyFormatter`] for formatting using the 3-letter ISO currency code from compiled locale data. + /// + /// ✨ *Enabled with the `compiled_data` Cargo feature.* + /// + /// [📚 Help choosing a constructor](icu_provider::constructors) + /// + /// # Examples + /// ``` + /// use icu::experimental::dimension::currency::formatter::CurrencyFormatter; + /// use icu::experimental::dimension::currency::CurrencyCode; + /// use icu::locale::locale; + /// use tinystr::*; + /// use writeable::assert_writeable_eq; + /// + /// let currency_preferences = locale!("en-US").into(); + /// let currency_code = CurrencyCode(tinystr!(3, "USD")); + /// let fmt = CurrencyFormatter::try_new_code(currency_preferences, ¤cy_code).unwrap(); + /// let value = "12345.67".parse().unwrap(); + /// assert_writeable_eq!(fmt.format_fixed_decimal(&value), "USD\u{a0}12,345.67"); + /// ``` + #[cfg(feature = "compiled_data")] + pub fn try_new_code( + prefs: CurrencyFormatterPreferences, + currency_code: &CurrencyCode, + ) -> Result { + Self::try_new_code_internal( + DecimalFormatter::try_new((&prefs).into(), Default::default())?, + prefs, + *currency_code, + ) + } + + #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_new_code)] + pub fn try_new_code_unstable( + provider: &D, + prefs: CurrencyFormatterPreferences, + currency_code: &CurrencyCode, + ) -> Result + where + D: ?Sized + + DataProvider + + DataProvider + + DataProvider, + { + Self::try_new_code_internal_unstable( + provider, + DecimalFormatter::try_new_unstable(provider, (&prefs).into(), Default::default())?, + prefs, + *currency_code, + ) + } + icu_provider::gen_buffer_data_constructors!( (prefs: CurrencyFormatterPreferences, currency_code: &CurrencyCode) -> error: DataError, functions: [ @@ -479,6 +611,11 @@ impl CurrencyFormatter { let pattern = essential.get().get_positive(true, true); (pattern, currency.0.as_str()) } + CurrencyFormatterData::IsoName { patterns, currency } => { + let currency_str = currency.0.as_str(); + let pattern = patterns.get().elements.get_default().1; + (pattern, currency_str) + } CurrencyFormatterData::Symbol { essential, symbol } => { let symbol = symbol.get(); let pattern = essential