asp.net-mvc - net - mezclar webforms y mvc
Establecer Culture en una aplicaciĆ³n ASP.Net MVC (8)
¿Cuál es el mejor lugar para establecer Cultura Cultura / UI en una aplicación MVC de ASP.net
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 estoy pensando que hay una forma más apropiada de hacerlo.
Estoy leyendo Culture 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 que estoy estableciendo esto en el lugar correcto. No quiero hacerlo en el 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 ser claros, no estoy tratando de decidir si usar sesión o no. Estoy contento con ese pedazo. Lo que estoy tratando de determinar es si lo mejor es hacer esto en un controlador de Cultura que tenga un método de acción para cada Cultura, o ¿hay un lugar mejor en la tubería de MVC para hacer esto?
1: cree un atributo personalizado y anule el método de esta manera:
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, encuentre 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 como este:
[Culture]
public class StudentsController : Controller
{
}
O:
[Culture]
public ActionResult Index()
{
return View();
}
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 toda la anotación de datos personalizados y el atributo en sus clases y en sus propiedades para ser localizado.
Por ejemplo, mi lógica de localización proviene de una clase que se inyecta a mi controlador personalizado. Tengo acceso a este objeto desde que se invoca Initialize después del constructor. Puedo hacer la asignación de cultura del subproceso y no tener todos los mensajes de error visualizados 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 el RouteData que puede hacer básicamente cualquier análisis posible.
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 Internacionalización, simplemente agrégalo a tu clase:
[Internationalization]
public class HomeController : Controller {
...
Ahora, cada vez que un visitante va a http://example.com/de-DE/Home/Index , se muestra el sitio en alemán.
Espero que estas respuestas te dirijan en la dirección correcta.
También hice un pequeño proyecto de ejemplo de MVC 5 que puedes encontrar here
Simplemente vaya a http: // {yourhost}: {puerto} / en-us / home / index para ver la fecha actual en inglés (EE. UU.), O cámbiela a http: // {yourhost}: {puerto} / de -de / home / index para etcetera alemana.
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;
}
Sé que esta es una vieja pregunta, pero si realmente desea que esto funcione con su ModelBinder (con respecto a DefaultModelBinder.ResourceClassKey = "MyResource";
así como los recursos indicados en las anotaciones de datos de las clases viewmodel), el controlador o incluso un ActionFilter
es demasiado tarde para establecer la cultura .
La cultura se puede establecer en 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
De hecho, hay una forma mejor de usar un manipulador de ruta personalizado que establece la cultura de acuerdo con la URL, perfectamente descrito por Alex Adamyan en su blog .
Todo lo que hay que hacer es anular el método GetHttpHandler
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);
}
}
Si utiliza Subdominios, por ejemplo, como "pt.midominio.com" para establecer portugués, por ejemplo, el uso de Application_AcquireRequestState no funcionará, ya que no se invoca en solicitudes de caché posteriores.
Para resolver esto, sugiero una implementación como esta:
Agregue el parámetro VaryByCustom a OutPutCache de la siguiente manera:
[OutputCache(Duration = 10000, VaryByCustom = "lang")] public ActionResult Contact() { return View("Contact"); }
En global.asax.cs, obtenga la cultura del host utilizando una llamada a 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 anula 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 invoca en llamadas no almacenadas en caché, lo que permite que el contenido se genere y almacene en caché. GetVaryByCustomString se invoca en las llamadas en caché para comprobar si el contenido está disponible en caché y, en este caso, examinamos el valor del dominio de host entrante, en lugar de basarnos solo en la información cultural actual, que podría haber cambiado para la nueva solicitud (porque estamos usando subdominios).
Siendo como es una configuración que se almacena por usuario, la sesión es un lugar apropiado para almacenar la información.
Cambiaría tu controlador para tomar la cadena cultural 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>
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;
}