Globalizing MVC razor views using cshtml per locale

30. juli 2013 21:33 by martijn in .net, globalization, MVC

There are quite a few ways to localize your MVC applications. The best is probably to reference resources (resx) from your cshtml files. For a overview see this complete guide and the post by Hanselman

This can be a bit awkward though if you need a different structure for a different country. Another option is to use a cshtml file per locale. For example use a index.nl.cshtml file for dutch users and index.cshtml for other users. How to do this is the topic of this post. Be aware that this should not be your first choice because you basically copy your HTML making changes later on harder. It can be good to know this trick sometimes... (If you use WebForms viewengine you should use this guide.)


To get this working we need to complete a few steps

  1. Create a localized view
  2. Create a viewengine that prefers localized views and uses the default view if none is found. This makes localization optional and nice to combine with other (resource based) solutions.
  3. Register the ViewEngine in global.asax

Create a localized view

How you structure your views is arbitrary. For this post I post the localized direct next to the normal view. Here I created a Index for the dutch (nl) locale:

Creating the globalized ViewEngine

We subclass the normal RazorViewEngine and only change the part that determines which file is used. We use the CurrentUICulture of the current thread to determine which culture should be used. Then we check to see if there is a view specific for this culture. If no specific view is found the regular view is used.

using System;
using System.IO;
using System.Web.Mvc;
namespace LocalizedViews
{
public sealed class RazorGlobalizationViewEngine : RazorViewEngine
{
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
partialPath = GlobalizeViewPath(controllerContext, partialPath);
return base.CreatePartialView(controllerContext, partialPath);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
viewPath = GlobalizeViewPath(controllerContext, viewPath);
return base.CreateView(controllerContext, viewPath, masterPath);
}
private static string GlobalizeViewPath(ControllerContext controllerContext, string viewPath)
{
var request = controllerContext.HttpContext.Request;
var lang = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
if (!string.IsNullOrEmpty(lang) && !string.Equals(lang, "en", StringComparison.InvariantCultureIgnoreCase))
{
string localizedViewPath = viewPath.Replace(".cshtml", "." + lang + ".cshtml");
if (File.Exists(request.MapPath(localizedViewPath)))
{
viewPath = localizedViewPath;
}
}
return viewPath;
}
}
}

Registering the ViewEngine in global.asax

We can register the viewengine in the global asax like this:

 protected void Application_Start()

        {

            ViewEngines.Engines.Clear();

            ViewEngines.Engines.Add(new RazorGlobalizationViewEngine());

           //the rest of it..

        }

And that's all there is too it. Happy coding!