ASP.NET MVC URLs localization example
I have been asked to provide a working example of the technics I showed in my last two posts.
This example is about localization with the ASP.NET MVC framework using routing, a custom MvcRouteHandler, an IRouteConstraint and cultures. It demonstrates an approach how to work with URLs like mysite.com/en-US/Search.
Localization of URLs using ASP.NET MVC, Routing & Constraints
Developing a multilingual website, besides localization of the page’s content it also makes sense to localize the URLs. In my first post dealing with localization I explained my basic approach how to localize pages using a derived MvcRouteHandler and a subtype of Route. In this post I am going to show a possibility to localize URLs by using routes and constraints.
What I want
1) I am going to have a search on my new site. I want this search page to have nice localized URLs for the different languages. The term “search” of the URL should be translated to each supported language.
Examples:
| USA | mysite.com/en-US/Search |
| Great Britain | mysite.com/en-GB/Search |
| Germany | mysite.com/de-DE/Suche |
| Switzerland | mysite.com/de-CH/Suche |
| Italy | mysite.com/it-IT/Ricerca |
2) Furthermore I want to have URLs with the same ending for different languages. This ist usefull for URLs, which do not have to be translated.
Examples:
| USA | mysite.com/en-US/FAQ |
| Great Britain | mysite.com/en-GB/FAQ |
| Germany | mysite.com/de-DE/FAQ |
| Switzerland | mysite.com/de-CH/FAQ |
| Italy | mysite.com/it-IT/FAQ |
Approach
1) This can be done by registering different routes and using custom constraints. I did it like this:
public static void RegisterRoutes(RouteCollection routes)
{
LanguageConstraint enLanguageConstraint = new LanguageConstraint("en");
LanguageConstraint deLanguageConstraint = new LanguageConstraint("de");
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(
new MultiLingualRoute("FAQ",
new RouteValueDictionary(new { controller = "CMS", action = "Show", Id = 1 }),
new RouteValueDictionary(new { validCountry = new CulturesConstraint() }),
new MvcApplication1.MultiLingualMvcRouteHandler())
);
routes.Add(
new MultiLingualRoute("Suche",
new RouteValueDictionary(new { controller = "Search", action = "AdvancedSearch" }),
new RouteValueDictionary(new { validLanguage = deLanguageConstraint }),
new MvcApplication1.MultiLingualMvcRouteHandler())
);
routes.Add(
new MultiLingualRoute("Search",
new RouteValueDictionary(new { controller = "Search", action = "AdvancedSearch" }),
new RouteValueDictionary(new { validLanguage = enLanguageConstraint }),
new MvcApplication1.MultiLingualMvcRouteHandler())
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
foreach (Route r in routes)
{
if (r.GetType() == typeof(MultiLingualRoute))
{
r.Url = "{culture}/" + r.Url;
r.Defaults.Add("culture", "en-US");
}
}
}
At the first lines two LanguageConstraints are created. These constraints make sure, that this route is only used, if the given language is used.
public class LanguageConstraint : IRouteConstraint
{
string Language { set; get; }
/// <summary>
/// Creates a new lanugage constraint
/// </summary>
/// <param name="Language">Allowed lanuage</param>
public LanguageConstraint(string language)
{
Language = language;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
String CultureFromRoute = values["culture"].ToString();
try
{
if (CultureFromRoute.Length > 2 &amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp; CultureFromRoute.StartsWith(Language))
{
//validate, that the given culture from routeData is a valid culture
System.Globalization.CultureInfo.GetCultureInfo(CultureFromRoute);
return true;
}
}
catch (Exception) { }
return false;
}
}
2) For those URLs with the same ending I crated a CulturesConstraint. This makes sure, that the page is only showed for given cultures
public class CulturesConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
List<String> CultureListe = new List<String>();
CultureListe.Add("en-GB");
CultureListe.Add("en-US");
CultureListe.Add("de-DE");
CultureListe.Add("it-IT");
return CultureListe.Contains(values["culture"].ToString());
}
}
Feel free to drop a line, Alebo
Localization with ASP.NET MVC using Routing
In this article I am showing how to localize pages with ASP.NET MVC. Therefor I create a new Type of Route and a special RouteHandler.
Perhaps I will use ASP.NET for a new multilingual website, therefore I had to figure out how to create localized pages with ASP.NET MVC. It took a lot of time to search for some implementations, but I could not find any useable code for my problem, so here is my approach.
What I want
For my site the URL schema should look like this in general:
/{culture}/{site}
Imagine there is a page called FAQ, which is available in different languages. Here are some sample URLs for these pages:
/en-US/FAQ
/de-DE/FAQ
/de-CH/FAQ
Additional I want to have some pages which are in a single language. The backend to modify the FAQ pages for example just needs to be in English.
My approach
My idea was to automatically add the {culture} parameter to those routes, which should be multilingual. I created a new class “MultiLingualMvcRouteHandler”. This Routehandler gets the culture from RouteData and sets CurrentCulture as well as CurrentUICulture. You can use this informations in custom HTMLHelpers.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplication1
{
public class MultiLingualMvcRouteHandler : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)
{
String Culture = requestContext.RouteData.Values["culture"].ToString();
System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfoByIetfLanguageTag(Culture);
System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfoByIetfLanguageTag(Culture);
return base.GetHttpHandler(requestContext);
}
}
}
I also created a new Subtype of Route. Mainly this new Route is to discern between non multilingual and multilingual Routes. As you can see there are no additional methods or attributes yet.
public class MultiLangualRoute : Route
{
public MultiLangualRoute(string url, IRouteHandler routeHandler) : base(url, routeHandler) { }
public MultiLangualRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler) : base(url, defaults, routeHandler) { }
public MultiLangualRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler) : base(url, defaults, constraints, routeHandler) { }
public MultiLangualRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler) : base(url, constraints, dataTokens, routeHandler) { }
}
After that I registered my FAQ page like in the global.asax. At the bottom of the method I check the RoutesCollection for Routes of my new type MultilingualRoute. The URLs of those types are expanded at the beginning with {culture}. The default culture is set to en-US.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(
new Route("Backend/Edit/FAQ",
new RouteValueDictionary(new { controller = "CMS", action = "Edit", Id = 1}),
new System.Web.Mvc.MvcRouteHandler())
);
routes.Add(
new MultiLangualRoute("FAQ",
new RouteValueDictionary(new { controller = "CMS", action = "Show", Id = 1 }),
new MvcApplication1.MultiLingualMvcRouteHandler())
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
foreach (Route r in routes)
{
if (r.GetType() == typeof(MultiLangualRoute))
{
r.Url = "{culture}/" + r.Url;
r.Defaults.Add("culture", "en-US");
}
}
}
You can create links like this:
<%= Html.ActionLink("Show me the FAQ in German", "Show", "CMS", new {id = 1, culture="de-DE"}) %>
If you want to generate a link to a page with the actual culture, you can use the ActionLink helper without setting the culture.
<%= Html.ActionLink("Show me the FAQ", "Show", "CMS", new {id = 1}) %>
That’s it so far. Hope this is helpfull for someone else.