Jakie jest najlepsze miejsce do ustawienia kultury / kultury interfejsu użytkownika w aplikacji ASP.net MVC
Obecnie mam klasę CultureController, która wygląda następująco:
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");
}
}
oraz hiperłącze dla każdego języka na stronie głównej z takim linkiem:
<li><%= Html.ActionLink("French", "SetFrenchCulture", "Culture")%></li>
<li><%= Html.ActionLink("Spanish", "SetSpanishCulture", "Culture")%></li>
co działa dobrze, ale myślę, że istnieje bardziej odpowiedni sposób, aby to zrobić.
Czytam kulturę za pomocą następującego filtra ActionFilter http://www.iansuttle.com/blog/post/ASPNET-MVC-Action-Filter-for-Localized-Sites.aspx . Jestem trochę noobem MVC, więc nie jestem pewien, czy ustawiam to we właściwym miejscu. Nie chcę tego robić na poziomie web.config, musi to być oparte na wyborze użytkownika. Nie chcę też sprawdzać ich nagłówków http, aby pobrać kulturę z ustawień przeglądarki.
Edytować:
Dla jasności - nie próbuję decydować, czy użyć sesji, czy nie. Jestem zadowolony z tego kawałka. Próbuję ustalić, czy najlepiej jest to zrobić w kontrolerze kultury, który ma ustawioną metodę akcji dla każdej kultury, czy też jest lepsze miejsce w potoku MVC, aby to zrobić?
źródło
Odpowiedzi:
Używam tej metody lokalizacji i dodałem parametr trasy, który ustawia kulturę i język za każdym razem, gdy użytkownik odwiedza example.com/xx-xx/
Przykład:
routes.MapRoute("DefaultLocalized", "{language}-{culture}/{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "", language = "nl", culture = "NL" });
Mam filtr, który wykonuje rzeczywiste ustawienie kultury / języka:
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)); } }
Aby aktywować atrybut Internationalization, po prostu dodaj go do swojej klasy:
[Internationalization] public class HomeController : Controller { ...
Teraz za każdym razem, gdy odwiedzający przechodzi do http://example.com/de-DE/Home/Index, wyświetlana jest niemiecka witryna.
Mam nadzieję, że ta odpowiedź wskazuje właściwy kierunek.
Zrobiłem również mały przykładowy projekt MVC 5, który możesz znaleźć tutaj
Po prostu przejdź do http: // {yourhost}: {port} / en-us / home / index, aby zobaczyć aktualną datę w języku angielskim (USA) lub zmień ją na http: // {yourhost}: {port} / de -de / home / index dla niemieckiego etcetera.
źródło
Wiem, że to stare pytanie, ale jeśli naprawdę chciałbyś, aby to działało z Twoim ModelBinder (w odniesieniu do
DefaultModelBinder.ResourceClassKey = "MyResource";
zasobów wskazanych w adnotacjach danych klas viewmodel), kontroler lub nawetActionFilter
jest za późno na ustawić kulturę .Kulturę można osadzić
Application_AcquireRequestState
na przykład w: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); }
EDYTOWAĆ
Właściwie jest lepszy sposób na użycie niestandardowego routera, który ustawia kulturę zgodnie z adresem URL, doskonale opisanym przez Alexa Adamyana na swoim blogu .
Wszystko, co trzeba zrobić, to przesłonić
GetHttpHandler
metodę i ustawić tam kulturę.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); } }
źródło
Zrobiłbym to w przypadku inicjalizacji kontrolera w ten sposób ...
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; }
źródło
Ponieważ jest to ustawienie przechowywane dla każdego użytkownika, sesja jest odpowiednim miejscem do przechowywania informacji.
Zmieniłbym kontroler tak, aby przyjmował ciąg kultury jako parametr, zamiast mieć inną metodę akcji dla każdej potencjalnej kultury. Dodanie linku do strony jest łatwe i nie powinno być konieczne wielokrotne pisanie tego samego kodu za każdym razem, gdy wymagana jest nowa kultura.
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>
źródło
Jakie jest najlepsze miejsce, to Twoje pytanie. Najlepsze miejsce znajduje się wewnątrz metody Controller.Initialize . MSDN pisze, że jest wywoływana po konstruktorze i przed metodą akcji. W przeciwieństwie do zastępowania OnActionExecuting, umieszczenie kodu w metodzie Initialize umożliwia skorzystanie z posiadania wszystkich niestandardowych adnotacji danych i atrybutów w klasach i we właściwościach, które mają być zlokalizowane.
Na przykład moja logika lokalizacji pochodzi z klasy, która jest wstrzykiwana do mojego kontrolera niestandardowego. Mam dostęp do tego obiektu, ponieważ Initialize jest wywoływana po konstruktorze. Mogę przypisać kulturę wątku i nie wyświetlać poprawnie wszystkich komunikatów o błędach.
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; }
Nawet jeśli twoja logika nie znajduje się w klasie takiej jak w przykładzie, który podałem , masz dostęp do RequestContext, który pozwala ci mieć adres URL i HttpContext oraz RouteData, które możesz wykonać w zasadzie każdą możliwą analizę.
źródło
Jeśli używasz subdomen, na przykład „pt.moja_domena.com”, aby ustawić portugalski, na przykład użycie Application_AcquireRequestState nie zadziała, ponieważ nie jest wywoływane przy kolejnych żądaniach pamięci podręcznej.
Aby rozwiązać ten problem, proponuję taką implementację:
Dodaj parametr VaryByCustom do OutPutCache w następujący sposób:
[OutputCache(Duration = 10000, VaryByCustom = "lang")] public ActionResult Contact() { return View("Contact"); }
W global.asax.cs pobierz kulturę od hosta za pomocą wywołania funkcji:
protected void Application_AcquireRequestState(object sender, EventArgs e) { System.Threading.Thread.CurrentThread.CurrentUICulture = GetCultureFromHost(); }
Dodaj funkcję GetCultureFromHost do 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; }
I na koniec zastąp GetVaryByCustomString (...), aby również użyć tej funkcji:
public override string GetVaryByCustomString(HttpContext context, string value) { if (value.ToLower() == "lang") { CultureInfo ci = GetCultureFromHost(); return ci.Name; } return base.GetVaryByCustomString(context, value); }
Funkcja Application_AcquireRequestState jest wywoływana w przypadku wywołań niebuforowanych, co umożliwia generowanie i buforowanie treści. GetVaryByCustomString jest wywoływana w wywołaniach buforowanych w celu sprawdzenia, czy zawartość jest dostępna w pamięci podręcznej, iw tym przypadku ponownie sprawdzamy przychodzącą wartość domeny hosta, zamiast polegać tylko na bieżących informacjach o kulturze, które mogły ulec zmianie dla nowego żądania (ponieważ używamy subdomen).
źródło
1: utwórz atrybut niestandardowy i zastąp metodę w następujący sposób:
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: w App_Start znajdź FilterConfig.cs, dodaj ten atrybut. (to działa dla CAŁEJ aplikacji)
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { // Add custom attribute here filters.Add(new CultureAttribute()); } }
Otóż to !
Jeśli chcesz zdefiniować kulturę dla każdego kontrolera / akcji zamiast całej aplikacji, możesz użyć tego atrybutu w następujący sposób:
[Culture] public class StudentsController : Controller { }
Lub:
[Culture] public ActionResult Index() { return View(); }
źródło
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; }
źródło