Businesses all over the world are going global and thus the services offered by them needs to be catered to people of different cultures. This means the services these businesses provide through a web based application needs to support multiple cultures i.e. the application should support multiple languages. This is where localization and globalization comes into picture when building web applications. From initial days, .Net Framework and Asp.Net has localization support using resources files which helps us to localize the views and thus preventing the need to build separate website for each locales. In this article, let’s see how to build a multi-language web site using Asp.Net MVC framework.
For easy understanding, let’s build a simple Employee Maintenance application that helps to maintain employee records using Asp.Net MVC 5.0 and Visual Studio 2015. We will use simple Employee-Department Model and Entity Framework Code First for data access. Assuming, you have the project created with all the necessary Nuget packages required to run Asp.Net MVC 5.0 and Entity Framework, let’s go ahead and add multi-language support to Employee Maintenance application. For simplicity, we will just add a Tamil language support in addition to the default English locale. Once built, the application will support both English and Tamil language as seen in the below screen shots.
Home Page
Employee List Page
Employee Detail Page
At high level, we need to do the below 3 steps to add multi-language support to the MVC application.
-
Create Resource Files
-
Modify Views to Use Resource Files
-
Set Request Thread Culture to User Locale
-
Create Resource Files
Asp.Net has special file called resource files (.resx) to define the default locale (English) and other language texts. In WebForms, we generally create one resource file for every .aspx files. In Asp.Net MVC, we can define one resource file for a view or for a controller whichever is best suited for your application.
For default locale English, the file extension should only include .resx and for other language we should include the culture code before the .resx extension. For Tamil, it is .ta.resx or .ta-IN.resx. Refer here to get the list of language culture codes for different cultures.
To add a new resource file, right click the project node in solution explorer, Add New Item. Select Resources File and rename the file to extension .ta.resx for adding Tamil language resource and only .resx when adding for English.
Note - Please do not create your resource files under specialized folders App_LocalResources or App_GlobalResources folder for Asp.Net MVC application since they are compiled by Asp.Net Compilers. Refer here to know more.
You can specify a namespace for resource file from the property window of the added resource files. The below image shows “Resources” namespace added to the Global.ta.resx file. You can do this to all resource file for the code generator to include all resource class under the specified namespace.
By default, the access modifier of the resource file is set to Internal. You can change this to Public from the Access Modifier dropdownlist from the resource file to make the resource files available to multiple assemblies (for example to access from unit tests, etc.).
You can now add the resource key value pairs in the resource files as seen in the below figures.
For employee maintenance application, I have created one resource file for each controller in addition to a Global.resx file for storing application wide texts, ErrorMessage.resx for storing error messages and a Common.resx file to store any common text that can be shared in multiple views. The final list of resource files including Tamil is as seen below in solution explorer under Resources folder.
-
Modify Views to Use Resource Files
This step is where we will replace the hard coded texts in the view files to resource file keys for fetching the texts based on the locale. The _Layout.cshtml file has some application wide mark-ups which will be available across application. So, as said in earlier section, we have kept application wide global text under Global.resx file. The changed layout mark-up that uses the resources file is seen below.
_Layout.cshtml
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@Html.ActionLink(Resources.Global.APP_NAME, "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink(Resources.Global.Home_Menu, "Index", "Home")</li>
<li>@Html.ActionLink(Resources.Global.About_Menu, "About", "Home")</li>
<li>@Html.ActionLink(Resources.Global.Contact_Menu, "Contact", "Home")</li>
</ul>
</div>
</div>
Similarly, the Employee.resx can be used in the views related to Employee controller. The below code shows employee list page changes using resource file.
EmployeeSummary.cshtml
@model IEnumerable<EmployeeMaintenance.Models.Employee>
@{
ViewBag.Title = Resources.Employee.Summary_Title;
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>@Resources.Employee.Summary_Title</h2>
Note – I have just added few properties for simplicity, you can add key-value pairs for the entire view UI elements in your case.
-
Set Request Thread Culture to User Locale
This is the final step where the application need to know which resource file (or language) it has to choose when rendering the view. This can be done by setting current request’s thread culture to user preferred locale.
There are multiple way of doing it, here in this article let’s see 2 of the very common ways of setting the thread’s culture.
-
Using Global.asax Application_AcquireRequestState
This is one of the common way to set the current request thread culture to user locale so that the Asp.Net MVC picks the correct resource file for the selected locale. The below code helps to do that.
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
string culture = "en-US";
if (Request.UserLanguages != null)
{
culture = Request.UserLanguages[0];
}
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture);
}
In the above code, the user preference of locale is got by Request.UserLanguages collection that is populated from the User Agent or Browser which made the request. The Request.UserLanguages[0] will give the user’s first preferred locale which will be used to pick correct resource file. When the user preferred locale is not available, it will be fall back to the default locale which is English.
Note - To make the browser add the language header, in Firefox, go to Tools>Options>Content. Click Choose.. button under Languages. Add the preferred language (Tamil in this article) from the dropdown and use the Move Up button to make it as first preference.
-
Using OWIN Startup.cs and IControllerActivator
This is the preferred approach when you use OWIN start-up and for future portability to OWIN based application. We need to create a custom controller activator and configure the MVC to use it when creating controller instance. The custom controller activator that adds the user’s preferred locale to request thread is below.
namespace EmployeeMaintenance.Infrastructure
{
public class MultiLanguageControllerActivator : IControllerActivator
{
private string FallBackLanguage = "en-US";
public IController Create(RequestContext requestContext, Type controllerType)
{
if (requestContext.HttpContext.Request.UserLanguages != null)
{
FallBackLanguage = requestContext.HttpContext.Request.UserLanguages[0] ?? FallBackLanguage;
}
Thread.CurrentThread.CurrentCulture = new CultureInfo(FallBackLanguage);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(FallBackLanguage);
return DependencyResolver.Current.GetService(controllerType) as IController;
}
}
}
Now, we need to configure the MVC to use this custom activator by configuring it from OWIN Startup class. Code below.
[assembly: OwinStartup(typeof(EmployeeMaintenance.Startup))]
namespace EmployeeMaintenance
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(new MultiLanguageControllerActivator()));
//Removed for brevity
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
}
}
}
Please make sure you have Microsoft.Owin.Host.SystemWeb Nuget package already added for the above code to work. Alos, make sure you have set owin:AutomaticAppStartup property to true in Web.Config file.
<add key="owin:AutomaticAppStartup" value="true"/>
Adding Localization Support to DataAnnotation Attributes
Since, we extensively use DataAnnotation attributes to render view elements in Asp.Net MVC we need to add localization support to those attributes as well. For example, the @Html.DisplayNameFor(model => model.FirstName), @Html.DisplayNameFor(model => model.LastName) helper methods use Display attributes to get the display column names in Employee Edit and Details screen. To add multi-language support or localization support to these atttibutes, read my article Using Resource File for DataAnnotations Display Attribute with Multi Language Support in Asp.Net MVC.
That’s it! You can run the application and see the Asp.Net MVC localization in action. Full source is attached with this article, please download and see it in action.
In my next article, we will see some advanced scenarios like giving user the option to change language on the site and sending the user preferred locale as a route parameter, etc.