90

在 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 菜鸟,所以我不确定我是否将其设置在正确的位置。我不想在 web.config 级别这样做,它必须基于用户的选择。我也不想检查他们的 http-headers 以从他们的浏览器设置中获取文化。

编辑:

只是要明确一点-我并不是要决定是否使用会话。我对那一点很满意。我要解决的是最好在具有要设置每个文化的操作方法的文化控制器中执行此操作,还是在 MVC 管道中有更好的位置来执行此操作?

4

8 回答 8

118

我正在使用这种本地化方法并添加了一个路由参数,用于在用户访问 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 属性,只需将其添加到您的类中:

[Internationalization]
public class HomeController : Controller {
...

现在,每当访问者访问http://example.com/de-DE/Home/Index时,都会显示德国站点。

我希望这个答案能为您指明正确的方向。

我还制作了一个小型 MVC 5 示例项目,您可以在此处找到

只需转到 http://{yourhost}:{port}/en-us/home/index 以查看英语(美国)的当前日期,或将其更改为 http://{yourhost}:{port}/de -de/home/index 用于德语等。

于 2009-10-13T17:02:23.850 回答
39

我知道这是一个老问题,但如果你真的想让它与你的 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 设置文化,Alex 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);
    }
}
于 2011-07-22T10:29:00.540 回答
25

我会在控制器的 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;
    }
于 2009-10-13T16:44:55.847 回答
7

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

我会更改您的控制器以将区域性字符串作为参数,而不是为每种潜在的区域性设置不同的操作方法。向页面添加链接很容易,并且您无需在需要新文化的任何时候重复编写相同的代码。

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>
于 2009-10-13T15:05:12.943 回答
6

什么是最好的地方是你的问题。最好的地方是在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,您基本上可以进行任何可能的解析。

于 2015-04-09T01:36:47.607 回答
4

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

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

  1. 像这样将 VaryByCustom 参数添加到 OutPutCache:

    [OutputCache(Duration = 10000, VaryByCustom = "lang")]
    public ActionResult Contact()
    {
        return View("Contact");
    }
    
  2. 在 global.asax.cs 中,使用函数调用从主机获取文化:

    protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        System.Threading.Thread.CurrentThread.CurrentUICulture = GetCultureFromHost();
    }
    
  3. 将 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;
    }
    
  4. 最后覆盖 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 在缓存调用中被调用以检查内容是否在缓存中可用,在这种情况下,我们再次检查传入的主机域值,而不是仅仅依赖于当前的文化信息,这可能会因新请求而改变(因为我们正在使用子域)。

于 2013-01-23T04:31:28.810 回答
4

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();
}
于 2016-11-22T19:36:51.013 回答
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;
        }
于 2013-10-09T06:56:20.250 回答