Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Long Currency Formatter #5456

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use super::CurrencyCode;
use fixed_decimal::{CompactDecimal, FixedDecimal};

use icu_decimal::FixedDecimalFormatter;
use icu_plurals::PluralRules;
use writeable::Writeable;

use crate::compactdecimal::CompactDecimalFormatter;
use crate::dimension::provider::currency_patterns::CurrencyPatternsDataV1;
use crate::dimension::provider::extended_currency::CurrencyExtendedDataV1;

pub struct LongCompactFormattedCurrency<'l> {
pub(crate) decimal_value: &'l FixedDecimal,
pub(crate) compact_value: &'l CompactDecimal,
// TODO: use this if the display name is not exist and make the extended data optional.
pub(crate) _currency_code: CurrencyCode,
pub(crate) extended: &'l CurrencyExtendedDataV1<'l>,
pub(crate) patterns: &'l CurrencyPatternsDataV1<'l>,
pub(crate) fixed_decimal_formatter: &'l FixedDecimalFormatter,
pub(crate) compact_decimal_formatter: &'l CompactDecimalFormatter,
pub(crate) plural_rules: &'l PluralRules,
}

writeable::impl_display_with_writeable!(LongCompactFormattedCurrency<'_>);

impl Writeable for LongCompactFormattedCurrency<'_> {
fn write_to<W>(&self, sink: &mut W) -> core::result::Result<(), core::fmt::Error>
where
W: core::fmt::Write + ?Sized,
{
let decimal_operands = self.decimal_value.into();
// let compact_operands = self.compact_value.into();
let display_name = self
.extended
.display_names
.get(decimal_operands, self.plural_rules);
let pattern = self
.patterns
.patterns
.get(decimal_operands, self.plural_rules);
let formatted_value = self.fixed_decimal_formatter.format(self.decimal_value);
let interpolated = pattern.interpolate((formatted_value, display_name));
interpolated.write_to(sink)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

//! Experimental.

use core::str::FromStr;

use fixed_decimal::{CompactDecimal, FixedDecimal};
use icu_decimal::{options::FixedDecimalFormatterOptions, FixedDecimalFormatter};
use icu_plurals::PluralRules;
use icu_provider::prelude::*;

use crate::{
compactdecimal::CompactDecimalFormatter,
compactdecimal::CompactDecimalFormatterOptions,
dimension::provider::{
currency_patterns::CurrencyPatternsDataV1Marker,
extended_currency::CurrencyExtendedDataV1Marker,
},
};

use super::{long_compact_format::LongCompactFormattedCurrency, CurrencyCode};

extern crate alloc;

/// A formatter for monetary values.
///
/// [`LongCompactCurrencyFormatter`] supports:
/// 1. Rendering in the locale's currency system.
/// 2. Locale-sensitive grouping separator positions.
///
/// Read more about the options in the [`super::options`] module.
pub struct LongCompactCurrencyFormatter {
/// Extended data for the currency formatter.
extended: DataPayload<CurrencyExtendedDataV1Marker>,

/// Formatting patterns for each currency plural category.
patterns: DataPayload<CurrencyPatternsDataV1Marker>,

/// A [`FixedDecimalFormatter`] to format the currency value.
fixed_decimal_formatter: FixedDecimalFormatter,

/// A [`CompactDecimalFormatter`] to format the currency value.
compact_decimal_formatter: CompactDecimalFormatter,

/// A [`PluralRules`] to determine the plural category of the unit.
plural_rules: PluralRules,
}

impl LongCompactCurrencyFormatter {
icu_provider::gen_any_buffer_data_constructors!(
(locale: &DataLocale, currency_code: &CurrencyCode) -> error: DataError,
functions: [
try_new: skip,
try_new_with_any_provider,
try_new_with_buffer_provider,
try_new_unstable,
Self
]
);

/// Creates a new [`LongCompactCurrencyFormatter`] 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(locale: &DataLocale, currency_code: &CurrencyCode) -> Result<Self, DataError> {
let fixed_decimal_formatter =
FixedDecimalFormatter::try_new(locale, FixedDecimalFormatterOptions::default())?;

let compact_decimal_formatter = CompactDecimalFormatter::try_new_long(
locale,
CompactDecimalFormatterOptions::default(),
)?;

let marker_attributes = DataMarkerAttributes::try_from_str(currency_code.0.as_str())
.map_err(|_| {
DataErrorKind::IdentifierNotFound
.into_error()
.with_debug_context("failed to get data marker attribute from a `CurrencyCode`")
})?;

let extended = crate::provider::Baked
.load(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
marker_attributes,
locale,
),
..Default::default()
})?
.payload;

let patterns = crate::provider::Baked.load(Default::default())?.payload;

let plural_rules = PluralRules::try_new_cardinal(locale)?;

Ok(Self {
extended,
patterns,
fixed_decimal_formatter,
compact_decimal_formatter,
plural_rules,
})
}

