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.
Other Posts you might also like