The goal of this post is to show how to get URL like this /{culture}/{Controller}/{Action}… in your application like /fa/Home/AboutUS
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.fa.ToString());
//Adding constraint for culture param
if (r.Constraints == null)
{
r.Constraints = new RouteValueDictionary();
}
r.Constraints.Add("culture", new CultureConstraint(Culture.en.ToString(),
Culture.fa.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
{
fa = 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("per", "ChangeCulture", "Account",
new { lang = (int)MvcLocalization.Helpers.Culture.fa, 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 with use of routing.