Currency Localization Issue

G

Guest

OK, I have a database table, it has prices of products in it, like so:

ProductPrice MONEY
ProductIsoCurrencyCode CHAR(3)

Now, both CultureInfo and RegionInfo have currency-handling functions inside
them, but I want to display the price on my web site given the information
above, plus the current culture of the web site visitor.

So, I have
- Value (e.g. 23834.23)
- IsoCurrencyCode (e.g. EUR)
- CultureCode (en-GB)

How do I combine the 3 above to get the correct formatting?

I used to store a "CurrencyFormat" string in the database, until I realised
that a lot of the Euro countries format the Euro differently and I was
displaying it quite incorrectly for several of them.

For instance:
- Ireland: €12.34
- France and Spain: 12,34€ (also 12€34 in France)
- Germany and Italy: €12,34

Thanks,
The Boris Yeltsin
 
M

Morten Wennevik

Hi Boris,

decimal d = 23834.23m;
string money = d.ToString(c, CultureInfo.CreateSpecificCulture("en-GB").NumberFormat);

Replace en-GB with culture of your choice.
 
M

Mihai N.

Hi Boris,

decimal d = 23834.23m;
string money = d.ToString(c, CultureInfo.CreateSpecificCulture("en- GB").NumberFormat);

Replace en-GB with culture of your choice.

Please ignore this advice. A currency is not a number + some symbol.
Even in US, -23834.23 is "-23,834.23" as a number but "($23,834.23)" as
currency (note minus vs brackets)

For Win32 API, see here: http://www.mihai-nita.net/20050709a.shtml
I know this is not what you need, I am looking for the right thing in .NET,
just wanted to stop you from going ahead that route :)
 
S

Steven Cheng[MSFT]

Hi Boris,

As Morten has mentioned, in .net framework, it has provide built-in
currency formatting interfaces and we can use the "ToString()" method to
get formatted number or datetime together with a given CultureInfo(actually
the NumberformatInfo attached on the CultureInfo). And you do not need to
store formatting info like CurrencySymbol, number format yourself in custom
storage. The following code will output the decimal value as the formatted
value according to the differen't cultureInfo(language/region) info.

==============================
protected void Page_Load(object sender, EventArgs e)
{

decimal num = 12.34M;

Response.Write("<br/>Auto Detected--" +
Thread.CurrentThread.CurrentCulture + ": " + num.ToString("C"));


CultureInfo ci = new CultureInfo("fr-FR");

Response.Write("<br/>French/fr-FR: " + num.ToString("C", ci));

ci = new CultureInfo("de-DE");

Response.Write("<br/>German/de-DE: " + num.ToString("C", ci));

ci = new CultureInfo("es-ES");

Response.Write("<br/>Spanish/es-ES: " + num.ToString("C", ci));


ci = new CultureInfo("en-IE");

Response.Write("<br/>Ireland/en-IE: " + num.ToString("C", ci));
}
=====================

Also, are you developing the web application through ASP.NET 1.1 or ASP.NET
2.0? In ASP.NET 2.0, we can use the following setting to let the page
request automatically set the current worker thread's "CurrentCulture"
according to the client user's Userlanguage setting(in browser). This can
also be configured in web.config file for application wide.

<%@ Page ................... Culture="Auto" %>

If you're using ASP. NET 1.1, you'll need to manually create the specific
cultureInfo object and attache to the current workerthread in BeginRequest
event , like:
=============
protected void Application_BeginRequest(object sender, EventArgs e)
{
System.Threading.Thread.CurrentThread.CurrentCulture =
System.Globalization.CultureInfo.CreateSpecificCulture(Request.UserLanguages
[0]);

}
===============

here is the reference about currency formatting on the microsoft global
development site:

#Globalization: currency Formatting in .net
http://www.microsoft.com/globaldev/getWR/steps/wrg_crncy.mspx#ETE

Hope this helps.

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead

This posting is provided "AS IS" with no warranties, and confers no rights.
 
M

Mihai N.

decimal d = 23834.23m;
Please ignore this advice. A currency is not a number + some symbol.
Even in US, -23834.23 is "-23,834.23" as a number but "($23,834.23)" as
currency (note minus vs brackets)

For Win32 API, see here: http://www.mihai-nita.net/20050709a.shtml
I know this is not what you need, I am looking for the right thing in .NET,
just wanted to stop you from going ahead that route :)

