.netformatcurrencycultureinfo

Format Currency for given culture and currency ISO code


I want to format a number with a given culture and a given currency ISO Code. I was not aware that besides the position and number format also the currency symbol changes based on culture.

That's what I want:

123456.79m

"en-US" and "USD" -> "$123,456.79"
"en-US" and "EUR" -> "€123,456.79"
"en-US" and "HUF" -> "HUF 123,456.79"
 
"de-DE" and "USD" -> "123.456,79 $"
"de-DE" and "EUR" -> "123.456,79 €"
"de-DE" and "HUF" -> "123.456,79 HUF"
 
"hu-HU" and "USD" -> "123 456,79 USD"
"hu-HU" and "EUR" -> "123 456,79 EUR"
"hu-HU" and "HUF" -> "123 456,79 Ft"

In JS there is an equivalent of exactly what I'd like to have in dotnet - format a number by culture and currency ISO code:

new Intl.NumberFormat('de-DE', {
  style: 'currency',
  currency: 'EUR'
}).format(123456.79);

Solution

  • To combine the number formatting from one culture with the currency symbol from another, you can create your own custom NumberFormatInfo that takes the NumberFormat of the specified culture and replaces its CurrencySymbol with one corresponding to the specified ISO currency code. Then pass that as the IFormatProvider parameter to Decimal.ToString("C", IFormatProvider provider) or double.ToString("C", IFormatProvider provider).

    First create the following extension methods, adapting this answer by spender to 3 Digit currency code to currency symbol to find a NumberFormatInfo for a given ISO currency code:

    public static partial class TextExtensions
    {
        public static bool TryGetNumberFormatByISOCurrencySymbol(string ISOCurrencySymbol, [NotNullWhen(returnValue: true)] out NumberFormatInfo? numberFormat)
        {
            // Adapted from the answer https://stackoverflow.com/a/12374378 by spender
            // To https://stackoverflow.com/questions/12373800/3-digit-currency-code-to-currency-symbol/
            // To return a NumberFormatInfo
            var query = 
                from c in CultureInfo.GetCultures(CultureTypes.AllCultures & ~(CultureTypes.NeutralCultures))
                let r = GetRegionInfo(c)
                where r != null && r.ISOCurrencySymbol == ISOCurrencySymbol
                select c.NumberFormat;
            numberFormat = query.FirstOrDefault();
            return numberFormat != null;
        }
    
        public static bool TryGetNumberFormatByCultureNameAndISOCurrencySymbol(string cultureName, string ISOCurrencySymbol, [NotNullWhen(returnValue: true)] out NumberFormatInfo? numberFormat)
        {
            if (!TryGetNumberFormatByISOCurrencySymbol(ISOCurrencySymbol, out var currencyNumberFormat))
            {
                numberFormat = null;
                return false;
            }
            
            numberFormat = new CultureInfo(cultureName).NumberFormat;
            // Copy over whatever currency properties you want.
            numberFormat.CurrencySymbol = currencyNumberFormat.CurrencySymbol;
            return true;
        }
    
        public static string Format(this decimal value, string cultureName, string ISOCurrencySymbol)
        {
            if (!TextExtensions.TryGetNumberFormatByCultureNameAndISOCurrencySymbol(cultureName, ISOCurrencySymbol, out var numberFormat))
                throw new ArgumentException($"Unknown culture {ISOCurrencySymbol} and currency {ISOCurrencySymbol}");
            return value.ToString("C", numberFormat);
        }
    
        static RegionInfo? GetRegionInfo(CultureInfo culture)
        {
            try {
                return new RegionInfo(culture.Name);
            }
            catch
            {
                return null;
            }
        }
    }
    

    Now, given some decimal value, string cultureName and string ISOCurrencySymbol, you can do:

    var formattedValue = value.Format(cultureName, ISOCurrencySymbol);
    

    Given the cultures and currencies, this outputs:

    "en-US" and "USD" -> "$123,456.79"
    "en-US" and "EUR" -> "€123,456.79"
    "en-US" and "HUF" -> "Ft123,456.79"
    "de-DE" and "USD" -> "123.456,79 $"
    "de-DE" and "EUR" -> "123.456,79 €"
    "de-DE" and "HUF" -> "123.456,79 Ft"
    "hu-HU" and "USD" -> "123 456,79 $"
    "hu-HU" and "EUR" -> "123 456,79 €"
    "hu-HU" and "HUF" -> "123 456,79 Ft"
    

    Notes:

    Demo fiddle here.