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.