One developer's supported scenario is another's awful bolt-on job...

by Michael S. Kaplan, published on 2011/04/01 07:01 -04:00, original URI:

The other day one of those questions that tends to catch my eye kind of caught my eye:

I've been trying to find a way of displaying currencies that is independent of the language set at the time.
The way described normally (ToString("C") or Format("{0:C}") doesn't work for us.

Scenario: we're serving web pages that carry price information.
Say the backend tells us a price is 25 dollars.
In en-US, we would display $25.
But if we changed the browser's language to Japanese, we'd see 25 JPY. Not acceptable.

The numeric price should come together with the ISO code for its currency.
When displaying the price, we should get the numeric format as specified in the NumberFormatInfo, but the currency symbol that corresponds to the data's ISO code.

I haven't been able to find infrastructure for this, so I'm writing my own, but this involves cloning NumberFormatInfo's, and building a Dictionary to get the currency symbol from the ISO code.

Is there currency display infrastructure that is language-independent?

Sometimes a scenario is one that functionality was simply not designed for.

When that happens, you are on your own a little bit, though of course sometimes you can weave the support together from the pieces that were implemented.

Different like the way One product's (or person's) intended design is another product's (or person's) bug. And vice versa talks about a case that is different. But different from that, in a different way.

In this case, there is no way to look up currency information by currency symbol.

But let's see what we do have:

  1. You can enumerate every RegionInfo by using this constructor and CultureInfo.GetCultures;
  2. Each RegionInfo has both a CurrencySymbol and an ISOCurrencySymbol -- you can index by ISOCurrencySymbol and ignore ones already indexed (hash tables can be great for that sort of thing but use whatever datas structure you like);
  3. You can use built-in methods to create a new [not read only] culture based on the browser language or perhaps settings chosen some other way, and then you can change the currency symbol to match.

Now this system lives up to our "spec" -- it is designed to combine a user's preferences with a different currency symbol.

But it is not perfect, since the spec doesn't take some things into account -- like the fact that the per locale preferences tend to be based on the currencies preferred by that locale. thus (for example) ja-JP (Japanese) will use the Yen (JPY) and have no currency decimal places -- an attribute that most other locales do not share.

Which means the expected format for a given locale doesn't always give the information you would need for every sigle currency type!

Now native code makes a partial effort to support this, via GetLocaleInfoEx's LOCALE_IINTLCURRDIGITS, though this is far from perfect and to be honest if it were me I'd try to build something slightly more sophisticated using the curency decimal places of both the user's settings and of the settings of the currency. You know, to find the happy medium.

Or maybe I'd always use the number of currency decimal places of the currency, while leaving grouping and currency decimal/grouping separators to the user preferences. That feels lik it would be a bit more homey for me.

Though it would require one to get some more data between steps 1 and 2 from a CultureInfo.NumberFormat that corresponds to each RegionInfo.

Perhaps the fact that System.Globalization was not designed with this not entirely uncommon ASP.NET/Internet commerce scenario in mind is becoming more painfully obvious as we progress? :-)

If I were in charge of ASP.Net, I'd probably be fighting for a better slice of the data here, one more suited to me than the current classes are.

Maybe someone thinking about this scenario will notice and think of ways to do all that. Because most of the pieces are there, if they can only be sliced up the right way....

Jan Kučera on 16 Apr 2011 4:43 AM:

What is the issue in using something like this?

NumberFormatInfo nfi = Thread.CurrentThread.CurrentCulture.NumberFormat;

return price.ToString("C", nfi).Replace(nfi.CurrencySymbol, " " + currency + " ").Trim();

Michael S. Kaplan on 16 Apr 2011 6:48 AM:

Well, the above gives some of the problems - like number of decimal digits. Plus figuring out where "currency" comes from....

go to newer or older post, or back to index or month or day