Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use crate::dimension::provider::currency::{essentials::*, extended::*, patterns::*};
use crate::dimension::provider::currency::{essentials::*, extended::*, patterns::*, symbols::*};
use icu_decimal::CompactDecimalFormatter;
use icu_provider::prelude::*;

Expand Down Expand Up @@ -75,6 +75,7 @@ impl CurrencyFormatter<CompactDecimalFormatter> {
where
D: ?Sized
+ DataProvider<CurrencyEssentialsV1>
+ DataProvider<CurrencySymbolsV1>
+ DataProvider<icu_decimal::provider::DecimalCompactShortV1>
+ DataProvider<icu_decimal::provider::DecimalSymbolsV1>
+ DataProvider<icu_decimal::provider::DecimalDigitsV1>
Expand Down Expand Up @@ -102,6 +103,7 @@ impl CurrencyFormatter<CompactDecimalFormatter> {
where
D: ?Sized
+ DataProvider<CurrencyEssentialsV1>
+ DataProvider<CurrencySymbolsV1>
+ DataProvider<icu_decimal::provider::DecimalCompactShortV1>
+ DataProvider<icu_decimal::provider::DecimalSymbolsV1>
+ DataProvider<icu_decimal::provider::DecimalDigitsV1>
Expand Down
21 changes: 21 additions & 0 deletions components/experimental/src/dimension/currency/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,25 @@ mod tests {
let fmt_narrow = CurrencyFormatter::try_new_narrow(prefs, &currency_code).unwrap();
assert_writeable_eq!(fmt_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, &currency_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, &currency_eur).unwrap();
assert_writeable_eq!(
fmt_code_fr.format_fixed_decimal(&value),
"12\u{202f}345,67\u{a0}EUR"
);
}
}
153 changes: 148 additions & 5 deletions components/experimental/src/dimension/currency/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use writeable::Writeable;

use super::super::provider::currency::{
essentials::CurrencyEssentialsV1, extended::CurrencyExtendedDataV1,
patterns::CurrencyPatternsDataV1,
patterns::CurrencyPatternsDataV1, symbols::CurrencySymbolsV1,
};
use super::CurrencyCode;
use super::options::Width;
Expand Down Expand Up @@ -48,9 +48,14 @@ prefs_convert!(
pub(crate) enum CurrencyFormatterData {
Essential {
essential: DataPayload<CurrencyEssentialsV1>,
symbols: DataPayload<CurrencySymbolsV1>,
width: Width,
currency: CurrencyCode,
},
Code {
essential: DataPayload<CurrencyEssentialsV1>,
currency: CurrencyCode,
},
Long {
extended: DataPayload<CurrencyExtendedDataV1>,
patterns: DataPayload<CurrencyPatternsDataV1>,
Expand Down Expand Up @@ -86,7 +91,10 @@ impl<V: AbstractFormatter> CurrencyFormatter<V> {
let default_id = DataIdentifierBorrowed::for_locale(&locale);
let ids = req_id.into_iter().chain(core::iter::once(default_id));
let essential =
load_with_fallback::<CurrencyEssentialsV1>(&crate::provider::Baked, ids)?.payload;
load_with_fallback::<CurrencyEssentialsV1>(&crate::provider::Baked, ids.clone())?
.payload;
let symbols =
load_with_fallback::<CurrencySymbolsV1>(&crate::provider::Baked, ids)?.payload;

if essential.get().standard_pattern().is_none() {
return Err(DataError::custom("missing standard pattern"));
Expand All @@ -96,6 +104,7 @@ impl<V: AbstractFormatter> CurrencyFormatter<V> {
value_formatter,
currency_data: CurrencyFormatterData::Essential {
essential,
symbols,
width,
currency,
},
Expand All @@ -110,15 +119,16 @@ impl<V: AbstractFormatter> CurrencyFormatter<V> {
width: Width,
) -> Result<Self, DataError>
where
D: ?Sized + DataProvider<CurrencyEssentialsV1>,
D: ?Sized + DataProvider<CurrencyEssentialsV1> + DataProvider<CurrencySymbolsV1>,
{
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::<CurrencyEssentialsV1>(provider, ids)?.payload;
let essential = load_with_fallback::<CurrencyEssentialsV1>(provider, ids.clone())?.payload;
let symbols = load_with_fallback::<CurrencySymbolsV1>(provider, ids)?.payload;

if essential.get().standard_pattern().is_none() {
return Err(DataError::custom("missing standard pattern"));
Expand All @@ -128,12 +138,81 @@ impl<V: AbstractFormatter> CurrencyFormatter<V> {
value_formatter,
currency_data: CurrencyFormatterData::Essential {
essential,
symbols,
width,
currency,
},
})
}

#[cfg(feature = "compiled_data")]
pub(crate) fn try_new_code_essential(
value_formatter: V,
prefs: CurrencyFormatterPreferences,
currency: CurrencyCode,
) -> Result<Self, DataError> {
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::<CurrencyEssentialsV1>(&crate::provider::Baked, ids)?.payload;

if essential
.get()
.standard_alpha_next_to_number_pattern()
.is_none()
&& essential.get().standard_pattern().is_none()
{
return Err(DataError::custom("missing standard pattern"));
}

Ok(Self {
value_formatter,
currency_data: CurrencyFormatterData::Code {
essential,
currency,
},
})
}

pub(crate) fn try_new_code_essential_unstable<D>(
provider: &D,
value_formatter: V,
prefs: CurrencyFormatterPreferences,
currency: CurrencyCode,
) -> Result<Self, DataError>
where
D: ?Sized + DataProvider<CurrencyEssentialsV1>,
{
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::<CurrencyEssentialsV1>(provider, ids)?.payload;

if essential
.get()
.standard_alpha_next_to_number_pattern()
.is_none()
&& essential.get().standard_pattern().is_none()
{
return Err(DataError::custom("missing standard pattern"));
}

Ok(Self {
value_formatter,
currency_data: CurrencyFormatterData::Code {
essential,
currency,
},
})
}

#[cfg(feature = "compiled_data")]
pub(crate) fn try_new_long_internal(
value_formatter: V,
Expand Down Expand Up @@ -236,6 +315,16 @@ impl CurrencyFormatter<DecimalFormatter> {
]
);

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
]
);

// We manually implement the compiled constructors because of the cross-crate dependency
// on `icu_decimal` markers (which are not present in `icu_experimental`'s local `Baked` provider).
// TODO: When CurrencyFormatter is migrated out of experimental, check if we can use the
Expand Down Expand Up @@ -286,6 +375,7 @@ impl CurrencyFormatter<DecimalFormatter> {
where
D: ?Sized
+ DataProvider<CurrencyEssentialsV1>
+ DataProvider<CurrencySymbolsV1>
+ DataProvider<icu_decimal::provider::DecimalSymbolsV1>
+ DataProvider<icu_decimal::provider::DecimalDigitsV1>,
{
Expand All @@ -307,6 +397,7 @@ impl CurrencyFormatter<DecimalFormatter> {
where
D: ?Sized
+ DataProvider<CurrencyEssentialsV1>
+ DataProvider<CurrencySymbolsV1>
+ DataProvider<icu_decimal::provider::DecimalSymbolsV1>
+ DataProvider<icu_decimal::provider::DecimalDigitsV1>,
{
Expand All @@ -319,6 +410,43 @@ impl CurrencyFormatter<DecimalFormatter> {
)
}

/// Creates a new [`CurrencyFormatter`] for formatting using the currency ISO 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_code(
prefs: CurrencyFormatterPreferences,
currency_code: &CurrencyCode,
) -> Result<Self, DataError> {
Self::try_new_code_essential(
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<D>(
provider: &D,
prefs: CurrencyFormatterPreferences,
currency_code: &CurrencyCode,
) -> Result<Self, DataError>
where
D: ?Sized
+ DataProvider<CurrencyEssentialsV1>
+ DataProvider<icu_decimal::provider::DecimalSymbolsV1>
+ DataProvider<icu_decimal::provider::DecimalDigitsV1>,
{
Self::try_new_code_essential_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: [
Expand Down Expand Up @@ -443,16 +571,31 @@ impl<V: AbstractFormatter> CurrencyFormatter<V> {
let (pattern, currency_str) = match &self.currency_data {
CurrencyFormatterData::Essential {
essential,
symbols,
width,
currency,
} => {
// TODO(#6064): Support plural-specific patterns and full currency formatting spec.
let (currency_str, pattern, _pattern_selection) =
essential.get().name_and_pattern(*width, currency);
symbols
.get()
.name_and_pattern(essential.get(), *width, currency);

let pattern = pattern.unwrap_or_else(|| <&DoublePlaceholderPattern>::default());
(pattern, currency_str)
}
CurrencyFormatterData::Code {
essential,
currency,
} => {
let currency_str = currency.0.as_str();
let pattern = essential
.get()
.standard_alpha_next_to_number_pattern()
.or_else(|| essential.get().standard_pattern())
.unwrap_or_else(|| <&DoublePlaceholderPattern>::default());
(pattern, currency_str)
}
CurrencyFormatterData::Long {
extended,
patterns,
Expand Down
Loading
Loading