Welcome to Neudesic Blogs Sign in | Join | Help

Formatting currency according to the 3-character ISO 4217 code

.NET framework provides us with correct currency formatting based on the CultureInfo.NumberFormat settings. There are two ways to change the display of the currency values:

1. Set CurrentCulture on the Thread:

   Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");

This method is used if you need to change currency formatting for all objects on the page. Don't confuse CurrentCulture with CurrentUICulture. CurrentUICulture is set to the culture of Windows installation, i.e. the language of the OS.

2. Use ToString() method with FormatProvider

   amount.ToString("C", new CultureInfo("en-US").NumberFormat);

NumberFormat member implements IFormatProvider and has properties that define the look of the currency in this particular culture. This method can be used when you need to display amounts in different currency on the same page, e.g. in a multi-currency grid.

The problem

One difficulty that is being discovered by many people is that in order to format currency according to a particular culture, one must know the .NET-centric culture name (en-US) or its numerical LCID to instantiate the CultureInfo object. In reality, currency is often identified by a 3-letter code (ISO 4217) such as USD, GBP, JPY, EUR etc. Suppose you have a table with Amount and Currency columns, such as:

-------------
1234       USD
456342    JPY
7894       USD
-------------

You need to display these values in the grid and use the correct currency formatting for each value. Unforunately the .NET framework provides no mechanism to map the 3-letter code to a culture identifier. When looking for a solution I have seen many variations with lookup tables, where the 3-letter code would map to the culture LCID. Other people resorted to writing their own formatters, which was even less appealing. There had to be a better way!

The solution

There is another class in .NET globalization namespace called RegionInfo. This class has the ISOCurrencySymbol property, which is the 3-letter ISO 4217 code we are after. And it is instantiated the same way as the CultureInfo object via culture name or culture ID. If we can find the RegionInfo via the currency ISO code, we can find the corresponding culture ID and create the CultureInfo object for currency formatting.

There is no method that will return a RegionInfo from a currency ISO code, so we need to enumerate all RegionInfo's in the system to find the right one. Unfortunately there is no easy way to get all RegionInfo's. But there is a way to get all cultures via the CultureInfo.GetCultures() static method. All we need to do now is create the RegionInfo object for each culture and check its ISOCurrencySymbol until we find the one we want. A quick and dirty solution looks like this:

protected CultureInfo CultureInfoFromCurrencyISO( string isoCode )
{
   
CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);

   foreach (CultureInfo ci in cultures)
   {
      
RegionInfo ri = new RegionInfo(ci.LCID);
      
if (ri.ISOCurrencySymbol == isoCode)
      {
   
      return ci;
      }
   }
   return null;
}

Note that we are interested only in specific cultures (CultureTypes.SpecificCultures parameter). Neutral cultures cannot produce the RegionInfo. An example of a neutral culture is "en", whereas specific cultures are "en-US" and "en-GB". Currency is defined for a specific culture such as USD for en-US and GBP for en-GB.

This method will return a CultureInfo from a currency ISO code, which can be used for currency formatting like this:

amount.ToString("C", CultureInfoFromCurrencyISO(currency).NumberFormat);

It will work, but you don't want to use it in any kind of serious production code. It will create up to 130 RegionInfo objects on each call and if you are using it in a long grid (as intended) it will tax the garbage collector significantly.

Improved solution

In order to improve the quick solution we will simply use the method described above to populate a lookup dictionary once and use it for all subsequent calls. Implementation is attached. It is a singleton class CurrencyHelper with the following static methods:

CultureInfo CultureInfoFromCurrencyISO(string isoCode)
string FormatCurrency(decimal amount, string currencyISO)

First method will return the CultureInfo from an ISO code and the second method will format the decimal amount according to the currency ISO code passed as a second parameter.

The benefit of this solution are:

1. No lookup tables and subsequent maintenance
2. No custom formatting code
3. Pure .NET implementation ensures it will work in future versions
4. Fast lookup via a generic Dictionary class

 

Published Tuesday, January 09, 2007 11:21 PM by Michael Morozov
Attachment(s): currency.zip

Comments

No Comments

Anonymous comments are disabled