Click here to Skip to main content
15,860,972 members
Articles / Web Development / ASP.NET

Creating a Bilingual ASP.NET MVC 3 Application – Part I

Rate me:
Please Sign up or sign in to vote.
4.79/5 (48 votes)
7 Jun 2011CPOL10 min read 381.3K   8.5K   128   47
This article provides a simple globalization mechanism for a bilingual website. Part I creates a simple bilingual website.

Introduction

This article is the first one of two covering a basic bilingual website in ASP.NET MVC 3. I have split the article into two parts because I feel it will become too long winded and complicated in one step. The two parts are:

  1. Creating a simple globalized MVC3 application
  2. Routes based globalization for an MVC3 application

This article is a beginner’s introduction to using internationalized resources. The code is written with the intention of being clear, rather than production quality. I will use Arabic as the second language, this is because the website I did the original work for uses Arabic, and it drives out some left-to-right and right-to-left support that would otherwise be missed. If you speak Arabic, apologies for any mistakes, I do speak a little, but have used Google Translate for most of the text!

The second article (available here) will cover route-based internationalization and will be more advanced. Both articles will assume a basic knowledge of how MVC works (especially the relevant components: View, Controller, and RouteHandler) and how .NET handles localisation and culture. The examples will focus on the default ASPX view engine rather than Razor, but will provide information where the Razor markup is different. The mechanism can be extended to allow multi-lingual websites with a little effort.

To run the code, you must have VS2010 installed (and .NET 4 :-)) as well as the MVC 3 framework.

Background

This article springs from a proof of concept website I wrote for my current employer using MVC 3. Prior to this, I had little to no experience of ASP.NET MVC 3. My employer has a requirement for their website to be available in English and Arabic, this article documents and shares what was achieved during that process. There are a few articles covering localisation in MVC 3 on the web, but none I could find that achieved exactly what I wanted, which was a bilingual website with a routing strategy similar to that of MSDN (as explained in part 2). That said, I must acknowledge a heavy debt to these articles:

When implementing localisation in my application, I found the process to be far less polished and intuitive than plain old ASP.NET. There is no way of generating the RESX files automatically, and extra steps must be taken to make the contents available to the view. I also could not find Microsoft best practices for localisation and globalization in MVC 3 available on the web.

The Code

In principle, the process of getting resources to work with MVC3 is fairly straightforward, but less so than for a normal ASP.NET application:

  1. Create the RESX file to hold the text.
  2. Make the RESX contents publically available.
  3. Repeat steps 1 and 2 for the other supported language and then for each view.
  4. Get the text from the RESX in the View or Controller (or View Model!).
  5. Set the culture from the browser default.

In this explanatory article and code, I have used the master page (or Layout for the Razor version) as an example. The same methodology applies to the child views, I have included a simple example of this in the download projects not reflected in the article.

Creating the RESX Files

I will start by assuming the application has a master page (or a Layout for Razor), and I will describe the process of getting it ready for localisation. Normal Views work in the same manner, so everything that is applied here can be applied to a View. Let’s take the initial mark-up in the Master Page "view" to be something simple like:

ASP.NET
<body>
    <h1>Welcome</h1>
    <div>
        <asp:ContentPlaceHolder ID="MainContent" runat="server">
        </asp:ContentPlaceHolder>
    </div>
</body>

An equivalent Razor Layout could look like:

HTML
<body>
    <h1>Welcome</h1>
    <div>
        @RenderBody()
    </div>
</body>

We need to transfer the heading text (“Welcome”) to a resource file. Unlike ASP.NET, we cannot just create the resource from the context menu. My advice is to mirror your view folder; my master page is in a folder called Views/Shared, so I will create a directory structure Resources/Shared. The MasterPage is called “MasterPage.Master” (Razor users, please see the note in red at the end of this section), so right-click the resources folder and follow these options:

Adding a new Item: Right click the folder, select 'Add' then 'New Item'

Select the General tab, Resources File, and change the filename:

Adding a resource file: Select the General Tab then Resource File

Once you have done this, you need to move the text into the resource file, giving it a sensible name, in this case “Heading”, and enter the text ("Welcome"). Now set the access modifier to public, without this step the view will be unable to access it:

