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.
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 me a line!