Aug 20 2008

What format do you want to use?

Category: Tips and TricksJoeGeeky @ 14:31

In this great big wide world of ours there are a lot of different ways to represent simple content such as Dates, Numbers, etc...  If your web applications are trying to reach international markets or you have an international team it makes sense to try and cater to the needs of your users. Fortunetally for us .NET has given us a lot of nice tools to simplify this for us.  Before we get into that, lets look at a couple very simple examples

The number 999999999:
Locale Format
England (en-GB) 999,999,999.00
France (fr-FR) 999 999 999,00
Denmark (de-DE) 999.999.999,00

The date 3 January 2006:
Locale Format
USA (en-US) 1/3/2006
France (fr-FR) 03/01/2006
Denmark (de-DE) 03.01.2006

Take a closer look at the dates above, as you will see, assumptions made by users from various locales can lead to very different meanings of what you are trying convey. In this case, some users will assume the date is 03 January 2006 while others will assume it is 01 March 2006.

There is a tendancy to use format specifiers or Culture objects such as follows:

CultureInfo culture = new CultureInfo("en-GB");
uint amount = 999999999;
amount.ToString("n", culture);
//Renders as 999,999,999.00

While this is useful for some scenarios it can lead to a load of variables being passed around all over the place. A more elegant way of dealing with this is the set the Culture on the executing thread which will default all format specifiers to the format for a given Culture.

Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB")
uint amount = 999999999;
amount.ToString("n");
//Renders as 999,999,999.00

Setting the thread can be done when an application starts, at the beginning of a request, or whenever it may be appropriate for your applications. The goal is to seperate this area of concern from other downstream components so they need not concern themselves with determining what Culture formats are required.

Determining the correct Culture/Formats to use can be tricky since it can be derived from so many places... Once again, you will have to do what makes since from your application. Whatever you decide to do, try to store/key this information using standard IETF tags as that will make things more standard (e.g. BCP 47, RFC 5646, RFC 4647, RFC 5645), portable, and easier to key on. Here are a few common examples:

  • For thick clients, you could use the Users Windows Culture settings
  • For web applications, Content-Language headers supplied by Browsers with HTTP Requests (e.g. RFC 2616, RFC 1766)
  • Request payloads such as XML (e.g. xs:lang attribute [RFC 3066])
  • Application supplied user preferences
  • Etc...

Here is an example of an HTTP Module that derives language preferences from Web Requests and sets the Culture for the target application to use. This is a nice pattern for ASP.NET applications because the module can be reused accross multiple applications or instances, and cleanly seperates this area of concern.


using System;
using System.Globalization;
using System.Threading;
using System.Web;
using Configuration;

public sealed class LocalizationService : IHttpModule
{
    #region IHttpModule Members

    public void Dispose()
    {
        //Nothing to do here
    }

    public void Init(HttpApplication context)
    {
        //subscribe to then Error event
        context.BeginRequest += SetLocalization;
    }

    internal void SetLocalization(object sender, EventArgs e)
    {
        var appInstance = (HttpApplication)sender;

        CultureInfo requestCulture;

        if (!ParseCultureFromRequestContext(appInstance, out requestCulture))
        {
            requestCulture = new CultureInfo(MyCustomConfigSection.Current.DefaultIetfCulture);
        }

        Thread.CurrentThread.CurrentCulture = requestCulture;
        Thread.CurrentThread.CurrentUICulture = requestCulture;
    }

    #endregion

    private static bool ParseCultureFromRequestContext(HttpApplication application, 
        out CultureInfo cultureInfo)
    {
        cultureInfo = null;
        string[] languages = application.Context.Request.UserLanguages;

        if (languages != null)
        {
            foreach (string language in languages)
            {
                try
                {
                    cultureInfo = new CultureInfo(language);

                    return true;
                }
                catch
                {
                    //Language tag not understood. Do nothing, go to the next one
                }
            }
        }

        return false;
    }
}

Tags: , ,

Comments are closed