The contents of the resx file: a name 'Heading' with a value 'Welcome'

Typically, you would repeat this for each section of text to be localized, but I have only entered one value to keep the example deliberately simple. The next step is to copy the RESX file into the same directory as itself (remember to save beforehand!), and rename it for the culture you wish to support. In my case, I am using Arabic, so “MasterPage.resx” has a companion MasterPage.ar.resx. Note that the copied file carries over the public access modifier so there is no need to re-set it. Now all we need to do is to repeat the process creating the RESX files for the remaining views. Once finished, we should have “mirrored” Views and Resources folders, with one RESX file for each language per view:

The resource file contents showing the views and the matching pair of resource files under a mirrored file structure

Note for Razor Users: As Razor uses a layout file called "_Layout.cshtml", by default, rather than MasterPage.Master, I have reflected this in the downloadable project; the RESX files are called "Layout.resx" and “Layout.ar.resx”, as opposed to "MasterPage.resx" and "MasterPage.ar.resx" for the ASPX engine, continuing the strategy of mirroring the "view" and its resources. What applies for the MasterPage and its resources applies for the equivalent Razor ones too. The namespace is also different, InternationalizedMvcApplicationRazor rather than InternationalizedMvcApplication. The code snippets in the article reflect these differences, but in the interests of clarity, I have not done so in the main text.

Adding the Text to the View

Getting the text from the RESX file to the view through the mark-up is reasonably easy, we are now effectively getting a property on a class. In the Master Page, we replace the word “Welcome” with the magic incantation:

ASP.NET
<body>
    <h1><%= InternationalizedMvcApplication.Resources.Shared.MasterPage.Heading %></h1>
    <div>
        <asp:ContentPlaceHolder ID="MainContent" runat="server">
        </asp:ContentPlaceHolder>
    </div>
</body>

For Razor, the markup is:

HTML
<body>
    <h1>@InternationalizedMvcApplicationRazor.Resources.Shared.Layout.Heading %>
</h1>
    <div>
        @RenderBody()
    </div>
</body>

Notice that the value is accessed like a property on the class (in fact, the resources are compiled down to a DLL): InternationalizedMvcApplication.Resources.Shared is a namespace (following the directory structure), MasterPage (or Layout for Razor) is the class name (following the RESX file name), and Heading is the property name (following the name key we gave it). As the resources are publically available to classes in the application, you can access them programmatically too:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using InternationalizedMvcApplication.Resources.Shared;
//using InternationalizedMvcApplicationRazor.Resources.Shared; //For Razor
 
namespace InternationalizedMvcApplication.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            //Note using InternationalizedMvcApplication.Resources.Shared; for brevity
            ViewBag.InternationalizedHeading = MasterPage.Heading;
            //Layout.Heading for razor
            return View();
        }
    }
}

Note that in the examples, the View does not access the ViewBag, but it can be accessed in the normal ways if wanted.

Working Out Which Culture to Use

The final step is to define which culture to use; in this article, I will use the browser’s default language for simplicity, and then extend the mechanism in the second article. One thing you should do in production code is to provide a way of overriding the browser’s default culture; it might not be the one the user wants, in an Internet Café when abroad, for example.

Once the culture is defined, it works in much the same way as standard ASP: if the culture specific RESX is available, it uses values from that; if no RESX is available, it falls back to the default culture (remember that I specified .ar.resx as the file extension for Arabic, but English only requires .resx as the default will be English). Just like standard ASP.NET, if an individual entry is missing, it falls back to the default. You can also extend the name to provide variants for British English (as opposed to the default US) or Jordanian Arabic (as opposed to the default Saudi).

Unlike ASP.NET, we cannot override the page’s InitializeCulture method: there is no code-behind, which is the preferred method for many developers. We can add this to global.asax:

C#
protected void Application_BeginRequest(object sender, EventArgs e)
{
  //Note everything hardcoded, for simplicity!
  if (Request.UserLanguages.Length == 0)
      return;
  string language = Request.UserLanguages[0];
  if (language.Substring(0, 2).ToLower() == "ar")
    Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("ar");
}

There is, however, a neater mechanism, I have left this code block commented in the sample code as I will not continue to use it. The mechanism I have adopted is to add the following into the web.config, under system.web:

