asp.net-mvc 在 ASP.Net MVC 应用程序中设置文化

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1560796/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-07 23:54:16  来源:igfitidea点击:

Set Culture in an ASP.Net MVC app

asp.net-mvclocalizationculture

提问by ChrisCa

What is the best place to set the Culture/UI Culture in an ASP.net MVC app

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

Currently I have a CultureController class which looks like this:

目前我有一个 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");
    }
}

and a hyperlink for each language on the homepage with a link such as this:

以及主页上每种语言的超链接,其中包含如下链接:

<li><%= Html.ActionLink("French", "SetFrenchCulture", "Culture")%></li>
<li><%= Html.ActionLink("Spanish", "SetSpanishCulture", "Culture")%></li>

which works fine but I am thinking there is a more appropriate way to do this.

这工作正常,但我认为有更合适的方法来做到这一点。

I am reading the Culture using the following ActionFilter http://www.iansuttle.com/blog/post/ASPNET-MVC-Action-Filter-for-Localized-Sites.aspx. I am a bit of an MVC noob so am not confident I am setting this in the correct place. I don't want to do it at the web.config level, it has to be based on a user's choice. I also don't want to check their http-headers to get the culture from their browser settings.

我正在使用以下 ActionFilter http://www.iansuttle.com/blog/post/ASPNET-MVC-Action-Filter-for-Localized-Sites.aspx阅读文化 。我有点 MVC 菜鸟,所以我不确定我是否将它设置在正确的位置。我不想在 web.config 级别执行此操作,它必须基于用户的选择。我也不想检查他们的 http-header 以从他们的浏览器设置中获取文化。

Edit:

编辑:

Just to be clear - I am not trying to decide whether to use session or not. I am happy with that bit. What I am trying to work out is if it is best to do this in a Culture controller that has an action method for each Culture to be set, or is there is a better place in the MVC pipeline to do this?

只是要清楚 - 我不是要决定是否使用会话。我很高兴这一点。我想要解决的是,如果最好在文化控制器中执行此操作,该控制器为每个文化设置一个操作方法,或者在 MVC 管道中是否有更好的位置来执行此操作?

回答by jao

I'm using this localization methodand added a route parameter that sets the culture and language whenever a user visits example.com/xx-xx/

我正在使用这种本地化方法并添加了一个路由参数,该参数在用户访问 example.com/xx-xx/ 时设置文化和语言

Example:

例子:

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

I have a filter that does the actual culture/language setting:

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

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));

    }
}

To activate the Internationalization attribute, simply add it to your class:

要激活 Internationalization 属性,只需将其添加到您的类中:

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

Now whenever a visitor goes to http://example.com/de-DE/Home/Indexthe German site is displayed.

现在,只要访问者访问http://example.com/de-DE/Home/Index,就会显示德国站点。

I hope this answers points you in the right direction.

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

I also made a small MVC 5 example project which you can find here

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

Just go to http://{yourhost}:{port}/en-us/home/index to see the current date in English (US), or change it to http://{yourhost}:{port}/de-de/home/index for German etcetera.

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

回答by marapet

I know this is an old question, but if you really would like to have this working with your ModelBinder (in respect to DefaultModelBinder.ResourceClassKey = "MyResource";as well as the resources indicated in the data annotations of the viewmodel classes), the controller or even an ActionFilteris too late to set the culture.

我知道这是一个老问题,但是如果你真的想让它与你的 ModelBinder 一起工作(关于DefaultModelBinder.ResourceClassKey = "MyResource";以及 viewmodel 类的数据注释中指示的资源),控制器甚至是一个ActionFilter为时已晚设置文化

The culture could be set in Application_AcquireRequestState, for example:

可以在 中设置文化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

编辑

Actually there is a better way using a custom routehandlerwhich sets the culture according to the url, perfectly described by Alex Adamyan on his blog.

