Skip to content

Commit

Permalink
Merge pull request #190 from sabify/currency
Browse files Browse the repository at this point in the history
feat: add `currency` formatter
  • Loading branch information
Baptistemontan authored Feb 12, 2025
2 parents 7f56909 + eddf40b commit cf350c5
Show file tree
Hide file tree
Showing 16 changed files with 404 additions and 19 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ icu_calendar = { version = "1.5", default-features = false }
icu_list = { version = "1.5", default-features = false }
icu_decimal = { version = "1.5", default-features = false }
icu_locid_transform = { version = "1.5", default-features = false }
icu_experimental = { version = "0.1.0", default_features = false }
tinystr = "0.7.6"

# internal use
tests_common = { path = "./tests/common", version = "0.1.0" }
4 changes: 4 additions & 0 deletions docs/book/src/06_features.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,7 @@ Allow the use of the `list` formatter.
#### `format_nums`

Allow the use of the `number` formatter.

#### `format_currency`

Allow the use of the `currency` formatter.
36 changes: 36 additions & 0 deletions docs/book/src/declare/08_formatters.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,42 @@ let num = move || 100_000;
t!(i18n, number_formatter, num);
```

## Currency (experimental)

```json
{
"currency_formatter": "{{ num, currency }}"
}
```

Will format the currency based on the locale.
The variable should be the same as [number](#number).

Enable the "format_currency" feature to use the number formatter.

### Arguments

There are two arguments at the moment for the currency formatter: `width` and `country_code`, which are based on [`icu_experimental::dimension::currency::options::Width`](https://docs.rs/icu_experimental/0.1.0/icu_experimental/dimension/currency/options/enum.Width.html) and [`icu_experimental::dimension::currency::formatter::CountryCode`](https://docs.rs/icu_experimental/0.1.0/icu_experimental/dimension/currency/formatter/struct.CurrencyCode.html).

`width` values:

- short (default)
- narrow

`country_code` value should be a [currency code](https://www.iban.com/currency-codes), such as USD or EUR. The USD is the default value.

### Example

```rust,ignore
use crate::i18n::*;
let i18n = use_i18n();
let num = move || 100_000;
t!(i18n, currency_formatter, num);
```

## Date

```json
Expand Down
19 changes: 18 additions & 1 deletion leptos_i18n/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ icu_list = { workspace = true, optional = true }
icu_decimal = { workspace = true, optional = true }
typed-builder = "0.20"
fixed_decimal = { workspace = true, optional = true, features = ["ryu"] }
icu_experimental = { workspace = true, optional = true, features = ["ryu"] }
writeable = "0.5"
serde = "1.0"
async-once-cell = { version = "0.5.3", optional = true }
Expand All @@ -36,6 +37,7 @@ serde-wasm-bindgen = { version = "0.6.5", optional = true }
futures = { version = "0.3.30", optional = true }
default-struct-builder = "0.5"
wasm-bindgen = "0.2.96"
tinystr = { workspace = true, optional = true }