XML
<globalization culture="en-GB" uiCulture="auto:en-GB" />

Now the UI Culture will be set to the default culture from the browser, falling back on British English. In both the programmatic example and the configuration based one, I have not set the main application culture, you may need to do this.

Testing and Directional Support

Now we can test what we have done. Running the application yields:

The Page rendered in English

Now we swap the default language; to do this in IE 9:

  1. Select the Tools icon, then Internet Options.
  2. Click the Language button, under Appearance. You get this screen:
  3. The IE9 Language Selection Screen

  4. If not already added (I have in this screen), alick "Add" and add any Arabic language variant.
  5. Select the version of Arabic you want to use and click "Move up" until it is at the top of the list.
  6. OK out of the dialog screens.

Now, the browser's default language is Arabic, a refresh on our page shows:

The Page rendered in Arabic, but the text is formatted left to right

This has been a good test! Although we have Arabic text, the page is still working left to right. This is easily fixed, the steps are similar to creating the original RESX files:

  1. Create the Common.resx resource file in the /Resources folder.
  2. Add TextDirection as a name and set its value to ltr (not rtl; this file defines the direction for English!).
  3. Make the Access Modifier of the RESX file public and save.
  4. Make a copy of the RESX file and rename its extension .ar.resx.
  5. Remember to change the direction value to rtl in the Arabic resource :-)

Now we can place the following in any tag of the view HTML:

ASP.NET
dir="<%= InternationalizedMvcApplication.Resources.Common.TextDirection %>"

Similarly for the Razor engine:

ASP.NET
dir="@InternationalizedMvcApplicationRazor.Resources.Common.TextDirection"

In the sample code, this has been placed into the root HTML tag, making the whole page either right to left or left to right. Sadly, the intellisense does not work here, so you will have to rely upon memory. Running once more provides:

The Page rendered in Arabic, but the text correctly formatted right to left

Remember to swap the language back to English, and check the text is running left to right. We now have a basic English/Arabic bilingual website.

Conclusion

We have created a simple, globalized MVC 3 application, this is not production quality (e.g., we have no override mechanism), but what we have can be easily adapted for production. The principles are similar to, but less smooth than standard ASP.NET practices:

  1. Create Resources for each culture.
  2. Make them public (note that this is the nth time I have mentioned this: I often forget!).
  3. Get the text from the resource in the mark-up.
  4. Set the UI culture (at least, you may need to set the main culture) on the server to the browser’s default coming in on the request.
  5. Depending upon the language, you may need to add bidirectional support.

In the next article, we will keep using the browser’s default culture so the page will display initially in that language, but we will make it overridable by clicking a link. The language can then be selected via its URL. For example, English will be: http://localhost/ (default) or http://localhost/en whereas Arabic will be http://localhost/ar. The URL will totally ignore subdivisions of the language (e.g., Saudi Arabic or Jordanian Arabic). URL based selection fits the MVC pattern better than parameterised URLs or cookies, it will also help search engines rank your site on a per-language basis.

For those interested: part 2 is available here.

If you have any comments or feedback, please feel free to ask. I hope that there will be suggestions for neater mechanisms, though I think the one here is pretty light-weight.

History

If you edit this article, please keep a running update of any changes or improvements you've made here.

  • 12th April 2011: Initial article.
  • 31st April 2011: Added Razor version and details.
  • 5th June 2011: Added reference to second article.
  • 15thJune 2011: Added obvious links to second article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I Graduated in Natural Sciences (Chemistry & Physics) from Durham University, where I did not cover myself with glory, but did fill, and often cover, myself with beer.

I qualified as a secondary school teacher, but I hated teaching. I worked as an IT techhie for a few years but kept being promoted to IT Trainer, thanks to my teaching experience, which I also disliked. I spent some time working out what else I could do instead and reduced beer intake.

I realised that I should work as a programmer, having enjoyed it a hobby since I was a nipper in the halcyon days of the Sinclair Spectrum (48k, Rubber Keyboard). Spent two weeks working out why I didn't think of this to start with, instead of starting my dull-as-ditch-water Chemistry degree 8 years earlier. Had a beer to celebrate.