Ok, here is the update:
NumberFormatInfo nf = CultureCode.NumberFormat;
nf.CurrencySymbol = IsoCurrencyCode;
Value.ToString("C", nf));

The problem is that I cannot find anything to map from 3 char ISO currency to
localized currency name (same problem as in Win32 NLS API, see my article
ref. above)
 
S

Steven Cheng[MSFT]

Hello Mihai,

If you have had a look at the article on windows globaldev portal I
mentioned in my last message, you'll find that the ISOCurrencySymbol is
contained in the RegionInfo class. e.g

===========
CultureInfo ci = new CultureInfo("fr-FR");
RegionInfo ri = new RegionInfo("FR");
ci.NumberFormat.CurrencySymbol = ri.ISOCurrencySymbol;

Response.Write("<br/>French/fr-FR: " + num.ToString("C", ci))
=============

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead

This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Hi Steven,

Many thanks for your swift reply.

This was a nice idea, but did you get that code snippet to work for you?

I find the CurrencySymbol property to be read-only...

Also, part of my confusion was caused by the naming - "CurrencySymbol". In
my mind this is what I would call the single character used to represent the
currency, e.g. "$":
http://www.xe.com/symbols.htm

Surely the property would be better known as "CurrencyCode", which is the
name that the ISO use:
http://www.iso.org/iso/en/prods-services/popstds/currencycodeslist.html

- Boris Yeltsin
 
S

Steven Cheng[MSFT]

Hello Boris,

Thanks for your reply. The following code has been tested on my side and
worked:

========================

CultureInfo ci = new CultureInfo("fr-FR");
RegionInfo ri = new RegionInfo("FR");
ci.NumberFormat.CurrencySymbol = ri.ISOCurrencySymbol;
Response.Write("<br/>French/fr-FR: " + num.ToString("C", ci))
=============

For a CultureInfo created on the fly , it's Numberformat property(
NumberFormatInfo type)'s "CurrencySymbol" can be modified.

Are you try directly changing the
CurrentThread.CurrentCulture.NumberFormat.CurrencySymbol when you get that
"read only .... " exception?

You need to reconstruct a new CultureInfo instance(modify its
CurrencySymbol) and assign it to the CurrentThread.CurrentCulture. Here is
the code that works:

=========================

decimal num = 12.34M;


CultureInfo dupci = CultureInfo.CurrentCulture.Clone() as
CultureInfo;
dupci.NumberFormat.CurrencySymbol =
RegionInfo.CurrentRegion.ISOCurrencySymbol;

Thread.CurrentThread.CurrentCulture = dupci;

Response.Write("<br/>Default Formatted: " + num.ToString("C"));

=============================

Hope this helps.

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

OK Steven - we're close now :)

The "read-only" issue was my fault - I apologise.

This:
ci.NumberFormat.CurrencySymbol = ri.ISOCurrencySymbol

Should actually be:
ci.NumberFormat.CurrencySymbol = ri.CurrencySymbol

The problem is the naming of the field "ISOCurrencySymbol" which should
actually be "ISOCurrencyCode".

Anyway, the last remaining problem to solve is that as I stated in my
original post, I only have the ISO Currency Code (e.g. "EUR", "GBP") and from
that I need to create a RegionInfo to get the CurrencySymbol property.

How do I do that? Do I just need to loop around all the cultures from
CultureInfo.GetCultures and feed each one into a RegionInfo until I find
where ri.ISOCurrencySymbol = myCurrencyCode ?

Thanks,
Boris
 
S

Steven Cheng[MSFT]

Hi Boris,

Thanks for your quick response. So far In the .net built-in interfaces, I
haven't found a directly method which will map a ISOCurrencyCode. The
currently API or methods can help use get a RegionInfo or CultureInfo
instance from the region name ('FR', 'US') or language/Region name
('en-US', 'fr-FR'), or LCID, and we can then query further regional info
such as NumberFormatInfo, DateFormatInfo from them. For your scenario, an
efficient means should be predefined a lookup table or a utility function
that help lookup the CultureInfo/RegionInfo collection, of course, we need
to cache the lookup info in a hashtable for performance.

Anyway, I'll help you confirm this question with some other Globalization
specialists. I'll update you soon.

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


This posting is provided "AS IS" with no warranties, and confers no rights.
 
M

Mihai N.

If you have had a look at the article on windows globaldev portal I
mentioned in my last message, you'll find that the ISOCurrencySymbol is
contained in the RegionInfo class. e.g

