Thanks for the kind words, and thanks for the very complete understanding of what you are trying to do.
As you know, each locale specifies currency formatting meant to be appropriate to that language and culture. The underlying data doesnāt attempt to provide a global canonical format for anything. So while CLDR data is an amazing resource, it doesnāt satisfy all requirements.
TLDR;
The nearest approximation I think is possible directly is to use the :currency_sumbol
option to Money.to_string/2
. This option is actually passed to Cldr.Number.to_string/3
and is documented there. I will make sure it is also documented in ex_money
. For example:
iex> Money.to_string Money.new(:USD, 100), currency_symbol: :iso, locale: "en"
{:ok, "USD 100.00"}
iex> Money.to_string Money.new(:USD, 100), currency_symbol: :iso, locale: "de"
{:ok, "100,00 USD"}
iex> Money.to_string Money.new(:USD, 100), currency_symbol: :iso, locale: "fr"
{:ok, "100,00 USD"}
This outputs a consistent currency indicator using the ISO 4217 code.
Longer version
In fact you can pass any string you like to the :currency_symbol
option but this definitely requires care. Hereās an example using galleons:
iex> Money.to_string Money.new(:USD, 100), currency_symbol: "Galleons", locale: "it"
{:ok, "100,00 Galleons"}
You can identify what format data drives the money - and any other CDLR number - formatting by introspection:
iex> MyApp.Cldr.Number.Format.formats_for "en", :native
{:ok,
%Cldr.Number.Format{
accounting: "Ā¤#,##0.00;(Ā¤#,##0.00)",
currency: "Ā¤#,##0.00",
currency_long: %{one: [0, " ", 1], other: [0, " ", 1]},
currency_short: [
[1000, %{one: ["Ā¤0K", 1], other: ["Ā¤0K", 1]}],
[10000, %{one: ["Ā¤00K", 2], other: ["Ā¤00K", 2]}],
[100000, %{one: ["Ā¤000K", 3], other: ["Ā¤000K", 3]}],
[1000000, %{one: ["Ā¤0M", 1], other: ["Ā¤0M", 1]}],
[10000000, %{one: ["Ā¤00M", 2], other: ["Ā¤00M", 2]}],
[100000000, %{one: ["Ā¤000M", 3], other: ["Ā¤000M", 3]}],
[1000000000, %{one: ["Ā¤0B", 1], other: ["Ā¤0B", 1]}],
[10000000000, %{one: ["Ā¤00B", 2], other: ["Ā¤00B", 2]}],
[100000000000, %{one: ["Ā¤000B", 3], other: ["Ā¤000B", 3]}],
[1000000000000, %{one: ["Ā¤0T", 1], other: ["Ā¤0T", 1]}],
[10000000000000, %{one: ["Ā¤00T", 2], other: ["Ā¤00T", 2]}],
[100000000000000, %{one: ["Ā¤000T", 3], other: ["Ā¤000T", 3]}]
],
currency_spacing: %{
after_currency: %{
currency_match: "[[:^S:]&[:^Z:]]",
insert_between: " ",
surrounding_match: "[[:digit:]]"
},
before_currency: %{
currency_match: "[[:^S:]&[:^Z:]]",
insert_between: " ",
surrounding_match: "[[:digit:]]"
}
},
decimal_long: [
[1000, %{one: ["0 thousand", 1], other: ["0 thousand", 1]}],
[10000, %{one: ["00 thousand", 2], other: ["00 thousand", 2]}],
[100000, %{one: ["000 thousand", 3], other: ["000 thousand", 3]}],
[1000000, %{one: ["0 million", 1], other: ["0 million", 1]}],
[10000000, %{one: ["00 million", 2], other: ["00 million", 2]}],
[100000000, %{one: ["000 million", 3], other: ["000 million", 3]}],
[1000000000, %{one: ["0 billion", 1], other: ["0 billion", 1]}],
[10000000000, %{one: ["00 billion", 2], other: ["00 billion", 2]}],
[100000000000, %{one: ["000 billion", 3], other: ["000 billion", 3]}],
[1000000000000, %{one: ["0 trillion", 1], other: ["0 trillion", 1]}],
[10000000000000, %{one: ["00 trillion", 2], other: ["00 trillion", 2]}],
[100000000000000, %{one: ["000 trillion", 3], other: ["000 trillion", 3]}]
],
decimal_short: [
[1000, %{one: ["0K", 1], other: ["0K", 1]}],
[10000, %{one: ["00K", 2], other: ["00K", 2]}],
[100000, %{one: ["000K", 3], other: ["000K", 3]}],
[1000000, %{one: ["0M", 1], other: ["0M", 1]}],
[10000000, %{one: ["00M", 2], other: ["00M", 2]}],
[100000000, %{one: ["000M", 3], other: ["000M", 3]}],
[1000000000, %{one: ["0B", 1], other: ["0B", 1]}],
[10000000000, %{one: ["00B", 2], other: ["00B", 2]}],
[100000000000, %{one: ["000B", 3], other: ["000B", 3]}],
[1000000000000, %{one: ["0T", 1], other: ["0T", 1]}],
[10000000000000, %{one: ["00T", 2], other: ["00T", 2]}],
[100000000000000, %{one: ["000T", 3], other: ["000T", 3]}]
],
other: %{
approximately: ["~", 0],
at_least: [0, "+"],
at_most: ["ā¤", 0],
range: [0, "ā", 1]
},
percent: "#,##0%",
scientific: "#E0",
standard: "#,##0.###"
}}
And use the returned data to make a decision about what format you might want use by introspecting the currency data:
iex> {:ok, currency} = MyApp.Cldr.Currency.currency_for_code :USD, locale: "fr"
{:ok, #Cldr.Currency<"USD">}
iex> currency.
__struct__ alt_code cash_digits cash_rounding
code count digits from
iso_digits name narrow_symbol rounding
symbol tender to
iex> currency.symbol
"$US"
iex> currency.narrow_symbol
"$"
Singapore dollar symbols
There is a bug in the CLDR data for the Singapore dollar that should be fixed for CLDR 41 that is currently in āpre alphaā which will probably be release in April. ex_cldr
will follow suit on the same day of CLDR release.
Hope this helps, happy to hear any other thoughts, ideas or suggestions!