- Posted by Ian Suttle on February 21, 2009
- Filed under .NET 3.5 | ASP.Net MVC
Disclaimer: This is my first attempt at creating an ASP.NET MVC Action Filter and was developed using the ASP.NET MVC Release Candidate Refresh 1 release. I doubt things change much on this topic before RTM.
Your mission: to make your site available to multiple cultures and languages by way of ASP.NET MVC localization, globalization, internationalization, or whatever term you prefer to use.
You’re familiar with cultures right? It's the combination of language and country, usually identified in software using ISO culture codes. The ISO culture code for English in the United States is "en-US", "en-CA" for English in Canada, "fr-CA" for French in Canada, and so on.
If you've got or want a site which supports multiple cultures using ASP.NET MVC localization you need to be familiar with setting CultureInfo values for the current thread and UI. The .NET Framework uses this info to do a few key things. First, if you're using resource files the framework knows which to pick up because the resource file name specifies the culture. Secondly the culture is used as a formatter argument for various methods such as ToString(). The culture needs to be set for each page request as it's based on the current thread. For more info on this end of things check out Localization in ASP.NET 2.0 - I found it quite helpful.
Once you know which culture the user's experience should use you'll want to persist that somehow. Maybe it's in a cookie, the session, embedded in the URL, etc. In ASP.NET web forms you might include this detection in a base page or the global.asax for each page load.
ASP.NET MVC provides another nifty trick for handling things on each action call - Action Filters. I thought it'd be interesting to figure out some ways of doing internationalization in ASP.NET MVC so I came up with an ASP.NET MVC action filter to include on a controller class for setting the culture for each action call. Currently it supports keeping the culture in a cookie, the querystring, session state, and the URL path.
Using the filter is simple. Assuming the SetCultureAttribute class is included in your project you decorate a controller class with it. You must set the CultureStore (type of CultureLocation) to indicate where the culture code comes from, and then one or two other properties to provide more hints at getting to the culture code. Here's an example for storing the culture code in the query string. In this example the URL would be expected to include a param called "culture" such as in this URL: "http://localhost/?culture=en-GB".
[HandleError]
[SetCulture(CultureStore = SetCultureAttribute.CultureLocation.QueryString, QueryStringParamName = "culture")]
public class HomeController : Controller
{ ... actions ... }
If stored in session you'd modify the SetCulture properties to something similar to:
[SetCulture(CultureStore = SetCultureAttribute.CultureLocation.Session, SessionParamName = "culture")]
If stored in a cookie you'd modify the SetCulture properties to something similar to:
[SetCulture(CultureStore = SetCultureAttribute.CultureLocation.Cookie, CookieName = "culture")]
If stored in the URL path your actions will need to expect language and country parameters and your route needs to account for the same. For my implementation I modified my default route and assumed the "en-US" culture if nothing else was specified. This code expects a URL formatted like "http://localhost/en/us/home/about". There will be some kinks to work out with this one:
routes.MapRoute(
"Default", // Route name
"{language}/{country}/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "", language = "en", country = "US" } // Parameter defaults
);
Using this updated route you'd modify the SetCulture properties to:
[SetCulture(CultureStore = SetCultureAttribute.CultureLocation.URL, CountryActionParamName = "country", LanguageActionParamName = "language")]
... etc.
Assuming the culture code is found then the thread and UI culture will be automatically set in this fashion:
CultureInfo culture = new CultureInfo (cultureCode);
System.Threading.Thread.CurrentThread.CurrentCulture = culture;
System.Threading.Thread.CurrentThread.CurrentUICulture = culture;
Click here to get the full SetCultureAttribute class. Don't forget to change the namespace to match your own.
Have you any experience with localized sites in ASP.NET MVC? How did you approach this topic?