Qual é o melhor lugar para definir a cultura / cultura da interface do usuário em um aplicativo ASP.net MVC
Atualmente, tenho uma classe CultureController que se parece com isto:
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");
}
}
e um hiperlink para cada idioma na página inicial com um link como este:
<li><%= Html.ActionLink("French", "SetFrenchCulture", "Culture")%></li>
<li><%= Html.ActionLink("Spanish", "SetSpanishCulture", "Culture")%></li>
que funciona bem, mas estou pensando que existe uma maneira mais apropriada de fazer isso.
Estou lendo a Cultura usando o seguinte ActionFilter http://www.iansuttle.com/blog/post/ASPNET-MVC-Action-Filter-for-Localized-Sites.aspx . Eu sou um pouco um novato MVC, então não tenho certeza se estou definindo isso no lugar correto. Eu não quero fazer isso no nível do web.config, tem que ser baseado na escolha do usuário. Eu também não quero verificar seus cabeçalhos http para obter a cultura de suas configurações de navegador.
Editar:
Só para deixar claro - não estou tentando decidir se devo usar a sessão ou não. Estou feliz com essa parte. O que estou tentando descobrir é se é melhor fazer isso em um controlador de cultura que tem um método de ação para cada cultura a ser definida, ou se há um lugar melhor no pipeline MVC para fazer isso?
fonte
Respostas:
Estou usando este método de localização e adicionei um parâmetro de rota que define a cultura e o idioma sempre que um usuário visita example.com/xx-xx/
Exemplo:
routes.MapRoute("DefaultLocalized", "{language}-{culture}/{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "", language = "nl", culture = "NL" });
Eu tenho um filtro que faz a configuração 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 ativar o atributo Internacionalização, basta adicioná-lo à sua classe:
[Internationalization] public class HomeController : Controller { ...
Agora, sempre que um visitante vai para http://example.com/de-DE/Home/Index, o site alemão é exibido.
Espero que essas respostas apontem na direção certa.
Eu também fiz um pequeno projeto de exemplo MVC 5 que você pode encontrar aqui
Basta ir para http: // {yourhost}: {port} / en-us / home / index para ver a data atual em inglês (EUA) ou alterá-la para http: // {yourhost}: {port} / de -de / home / index para alemão etc.
fonte
Eu sei que esta é uma questão antiga, mas se você realmente gostaria de ter isso funcionando com o seu ModelBinder (em relação a
DefaultModelBinder.ResourceClassKey = "MyResource";
, bem como os recursos indicados nas anotações de dados das classes de viewmodel), o controlador ou mesmo umActionFilter
é tarde demais para definir a cultura .A cultura pode ser definida
Application_AcquireRequestState
, por exemplo: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
Na verdade, existe uma maneira melhor de usar um gerenciador de rotas personalizado que define a cultura de acordo com a url, perfeitamente descrito por Alex Adamyan em seu blog .
Tudo o que há a fazer é substituir o
GetHttpHandler
método e definir a cultura lá.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); } }
fonte
Eu faria isso no evento Initialize do controlador assim ...
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; }
fonte
Por se tratar de uma configuração que é armazenada por usuário, a sessão é um local adequado para armazenar as informações.
Eu mudaria seu controlador para usar a string de cultura como parâmetro, em vez de ter um método de ação diferente para cada cultura potencial. Adicionar um link à página é fácil e você não deve precisar escrever o mesmo código repetidamente sempre que uma nova cultura for necessária.
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>
fonte
Qual é o melhor lugar é a sua pergunta. O melhor lugar é dentro do método Controller.Initialize . O MSDN escreve que ele é chamado após o construtor e antes do método de ação. Ao contrário de substituir OnActionExecuting, colocar seu código no método Initialize permite que você se beneficie de ter todas as anotações e atributos de dados personalizados em suas classes e propriedades a serem localizados.
Por exemplo, minha lógica de localização vem de uma classe que é injetada em meu controlador personalizado. Tenho acesso a este objeto porque Initialize é chamado após o construtor. Eu posso fazer a atribuição de cultura do Thread e não ter todas as mensagens de erro exibidas corretamente.
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; }
Mesmo que sua lógica não esteja dentro de uma classe como o exemplo que forneci, você tem acesso ao RequestContext que permite que você tenha a URL e HttpContext e o RouteData, que você pode fazer basicamente qualquer análise possível.
fonte
Se estiver usando Subdomínios, por exemplo, como "pt.mydomain.com" para definir o português, por exemplo, usar Application_AcquireRequestState não funcionará, porque não é chamado em solicitações de cache subsequentes.
Para resolver isso, sugiro uma implementação como esta:
Adicione o parâmetro VaryByCustom ao OutPutCache assim:
[OutputCache(Duration = 10000, VaryByCustom = "lang")] public ActionResult Contact() { return View("Contact"); }
Em global.asax.cs, obtenha a cultura do host usando uma chamada de função:
protected void Application_AcquireRequestState(object sender, EventArgs e) { System.Threading.Thread.CurrentThread.CurrentUICulture = GetCultureFromHost(); }
Adicione a função 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; }
E, finalmente, substitua GetVaryByCustomString (...) para usar também esta função:
public override string GetVaryByCustomString(HttpContext context, string value) { if (value.ToLower() == "lang") { CultureInfo ci = GetCultureFromHost(); return ci.Name; } return base.GetVaryByCustomString(context, value); }
A função Application_AcquireRequestState é chamada em chamadas não armazenadas em cache, o que permite que o conteúdo seja gerado e armazenado em cache. GetVaryByCustomString é chamado em chamadas em cache para verificar se o conteúdo está disponível no cache e, neste caso, examinamos o valor do domínio do host de entrada, novamente, em vez de confiar apenas nas informações da cultura atual, que poderiam ter mudado para a nova solicitação (porque estamos usando subdomínios).
fonte
1: Crie um atributo personalizado e um método de substituição 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: Em App_Start, encontre FilterConfig.cs, adicione este atributo. (funciona para TODO o aplicativo)
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { // Add custom attribute here filters.Add(new CultureAttribute()); } }
É isso aí !
Se você deseja definir a cultura para cada controlador / ação em vez de todo o aplicativo, você pode usar este atributo assim:
[Culture] public class StudentsController : Controller { }
Ou:
[Culture] public ActionResult Index() { return View(); }
fonte
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; }
fonte