Addition to ASP.NET MVC Localization - Using Routing

Original author: Alex Adamyan
  • Transfer
Addition to the last post from Alex Adamyan , dedicated to localization in ASP.NET MVC applications. Although this material relates to ASP.NET MVC 2, it can be safely used in version 3.

In my previous post I described the possibility of localization using a session, but for real applications this is absolutely not the best way. Now I will describe a very simple and at the same time very powerful localization method, placing it in the URL using the routing mechanism.

Also, this localization method will not require a trick with OutputCache, as described in a previous post.

The purpose of this post is to show how to get the URL of the form / ru / Home / About from the URL of the form / {culture} / {Controller} / {Action} ... in your application..

Own Route Handlers

First of all, we will have to extend the standard MvcRouteHandler class . The MultiCultureMvcRouteHandler class will be designed for routes that will take the culture value from the parameters and the SingleCultureMvcRouteHandler class (it will be used as a marker, without additional implementation).

public class MultiCultureMvcRouteHandler : MvcRouteHandler
{
  protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
  {
    var culture = requestContext.RouteData.Values["culture"].ToString();
    var ci = new CultureInfo(culture);
    Thread.CurrentThread.CurrentUICulture = ci;
    Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
    return base.GetHttpHandler(requestContext);
  }
}

In the overridden GetHttpHandler method , before invoking its basic implementation, we simply get the “culture” parameter from the RouteData array , create a CultureInfo object and set it as the current culture of the current stream. This is where we set the culture and will not use the Application_AcquireRequestState method in Global.asax.

public class SingleCultureMvcRouteHandler : MvcRouteHandler {}

As I noticed, this class will be used only to highlight those routes when you need them to be independent of culture.

We register routes

Now let's move on to the Global.asax file, where we have the RegisterRoutes () method that registers the routes. Immediately after the last route binding, add the foreach construct as in the following example.

public static void RegisterRoutes(RouteCollection routes)
{
  routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
  routes.MapRoute(
     "Default", // Route name
     "{controller}/{action}/{id}", // URL with parameters
     new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
  );
  foreach (Route r in routes)
  {
    if (!(r.RouteHandler is SingleCultureMvcRouteHandler))
    {
      r.RouteHandler = new MultiCultureMvcRouteHandler();
      r.Url = "{culture}/" + r.Url;
      //Adding default culture 
      if (r.Defaults == null)
      {
        r.Defaults = new RouteValueDictionary();
      }
      r.Defaults.Add("culture", Culture.ru.ToString());
      //Adding constraint for culture param
      if (r.Constraints == null)
      {
        r.Constraints = new RouteValueDictionary();
      }
      r.Constraints.Add("culture", new CultureConstraint(Culture.en.ToString(), 
Culture.ru.ToString()));
    }
  }
}

Great, let's go through this code. So, for each route, we first of all check whether the handler type is SingleCultureMvcRouteHandler or not. So, if we change the RouteHandler property of the current route to MultiCulture , we will need to add a prefix to the URL, add a default culture, and finally add a handler to check the value of the culture parameter.

public class CultureConstraint : IRouteConstraint
{
  private string[] _values;
  public CultureConstraint(params string[] values)
  {
    this._values = values;
  }
  public bool Match(HttpContextBase httpContext,Route route,string parameterName,
            RouteValueDictionary values, RouteDirection routeDirection)
  {
    // Get the value called "parameterName" from the 
    // RouteValueDictionary called "value"
    string value = values[parameterName].ToString();
    // Return true is the list of allowed values contains 
    // this value.
    return _values.Contains(value);
  }
}

And listing of cultures

public enum Culture
{
  ru = 1,
  en = 2
}

Simple crop switching mechanism

To change cultures, we need a simple action that I put in the AccountController.

public ActionResult ChangeCulture(Culture lang, string returnUrl)
{
   if (returnUrl.Length >= 3)
   {
     returnUrl = returnUrl.Substring(3);
   }
   return Redirect("/" + lang.ToString() + returnUrl);
}

and partial view with links - CultureSwitchControl.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%= Html.ActionLink("eng", "ChangeCulture", "Account",
  new { lang = (int)MvcLocalization.Helpers.Culture.en, returnUrl = 
  this.Request.RawUrl }, new { @class = "culture-link" })%>
<%= Html.ActionLink("рус", "ChangeCulture", "Account",
  new { lang = (int)MvcLocalization.Helpers.Culture.ru, returnUrl = 
  this.Request.RawUrl }, new { @class = "culture-link" })%>

A simple example of using culture

And finally, if we need a culture-independent route, all we need to do is set the RouteHandler property as SingleCultureMvcRouteHandler , for example:

routes.MapRoute(
     "AboutRoute",
     "About",
     new { controller = "Home", action = "About"}
  ).RouteHandler = new SingleCultureMvcRouteHandler();

So, that’s all :) Localization without using a session, without problems with OutputCache (to be discussed in my next post) and using routing.

Here is a link to the source code (the project was created in VS2010)

Also popular now: