9

I have a table of charges with the amount, and the currency code (USD, JPY, CAD, EUR etc.), and am looking for the easiest way to properly format the currency. Using my local culture code (USA) and taking my decimal.ToString("c") gets me $0.00 output, but I'd like the correct currency sign and number of decimals based on the code.

Do any libraries exist for this? I can of course write up a switch statement and custom rules for each one, but thought this must have been done before.

Update:

I've modified Jon B's sample code as follows

static IDictionary<string, string> GetCurrencyFormatStrings()
{
    var result = new Dictionary<string, string>();

    foreach (var ci in CultureInfo.GetCultures(CultureTypes.AllCultures))
    {
        try
        {
            var ri = new RegionInfo(ci.Name);
            result[ri.ISOCurrencySymbol] =
                  string.Format("{0}#,#0.{1};({0}#,#0.{1})",
                                ri.CurrencySymbol,
                                new string('0', 
                                i.NumberFormat.CurrencyDecimalDigits));
        }
        catch { }
    }

    return result;
}

This allows me to simply go Amount.ToString(Currency["JPY"]), and the format will output the comma separator in my local context, but put the correct currency symbol and decimal places in automatically.

Let me know if anyone has a cleaner way of doing this, or I will mark Jon's answer as correct shortly.

Superman
  • 3,686
  • 6
  • 34
  • 46
  • @AakashM - Actually it does exist. Its called `CultureInfo`. – Security Hound Nov 13 '12 at 17:02
  • Retracting my previous comments, I note that [`RegionInfo` knows the currency code for a region's currency](http://msdn.microsoft.com/en-us/library/system.globalization.regioninfo.isocurrencysymbol.aspx), so the Framework does have some knowledge of these. I suspect you're going to have to iterate though, as I don't see any currency-code-based lookup. – AakashM Nov 13 '12 at 17:11
  • @Ramhound `CultureInfo` tells you how to format a currency-ish quantity in a given culture. It doesn't tell you that the symbol for JPY is ¥. – AakashM Nov 13 '12 at 17:14
  • 1
    Look here ? [Previous question from currency ISO codes][1] [1]: http://stackoverflow.com/questions/2763128/get-the-currency-from-current-culture – Black Light Nov 13 '12 at 17:34

3 Answers3

30
public static class DecimalExtension
{
    private static readonly Dictionary<string, CultureInfo> ISOCurrenciesToACultureMap =
        CultureInfo.GetCultures(CultureTypes.SpecificCultures)
            .Select(c => new {c, new RegionInfo(c.LCID).ISOCurrencySymbol})
            .GroupBy(x => x.ISOCurrencySymbol)
            .ToDictionary(g => g.Key, g => g.First().c, StringComparer.OrdinalIgnoreCase);

    public static string FormatCurrency(this decimal amount, string currencyCode)
    {
        CultureInfo culture;
        if (ISOCurrenciesToACultureMap.TryGetValue(currencyCode, out culture))
            return string.Format(culture, "{0:C}", amount);
        return amount.ToString("0.00");
    }
}

Here are the results of calling FormatCurrency for a few different currency codes:

decimal amount = 100;

amount.FormatCurrency("AUD");  //$100.00
amount.FormatCurrency("GBP");  //£100.00
amount.FormatCurrency("EUR");  //100,00 €
amount.FormatCurrency("VND");  //100,00 ?
amount.FormatCurrency("IRN");  //? 100.00
weston
  • 54,145
  • 21
  • 145
  • 203
jignesh
  • 1,639
  • 1
  • 16
  • 18
  • 4
    Great snippet, but it's better to use `c.Name` instead of `c.LCID` because you can get machine-specific [CultureNotFoundExceptions](https://msdn.microsoft.com/en-us/library/system.globalization.culturenotfoundexception(v=vs.110).aspx) – Chad Hedgcock Feb 22 '17 at 18:10
  • Thanks very much @ChadHedgcock , you made my day! – Chandler Dec 12 '18 at 21:10
  • This such a corner case, but when you need it it is amazingly useful. Thank you so much jignesh and @ChadHedgcock. – Zodman Apr 17 '20 at 03:18
5

Should be done by passing the CultureInfo:

CultureInfo ci = new CultureInfo("en-GB");
amount.ToString("C", ci);
Francis P
  • 13,377
  • 3
  • 27
  • 51
  • 4
    +0. I don't think it will give @Superman result that looks reasonable - number formatting portion of the output will not match if 2 currency values are using different decimal separator (i.e. USD and EUR would be something like $1.23 and "Eur"1,23 - an one of the numbers will look like 123 to the reader depending on reader's locale). – Alexei Levenkov Nov 13 '12 at 17:32
2

You could build a dictionary to go from ISO currency symbol (USD) to currency symbol ($):

static void Main(string[] args)
{
    var symbols = GetCurrencySymbols();

    Console.WriteLine("{0}{1:0.00}", symbols["USD"], 1.5M);
    Console.WriteLine("{0}{1:0.00}", symbols["JPY"], 1.5M);

    Console.ReadLine();
}

static IDictionary<string, string> GetCurrencySymbols()
{
    var result = new Dictionary<string, string>();

    foreach (var ci in CultureInfo.GetCultures(CultureTypes.AllCultures))
    {
        try
        {
            var ri = new RegionInfo(ci.Name);
            result[ri.ISOCurrencySymbol] = ri.CurrencySymbol;                    
        }
        catch { }
    }

    return result;
}

That's the basic idea, you'll need to tweak that to suit your needs.

Note that you can certainly use the CultureInfo class to convert to a string for a specific culture, but as noted in Alexei's comment, that will cause each string to be a little different (like 1.00 vs 1,00). Whichever you use depends on your needs.

Jon B
  • 51,025
  • 31
  • 133
  • 161
  • 1
    This looks pretty close - Not all currencies have 2 decimal places (the smallest increment in yen is 1), is this in the region info as well? – Superman Nov 13 '12 at 19:04
  • 1
    Looks like referring to ci.NumberFormat.CurrencyDecimalDigits gets me everything I need - thanks! – Superman Nov 13 '12 at 19:09
  • 1
    You're going to have problems with countries like India, which has 14 languages, with multiple currency symbols (depending on language), and multiple currency formats (again, dependent upon language). Further, @Superman probably doesn't want to use the currency symbol for India's `RegionInfo` (`रु`) for the Indian rupee; I suspect his customers/users would prefer to see the currency symbol for the specific culture "en-IN" (`Rs.`) rather than something they will most likely read as '_squiggle_'. Doing this stuff well is hard and requires a **lot** of cultural knowledge. – Nicholas Carey Nov 13 '12 at 19:44