Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

asp.net mvc multilanguage urls/routing

Tags:

This is a two part question regarding asp.net mvc multilanguage urls/routing and SEO best practices/benefits…

Question Part 1)

I’m being asked to create a new ASP.NET MVC website that will support a minimum (at first) of two languages (English and French) perhaps in the future, 3 languages…

As far as localizing the application (labels, jQuery errors, etc) things should be ok using resource files and I’ve found many examples on this…but my concern/question is more about the URLs.

In terms of SEO, what is the recommended approach between these two fashions?

Fashion 1 (no culture folder)  
www.mydomain.com/create-account 
www.mydomain.com/creer-un-compte

Fashion 2 (with built in culture folder)
www.mydomain.com/create-account 
www.mydomain.com/fr/creer-un-compte <--notice the “fr” folder 

Is there a known issue/penalty in using one over the other?

Or is it so small that it becomes irrelevant!


Question Part 2)

To achieve Fashion 2, I’ve already found an article here: ASP.NET MVC - Localization route

But I’d be curious to find how to achieve Fashion 1.

Does anyone have any links?

In addition and as far as I know, URL Rewriting is not what I’m looking for since I do not wish to “redirect” users…I simply want the urls to be showing in the appropriate language without having to show the culture in the url

Thanks in advance for any help on this!

like image 756
Vlince Avatar asked Jul 19 '11 15:07

Vlince


2 Answers

You can create a base controller that has the localization logic as below:

public abstract class LocalizedController : Controller
{
  protected override void ExecuteCore()
  {
    HttpCookie cookie;
    string lang = GetCurrentCulture();
    Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang, false);

    // set the lang value into route data
    RouteData.Values["lang"] = lang;

    // save the location into cookie
    cookie = new HttpCookie("DPClick.CurrentUICulture",
    Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName)
    {
      Expires = DateTime.Now.AddYears(1)
    };

    HttpContext.Response.SetCookie(cookie);  
    base.ExecuteCore();
  }

  private string GetCurrentCulture()
  {
    string lang;

    // set the culture from the route data (url)

    if (RouteData.Values["lang"] != null &&
      !string.IsNullOrWhiteSpace(RouteData.Values["lang"].ToString()))
    {
      lang = RouteData.Values["lang"].ToString();
      if (Localization.Locales.TryGetValue(lang, out lang))
      {
        return lang;
      }
    }

    // load the culture info from the cookie
    HttpCookie cookie = HttpContext.Request.Cookies["DPClick.CurrentUICulture"];
    if (cookie != null)
    {
      // set the culture by the cookie content
      lang = cookie.Value;
      if (Localization.Locales.TryGetValue(lang, out lang))
      {
        return lang;
      }
    }

    // set the culture by the location if not speicified
    lang = HttpContext.Request.UserLanguages[0];
    if (Localization.Locales.TryGetValue(lang, out lang))
    {
      return lang;
    }

    //English is default
    return Localization.Locales.FirstOrDefault().Value;
  }
}

The above controller satisfy part 2 of your question if you want to ignore the culture folder just don't assign the lang in the RouteData. Of course to achieve part 2 you have to add routing for culture as below:

routes.MapRoute(
  "Localization", // Route name
  "{lang}/{controller}/{action}/{id}", // URL with parameters
  new {controller = "Default", action = "Index", id = UrlParameter.Optional}, // Parameter defaults
  new {lang = @"\w{2,3}(-\w{4})?(-\w{2,3})?"}
);
like image 172
Feras Kayyali Avatar answered Sep 20 '22 05:09

Feras Kayyali


To achieve what you want you basically need to implement three things:

A multi-language aware route to handle incoming URLs:

routes.MapRoute(
    name: "DefaultLocalized",
    url: "{lang}/{controller}/{action}/{id}",
    constraints: new { lang = @"(\w{2})|(\w{2}-\w{2})" },   // en or en-US
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

A LocalizationAttribute to handle these kinds of multi-language requests:

public class LocalizationAttribute : ActionFilterAttribute
{
    private string _DefaultLanguage = "en";

    public LocalizationAttribute(string defaultLanguage)
    {
        _DefaultLanguage = defaultLanguage;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        string lang = (string)filterContext.RouteData.Values["lang"] ?? _DefaultLanguage;
        if (lang != _DefaultLanguage)
        {
            try
            {
                Thread.CurrentThread.CurrentCulture =
                Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang);
            }
            catch (Exception e)
            {
                throw new NotSupportedException(String.Format("ERROR: Invalid language code '{0}'.", lang));
            }
        }
    }
}

An helper method to generate these URLs within your application: this can be done in multiple ways, depending on your application logic. For example, if you need to do it within your Razor Views, the best thing you can do is to write a couple extension methods to make your Html.ActionLink and Url.Action accept a CultureInfo object as a parameter (and/or use CultureInfo.CurrentCulture as the default one), such as the following:

  • Code sample for multi-language Html.ActionLink extension
  • Code sample for multi-language Url.Action extension

(both are written in C#)

You can also avoid the extension method pattern and write them as MultiLanguageActionLink / MultiLanguageAction instead.

For additional info and further samples on this topic you can also read this post on my blog.

like image 3
Darkseal Avatar answered Sep 24 '22 05:09

Darkseal