¿Cuál es el mejor lugar para configurar la cultura / UI Culture en una aplicación ASP.net MVC?
Actualmente tengo una clase CultureController que se ve así:
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");
}
}
y un hipervínculo para cada idioma en la página de inicio con un enlace como este:
<li><%= Html.ActionLink("French", "SetFrenchCulture", "Culture")%></li>
<li><%= Html.ActionLink("Spanish", "SetSpanishCulture", "Culture")%></li>
que funciona bien, pero creo que hay una forma más apropiada de hacerlo.
Estoy leyendo la Cultura usando el siguiente ActionFilter http://www.iansuttle.com/blog/post/ASPNET-MVC-Action-Filter-for-Localized-Sites.aspx . Soy un poco novato de MVC, así que no estoy seguro de estar configurando esto en el lugar correcto. No quiero hacerlo a nivel web.config, tiene que basarse en la elección del usuario. Tampoco quiero verificar sus encabezados http para obtener la cultura de la configuración de su navegador.
Editar:
Para que quede claro, no estoy tratando de decidir si usar sesión o no. Estoy feliz con esa parte. Lo que estoy tratando de resolver es si es mejor hacer esto en un controlador de Cultura que tiene un método de acción para cada Cultura que se va a configurar, o ¿hay un lugar mejor en la canalización de MVC para hacer esto?
fuente
Respuestas:
Estoy usando este método de localización y agregué un parámetro de ruta que establece la cultura y el idioma cada vez que un usuario visita example.com/xx-xx/
Ejemplo:
routes.MapRoute("DefaultLocalized", "{language}-{culture}/{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "", language = "nl", culture = "NL" });
Tengo un filtro que hace la configuración real de cultura / idioma:
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)); } }
Para activar el atributo de internacionalización, simplemente agréguelo a su clase:
[Internationalization] public class HomeController : Controller { ...
Ahora, cada vez que un visitante accede a http://example.com/de-DE/Home/Index , se muestra el sitio en alemán.
Espero que esta respuesta le indique la dirección correcta.
También hice un pequeño proyecto de ejemplo MVC 5 que puedes encontrar aquí
Simplemente vaya a http: // {yourhost}: {port} / en-us / home / index para ver la fecha actual en inglés (EE. UU.), O cámbiela a http: // {yourhost}: {port} / de -de / home / index para etcétera alemana.
fuente
Sé que esta es una pregunta antigua, pero si realmente desea que esto funcione con su ModelBinder (con respecto a
DefaultModelBinder.ResourceClassKey = "MyResource";
los recursos indicados en las anotaciones de datos de las clases de viewmodel), el controlador o incluso unActionFilter
es demasiado tarde para establecer la cultura .La cultura se podría establecer
Application_AcquireRequestState
, por ejemplo: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); }
EDITAR
En realidad, hay una mejor manera de usar un controlador de ruta personalizado que establece la cultura de acuerdo con la URL, perfectamente descrita por Alex Adamyan en su blog .
Todo lo que hay que hacer es anular el
GetHttpHandler
método y establecer la cultura allí.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); } }
fuente
Lo haría en el evento Initialize del controlador como este ...
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; }
fuente
Siendo una configuración que se almacena por usuario, la sesión es un lugar apropiado para almacenar la información.
Cambiaría su controlador para tomar la cadena de cultura como parámetro, en lugar de tener un método de acción diferente para cada cultura potencial. Agregar un enlace a la página es fácil y no debería necesitar escribir el mismo código repetidamente cada vez que se requiera una nueva cultura.
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>
fuente
Cuál es el mejor lugar es tu pregunta. El mejor lugar es dentro del método Controller.Initialize . MSDN escribe que se llama después del constructor y antes del método de acción. A diferencia de anular OnActionExecuting, colocar su código en el método Initialize le permite beneficiarse de tener todas las anotaciones y atributos de datos personalizados en sus clases y en sus propiedades para ser localizadas.
Por ejemplo, mi lógica de localización proviene de una clase que se inyecta en mi controlador personalizado. Tengo acceso a este objeto ya que se llama a Initialize después del constructor. Puedo hacer la asignación de cultura del hilo y no tener todos los mensajes de error mostrados correctamente.
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; }
Incluso si su lógica no está dentro de una clase como el ejemplo que proporcioné, tiene acceso al RequestContext que le permite tener la URL y HttpContext y RouteData, que puede hacer básicamente cualquier análisis posible.
fuente
Si usa subdominios, por ejemplo, como "pt.mydomain.com" para establecer portugués, por ejemplo, el uso de Application_AcquireRequestState no funcionará, porque no se invoca en solicitudes de caché posteriores.
Para resolver esto, sugiero una implementación como esta:
Agregue el parámetro VaryByCustom al OutPutCache de esta manera:
[OutputCache(Duration = 10000, VaryByCustom = "lang")] public ActionResult Contact() { return View("Contact"); }
En global.asax.cs, obtenga la cultura del host mediante una llamada a la función:
protected void Application_AcquireRequestState(object sender, EventArgs e) { System.Threading.Thread.CurrentThread.CurrentUICulture = GetCultureFromHost(); }
Agregue la función GetCultureFromHost a 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; }
Y finalmente anule GetVaryByCustomString (...) para usar también esta función:
public override string GetVaryByCustomString(HttpContext context, string value) { if (value.ToLower() == "lang") { CultureInfo ci = GetCultureFromHost(); return ci.Name; } return base.GetVaryByCustomString(context, value); }
La función Application_AcquireRequestState se llama en llamadas no almacenadas en caché, lo que permite que el contenido se genere y se almacene en caché. GetVaryByCustomString se llama en las llamadas almacenadas en caché para verificar si el contenido está disponible en la caché, y en este caso examinamos el valor del dominio del host entrante, nuevamente, en lugar de confiar solo en la información de la cultura actual, que podría haber cambiado para la nueva solicitud (porque estamos usando subdominios).
fuente
1: Cree un atributo personalizado y anule un método como este:
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: En App_Start, busque FilterConfig.cs, agregue este atributo. (esto funciona para TODA la aplicación)
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { // Add custom attribute here filters.Add(new CultureAttribute()); } }
Eso es !
Si desea definir la cultura para cada controlador / acción en lugar de toda la aplicación, puede usar este atributo de esta manera:
[Culture] public class StudentsController : Controller { }
O:
[Culture] public ActionResult Index() { return View(); }
fuente
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; }
fuente