Localizing ASP.NET MVC routes/URLs

file0001815392240

Most websites now use some form of “pretty” URL. Gone are the days of URLs that exposed the relative path structure of the server…ending in “Default.aspx”.

There are many benefits to decoupling your URLs from the underlying technology. URLs should really be viewed as part of the presentation layer. They are part of the user interface and so, as much as possible, they should not contain nonsensical data.

Good URLs are “hackable.” A web-savvy user can navigate your site by editing the URL bar. Such URLs can also act as breadcrumbs showing the location of the page within the wider site.

Also, readable URLs help with security. Phishing attacks exploit the fact that many URLs contain so much technical garbage that we stop looking at them.

As user interface elements, URLs should really be subject to localization. It’s jarring when a site that has been expertly translated from English to German has URLs like company.com/About and company.com//Contact. Clearly, German speakers would wish to see company.com/ÜberUns and company.com//Kontakt. English URLs unnecessarily expose the development history of the application.

Using ‘RouteLocalization’

The default routing in ASP.NET MVC applications is based on the controller and action names. While we could add new controllers and actions with translated names, this would be a messy approach to user interface translation.

We could add multiple routes to each action…one for each of the languages we wished to support. While this would work for inbound routing, when the user clicked on an internal link (outbound routing), the URLs would revert to the first rule (e.g. default language).

Fortunately, there’s a package available to help us out—RouteLocalization. There are actually two NuGet packages—one for MVC and one for Web API.

We are going to localize the default Visual Studio ASP.NET MVC application to English and German. It has three action methods in a Home controller.

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult About()
    {
        ViewBag.Message = "Your application description page.";

        return View();
    }

    public ActionResult Contact()
    {
        ViewBag.Message = "Your contact page.";

        return View();
    }
}

We’ll use the more modern attribute-based routing model in place of the default convention-based routing. Add the routing attributes to the action methods.

[Route("~/")]
public ActionResult Index()
{
    ...
}

[Route("~/About")]
public ActionResult About()
{
    ...
}

[Route("~/Contact")]
public ActionResult Contact()
{
    ...
}

This will result in us having three routes

  • company.com
  • company.com/About
  • company.com/Contact

It won’t work just now as we’ll have to initialize the attribute-based routing. We’ll do that as we configure the localization.

We’d like the German language routes to be

  • company.com
  • company.com/ÜberUns
  • company.com/Kontakt

Clearly the first (default) route is culture neutral.

Configure the localized routing

Install the RouteLocalization.Mvc package

Install-Package RouteLocalization.Mvc

Now it’s time to set up the localized routing in the RouteConfig class.

We’ll need to import some namespaces.

using RouteLocalization.Mvc;
using RouteLocalization.Mvc.Setup;

using Controllers;

Delete the convention-based routing rules in the RegisterRoutes method, leaving

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
}

Let’s add code to the RegisterRoutes method to do the necessary configuration.

First, initialize attribute-based routing with a provider that allows it to proxy routes.

routes.MapMvcAttributeRoutes(Localization.LocalizationDirectRouteProvider);

Now create and configure the localization manager.

ISet<string> acceptedCultures = new HashSet<string>() { "en", "de" };

var configuration = 
    new Configuration
    {
        DefaultCulture = DefaultCulture,
        AcceptedCultures = acceptedCultures,
        AttributeRouteProcessing = 
            AttributeRouteProcessing.AddAsNeutralAndDefaultCultureRoute,
        AddCultureAsRoutePrefix = true
    };

var localization = new Localization(configuration);

English (en) is the default language and we are going to have localizations for English and German (de). As we have a netural route, we’ll using AttributeRouteProcessing.AddAsNeutralAndDefaultCultureRoute as the route processing setting. We are also going to add the culture as a route prefix, so our URLs will now be of the form

  • company.com/en/About
  • company.com/de/ÜberUns

We’re now ready to create the initial routes.

localization.TranslateInitialAttributeRoutes();

All that’s left to do is specify the translations. RouteLocalization supports fluid configuration, so we can add the translations in one statement.

localization.ForCulture("de")
    .ForController<HomeController>()
    .ForAction(x => x.About())
    .AddTranslation("ÜberUns")
    .ForAction(x => x.Contact())
    .AddTranslation("Kontakt");

Finally, let’s tell the application to use the culture specified by the user’s browser.

CultureSensitiveHttpModule.GetCultureFromHttpContextDelegate = 
    Localization.DetectCultureFromBrowserUserLanguages(acceptedCultures, 
        DefaultCulture);

You may prefer to allow the user to chose their preferred language manually—e.g. if your application might be accessed from public terminals.

That’s it. Run you application, change your browser language to English or German and gaze in wonder at your localized URLs.

I’ve created a Gist containing the code for the entire RouteConfig.cs class.

If you are interesting in improving your ASP.NET MVC skills Learning Tree runs a four-day course that covers the main elements of the framework.

There’s also a one-day, getting-started course for those who need the basics in a hurry.

Type to search blog.learningtree.com

Do you mean "" ?

Sorry, no results were found for your query.

Please check your spelling and try your search again.