#[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
pub fn try_new_unstable<D>(
provider: &D,
locale: &DataLocale,
currency_code: &CurrencyCode,
) -> Result<Self, DataError>
where
D: ?Sized
+ DataProvider<super::super::provider::extended_currency::CurrencyExtendedDataV1Marker>
+ DataProvider<super::super::provider::currency_patterns::CurrencyPatternsDataV1Marker>
+ DataProvider<icu_decimal::provider::DecimalSymbolsV1Marker>
+ DataProvider<icu_plurals::provider::CardinalV1Marker>,
{
let fixed_decimal_formatter = FixedDecimalFormatter::try_new_unstable(
provider,
locale,
FixedDecimalFormatterOptions::default(),
)?;

let marker_attributes = DataMarkerAttributes::try_from_str(currency_code.0.as_str())
.map_err(|_| {
DataErrorKind::IdentifierNotFound
.into_error()
.with_debug_context("failed to get data marker attribute from a `CurrencyCode`")
})?;
let extended = provider
.load(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
marker_attributes,
locale,
),
..Default::default()
})?
.payload;

let patterns = provider.load(Default::default())?.payload;

let plural_rules = PluralRules::try_new_cardinal_unstable(provider, locale)?;

Ok(Self {
extended,
patterns,
fixed_decimal_formatter,
plural_rules,
})
}

/// Formats in the long format a [`FixedDecimal`] value for the given currency code.
///
/// # Examples
/// ```
/// use icu::experimental::dimension::currency::long_formatter::LongCurrencyFormatter;
/// use icu::experimental::dimension::currency::CurrencyCode;
/// use icu::locale::locale;
/// use tinystr::*;
/// use writeable::Writeable;
///
/// let locale = locale!("en-US").into();
/// let currency_code = CurrencyCode(tinystr!(3, "USD"));
/// let fmt = LongCurrencyFormatter::try_new(&locale, &currency_code).unwrap();
/// let value = "12345.67".parse().unwrap();
/// let formatted_currency = fmt.format_fixed_decimal(&value, currency_code);
/// let mut sink = String::new();
/// formatted_currency.write_to(&mut sink).unwrap();
/// assert_eq!(sink.as_str(), "12,345.67 US dollars");
/// ```
pub fn format_fixed_decimal<'l>(
&'l self,
value: &'l FixedDecimal,
currency_code: CurrencyCode,
) -> LongCompactFormattedCurrency<'l> {
LongCompactFormattedCurrency {
decimal_value: value,
// TODO: fix this.
compact_value: CompactDecimal::from_str(value.to_string().as_str()).unwrap(),
_currency_code: currency_code,
extended: self.extended.get(),
patterns: self.patterns.get(),
fixed_decimal_formatter: &self.fixed_decimal_formatter,
compact_decimal_formatter: &self.compact_decimal_formatter,
plural_rules: &self.plural_rules,
}
}
}
2 changes: 2 additions & 0 deletions components/experimental/src/dimension/currency/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use tinystr::TinyAsciiStr;

pub mod format;
pub mod formatter;
pub mod long_compact_format;
pub mod long_compact_formatter;
pub mod long_format;
pub mod long_formatter;
pub mod options;
Expand Down
Loading