I did, but it does not help.

Because the currency symbol and the currency format are two different beasts.
Currency symbol is not about formatting. Same like measurements.
You don't just take a page "8 x 11 inches" and show it in
French as "8 x 11 cm".
You either say "20,32 x 27,04 cm" (doing the conversion) or "8 x 11 pouces"
(keeping the original unit, translated in French.

Back to currency, if you talk about "$12345.67" and display in French
context, the result you want is "12 345,67 dollar des États-Unis"
or "9 589.25 Euro" (with the problem that you should have access to the daily
rate and should probably also add also

So what is needed is a way to the full localized name of currency in language
X for the currency of locale Y (using the iso as "currency id").
That info is not available in .NET or Win32 NLS API.
===========
CultureInfo ci = new CultureInfo("fr-FR");
RegionInfo ri = new RegionInfo("US");
ci.NumberFormat.CurrencySymbol = ri.ISOCurrencySymbol;
Response.Write( num.ToString("C", ci) );
===========
gives "12 345,67 USD"
we would like something like
ci.NumberFormat.CurrencySymbol = ci.GetCurrencyName( ri.ISOCurrencySymbol );
giving "12 345,67 dollar des États-Unis"


Currency format is one of the week points in Windows globalization support
(and in fact on many platforms). And in fact on most systems/libraries.
From all I know, only CLDR/ICU gets it right for now.

You can also see the issue explained here:
http://www.mihai-nita.net/20050709a.shtml
 
S

Steven Cheng[MSFT]

Hi Boris,

After discussed with some other globalization engineers, we concluded that
we can not directly map a ISO 3166 currency code to a symbol char(a manual
lookup table or API). The is the ISO 3166 alphabetic codes are unique, but
the ”symbols?are not. It’s easy for the currencies that have a single
special Unicode currency symbol (€, $, ? and a few other).

Most of the ”symbols?for other currencies use abbreviations of the full
name in their native language. Then you get a dependency of the language
and the script—if the country has multiple official languages and/or
multiple scripts. E.g. the ”symbol?(i.e. the abbreviation) for the Serbian
currency can be written with both Latin (Din.) and Cyrillic (???.) scripts.
This is a one-to-many mapping, and then you can’t just call an API to get
the symbol for the code—without possible geopolitical issues. You need to
know the locale (country, language, script, etc.).

Therefore, if you want to supply symbol char information for each record in
your database table, you need to supply the full language-country
info(locale), e.g en-US, fr-FR .... so that we can use API to get the
currency symbol char(or currency code).


Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead



This posting is provided "AS IS" with no warranties, and confers no rights.
 
S

Steven Cheng[MSFT]

Hello Mihai,

Thanks for your reply.

I've posted a new reply to Boris and based on the discussion with some
other engineers, we think it's impossible to directly get currency char
from a given currency code(ISO 3166) since this is not a 1:1 mapping. For
Boris's scenario, I think we need to store the the complete locale
info(lang-Region) in each data record (or directly store the symbol char).
Do you have any other ideas?

Thanks again for your informative inputs.

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead

This posting is provided "AS IS" with no warranties, and confers no rights.
 
S

Steven Cheng

Hi Boris,

Just reposted this message since there're some special chars which is
corrupted due to wrong message encoding:
=========================================

After discussed with some other globalization engineers, we concluded that
we can not directly

map a ISO 3166 currency code to a symbol char(a manual lookup table or API).
The is the ISO 3166

alphabetic codes are unique, but the ¡±symbols¡± are not. It¡¯s easy for the
currencies that have a

single special Unicode currency symbol (€, $, ¡ê and a few other).

Most of the ¡±symbols¡± for other currencies use abbreviations of the full
name in their native

language. Then you get a dependency of the language and the script¡ªif the
country has multiple

official languages and/or multiple scripts. E.g. the ¡±symbol¡± (i.e. the
abbreviation) for the

Serbian currency can be written with both Latin (Din.) and Cyrillic
(§¥§Ú§ß.) scripts. This is a

one-to-many mapping, and then you can¡¯t just call an API to get the symbol
for the code¡ªwithout

possible geopolitical issues. You need to know the locale (country,
language, script, etc.).

Therefore, if you want to supply symbol char information for each record in
your database table,

you need to supply the full language-country info(locale), e.g en-US, fr-FR
..... so that we

can use API to get the currency symbol char(or currency code).
============================================


Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead



This posting is provided "AS IS" with no warranties, and confers no rights.
 
M

Mihai N.

Hi Steven,
I've posted a new reply to Boris and based on the discussion with some
other engineers, we think it's impossible to directly get currency char
from a given currency code(ISO 3166) since this is not a 1:1 mapping. For
Boris's scenario, I think we need to store the the complete locale
info(lang-Region) in each data record (or directly store the symbol char).
Do you have any other ideas?
I have read the answer and I agree there is no easy solution, and the full
locale info is needed. (this is why my code was ci.GetCurrencyName(
ISOCurrencySymbol ), whith ci a CutureInfo).

It is no 1:1 mapping, indeed. In fact, for X cultures and Y currencies
X*Y translations are needed (quite a few).

It might be possuble to "directly get currency char from a given currency
code(ISO 3166)" in the context of a culture.
Think day of week. There is a GetLocaleInfo( lcid, LOCALE_SDAYNAME1, ... );
Similar, we can have a GetCurrencyName( lcid, ISOCode, ... );

Since Vista is already movind towards string LCID:
GetCurrencyName( "en-US", _T("USD"), ... ) => "$"
GetCurrencyName( "en", _T("USD"), ... ) => "US Dollar"
GetCurrencyName( "en", _T("FRF"), ... ) => "French Franc"

GetCurrencyName( "de", _T("FRF"), ... ) => "Französischer Franc"
GetCurrencyName( "de", _T("USD"), ... ) => "US Dollar"
GetCurrencyName( "de", _T("RUB"), ... ) => "Russischer Rubel (neu)"

GetCurrencyName( "fr", _T("FRF"), ... ) => "franc français"
GetCurrencyName( "fr-FR", _T("FRF"), ... ) => "franc"
GetCurrencyName( "fr", _T("USD"), ... ) => "dollar des États-Unis"
GetCurrencyName( "fr", _T("RUB"), ... ) => "rouble"

I am still not sure what is the safe decision for the locale level.
Sometimes the language might be enoguh, sometimes the region might change
things. Is quite typical for then inhabitants of a region to exclude the
region name from the currency name (franc français/franc, US Dollar/$)
Once you are out of that region, better spec is needed ("franc" might be
Belgian franc, "$" might be Mexican Peso, or New Zealand Dollar).
A fallback mechanism might be a good idea.

I think CLDR/ICU might provide some inspiration here, is the best effort I
know in this direction.

Thanks for
Mihai
 
S

Steven Cheng[MSFT]

Thanks for your reply Mihai,

So it is apparent that we must have a given locale info to query a currency
code/symbol.

And as for the below
=====================
I am still not sure what is the safe decision for the locale level.
Sometimes the language might be enoguh, sometimes the region might change
things. Is quite typical for then inhabitants of a region to exclude the
region name from the currency name (franc français/franc, US Dollar/$)
Once you are out of that region, better spec is needed ("franc" might be
Belgian franc, "$" might be Mexican Peso, or New Zealand Dollar).
A fallback mechanism might be a good idea.
=====================

yes, I quite agree that it's hard to determine whether language or both
language/region would be necessary, I would rather think it the
applcation's developer who need to determine this requirement according to
the granularity of its applicaiton data.

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


This posting is provided "AS IS" with no warranties, and confers no rights.
 
C

Cor Ligthert [MVP]

Boris,

As soon as you have the CultureCode from the client, than it should be
simple, however, I am curious, how do you retrieve that CultureCode from
your client using a webpage?

Be aware that the used language does not tell much, you need for this the
complete culture.

Cor
 
M

Mihai N.

Hi Steven,
So it is apparent that we must have a given locale info to query a currency
code/symbol. ....
yes, I quite agree that it's hard to determine whether language or both
language/region would be necessary, I would rather think it the
applcation's developer who need to determine this requirement according to
the granularity of its applicaiton data.

Sounds like you are considering this seriously (an I am glad to see it :)
If you think I might be able to help a bit, please feel free to contact me
directly.
I check the email below daily.
Or you can use the contact form at http://www.mihai-nita.net
(directed to an email that I check every 10 minutes :)

Best regards,
Mihai
 
S

Steven Cheng[MSFT]

Hi Mihai,

Thanks a lot for your care.

I'll wait for Boris's further comments since the problem here is specific
to his application scenario. Anyway, I'll let you know if we need any
help. So far your information has been quite helpful :)

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top