Monday, January 10, 2011

Localization in ASP.NET MVC2 with Data Annotations

Let’s start by getting everyone on the same page. Create a new Project | C# | Web | ASP.NET MVC 2 Web Application using the .NET Framework 4. With or without a test project, it’s up to you. In this example we will setting up both English and French translation. Adding more languages should be straight forward.

Setting up the Resource folder structure.

At the root of your MVC application create a Resources folder. Inside this folder create a folder structure like the one to the left.

I like to try and keep all the resources organized so under the Models and Views folders I like to break things apart by controller. Ultimately how you choose to store these items is up to you. Changes to the folder structure will affect the namespaces.

Adding resource files and what you need to know.Adding a resource file is the easy part. Making the setting changes that are needed turns out to be a repetitive process that will get old real fast. Right click on the Views | Home folder and select Add | New Item. In the Add new item dialog type the word resource into the search installed templates textbox in the upper right hand corner. Select the Resource File template and type Index.resx.

Once the file has been added it should open up in edit mode. Make the following changes:

Accessing the resource details is pretty straight forward after this part. For example you could type the fully qualified name of a resource property like “applicationname.Resources.Views.Home.Index.PageTitle”.

That namespace tends to be a little long so add the following to the properties of the resource file.

Add a custom tool namespace to the resource file “ViewRes.Home”. You will then be able to access the values like “ViewRes.Home.Index.PageTitle”.

This process turns out to be one of the repetitive processes I was referencing above. In order to limit the namespace length this modification must be made on each file for each language although its not required.

Also note that the second part of the entered namespace will need to change depending on what folder you are in for example you may change the namespace to “ViewRes.Account”.

Let’s go ahead and add the French file. Do the same as above but make sure the name of the resource file is Index.fr.resx. Don’t forget to change the Custom Tool Namespace value in the properties of the resource file. Since this would be the French file make sure the values in the resource file are French. I like to use Google translate (http://translate.google.com/) although I strongly recommend having this translation done professionally. Here is the translation:

The GetCookieCulture, GetSessionCulture, GetBrowserCulture and SetCurrentLanguage are not required to be in the global.asax.cs file although I have put them there ease of explanation. Some folks may look at this and decide to take this code and put it in an attribute and assign that attribute to every class. This will cause problems when applying localization to the data annotations. This is caused by the order of events in the ASP.NET MVC pipeline.

Once this code is in place lets add the following action to our Home controller.

Now we need to make a culture selector user control. Since we want this to be available anywhere in the site lets add a new partial view to the Views | Shared and call it CultureUserControl.ascx. Insert the following code into that user control.

Run the application and click the French link next to the login link in the upper right hand corner. Note that the Page Title and the welcome message are now in French.

Getting localization to work with Data AnnotationsGetting localization to work with data annotations is not difficult but can be confusing. Some attributes support localization and other do not. Luckily you can create your own attributes to solve this problem. That being said we will use two attributes Required which supports localization and DisplayName that does not support localization so we will have to create our own attribute.

Lets get started with the new DisplayName attribute. At the root of the application create a folder called “Infrastructure” inside that folder create another folder called “Attributes”. This is where all the attributes your create will go. Lets create a new class in this folder called “LocalizedDisplayNameAttribute”. Paste the following code into the file.

Now we have a new DisplayName attribute that supports localization. The next step would be to create the localization files needed for the Account | LogonModel class. Using the section above lets create the following files in the Resources | Models | Accounts folder:

LogonModel.resx

LogonModel.fr.resx

When creating these files I used the custom tool namespace of “ModelRes.Account”. You will see how this is used in a moment. Now inside of both the resource files make sure they have the following name value pairs:

To test this lets start the application and click the logon link in the upper right hand corner. On the login page you should see the labels already in English for username, password and remember me. Click the French link in the upper right hand corner next to the logon link will cause those labels to now turn into French. Clicking the login button without adding any information in the username and password field will cause a post back which should return validation errors. Those should now be in French. here is an example of what it should look like if everything worked correctly.

This should be enough to get you on your way to localizing an ASP.NET MVC 2 application with data annotations.