Localize Asp.Net MVC Views using a LocalizedViewEngine

Localizing content is never an easy task.  Asp.Net tries to make localization and globalization easier with built in support for resources and cultures.  While I think that is a good start, I feel that the typical localization technique for asp.net applications is slightly misdirected and could be implemented easier.

In the past, I’ve put every sentence into a culture specific resource file.  Those sentences may be composite format strings or they could just be fragments.

This not only makes it difficult to rapidly develop, but can also create some rather difficult situations when special characters are introduced.  Think percent signs and currency symbols on Edit views.  Not to mention getting right-to-left languages like Arabic to display nicely.

A different approach

I propose a different solution to resource files that contain string fragments.  Rather than piecing together views with resource fragments, why not just have one view per language, per action.  Each language specific view can be identified by including culture names in their file name.

So if you have an Index action on the Home controller and want to support the default language (en-US) and Japanese (ja-JP), you would have the following files:

/Views/Home/Index.aspx
/Views/Home/Index.ja-JP.aspx

An added benefit to this method, is that it allows you to add new translations to your web application without requiring a recompile.  Along those lines, you can incrementally translate your site as budget and time allow.  If you haven’t added a translated view yet, the view engine will fall back on the default language view.

What are the downsides?

While this all sounds like a nice solution, there is one major downfall.  You duplicate the markup in many places.  So if or when you make a change in the future, you’ll have to go through each language specific view and make the change there as well.  That’s a lot to ask of a developer, but I feel that this method outweighs trying to piece together fragments and maintain text outside of the view.

How is this accomplished?

As everyone is aware, Asp.net MVC allows developers to extend the framework rather easily.  To allow for language specific views, we just need to tweak the WebFormViewEngine to first check for the view of the CurrentUICulture.  If that page is not found, let the view engine continue as it normally would.

public class LocalizedWebFormViewEngine : WebFormViewEngine
{
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
string localizedPartialViewName = partialViewName;
if (!string.IsNullOrEmpty(partialViewName))
localizedPartialViewName += “.” + Thread.CurrentThread.CurrentUICulture.Name;

var result = base.FindPartialView(controllerContext, localizedPartialViewName, useCache);

if (result.View == null)
result = base.FindPartialView(controllerContext, partialViewName, useCache);

return result;
}

public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
string localizedViewName = viewName;
if (!string.IsNullOrEmpty(viewName))
localizedViewName += “.” + Thread.CurrentThread.CurrentUICulture.Name;

string localizedMasterName = masterName;
if (!string.IsNullOrEmpty(masterName))
localizedMasterName += “.” + Thread.CurrentThread.CurrentUICulture.Name;

var result = base.FindView(controllerContext, localizedViewName, localizedMasterName, useCache);

if (result.View == null)
result = base.FindView(controllerContext, viewName, masterName, useCache);

return result;
}
}

To specify that you would like to use the LocalizedViewEngine, modify the Application_Start method in your Global.asax.cs file to be similar to:

protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();

RegisterRoutes(RouteTable.Routes);

ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new LocalizedWebFormViewEngine());
}

That’s it.  I’m interested in hearing your thoughts about this method.

11 thoughts on “Localize Asp.Net MVC Views using a LocalizedViewEngine

  1. Another problem is that it’s usually not developers that provide the translations, but actual translators. Do you really want translators to edit your views?
    And because you will also need translations in other parts of the application (not only views, but also e-mail templates, JavaScript, etc…), it’s a good idea to store all the translations in a single place (usually RESX-files).

    1. I’ve experienced issues with translators breaking html as well. I think it tends to be just part of the world we live in, unless the translator has some knowledge of html.

      Maybe a general solution would be to send them a text file of just the text on the page. Separate it out in paragraphs, etc. Then they would send you a file back and you put their translation into the localized view file. Afterward, they could verify the view looks as expected. This may be too many steps and may increase the price of localization a bit, but it might also be the easiest for everyone involved.

      Hernan, does your application allow the translator to submit their translations directly from your application? Or do they have to go through more of a human step to get the translations in your app?

      1. I’ve noticed that developing applications from the start with localization in mind is a lot easier than localizing an existing application. Once you have the habit of storing display strings in resources, it’s very easy to develop and maintain.

        1. I fully agree that it’s easier to prepare for localization from the beginning when you know it’s a requirement. Many times, however, that requirement doesn’t come until the site or app has proven itself in the native language.

          I’m not sold that this method is the best method out there for localization, but it does provide an easy way to incrementally localize a web app.

  2. I work in a multicultural multicurrency, multilanguage project. This is a nice idea and will work better that resource files (because of the compilation step needed and the other points in your post).
    Another advantage of this is that the translator has some context so the translation will be better than in isolation.

    We actually use a similar approach but we save the localized html snipped (partial views) in a db.
    The main issues we found are translators touching and braking the html.
    They use either word or more speciallized tools like Trados.
    You also need to have a word count to pay them, what is specially tricky when you do touch ups or minimal changes.
    We provide to them an editor tool that does those things in the snippeds but it’s still tricky.
    Further more the translation of duplicate phrases is something that you may want to prevent in the long run with bigger projects (cost wise).
    I don’t have the answers since these are issues we see every day when using a similar approach to yours, just providing some feeedback from my experience.

    1. I understand your points regarding email templates and javascript. I will probably handle those differently. There shouldn’t be a lot of text coming from javascript, but I think it does make sense to store that text in resource files.

      E-mail templates are similar to views in some cases. Depending on how detailed the email templates are, I think it could make sense to either store the text in the db or using a similar technique as I’ve outlined above.

      While I think it would be ideal to store translations in a single place, sometimes that’s not the most efficient practice to get things done. In my experience, you develop the application and the translations come afterward. It’s nice to develop the app without having to treat every word/fragment as a special control.

Leave a Reply

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

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>