Internationalization in ASP.NET MVC

Anyone who is starting almost any kind of software nowadays should be thinking on internationalization, even more if it is web-based. The effort required to prepare a system for localization (the difference between internationalization and localization is subtle, you can find it on Wikipedia) from the very beginning is minimum and the payoff is huge.

In this post I will first describe in the large how we can approach this problem with ASP.NET MVC, and then I will provide some easy steps to accomplish the task.

General idea of i18n

We will start by setting all the strings that our system uses in a key-value pair file. We will have one of these files for each language we choose to support, for instance the values for the key “colors” would be “colors”, “colours” and “colores” in the files Resources.en-US.resx, Resources.en-UK.resx and Resources.es.resx respectively. After that we only have to know which file to pick, in order to do that we read the preferred language sent by the browser on every Http request. Finally we should give the user the option to change the language, imagine that an English speaker would not be very happy to check his e-mail with the browser preferred language during his vacations in China!

1. Create the resources files

The first thing we are going to do is to create the resources file/s. Right-click on the App_GlobalResources folder, add a new resource file and name it Resources.resx: this would be the default language. By adding this item, another file called Resource.designer.cs is added to the project, it provides direct access to the key-value pairs we define on the resource file/s. In this post we will show how to support English (default) and Spanish, so we add another resource file called Resources.es.resx. Finally we add some string to those files, for instance: “Welcome” associated with “Welcome to MySite!” and “Bienvenido/a a MiSitio!”.

When we access these string within the code the Framework first try to do an exact match with the language. For instance if the language is set to es-AR (Spanish from Argentina) the framework will look first for the more specific resource file (Resources.es-AR.resx in this case), if that file does not exist the one corresponding to the parent language would be chosen (Resources.es.resx) and if that is not found either, then the default resource file is used.

2. Use the browser preferred language

In ASP.NET MVC is very easy to set the current culture to the browser’s preference. We just need to add a single line to the Web.config file: after the <system.web> tag we add:

<system.web>
    <globalization culture="auto" uiCulture="auto"/>

Now we are ready to test what  we have done so far. We just need to change the access modifier of the default resource file from internal to public. Then every string defined on these files could be accessed by just writing:

App_GlobalResources.Resources.Welcome;

3. Set the user preferred language and store it in a cookie

We have two ways to do this: server-side or client-side. In my personal opinion, as we only need to create and store a cookie and reload the site, is not convenient to go to the server. On the other hand, doing this client-side could be a bit more difficult. Luckily, we have jQuery!

First we need to have some links or cute images associated with onclick events:

<a href="#" onclick="changeLang('es-AR')">es-AR</a>
<a href="#" onclick="changeLang('en-US')">en-US</a>

On the <head> section we need to import jQuery (line 1) and the cookie plugin (line 2), which can be found here and a nice demo here.

 

   1: <script src="/Scripts/jquery-1.2.6.js" type="text/javascript"></script>
   2: <script src="/Scripts/jquery.cookie.js" type="text/javascript"></script>
   3: <script type="text/javascript">
   4:     function changeLang(lang) {
   5:         $.cookie('lang', lang);
   6:         window.location.reload();
   7:         return false;
   8:     }
   9: </script>

 

In line 5 the jQuery Cookie Plugin is used to store the chosen language in a variable named ‘lang’, and the statement in line 6 tell the browser to reload the site. What for? We will see in the next (and final) step.

4. Eat the cookie (before everybody else!)

As we all know, every cookie which is associated with the current domain is sent with every Http request. It would be great if we could write some code to “catch” each request, see if there is a cookie with the preferred language, and set it.

ASP.NET works with Http Modules which, in short, are pieces of code that can play with the Http request before it reaches to the handlers. Mansoor Ahmed Siddiqui wrote a great post about this, which I strongly recommend.

First we register the module in the Web.config file, so the Framework knows it have to be executed with every request:

