Monday 10 November 2008

Localization with ASP.NET MVC

When investigating the subject of Areas I came across some recent posts from Phil Haack and Steve Sanderson that made great use of areas to provide exactly what I wanted.  But it got me to thinking, in our application we wish to implement localisation as well as areas where the views have their own sub directories depending on the browser language selection, defaulting to English when no supported language is chosen.

I came up with a very simple solution, and while it’s not the most elegant it works very well.  Using Phil Haack’s code, I implemented my view engine, however I subdivided my views into separate directories for English and Japanese like so:

image

Setting up for English in my view engine was very easy, I just changed the view engine’s constructor to specifically point to my EN views like so in the AreaViewEngine.cs.

public AreaViewEngine() : base() 
{
    ViewLocationFormats = new[] 
    { 
        "~/{0}.aspx",
        "~/{0}.ascx",
        "~/Views/EN/{1}/{0}.aspx",
        "~/Views/EN/{1}/{0}.ascx",
        "~/Views/EN/Shared/{0}.aspx",
        "~/Views/EN/Shared/{0}.ascx",
    };
 
    MasterLocationFormats = new[] 
    {
        "~/{0}.master",
        "~/Shared/{0}.master",
        "~/Views/EN/{1}/{0}.master",
        "~/Views/EN/Shared/{0}.master",
    };
 
    PartialViewLocationFormats = ViewLocationFormats;
}

And made sure the formatting functions in Phil’s view engine looked specifically for my views.

private static string FormatViewName(ControllerContext controllerContext, string viewName) 
{
    string controllerName = controllerContext.RouteData.GetRequiredString("controller");
 
    string area = controllerContext.RouteData.Values["area"].ToString();
    return "Areas/" + area + "/Views/EN/" + controllerName + "/" + viewName;
}
 
private static string FormatSharedViewName(ControllerContext controllerContext, string viewName) 
{
    string area = controllerContext.RouteData.Values["area"].ToString();
    return "Areas/" + area + "/Views/EN/Shared/" + viewName;
}

But how do I tell the system that when Japanese is the default language to look into the JP folder?

There are two ways that I can see.  Phil’s source uses the base.FindView from within the FindView method of his custom view engine.  By completely overriding the FindView method you could specify the language directories to look into depending on the language selection.  This seemed like quite a difficult task and quite a lot of code.

I went down the very simple path of redirecting to the Japanese views once the view selection is complete.

protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
    viewPath = viewPath.Replace("/EN/", "/JP/");
    return base.CreateView(controllerContext, viewPath, masterPath);
}

I do realise that this code will ALWAYS route from English into Japanese, it’s just an example.  And before anyone jumps up and down and shouts “Bad Developer” I realise this is not the prettiest way to do it, but it is exactly ideal for our purposes and it is very simple.

The view must exist in the English language for it to be routable to in any other language and must exist in exactly the same location in the Japanese folders.

If anyone reading this knows a better way this should be done, please let me know. 

No comments: