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.

Post #1
Post #2

Download

,

Keine Kommentare

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;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

Keine Kommentare

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.

,

5 Kommentare