ASP.NET MVC and Localization

I live in Montreal – Canada, and here we have two officials languages, French and English, that’s why every website I build must be available in both languages. Building an ASP.NET MVC Website using Localization/Globalization is fairly simple and is pretty much like doing it on a plain old ASP.NET Website.

We will start this example from a blanc ASP.NET MVC Website. This will be easier for you to follow. Now that you have your new ASP.NET MVC Website, create a new class and name it SetCultureAttribute.cs in a new ActionFilter directory and paste this code inside of it.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication1.ActionFilter
{
    public class SetCultureAttribute : FilterAttribute, IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext 
            filterContext)
        {
            string cultureCode = SetCurrentLanguage(filterContext);

            
            if (string.IsNullOrEmpty(cultureCode)) return;

            HttpContext.Current.Response.Cookies.Add(
                new HttpCookie("Culture", cultureCode)
                {
                    HttpOnly = true, 
                    Expires = DateTime.Now.AddYears(100)
                }
            );
            
            filterContext.HttpContext.Session["Culture"] = cultureCode;

            CultureInfo culture = new CultureInfo(cultureCode);
            System.Threading.Thread.CurrentThread.CurrentCulture = 
                culture;
            System.Threading.Thread.CurrentThread.CurrentUICulture = 
                culture;
        }

        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            
        }

        private static string GetCookieCulture(ActionExecutingContext 
            filterContext, ICollection Cultures)
        {
            /* Get the language in the cookie*/
            HttpCookie userCookie = filterContext.RequestContext
                                                .HttpContext
                                                .Request
                                                .Cookies["Culture"];

            if (userCookie != null)
            {
                if (!string.IsNullOrEmpty(userCookie.Value))
                {
                    if (Cultures.Contains(userCookie.Value))
                    {
                        return userCookie.Value;
                    }
                    return string.Empty;
                }
                return string.Empty;
            }
            return string.Empty;
        }

        private static string GetSessionCulture(ActionExecutingContext 
            filterContext, ICollection Cultures)
        {
            if (filterContext.RequestContext.HttpContext
                                               .Session["Culture"] 
                                                             != null)
            {
                string SessionCulture = filterContext.RequestContext
                                                .HttpContext
                                                .Session["Culture"]
                                                .ToString();

                if (!string.IsNullOrEmpty(SessionCulture))
                {
                    return Cultures.Contains(SessionCulture) 
                                 ? SessionCulture 
                                 : string.Empty;
                }
                return string.Empty;
            }
            return string.Empty;
        }

        private static string GetBrowserCulture(ActionExecutingContext 
            filterContext, IEnumerable Cultures)
        {
            /* Gets Languages from Browser */
            IList BrowserLanguages = filterContext.RequestContext
                                                         .HttpContext
                                                         .Request
                                                         .UserLanguages;

            foreach (var thisBrowserLanguage in BrowserLanguages)
            {
                foreach (var thisCultureLanguage in Cultures)
                {
                    if (thisCultureLanguage != thisBrowserLanguage) 
                        continue;

                    return thisCultureLanguage;
                }
            }
            return string.Empty;
        }

        private static string SetCurrentLanguage(ActionExecutingContext 
             filterContext)
        {
            IList Cultures = new List 
            {
                "en-CA", 
                "fr-CA"
            };

            string CookieValue = GetCookieCulture(
                                            filterContext, 
                                            Cultures);

            if (string.IsNullOrEmpty(CookieValue))
            {
                string SessionValue = GetSessionCulture(
                                                  filterContext, 
                                                  Cultures);

                if (string.IsNullOrEmpty(SessionValue))
                {
                    string BrowserCulture = GetBrowserCulture(
                                                         filterContext, 
                                                         Cultures);
                    return string.IsNullOrEmpty(BrowserCulture) 
                             ? "en-CA" 
                             : BrowserCulture;
                }
                return SessionValue;
            }
            return CookieValue;
        }
    }
}

Once this is done, we need to tell our controllers to use this attribute, to do this we could simply go on top of every controller and decorate it with [SetCulture] attribute. The down side with this is that we want the complete site multicultural, not just a few controllers or actions. To fix this problem we’ll make a BaseController which will inherit Controller and will be inherited from all our controllers.

To do this, create a new directory and name it Infrastructure and then add a new class called BaseController.cs in it. This class should contain the following code:

using System.Web.Mvc;
using MvcApplication1.ActionFilter;

namespace MvcApplication1.Infrastructure
{
    [SetCulture]
    public class BaseController : Controller 
    {
         // Anything you put here will be accessible 
         // in every controllers
    }
}

As I said before, we need to tell every controllers to use BaseController, so open every controllers and change the inheritance from Controller to BaseController. As of now, your website knows how to handle localization!!!

What we need now, is a way to switch between french and english… To do this I simply created a new ASP.NET MVC View User Controller (CultureUserControl.ascx) in Views/Shared with the following code.

<% if (Session["Culture"].ToString() == "en-CA") {%>
    [ Fran├žais ]
<% } else if (Session["Culture"].ToString() == "fr-CA") { %>
    [ English ] 
<% } %>

Then I added it to the masterpage, right next to the Log On link.

<% Html.RenderPartial("LogOnUserControl"); %> <% Html.RenderPartial("CultureUserControl"); %>

Last, but not least, add a new Global.resx and Global.fr.rex in the App_GlobalResources folder. You can start with the welcome message in About – Index and call it using Resources.Global.Welcome

1121

You can find a working example of this tutorial here.

