Pull to refresh

Дополнение к локализации ASP.NET MVC – Используем маршрутизацию

Reading time4 min
Views2.7K
Original author: Alex Adamyan
Дополнение к прошлому посту от Alex Adamyan, посвященный локализации в ASP.NET MVC приложениях. Хотя это материал относится к ASP.NET MVC 2, его можно смело использовать и в версии 3.

В моем предыдущем посте я описал возможность локализации, используя сессию, однако для реальных приложений — это абсолютно не лучший путь. Сейчас я опишу очень простой и при этом очень мощный метод локализации, расположив ее в URL-адресе, используя механизм маршрутизации.

Также этот метод локализации не потребует трюк с OutputCache, как было описано в предыдущем посте.

Цель этого поста – возможность показать, как получить из URL-адреса вида /{culture}/{Controller}/{Action}... в Вашем приложении, URL-адрес вида /ru/Home/About.

Собственные Обработчики Маршрутов

Прежде всего, нам придется расширить стандартный класс MvcRouteHandler. Класс MultiCultureMvcRouteHandler будет предназначен для маршрутов, которые будут брать значение культуры из параметров и класс SingleCultureMvcRouteHandler (будет использоваться как маркер, без дополнительной имплементации).

public class MultiCultureMvcRouteHandler : MvcRouteHandler
{
  protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
  {
    var culture = requestContext.RouteData.Values["culture"].ToString();
    var ci = new CultureInfo(culture);
    Thread.CurrentThread.CurrentUICulture = ci;
    Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
    return base.GetHttpHandler(requestContext);
  }
}

В переопределенном методе GetHttpHandler перед вызовом его базовой имплементации мы просто получаем параметр «культуры» из массива RouteData, создаем объект CultureInfo и задаем его как текущую культуру текущего потока. Именно здесь мы устанавливаем культуру и не будем использовать метод Application_AcquireRequestState в Global.asax.

public class SingleCultureMvcRouteHandler : MvcRouteHandler {}

Как я заметил, данный класс будет использоваться только для выделения тех маршрутов, когда Вам потребуется, чтобы они были независимы от культуры.

Регистрируем маршруты

Теперь перейдем к файлу Global.asax, где мы имеем метод RegisterRoutes(), регистрирующий маршруты. Сразу же после последней привязки маршрута добавьте конструкцию foreach как в следующем примере.

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

  routes.MapRoute(
     "Default", // Route name
     "{controller}/{action}/{id}", // URL with parameters
     new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
  );

  foreach (Route r in routes)
  {
    if (!(r.RouteHandler is SingleCultureMvcRouteHandler))
    {
      r.RouteHandler = new MultiCultureMvcRouteHandler();
      r.Url = "{culture}/" + r.Url;
      //Adding default culture 
      if (r.Defaults == null)
      {
        r.Defaults = new RouteValueDictionary();
      }
      r.Defaults.Add("culture", Culture.ru.ToString());

      //Adding constraint for culture param
      if (r.Constraints == null)
      {
        r.Constraints = new RouteValueDictionary();
      }
      r.Constraints.Add("culture", new CultureConstraint(Culture.en.ToString(), 
Culture.ru.ToString()));
    }
  }

}

Отлично, давайте пройдемся по этому коду. Итак, для каждого маршрута мы, прежде всего, проверяем, является ли тип обработчика SingleCultureMvcRouteHandler, или нет. Так что, если мы поменяем свойство RouteHandler текущего маршрута на MultiCulture, то должны будем добавить префикс к URL-адресу, добавить культуру по-умолчанию и, наконец, добавить обработчик для проверки значения параметра культуры.

public class CultureConstraint : IRouteConstraint
{
  private string[] _values;
  public CultureConstraint(params string[] values)
  {
    this._values = values;
  }

  public bool Match(HttpContextBase httpContext,Route route,string parameterName,
            RouteValueDictionary values, RouteDirection routeDirection)
  {

    // Get the value called "parameterName" from the 
    // RouteValueDictionary called "value"
    string value = values[parameterName].ToString();
    // Return true is the list of allowed values contains 
    // this value.
    return _values.Contains(value);

  }

}

И перечисление культур

public enum Culture
{
  ru = 1,
  en = 2
}

Простой механизм переключения культур

Для изменения культур нам потребуется простое действие, которое я поместил в AccountController.

public ActionResult ChangeCulture(Culture lang, string returnUrl)
{
   if (returnUrl.Length >= 3)
   {
     returnUrl = returnUrl.Substring(3);
   }
   return Redirect("/" + lang.ToString() + returnUrl);
}

и частичное представление со ссылками — CultureSwitchControl.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

<%= Html.ActionLink("eng", "ChangeCulture", "Account",
  new { lang = (int)MvcLocalization.Helpers.Culture.en, returnUrl = 
  this.Request.RawUrl }, new { @class = "culture-link" })%>

<%= Html.ActionLink("рус", "ChangeCulture", "Account",
  new { lang = (int)MvcLocalization.Helpers.Culture.ru, returnUrl = 
  this.Request.RawUrl }, new { @class = "culture-link" })%>

Простой пример использования культуры

И наконец, если нам потребуется какой-либо маршрут независимый от культуры, все что нам нужно сделать – это задать свойство RouteHandler как SingleCultureMvcRouteHandler, например:

routes.MapRoute(
     "AboutRoute",
     "About",
     new { controller = "Home", action = "About"}
  ).RouteHandler = new SingleCultureMvcRouteHandler();

Итак, все :) Локализация без использования сессии, без проблем с OutputCache (будет рассмотрен в моем следующем посте) и с использованием маршрутизации.

Здесь находится ссылка на исходный код (проект создан в VS2010)
Tags:
Hubs:
+18
Comments10

Articles