在ASP.Net MVC应用程序中设置文化
在ASP.net MVC应用程序中设置文化/ UI文化的最佳地点是什么?
目前我有一个CultureController类看起来像这样:
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"); } }
并在主页上为每种语言提供一个超链接,链接如下:
<li><%= Html.ActionLink("French", "SetFrenchCulture", "Culture")%></li> <li><%= Html.ActionLink("Spanish", "SetSpanishCulture", "Culture")%></li>
这工作正常,但我认为有一个更合适的方法来做到这一点。
我正在阅读文化使用以下的ActionFilter http://www.iansuttle.com/blog/post/ASPNET-MVC-Action-Filter-for-Localized-Sites.aspx 。 我是一个MVC noob的一点,所以我没有把握在正确的位置。 我不想在web.config级别执行它,它必须基于用户的select。 我也不想检查他们的http头文件从他们的浏览器设置。
编辑:
只是要清楚 – 我不是要决定是否使用会议。 我对这一点感到满意。 我正在努力解决的问题是,如果最好在文化控制器中做到这一点,该文化控制器对每个文化都有一个操作方法,或者在MVCpipe道中有一个更好的地方来做到这一点?
我正在使用这种本地化方法,并添加了一个路由参数,用于在用户访问example.com/xx-xx/时设置文化和语言
例:
routes.MapRoute("DefaultLocalized", "{language}-{culture}/{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "", language = "nl", culture = "NL" });
我有一个filter,实际的文化/语言设置:
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)); } }
要激活国际化属性,只需将其添加到您的类:
[Internationalization] public class HomeController : Controller { ...
现在,无论何时访问者访问http://example.com/de-DE/Home/Index ,都会显示德语网站。
我希望这个答案能指出你正确的方向。
我也做了一个小MVC 5示例项目,你可以在这里find
只需转至http:// {yourhost}:{port} / en-us / home / index即可查看英文(US)的当前date,或将其更改为http:// {yourhost}:{port} / de -de / home / index for German etcetera。
我知道这是一个古老的问题,但如果你真的想与ModelBinder一起工作(关于DefaultModelBinder.ResourceClassKey = "MyResource";
以及viewmodel类的数据注释中指出的资源), 控制器甚至是ActionFilter
都不太适合设置文化 。
文化可以在Application_AcquireRequestState
设置,例如:
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); }
编辑
其实有一个更好的方法,使用一个自定义的路由处理程序 ,根据url设置文化, 亚历克斯Adamyan在他的博客完美描述。
所有要做的就是重写GetHttpHandler
方法并在那里设置文化。
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); } }
我会在控制器的Initialize事件中这样做…
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; }
由于这是一个按用户存储的设置,会话是存储通知的适当位置。
我会改变你的控制器把文化string作为参数,而不是为每个潜在的文化有不同的行动方法。 添加一个链接到页面很容易,你不需要重复编写相同的代码,每当一个新的文化是必需的。
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>
什么是最好的地方是你的问题。 最好的地方在Controller.Initialize方法里面。 MSDN写道它在构造函数之后和动作方法之前被调用。 与重载OnActionExecuting相反,把你的代码放在Initialize方法中,可以使你的类和属性上的所有自定义数据注释和属性都被本地化。
例如,我的本地化逻辑来自一个注入到我的自定义控制器的类。 我有权访问这个对象,因为Initialize是在构造函数之后调用的。 我可以做线程的文化分配,并没有正确显示每个错误信息。
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; }
即使你的逻辑不在像我提供的例子那样的类中,你也可以访问RequestContext ,它允许你拥有URL和HttpContext以及RouteData ,你可以基本上做任何parsing。
如果使用Subdomains,例如像“pt.mydomain.com”设置葡萄牙语例如,使用Application_AcquireRequestState将无法正常工作,因为它不被后续caching请求调用。
为了解决这个问题,我build议一个像这样的实现:
-
将VaryByCustom参数添加到OutPutCache,如下所示:
[OutputCache(Duration = 10000, VaryByCustom = "lang")] public ActionResult Contact() { return View("Contact"); }
-
在global.asax.cs中,使用函数调用从主机获取文化:
protected void Application_AcquireRequestState(object sender, EventArgs e) { System.Threading.Thread.CurrentThread.CurrentUICulture = GetCultureFromHost(); }
-
将GetCultureFromHost函数添加到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; }
-
最后重写GetVaryByCustomString(…)来使用这个函数:
public override string GetVaryByCustomString(HttpContext context, string value) { if (value.ToLower() == "lang") { CultureInfo ci = GetCultureFromHost(); return ci.Name; } return base.GetVaryByCustomString(context, value); }
Application_AcquireRequestState函数在非caching调用上被调用,这允许内容被生成并被caching。 GetVaryByCustomString在caching调用上被调用来检查内容是否在caching中可用,在这种情况下,我们再次检查传入的主机域值,而不仅仅依赖于当前的文化信息,这可能已经改变了新的请求(因为我们正在使用子域名)。
1:创build一个自定义属性并覆盖像这样的方法:
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:在App_Start中,findFilterConfig.cs,添加这个属性。 (这适用于整个应用程序)
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { // Add custom attribute here filters.Add(new CultureAttribute()); } }
而已 !
如果您想为每个控制器/操作定义文化而不是整个应用程序,则可以使用以下属性:
[Culture] public class StudentsController : Controller { }
要么:
[Culture] public ActionResult Index() { return View(); }
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; }