This entry was posted on Wednesday, May 27th, 2009 at 4:50 pm and is filed under Active Directory, ASP.NET, CSS, Developement, Globalization, MVC, Photoshop, PHP, Server, SQL Server, Uncategorized. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

23 Responses to “ASP.NET MVC and Localization”

Paco June 26th, 2009 at 6:18 am

Tanks for a great blog post. I’ve noticed that the GetBrowserCulture-method only returns the first part of the culture language, that is ‘sv’ instead of ‘sv-SE’. In Chrome the Request.UrlReferrer always is null. I guess you have to redirect to the main page for Crome users.

Mike June 27th, 2009 at 10:43 pm

@PACO: Hmm this is good to know! I’ll try it and update my article! Thanks for the tip

DJ Pepper August 16th, 2009 at 1:11 pm

Very cool post. Thank you very much.

Rein Dolfing August 24th, 2009 at 6:03 am

Thank you, This is the best “working” solution could find.

Paulo Crespo September 7th, 2009 at 9:49 am

Fantastic article, simply idea and work perfectly !!!

The best ! Congratulations !

Sosh October 4th, 2009 at 8:35 am

Interesting, but I don’t really understand what the benefit of implementing this as an attribute is, if you are only going to use the attribute once (on the base controller).

Tran Chi Khanh October 25th, 2009 at 4:55 am

Hi,
Please help me how to use with App_LocalResources. With your demo code, i create localresource for index.aspx (views\home\index.aspx) with Index.aspx.fr.resx and Index.aspx.resx. But when click button in CultureUserControl.ascx from English to French, my content in this file don’t change

Quyetle November 5th, 2009 at 11:31 pm

This is best solution i found on the net. Thanks

Pablo December 3rd, 2009 at 8:45 am

Great Article! I was wondering how you deal with databases. Do you use two database, one for each language? Could anyone provide me some information, link, etc or a working example?
Thanks!

Miguel January 30th, 2010 at 3:18 pm

Hello,

I am not sure but I think I probably found a problem with your SetCultureAttribute code in http://helios.ca/2009/05/27/aspnet-mvc-and-localization/.

Everything works fine but it is impossible to validate the page with W3C or Verify the page with Google Verify … Or even index the page in Google. This is because I always get a 500 error.

I posted a thread in ASP.NET MVC but until now I wasn’t able to solve it:
http://forums.asp.net/t/1520173.aspx

Please, could you help me out?

Mike February 2nd, 2010 at 2:48 pm

Hey Miguel,

This probably happens because it’s no a browser calling the page, which means “filterContext.RequestContext.HttpContext.Request.UserLanguages;” returns null. Try checking if BrowserLanguages is null or empty before using it.

Alfred February 24th, 2010 at 1:08 pm

Why not handle localization at the application level instead of the controller level?

I came across this other blog post for MVC2: http://adamyan.blogspot.com/2010/02/aspnet-mvc-2-localization-complete.html

Basically, you can set the culture using a specific controller action (into session/cookie/url/etc). Then for every request, lookup the culture and set it on the thread in Global.asax Application_AcquireRequestState() event handler. This way, your localization management is centralized in one spot, and you won’t have to manage it for every controller.

karlito September 9th, 2010 at 6:50 am

It’s not very good code below. What if you need support 10 languages?

01.
02. [ Fran├žais ]
03.
04. [ English ]
05.

Better to remain code in single line and move all strings into resource files
Html.ActionLink(Resources.SiteResources.Country, “SetCulture”, “Home”)

Culture specific website in ASP.NET MVC « Koen about .Net January 21st, 2011 at 5:44 pm

[...] and that was by using a FilterAttribute in combination with an ActionFilter which I found here: http://helios.ca/2009/05/27/aspnet-mvc-and-localization/. Nice solution I thought, so I took that code, modified it a bit to match my scenario and it [...]

Jarod Weitnauer February 3rd, 2011 at 6:37 am

Admiring the time and energy you put into your blog and in depth information you present. It’s great to come across a blog every once in a while that isn’t the same old rehashed material. Great read! I’ve saved your site and I’m adding your RSS feeds to my Google account.

Tony Basallo February 3rd, 2011 at 3:45 pm

It would seem that this is still relevant and useful for MVC 3.

What is line 26 for (filterContext.HttpContext.Session["Culture"] = cultureCode;)?

Mathieu Bergeron March 22nd, 2011 at 10:01 am

Merci beaucoup pour cet exemple !

You just saved me a lot of time !

vijay July 10th, 2011 at 2:36 am

I downloaded your code it’s working fine. Then i created new resource file for hindi language as global.hi.resx. Now i am not able to fetch the hindi language in view page. What are the changes i have to take care when i created any new resource file.

Mike July 12th, 2011 at 3:54 pm

@Vijay: You should only have to change the default language of your browser.

PS: This is an old implementation, it still works but there are better solutions with MVC 2 and MVC 3. I know I should update my blog, but I’m lazy! =)

asp.net, c#,javascript September 29th, 2011 at 9:29 pm

asp.net, c#,javascript…

[...]ASP.NET MVC and Localization | MIKE[...]…

Esteban September 30th, 2011 at 7:01 pm

Great Post, but why dont you put that logic in the Initialize() method of the base controller? I have used the OnExecute to do this but it happened that validations were invoked before the OnExecute and so Localization was not working fine. The Initialize() is called before the Model Validation and e OnExecute.

catsailor.net October 5th, 2012 at 6:55 am

Hello therе! Do you use Twitter? I’d like to follow you if that would be okay. I’m definіtely enjoyіng your blog and look
forwaгd tο new posts.

Leave a Reply