Ex_cldr.number: currency with a default symbol for the particular locale

I’m struggling to get a ‘default currency symbol’ for the actual locale used. Perhaps it is wrong to assume that there is such a thing for a certain region/country?

Using the :currency format just gives the number but without a currency symbol:

Cldr.Number.to_string 1234.31, format: :currency, locale: "de"
{:ok, "1.234,31"}

Explicitly specifying the currency gives the desired result:

Cldr.Number.to_string 1345, currency: :EUR, locale: "de"
{:ok, "1.345,00 €"}

Can :EUR somehow be derived from the given locale?

My Cldr config is as follows:

defmodule Backend.Cldr do
  use Cldr,
    locales: ["de", "en"],
    default_locale: "en",
    json_library: Jason,
    gettext: Backend.Gettext,
    data_dir: "./priv/cldr",
    otp_app: :backend,
    precompile_number_formats: ["¤¤#.##0,##"],
    providers: [Cldr.Number, Cldr.Unit, Cldr.List, Cldr.Calendar, Cldr.DateTime],
    generate_docs: true
end

I don’t think so. Currencies are more related to territories/countries than they are to language and even that’s not a 1:1 mapping.

Yes, in previous versions it was derived from the locale. However with CLDR 42 a new format was added to format currencies without the currency symbol. As a result to use the :currency format with a currency symbol, you can do the following:

iex> Cldr.Number.to_string 1345, currency: Cldr.Currency.currency_from_locale("de"), locale: "de"
{:ok, "1.345,00 €"}

# Or
iex> Cldr.put_locale("de")
{:ok, #Cldr.LanguageTag<de [validated]>}
iex> Cldr.Number.to_string 1345, currency: Cldr.Currency.currency_from_locale(Cldr.get_locale())
{:ok, "1.345,00 €"}

# There is a lot that a language tag can describe so you could even have the "de"
# locale but require :AUD as the currency
iex> Cldr.Number.to_string 1345, currency: Cldr.Currency.currency_from_locale("de-u-cu-aud"), locale: "de"
{:ok, "1.345,00 AU$"}

I will work on a documentation update. Here is the relevant changelog entry:

Behavior change

  • In prior releases formatting a number as format: :currency would derive the currency code from the locale if no :currency was provided. This is no longer the case. As of CLDR42 a format :currency_no_symbol is defined to allow formatting of the number without an associated symbol. Now when format: :currency is passed without a :currency option, the format is changed to format: :currency_no_symbol. To retain the existing behaviour, pass currency: Cldr.Currency.currency_from_locale(locale) as an option.

Thank you both very much for the quick confirmation/help. :+1:

Deriving the currency symbol from the used language did not look very ‘clean’ to me either. Probably it’s better to set this explicitly or by region/country.

Still making up my mind.

My preference is typically to derive everything possible from the language tag since that expresses the users preferences. But of course not everyone agrees with that. Part of the rational is that for a given language (like “de”) a territory can always be derived. Typically it is the territory that has the highest population of speakers of that language.

# What is the derived territory for "de"?
iex> {:ok, locale} = Cldr.validate_locale("de")
{:ok, #Cldr.LanguageTag<de [validated]>}
iex> locale.territory
:DE
# And each territory has one (or none) current currency:
iex> Cldr.Currency.current_currency_for_territory locale.territory
:EUR

So in that respect I think its quite “clean” and consistent.

The functions that can help most commonly are:

iex> Cldr.Currency.current_currency_for_territory "de"
:EUR
iex> Cldr.Currency.currency_from_locale "de-AT"
:EUR
2 Likes

Thank you @kip for your further and detailed explanation.
That convinced me to derive from locale (for now)