Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Switching CurrentCulture in ASP.NET Pages Gotchas


:P
On this page:

A while back I posted about changing the CurrentCulture of a Web Request on a fly to display culture related data (number and date formats) in their culture specific views. Without re-hashing everything discussed there the core of what needs to happen is that the Threads culture can be easily set based on the browser’s Culture returned to the server. You can do this in Global.asax and Begin_Request() or if you want a little more control over when this happens as part of a custom subclass of the ASP.NET Page class.

 

Here’s how you read the main installed language and assign the related culture onto the current Web request thread (for a more complete implementation see the above link):

 

string Lang =  Request.UserLanguages[0];

 

System.Threading.Thread.CurrentThread.CurrentCulture = 
                        new System.Globalization.CultureInfo(Lang) ;

 

You’ll need to check for errors and on an error continue using the default locale.

 

So this is really nothing new. However, I ran into some interesting problems with this over the last week or so as I installed a new Credit Card Processing provider (Authorize.Net) on my server.

 

The problem is that if you switch the thread’s culture you will indiscriminately affect all output of ToString() and format related string conversions. In most cases this exactly what we want so when you display a Date and numeric information like this, which is an invoice from the West Wind Web Store display for the French Canadian Locale:

 

 

The problem comes in if you need to do things in the default locale. For example, when I communicate with my Credit Card provider I need to POST data to the server using text string. Translating the Order amount of 310.96 yields 310,96 when sent under the above Culture. My previous provider AccessPoint was smart enough to figure out that value, but Authorize.NET turned this value into $310,960.00.  No wonder the transaction failed. Whew…

 

This problem is easily fixed by overriding ToString() with an appropriate Locale override which can be accomplished like this:

 

this.Http.AddPostKey("x_amount",this.OrderAmount.ToString(

System.Globalization.CultureInfo.InstalledUICulture.NumberFormat) );

InstalledUICulture returns the currently active system culture which on my server is EN-US. This overrides the temporary FR-CA locale that has been set by the ASP.NET thread.  For Dates you’d use the DateFormat property.

 

This is easy enough to do the trick here is to remember to do this consistently and remember when this is required. Of course if you call into a third party component you may not have this sort of freedom. This can bite you in a number of places.

 

Another place where I ended up with problems was with my configuration class. This class reads and writes values from the web.config file into class properties so I have access to all the configuration settings in a strongly typed class. Well, this class reads values that were written most likely in the default EN-US locale (mine). If the app starts in a different Locale any value inside in config file will not convert back to a numeric or date value correctly as Decimal.Parse() for example will choke if there’s a comma where a decimal point should be.

 

This is a bug that had been showing up in my Error logs for a while and I could never figure out why these were happening and so inconsistently. This bug crept up when IIS had released my IIS 6 Application Pool when the app has been idle for more than 30 minutes (at night usually) and somebody from a foreign country would hit the site first. The app would run under the foreign locale due to the locale switching and BOOM! It fails to load the configuration data because of the parsing.

 

For this specific scenario the fix was easy enough to perform by moving the thread switching after the first access to the Config class so that the Config class would always load under the default Locale. Still, there’s potential for problems here too: If I test with a different locale and save my configuration settings they’ll get written out with the foreign locale settings. Now when the Config class tries to load the data it will fail because the saved data is in a foreign locale and can’t parse to the US locale.

 

This is obviously an involved problem that has much lower level roots. Specifically my configuration class should probably always write out data in a culture neutral way (or using the same culture everytime) and read them back in in the same way. If you’re building components for consumption by other developers this is an important, but easily forgotten point.

 

The point of this is post is that if you’re switching locale on the fly be sure you really understand what it’s affecting and test your application’s carefully for this sort of switch. Make this switch optional so it can be easily turned off if necessary as this can break a lot of code in subtle and not so subtle ways that can be very difficult to debug.

 

It might be a good idea to turn this concept of switching the culture on its head. Instead of switching culture just apply the correct culture where it matters control by control. This is definitely more work, but at least you won't screw with an existing application.


The Voices of Reason


 

Michael Giagnocavo
August 13, 2004

# re: Switching CurrentCulture in ASP.NET Pages Gotchas

Actually, the way I prefer is to use CultureInfo.Invariant culture for any "internal" code that needs to be later read by the computer. Using the installed culture only works if it's the same at print and parse time.

In fact, a week ago I fixed a problem where I forgot to use InvariantCulture (go figure, out of the all the times, I forget once, and that breaks something :S) -- reading the VS Solution file format on French Windows failed (since regardless of the UI, the fileformat is the same).

Rick Strahl
August 13, 2004

# re: Switching CurrentCulture in ASP.NET Pages Gotchas

Michael, I agree 100% and that's part of why I posted this. It's easy to forget this though and sure enough that's bitten me today a fair time AFTER the code had been written. When it bites it often looks like a completely different problem.

Rick Strahl's WebLog
October 19, 2004

# Converting values to string and back with Type Converters

Type Converters can make conversion of values/objects to strings and back a lot easier. More importantly though it allows you to create customer serialization mechanisms for storing data in things like a config file.

Scott McMaster
October 20, 2004

# re: Switching CurrentCulture in ASP.NET Pages Gotchas

If you run FxCop, the Globalization rules "CultureInfo should be passed" and "IFormatProvider should be passed" will warn you about these problems if you forget.

Rob Meyer
September 12, 2006

# re: Switching CurrentCulture in ASP.NET Pages Gotchas

This mechanism seems to terrify me...it's not only your code that always must specify culture on any locale sensitive parsing routines, but any third party libraries that you use in the thread. That's a lot of trust that it all does the right thing. It certainly expands your testing requirements, since you must validate all locales you support. I would -certainly- only allow locales that I know the application has been verified with.

Definitely would not want to send 35,00 instead of 35.00 to a remote system that might interpert 35,00 as 3500...


Rick Strahl's Web Log
October 24, 2006

# Detecting and setting the Locale on the Current ASP.Net Web Request - Rick Strahl's Web Log

Here's a useful tip on how to switch the Locale in your Web Applications to provide proper number and date formatting for users from different countries. Detect the Locale and then switch the current request to this Locale to provide a more customized user experience.

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024