Установить культуру в приложении ASP.Net MVC
Какое место лучше всего подходит для культуры Culture/UI в приложении 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 noob, поэтому не уверен, что я устанавливаю это в нужном месте. Я не хочу делать это на уровне web.config, он должен основываться на выборе пользователя. Я также не хочу проверять их http-заголовки, чтобы получить культуру из настроек браузера.
Edit:
Просто, чтобы быть ясным - я не пытаюсь решить, использовать ли сеанс или нет. Я доволен этим. То, что я пытаюсь решить, - это лучше всего сделать это в контроллере культуры, который имеет метод действий для каждой культуры, который должен быть установлен, или есть лучшее место в конвейере MVC для этого?
Ответы
Ответ 1
Я использую этот метод локализации и добавил параметр маршрута, который устанавливает культуру и язык всякий раз, когда пользователь посещает example.com/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 для немецкого итератора.
Ответ 2
Я знаю, что это старый вопрос, но если вы действительно хотите, чтобы это работало с вашим ModelBinder (в отношении DefaultModelBinder.ResourceClassKey = "MyResource";
, а также с ресурсами, указанными в аннотациях данных классов viewmodel), контроллер или даже 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-адресом, отлично описанным Alex Adamyan on его блог.
Все, что нужно сделать, это переопределить метод 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);
}
}
Ответ 3
Я бы сделал это в событии 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;
}
Ответ 4
Будучи тем, что это параметр, который сохраняется для каждого пользователя, сеанс является подходящим местом для хранения информации.
Я бы сменил ваш контроллер, чтобы взять строку культуры в качестве параметра, вместо того, чтобы иметь разные методы действий для каждой потенциальной культуры. Добавление ссылки на страницу легко, и вам не нужно будет писать один и тот же код повторно в любое время, когда требуется новая культура.
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>
Ответ 5
Какое лучшее место - ваш вопрос. Лучшее место находится внутри метода Controller.Initialize. MSDN пишет, что он вызывается после конструктора и перед действием. В отличие от переопределения OnActionExecuting, размещение вашего кода в методе Initialize позволяет вам использовать все пользовательские аннотации и атрибуты данных для ваших классов и локализации ваших свойств.
Например, моя логика локализации происходит из класса, который вводится в мой пользовательский контроллер. У меня есть доступ к этому объекту, так как Initialize вызывается после конструктора. Я могу выполнить назначение культуры Thread и не показывать каждое сообщение об ошибке правильно.
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 и RouteDatastrong > , которые вы можете сделать в основном при любом анализе.
Ответ 6
Если вы используете поддомены, например, например, "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 вызывается на кешированные вызовы, чтобы проверить, доступно ли содержимое в кеше, и в этом случае мы снова рассмотрим значение входящего хоста, вместо того, чтобы полагаться только на текущую информацию о культуре, которая могла бы измениться для нового запроса (потому что мы используем субдомены).
Ответ 7
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, добавьте этот атрибут. (это работает для приложения WHOLE)
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();
}
Ответ 8
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;
}