1. In my previous post I described the way of localization using session, but in real-world applications it's definitely not the best way of localization. Now I'll describe very simple and very powerful way of storing it in URL using routing mechanism.

    Also this way of localization will not require OutputCache tricks described in previous post

    The goal of this post is to show how to get URL  like this /{culture}/{Controller}/{Action}... in your application like /ru/Home/About.


    Custom Route Handlers

    First of all we'll need to extend standard MvcRouteHandler class. One class MultiCultureMvcRouteHandler for routes that will use culture in params and SingleCultureMvcRouteHandler class (will be used as a marker, no implementation changes)

    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 before calling it's base implementation we just get "culture" param from RouteData collection, create CultureInfo object and set it to current thread current culture. So here is a place where we set culture and will not use Application_AcquireRequestState method in Global.asax

    public class SingleCultureMvcRouteHandler : MvcRouteHandler {}

    As I mention this class will be used only for marking some routes for case if you'll need some routes to be culture independent.


    Registering routes

    Now lets go to Global.asax file where we have route registering method RegisterRoutes(). Right after last route mapping add foreach statement code snippet like 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()));
            }
       }
    
    }
    
    

    OK, lets go through this code... So for each route we first of all check whether its handler type is SingleCultureMvcRouteHandler or not... So if not we change RouteHandler property of the current route to MultiCulture one, add prefix to Url, add default culture and finally add constraint for culture param checking.

    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 enum of cultures
        public enum Culture
        {
            ru = 1,
            en = 2
        }


    Simple culture switching mechanism

    For changing culture we'll need following simple action which I placed in 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 languages 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" })%>



    Single culture case

    Finally, if we need some single culture route all we need to do is to set RouteHandler property to SingleCultureMvcRouteHandler  like this

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


    So, that's it :) Localization without using Session, without problems with OutputCache(will be explained in my next post) and with use of routing.

    Here is the link of  source code(project created in VS2010)



    kick it on DotNetKicks.com
    42

    View comments

About Me
About Me
My Photo
We are a team of enthusiastic developers with strong believes that

"source code can be beautiful"

"software can be 100% bug free"

"perfectness is not a feature, it's mandatory requirement"

"things are correct only if they change the world to make it better"

"nothing is impossible"

"be successful is as easy as thinking you are"

e: info(at)simplytech.co
fb: www.facebook.com/alexander.adamyan
t: +(37455)520005
a: Proshyan 2/1, Yerevan, Armenia, 0010
Blog Archive
Labels
Loading