首页 文章

在ASP.Net MVC应用程序中设置文化

提问于
浏览
80

在ASP.net MVC应用程序中设置Culture / UI Culture的最佳位置是什么

目前我有一个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菜鸟,所以我不相信我在正确的位置设置它 . 我不喜欢_160657的选择 . 我也不想检查他们的http-header以从他们的浏览器设置中获取文化 .

编辑:

只是要明确 - 我不是要决定是否使用会话 . 我很满意 . 我想要解决的是,如果最好在文化控制器中执行此操作,该控制器具有要设置的每个文化的操作方法,或者在MVC管道中是否有更好的位置来执行此操作?

8 回答

  • 106

    由于它是按用户存储的设置,因此会话是存储信息的适当位置 .

    我会更改您的控制器以将文化字符串作为参数,而不是为每个潜在的文化使用不同的操作方法 . 添加页面链接很简单,只要需要新的文化,就不需要重复编写相同的代码 .

    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>
    
  • 5

    1:创建自定义属性并覆盖如下方法:

    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中,找到FilterConfig.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();
    }
    
  • 4

    我知道这是一个老问题,但是如果你真的想让你使用你的ModelBinder(关于 DefaultModelBinder.ResourceClassKey = "MyResource"; 以及viewmodel类的数据注释中指示的资源), the controller or even an ActionFilter is too late to set the culture .

    文化可以在 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);
        }
    

    EDIT

    实际上有一个更好的方法使用 custom routehandler 根据网址设置文化,Alex Adamyan on his blog完美描述 .

    所有要做的就是覆盖 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);
        }
    }
    
  • 24

    我会在控制器的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;
        }
    
  • 7

    什么是最好的地方是你的问题 . 最好的地方是 Controller.Initialize 方法 . MSDN写道它是在构造函数之后和操作方法之前调用的 . 与覆盖OnActionExecuting相反,将代码放在Initialize方法中可以让您在类和属性上对所有自定义数据注释和属性进行本地化 .

    例如,我的本地化逻辑来自注入我的自定义控制器的类 . 我可以访问此对象,因为在构造函数之后调用Initialize . 我可以执行Thread的文化分配,而不是正确显示每条错误消息 .

    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 ,您可以基本上执行任何解析 .

  • 36

    如果使用子域,例如像“pt.mydomain.com”来设置portuguese,则使用Application_AcquireRequestState将不起作用,因为它不会在后续缓存请求中调用 .

    为了解决这个问题,我建议像这样实现:

    • 将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,这允许生成和缓存内容 . 在缓存的调用上调用GetVaryByCustomString以检查内容是否在缓存中可用,在这种情况下,我们再次检查传入的主机域值,而不是仅仅依赖当前的文化信息,这可能已针对新请求进行了更改(因为我们正在使用子域名) .

  • 3

    我正在使用这个localization method并添加了一个路由参数,可以在用户访问example.com/xx-xx/时设置文化和语言

    例:

    routes.MapRoute("DefaultLocalized",
                "{language}-{culture}/{controller}/{action}/{id}",
                new
                {
                    controller = "Home",
                    action = "Index",
                    id = "",
                    language = "nl",
                    culture = "NL"
                });
    

    我有一个过滤器,可以进行实际的文化/语言设置:

    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示例项目,你可以找到here

    只需转到http:// / en-us / home / index查看当前日期(英文),或将其更改为http:// / de -de / home / index for German etcetera .

  • 0
    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;
            }
    

相关问题