<httpModules>
    <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <!-- this one is our module =) -->
    <add name="CookieLocalizationModule" type="MyProject.CookieLocalizationModule, MyProject"/>
    <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>

Finally the code, which is pretty what you can image: getting the cookie and setting the language, with some more lines the modules require:

public class CookieLocalizationModule : IHttpModule
{
    public void Dispose()
    {
    }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(context_BeginRequest);
    }

    void context_BeginRequest(object sender, EventArgs e)
    {
        // eat the cookie (if any) and set the culture
        if (HttpContext.Current.Request.Cookies["lang"] != null)
        {
            HttpCookie cookie = HttpContext.Current.Request.Cookies["lang"];
            string lang = cookie.Value;
            var culture = new System.Globalization.CultureInfo(lang);
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = culture;
        }
    }
}

And that is all?

Almost. There is one more thing we need to do: internationalize images. Although it is quite an easy task and there are many ways of doing it, this post is long enough so I will write about it later.

This should be enough for lots of applications and many languages. However there exists some issues related with Arabic languages (which are written from right to left) and some Asian ones (which use symbols) that are not at all cover in this post.

Thanks a lot Brian for all the help!
I hope this have been useful. Have a nice day!

15 Responses to Internationalization in ASP.NET MVC

  1. Feryt says:

    Hi,
    if anyone needs to set cultures based on URL request, check it out here:
    http://feryt.spaces.live.com/blog/cns!2A7829BEF8D0664B!160.entry

  2. Pingback: Shared Tutorials » Blog Archive » Sergio A. Medina [aka SERch] » Internationalization in ASP.NET MVC

  3. Pingback: ASP.NET MVC Archived Buzz, Page 1

  4. Ramon Lopes says:

    Hi,

    first of all, I would like to congratulate you for the magnificent tutorial.
    A friend of mine faced some troubles concerning accessing string in the resource file following the steps described in this tutorial.
    The solution found was:

    “res” means the resource file
    “key” means the string

    Best Regards!

  5. Pingback: Santiago Palladino » Using GNU Gettext for i18n in C# and ASP.NET

  6. szataninside says:

    You saved my life with that js script! Thanks!

  7. Young says:

    I am getting configuration Error. Anyone know why? It shows the error is from web.config

    Parser Error Message: Could not load file or assembly ‘MyProject’ or one of its dependencies. The system cannot find the file specified. (C:UsersYKDocumentsVisual Studio 2008WebSiteLocalizationweb.config line 86)

  8. sruthi says:

    For it is realy difficult to use gettext in C#, could you provide or point out any walkthrough of gettext in C#

  9. sruthi says:

    i have installed GNU gettext 0.14.0 version, but still i couldn’t able to find gnu.gettext.dll; which i can use it in C# application

  10. Sebastian says:

    Sergio,

    muy buen tutorial, queria aprovechar para consultarte sobre categorias. Si preciso internacionalizar las categorias de un sitio ecommerce, se puede usar resx tambien o recomendas hacerlo por base de datos?. Tipo tabla categoria con categorialocalizada relacionada. Sino como puedo hacer para usarlo por resx?.

    gracias
    sebastian.

  11. smedina says:

    Sebas,

    No logro ver porque las categorías son diferentes de cualquier otro string, con lo cual aplicaría los pasos detallados en este post. Quizás lo que tenés que hacer vos es un laburo de i18n y l10n mucho más grande y complejo que el que me toco hacer a mi, en cuyo caso te recomiento que leas los post que escribió un amigo, quizás encajan más con tus requerimientos: http://www.manas.com.ar/spalladino/category/i18n/gettext/. Abrazo!

  12. sebastian says:

    Muchas gracias Sergio, no habia visto la respuesta. voy a ver lo de gettext. gracias!

  13. Patrick says:

    Thanks, very useful information!

  14. mohanad says:

    thanks , very much it was a helpful post

Leave a Reply

Your email address will not be published. Required fields are marked *


× nine = 45

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>