实际上,有一种更好的方法使用自定义路由处理程序,它根据 url 设置文化,Alex Adamyan 在他的博客上完美地描述了这一点

All there is to do is to override the GetHttpHandlermethod and set the culture there.

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

回答by Jace Rhea

I would do it in the Initialize event of the controller like this...

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

回答by NerdFury

Being as it is a setting that is stored per-user, the session is an appropriate place to store the informtion.

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

I would change your controller to take the culture string as a parameter, rather than having a different action method for each potential culture. Adding a link to the page is easy, and you shouldn't need to write the same code repeatedly any time a new culture is required.

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

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>

回答by Patrick Desjardins

What is the best place is your question. The best place is inside the Controller.Initializemethod. MSDN writes that it is called after the constructor and before the action method. In contrary of overriding OnActionExecuting, placing your code in the Initialize method allow you to benefit of having all custom data annotation and attribute on your classes and on your properties to be localized.

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

For example, my localization logic come from an class that is injected to my custom controller. I have access to this object since Initialize is called after the constructor. I can do the Thread's culture assignation and not having every error message displayed correctly.

例如,我的本地化逻辑来自注入到我的自定义控制器的类。我可以访问这个对象,因为 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;
 }

Even if your logic is not inside a class like the example I provided, you have access to the RequestContextwhich allow you to have the URL and HttpContextand the RouteDatawhich you can do basically any parsing possible.

即使您的逻辑不在我提供的示例之类的类中,您也可以访问RequestContext,它允许您拥有 URL 和HttpContext以及RouteData,您基本上可以进行任何可能的解析。

回答by Meng Xue

1: Create a custom attribute and override method like this:

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: In App_Start, find FilterConfig.cs, add this attribute. (this works for WHOLE application)

2:在App_Start中,找到FilterConfig.cs,添加这个属性。(这适用于整个应用程序)

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
    // Add custom attribute here
    filters.Add(new CultureAttribute());
    }
}    

That's it !

就是这样 !

If you want to define culture for each controller/action in stead of whole application, you can use this attribute like this:

如果你想为每个控制器/动作而不是整个应用程序定义文化,你可以像这样使用这个属性:

[Culture]
public class StudentsController : Controller
{
}

Or:

或者:

[Culture]
public ActionResult Index()
{
    return View();
}

回答by Andy

If using Subdomains, for example like "pt.mydomain.com" to set portuguese for example, using Application_AcquireRequestState won't work, because it's not called on subsequent cache requests.

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

To solve this, I suggest an implementation like this:

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

  1. Add the VaryByCustom parameter to the OutPutCache like this:

    [OutputCache(Duration = 10000, VaryByCustom = "lang")]
    public ActionResult Contact()
    {
        return View("Contact");
    }
    
  2. In global.asax.cs, get the culture from the host using a function call:

    protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        System.Threading.Thread.CurrentThread.CurrentUICulture = GetCultureFromHost();
    }
    
  3. Add the GetCultureFromHost function to 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. And finally override the GetVaryByCustomString(...) to also use this function:

    public override string GetVaryByCustomString(HttpContext context, string value)
    {
        if (value.ToLower() == "lang")
        {
            CultureInfo ci = GetCultureFromHost();
            return ci.Name;
        }
        return base.GetVaryByCustomString(context, value);
    }
    
  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);
    }
    

The function Application_AcquireRequestState is called on non-cached calls, which allows the content to get generated and cached. GetVaryByCustomString is called on cached calls to check if the content is available in cache, and in this case we examine the incoming host domain value, again, instead of relying on just the current culture info, which could have changed for the new request (because we are using subdomains).

函数 Application_AcquireRequestState 在非缓存调用上被调用,它允许生成和缓存内容。在缓存调用中调用 GetVaryByCustomString 以检查内容是否在缓存中可用,在这种情况下,我们再次检查传入的主机域值,而不是仅依赖当前的区域性信息,这可能会因新请求而更改(因为我们正在使用子域)。

回答by user2861593

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