Как лучше всего установить культуру / культуру пользовательского интерфейса в приложении ASP.net MVC
В настоящее время у меня есть класс CultureController, который выглядит так:
public class CultureController : Controller
{
public ActionResult SetSpanishCulture()
{
HttpContext.Session["culture"] = "es-ES";
return RedirectToAction("Index", "Home");
}
public ActionResult SetFrenchCulture()
{
HttpContext.Session["culture"] = "fr-FR";
return RedirectToAction("Index", "Home");
}
}
и гиперссылку для каждого языка на домашней странице со ссылкой, например, этой:
<li><%= Html.ActionLink("French", "SetFrenchCulture", "Culture")%></li>
<li><%= Html.ActionLink("Spanish", "SetSpanishCulture", "Culture")%></li>
который отлично работает, но я думаю, что есть более подходящий способ сделать это.
Я читаю Культуру, используя следующий ActionFilter http://www.iansuttle.com/blog/post/ASPNET-MVC-Action-Filter-for-Localized-Sites.aspx . Я немного новичок в MVC, поэтому не уверен, что устанавливаю это в правильном месте. Я не хочу делать это на уровне web.config, это должно быть основано на выборе пользователя. Я также не хочу проверять их http-заголовки, чтобы узнать культуру из настроек браузера.
Редактировать:
Чтобы быть ясным - я не пытаюсь решить, использовать сеанс или нет. Я доволен этим. Я пытаюсь понять, лучше ли это сделать в контроллере культуры, который имеет метод действия для каждой культуры, который должен быть установлен, или есть лучшее место в конвейере MVC для этого?
источник
Ответы:
Я использую этот метод локализации и добавляю параметр маршрута, который устанавливает культуру и язык всякий раз, когда пользователь посещает example.com/xx-xx/.
Пример:
routes.MapRoute("DefaultLocalized", "{language}-{culture}/{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "", language = "nl", culture = "NL" });
У меня есть фильтр, который выполняет фактическую настройку культуры / языка:
using System.Globalization; using System.Threading; using System.Web.Mvc; public class InternationalizationAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { string language = (string)filterContext.RouteData.Values["language"] ?? "nl"; string culture = (string)filterContext.RouteData.Values["culture"] ?? "NL"; Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(string.Format("{0}-{1}", language, culture)); Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(string.Format("{0}-{1}", language, culture)); } }
Чтобы активировать атрибут интернационализации, просто добавьте его в свой класс:
[Internationalization] public class HomeController : Controller { ...
Теперь всякий раз, когда посетитель переходит на http://example.com/de-DE/Home/Index, отображается немецкий сайт.
Надеюсь, этот ответ укажет вам правильное направление.
Я также сделал небольшой пример проекта MVC 5, который вы можете найти здесь
Просто перейдите по адресу http: // {yourhost}: {port} / en-us / home / index, чтобы увидеть текущую дату на английском языке (США), или измените ее на http: // {yourhost}: {port} / de -de / home / index для немецкого языка и т. д.
источник
Я знаю, что это старый вопрос, но если вы действительно хотите, чтобы это работало с вашим ModelBinder (в отношении
DefaultModelBinder.ResourceClassKey = "MyResource";
ресурсов, указанных в аннотациях данных классов модели представления), контроллер или дажеActionFilter
слишком поздно установить культуру .Культура может быть установлена
Application_AcquireRequestState
, например:protected void Application_AcquireRequestState(object sender, EventArgs e) { // For example a cookie, but better extract it from the url string culture = HttpContext.Current.Request.Cookies["culture"].Value; Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture); Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture); }
РЕДАКТИРОВАТЬ
На самом деле, есть лучший способ использовать собственный обработчик маршрутов, который устанавливает культуру в соответствии с URL-адресом, прекрасно описанный Алексом Адамяном в своем блоге .
Все, что нужно сделать, - это переопределить
GetHttpHandler
метод и установить там культуру.public class MultiCultureMvcRouteHandler : MvcRouteHandler { protected override IHttpHandler GetHttpHandler(RequestContext requestContext) { // get culture from route data 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); } }
источник
Я бы сделал это в событии Initialize контроллера вот так ...
protected override void Initialize(System.Web.Routing.RequestContext requestContext) { base.Initialize(requestContext); const string culture = "en-US"; CultureInfo ci = CultureInfo.GetCultureInfo(culture); Thread.CurrentThread.CurrentCulture = ci; Thread.CurrentThread.CurrentUICulture = ci; }
источник
Поскольку это параметр, который сохраняется для каждого пользователя, сеанс является подходящим местом для хранения информации.
Я бы изменил ваш контроллер, чтобы он принимал строку культуры в качестве параметра, вместо того, чтобы иметь другой метод действия для каждой потенциальной культуры. Добавить ссылку на страницу легко, и вам не нужно писать один и тот же код повторно каждый раз, когда требуется новая культура.
public class CultureController : Controller { public ActionResult SetCulture(string culture) { HttpContext.Session["culture"] = culture return RedirectToAction("Index", "Home"); } } <li><%= Html.ActionLink("French", "SetCulture", new {controller = "Culture", culture = "fr-FR"})%></li> <li><%= Html.ActionLink("Spanish", "SetCulture", new {controller = "Culture", culture = "es-ES"})%></li>
источник
Какое лучшее место - ваш вопрос. Лучшее место - внутри метода Controller.Initialize . MSDN пишет, что он вызывается после конструктора и перед методом действия. В отличие от переопределения OnActionExecuting, размещение вашего кода в методе Initialize позволяет вам получить выгоду от локализации всех настраиваемых аннотаций и атрибутов данных в ваших классах и ваших свойствах.
Например, моя логика локализации исходит из класса, который вводится в мой настраиваемый контроллер. У меня есть доступ к этому объекту, поскольку Initialize вызывается после конструктора. Я могу назначать культуру потока и не отображать все сообщения об ошибках правильно.
public BaseController(IRunningContext runningContext){/*...*/} protected override void Initialize(RequestContext requestContext) { base.Initialize(requestContext); var culture = runningContext.GetCulture(); Thread.CurrentThread.CurrentUICulture = culture; Thread.CurrentThread.CurrentCulture = culture; }
Даже если ваша логика не находится внутри класса, подобного приведенному мной примеру, у вас есть доступ к RequestContext, который позволяет вам иметь URL-адрес, HttpContext и RouteData, которые вы можете выполнять практически любым возможным синтаксическим анализом .
источник
Если вы используете субдомены, например, pt.mydomain.com для установки португальского, например, использование Application_AcquireRequestState не будет работать, потому что оно не вызывается при последующих запросах кеша.
Чтобы решить эту проблему, я предлагаю такую реализацию:
Добавьте параметр VaryByCustom в OutPutCache следующим образом:
[OutputCache(Duration = 10000, VaryByCustom = "lang")] public ActionResult Contact() { return View("Contact"); }
В global.asax.cs получите культуру от хоста с помощью вызова функции:
protected void Application_AcquireRequestState(object sender, EventArgs e) { System.Threading.Thread.CurrentThread.CurrentUICulture = GetCultureFromHost(); }
Добавьте функцию GetCultureFromHost в global.asax.cs:
private CultureInfo GetCultureFromHost() { CultureInfo ci = new CultureInfo("en-US"); // en-US string host = Request.Url.Host.ToLower(); if (host.Equals("mydomain.com")) { ci = new CultureInfo("en-US"); } else if (host.StartsWith("pt.")) { ci = new CultureInfo("pt"); } else if (host.StartsWith("de.")) { ci = new CultureInfo("de"); } else if (host.StartsWith("da.")) { ci = new CultureInfo("da"); } return ci; }
И, наконец, переопределите GetVaryByCustomString (...), чтобы также использовать эту функцию:
public override string GetVaryByCustomString(HttpContext context, string value) { if (value.ToLower() == "lang") { CultureInfo ci = GetCultureFromHost(); return ci.Name; } return base.GetVaryByCustomString(context, value); }
Функция Application_AcquireRequestState вызывается для некэшированных вызовов, что позволяет создавать и кэшировать контент. GetVaryByCustomString вызывается при кэшированных вызовах, чтобы проверить, доступен ли контент в кеше, и в этом случае мы снова проверяем входящее значение домена хоста, вместо того, чтобы полагаться только на текущую информацию о культуре, которая могла измениться для нового запроса (потому что мы используем поддомены).
источник
1: Создайте настраиваемый атрибут и переопределите метод следующим образом:
public class CultureAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { // Retreive culture from GET string currentCulture = filterContext.HttpContext.Request.QueryString["culture"]; // Also, you can retreive culture from Cookie like this : //string currentCulture = filterContext.HttpContext.Request.Cookies["cookie"].Value; // Set culture Thread.CurrentThread.CurrentCulture = new CultureInfo(currentCulture); Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(currentCulture); } }
2: В App_Start найдите FilterConfig.cs, добавьте этот атрибут. (это работает для ВСЕГО приложения)
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { // Add custom attribute here filters.Add(new CultureAttribute()); } }
Это оно !
Если вы хотите определить культуру для каждого контроллера / действия вместо всего приложения, вы можете использовать этот атрибут следующим образом:
[Culture] public class StudentsController : Controller { }
Или:
[Culture] public ActionResult Index() { return View(); }
источник
protected void Application_AcquireRequestState(object sender, EventArgs e) { if(Context.Session!= null) Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = (Context.Session["culture"] ?? (Context.Session["culture"] = new CultureInfo("pt-BR"))) as CultureInfo; }
источник