Establecer la cultura en un ASP.Net Aplicación MVC


¿Cuál es el mejor lugar para establecer la Cultura / UI en un ASP.net MVC app

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 manera más apropiada de hacer esto.

Estoy leyendo la Cultura usando lo siguiente ActionFilter http://www.iansuttle.com/blog/post/ASPNET-MVC-Action-Filter-for-Localized-Sites.aspx. Soy un poco noob MVC así que no estoy seguro de que estoy poniendo esto en el lugar correcto. No quiero hacerlo en la web.nivel de configuración, tiene que basarse en la elección del usuario. Tampoco quiero comprobar sus encabezados http para obtener la cultura de la configuración de su navegador.

Editar:

Solo para ser claro, no estoy tratando de decidir si usar session o no. Yo estoy contento con esa parte. Lo que estoy tratando de averiguar es si es mejor hacer esto en un controlador de Cultura que tiene un método de acción para cada Cultura a establecer, o hay un lugar mejor en la tubería MVC para hacer esto?

Author: ViVi, 2009-10-13

8 answers

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 actual 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 Internationalization, 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 alemán.

Espero esta respuesta te apunta en 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 alemán, etc.

 103
Author: jao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-25 12:32:14

Sé que esta es una pregunta antigua, pero si realmente desea que esto funcione con su ModelBinder (con respecto a DefaultModelBinder.ResourceClassKey = "MyResource"; así como a 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 podría establecerse 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

En realidad hay una mejor manera de usar un enrutador personalizado que establece la cultura según la url, perfectamente descrita por Alex Adamyan en su blog .

Todo lo que hay que hacer es anular la 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);
    }
}
 36
Author: marapet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-08 05:57:17

Lo haría en el evento Initialize del controlador de esta manera...

    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;
    }
 24
Author: Jace Rhea,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-13 16:44:55

Al ser 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>
 7
Author: NerdFury,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-13 16:54:37

Cuál es el mejor lugar es tu pregunta. El mejor lugar es dentro de la Controlador.Inicializar método. MSDN escribe que se llama después del constructor y antes del método action. Al contrario de sobreescribir 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 localizados.

Por ejemplo, mi lógica de localización proviene de una clase que es inyectado a mi controlador personalizado. Tengo acceso a este objeto ya que Inicialize se llama 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 a la RequestContext que le permiten tener la URL y HttpContext y el RouteData que puede hacer básicamente cualquier análisis posible.

 5
Author: Patrick Desjardins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-09 01:36:47

Si se usan subdominios, por ejemplo como "pt.mydomain.com" para establecer portugués, por ejemplo, el uso de Application_AcquireRequestState no funcionará, porque no se llama en las solicitudes de caché posteriores.

Para resolver esto, sugiero una implementación como esta:

  1. Agregue el parámetro VaryByCustom al OutputCache de la siguiente manera:

    [OutputCache(Duration = 10000, VaryByCustom = "lang")]
    public ActionResult Contact()
    {
        return View("Contact");
    }
    
  2. En global.asax.cs, obtenga la cultura del host usando una llamada a la función:

    protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        System.Threading.Thread.CurrentThread.CurrentUICulture = GetCultureFromHost();
    }
    
  3. Añadir el 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;
    }
    
  4. Y finalmente sobrescribir el 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 almacene en caché. GetVaryByCustomString se llama en las llamadas en caché para comprobar si el contenido está disponible en caché, y en este caso examinamos el valor de dominio de host entrante, de nuevo, 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).

 3
Author: Andy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-23 04:31:28

1: Cree un atributo personalizado y un método de anulación 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, busca FilterConfig.cs, agrega 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 todo !

Si desea definir la cultura para cada controlador / acción en lugar de toda la aplicación, puede usar este atributo de la siguiente manera:

[Culture]
public class StudentsController : Controller
{
}

O:

[Culture]
public ActionResult Index()
{
    return View();
}
 2
Author: Meng Xue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-22 19:36:51
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;
        }
 0
Author: user2861593,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-09 07:16:29