I Graduated in 2001 with an MSc from Newcastle Uni in Comp Sci. Did cover myself with glory, and drank some beer.

.Netting ever since, and loving it. Though I have largely given up beer due to not being able to hack the pace like I used to.

I was born, brought up, and have lived most of my life near Newcastle. In a fit of temporary insanity I moved to Amman, in my wife's homeland of Jordan, but made it back safely to the UK without any extra holes being made in my person by bullets. To be fair it was pretty safe at the time, if you ignored the roads.

Visit Jordan if you can by the way, the landscape is beautiful and varied, the food excellent and the people the friendliest on earth, after Geordies naturally Smile | :) .

Comments and Discussions

 
SuggestionThank you veeeery much!! Pin
armanvesta6-Apr-16 9:26
armanvesta6-Apr-16 9:26 
GeneralMy vote of 4 Pin
Syed Baqer Naqvi5-Jun-14 6:23
professionalSyed Baqer Naqvi5-Jun-14 6:23 
QuestionThanks Keith for the great article!!!! Pin
JihedHalimi21-Aug-13 10:13
JihedHalimi21-Aug-13 10:13 
GeneralMy vote of 1 Pin
dnxit5-Mar-13 8:34
dnxit5-Mar-13 8:34 
GeneralI have reported you Pin
Keith Barrow7-Mar-13 9:08
professionalKeith Barrow7-Mar-13 9:08 
GeneralMy vote of 5 Pin
MAlshehri4-Aug-12 13:57
MAlshehri4-Aug-12 13:57 
GeneralRe: My vote of 5 Pin
Keith Barrow16-Aug-12 2:21
professionalKeith Barrow16-Aug-12 2:21 
GeneralMy vote of 3 Pin
AbdullaMohammad23-Jun-12 4:51
AbdullaMohammad23-Jun-12 4:51 
GeneralRe: My vote of 3 Pin
Keith Barrow16-Jul-12 13:38
professionalKeith Barrow16-Jul-12 13:38 
QuestionThanks a ton!!! Pin
finsj66626-Apr-12 18:29
finsj66626-Apr-12 18:29 
AnswerRe: Thanks a ton!!! Pin
Keith Barrow16-Jul-12 13:22
professionalKeith Barrow16-Jul-12 13:22 
QuestionThanks Pin
Member 821673115-Feb-12 8:53
Member 821673115-Feb-12 8:53 
AnswerRe: Thanks Pin
Keith Barrow29-Mar-12 2:58
professionalKeith Barrow29-Mar-12 2:58 
QuestionLink to part 2... Pin
Yop8321-Jan-12 7:41
Yop8321-Jan-12 7:41 
AnswerRe: Link to part 2... Pin
Keith Barrow29-Jan-12 9:20
professionalKeith Barrow29-Jan-12 9:20 
QuestionGreat article Pin
John Verco21-Oct-11 5:23
John Verco21-Oct-11 5:23 
AnswerRe: Great article Pin
Keith Barrow14-Nov-11 1:40
professionalKeith Barrow14-Nov-11 1:40 
GeneralMy vote of 5 Pin
nazishrizvi10-Aug-11 8:42
nazishrizvi10-Aug-11 8:42 
GeneralRe: My vote of 5 Pin
Keith Barrow14-Aug-11 11:11
professionalKeith Barrow14-Aug-11 11:11 
QuestionBut Why give up on beer? Pin
Devaiah Kokkalemada18-Jun-11 16:25
Devaiah Kokkalemada18-Jun-11 16:25 
AnswerRe: But Why give up on beer? Pin
Keith Barrow22-Jun-11 22:56
professionalKeith Barrow22-Jun-11 22:56 
GeneralMy vote of 5 Pin
Anuj Tripathi15-Jun-11 2:36
Anuj Tripathi15-Jun-11 2:36 
GeneralRe: My vote of 5 Pin
Keith Barrow22-Jun-11 22:56
professionalKeith Barrow22-Jun-11 22:56 
GeneralAwesome Pin
Anuj Tripathi15-Jun-11 2:36
Anuj Tripathi15-Jun-11 2:36 
GeneralRe: Awesome Pin
Keith Barrow15-Jun-11 4:29
professionalKeith Barrow15-Jun-11 4:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.