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:

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:

Create the RESX file to hold the text.

Make the RESX contents publically available.

Repeat steps 1 and 2 for the other supported language and then for each view.

Get the text from the RESX in the View or Controller (or View Model!).

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:

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:

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

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:

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:

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:

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:

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:

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:

<globalizationculture="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:

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

Select the Tools icon, then Internet Options.

Click the Language button, under Appearance. You get this screen:

If not already added (I have in this screen), alick "Add" and add any Arabic language variant.

Select the version of Arabic you want to use and click "Move up" until it is at the top of the list.

OK out of the dialog screens.

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

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:

Create the Common.resx resource file in the /Resources folder.

Add TextDirection as a name and set its value to ltr (notrtl; this file defines the direction for English!).

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:

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:

Create Resources for each culture.

Make them public (note that this is the nth time I have mentioned this: I often forget!).

Get the text from the resource in the mark-up.

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.

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.

Share

About the Author

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 .

I'm not sure which article you read, but it doesn't seem like you read mine. This article doesn't multiply the number of views at all, you could take this approach and not add one single view depending upon your needs. If you'd bothered to download the source code, you'd see that the number of views is the same as the default MVC3 project, I could have continued to add languages without adding more views had I wished.

Even so, this isn't the reason I have reported you. There is no need for this kind of language or attitude.

Hi thanks for this very relevant article, however I have some things I do not agree with you. First you create the resx in every relevant folder calling them the name of the folder. This is not what I would have done. I thinks its much better to follow Nadeem Afanas example and have a seperate project for the resx files and then only have one resx file for each language. Second I cant see any reason to have common.resx and common.ar.resx files, the information within these files can be inserted into resources.resx and resources.ar.resx
However the article is written with a clear language and is short and straighforward.

The "mirroring" of the folder structure is really just to organize the folders in some way that makes the resource easy to find in the IDE. I have seen this suggested elsewhere, and it semi-follows what is done in a classic ASP.NET app. As for one resource file per language, I don't think this is a good idea at all, nor do I think this was Nadeem's intention. Like me he was trying to write an introductory article and doesn't need many resources for his demo so a single file is simpler to grasp. On a large project, a single file would be a disaster IMO: finding a value that needs to changed will be hard, and if a team is working on this, the likelyhood is they'll all be altering it at the same time, and the merge is tricky.

For larger projects I've seen the resources compiled in their own dll, this does have the great merit of being able to deploy just the resource dll if spelling mistakes are fixed, but for an introductory article I felt it unnecessary.

It looks like there has been a technical problem. This seems to be resolved as there have been no article updates since your post and the links now work. I tested when I saw your message and they were broken as you said. Thanks for the heads-up though!

Thanks for adding the information about the <globalization> web.config tag. It seems to be the only way to enable automatic culture info recognition using MVC3.

In WebForms applications you were able to set the Cuture and UICulture attributes for the page to "auto" in the @Page directive, allowing for .NET to determine the appropriate locale based on browser settings. It seems like in MVC3 this is no longer allowed. Am I correct?

Wow sorry for the slow reply, this fell off my RADAR. First, thanks for your feedback.

John Verco wrote:

In WebForms applications you were able to set the Cuture and UICulture
attributes for the page to "auto" in the @Page directive, allowing for .NET to
determine the appropriate locale based on browser settings. It seems like in
MVC3 this is no longer allowed. Am I correct?

I actually don't know: I've only ever used it in the web.config, and in code.

Indeed an article to be bookmarked.
My vote of 5.
Can I see a little hope where user needs to select the culture and will override the InitializeCulture class...
But definetly you saved my loads of time.. which I can now inverst in having "beer" !