[features]
default = ["cookie", "json_files", "icu_compiled_data"]
Expand All @@ -46,6 +48,7 @@ icu_compiled_data = [
"icu_calendar?/compiled_data",
"icu_list?/compiled_data",
"icu_decimal?/compiled_data",
"icu_experimental?/compiled_data",
"leptos_i18n_macro/icu_compiled_data",
]
plurals = ["dep:icu_plurals", "dep:icu_provider", "leptos_i18n_macro/plurals"]
Expand All @@ -66,6 +69,13 @@ format_nums = [
"dep:icu_provider",
"leptos_i18n_macro/format_nums",
]
format_currency = [
"format_nums",
"dep:tinystr",
"dep:icu_experimental",
"dep:icu_provider",
"leptos_i18n_macro/format_currency",
]
actix = ["ssr", "leptos-use/actix"]
axum = ["ssr", "leptos-use/axum"]
hydrate = [
Expand Down Expand Up @@ -102,7 +112,13 @@ track_locale_files = ["leptos_i18n_macro/track_locale_files"]

[package.metadata."docs.rs"]
# Features needed for the doctests
features = ["plurals", "format_datetime", "format_list", "format_nums"]
features = [
"plurals",
"format_datetime",
"format_list",
"format_nums",
"format_currency",
]


[package.metadata.cargo-all-features]
Expand Down Expand Up @@ -163,4 +179,5 @@ always_include_features = [
"format_datetime",
"format_list",
"format_nums",
"format_currency",
]
9 changes: 7 additions & 2 deletions leptos_i18n/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,18 @@ pub mod __private {

/// This module contain utilities to create custom ICU providers.
pub mod custom_provider {
pub use crate::macro_helpers::formatting::data_provider::IcuDataProvider;
pub use crate::macro_helpers::formatting::inner::set_icu_data_provider;
pub use crate::macro_helpers::formatting::{
data_provider::IcuDataProvider, inner::set_icu_data_provider,
};
pub use leptos_i18n_macro::IcuDataProvider;
}

/// Reexports of backend libraries, mostly about formatting.
pub mod reexports {
#[cfg(feature = "format_nums")]
pub use fixed_decimal;
#[cfg(feature = "format_currency")]
pub use tinystr::tinystr;

/// module containing reexports of crates from the icu project
pub mod icu {
Expand All @@ -189,6 +192,8 @@ pub mod reexports {
pub use icu_datetime as datetime;
#[cfg(feature = "format_nums")]
pub use icu_decimal as decimal;
#[cfg(feature = "format_currency")]
pub use icu_experimental::dimension::currency;
#[cfg(feature = "format_list")]
pub use icu_list as list;
#[cfg(feature = "plurals")]
Expand Down
91 changes: 91 additions & 0 deletions leptos_i18n/src/macro_helpers/formatting/currency.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use super::{IntoFixedDecimal, NumberFormatterInputFn};
use crate::Locale;
use core::fmt::{self, Display};
use icu_experimental::dimension::currency::{
formatter::CurrencyCode, options::Width as CurrencyWidth,
};
use leptos::IntoView;

use serde::{Deserialize, Serialize};
use writeable::Writeable;

// TODO: this struct should be removed in version ICU4x v2
// Reference: https://docs.rs/icu_experimental/0.1.0/icu_experimental/dimension/currency/options/enum.Width.html
// Issue: https://github.com/unicode-org/icu4x/pull/6100
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, Serialize, Deserialize)]
#[non_exhaustive]
#[doc(hidden)]
pub enum Width {
#[serde(rename = "short")]
Short,

#[serde(rename = "narrow")]
Narrow,
}

impl Default for Width {
fn default() -> Self {
Self::Short
}
}

impl From<CurrencyWidth> for Width {
fn from(value: CurrencyWidth) -> Self {
match value {
CurrencyWidth::Short => Self::Short,
CurrencyWidth::Narrow => Self::Narrow,
_ => unimplemented!(),
}
}
}

#[doc(hidden)]
pub fn format_currency_to_view<L: Locale>(
locale: L,
number: impl NumberFormatterInputFn,
width: CurrencyWidth,
currency_code: CurrencyCode,
) -> impl IntoView + Clone {
let currency_formatter = super::get_currency_formatter(locale, width);

move || {
let fixed_dec = number.to_fixed_decimal();
let currency = currency_formatter.format_fixed_decimal(&fixed_dec, currency_code);
let mut formatted_currency = String::new();
currency.write_to(&mut formatted_currency).unwrap();
formatted_currency
}
}

#[doc(hidden)]
pub fn format_currency_to_formatter<L: Locale>(
f: &mut fmt::Formatter<'_>,
locale: L,
number: impl IntoFixedDecimal,
width: CurrencyWidth,
currency_code: CurrencyCode,
) -> fmt::Result {
let currency_formatter = super::get_currency_formatter(locale, width);
let fixed_dec = number.to_fixed_decimal();
let formatted_currency = currency_formatter.format_fixed_decimal(&fixed_dec, currency_code);
formatted_currency.write_to(f)
}

/// This function is a lie.
/// The only reason it exist is for the `format` macros.
/// It does NOT return a `impl Display` struct with no allocation like the other
/// This directly return a `String` of the formatted num, because borrow issues.
#[doc(hidden)]
pub fn format_currency_to_display<L: Locale>(
locale: L,
number: impl IntoFixedDecimal,
width: CurrencyWidth,
currency_code: CurrencyCode,
) -> impl Display {
let currency_formatter = super::get_currency_formatter(locale, width);
let fixed_dec = number.to_fixed_decimal();
let currency = currency_formatter.format_fixed_decimal(&fixed_dec, currency_code);
let mut formatted_currency = String::new();
currency.write_to(&mut formatted_currency).unwrap();
formatted_currency
}
Loading

0 comments on commit cf350c5

Please sign in to comment.