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

时间:2009-10-13 15:00:44

标签: asp.net-mvc localization culture

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

编辑:

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

8 个答案:

答案 0 :(得分:109)

我正在使用此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:// {yourhost}:{port} / en-us / home / index查看当前日期(英文),或将其更改为http:// {yourhost}:{port德语等的/ de-de / home / index。

答案 1 :(得分:37)

我知道这是一个老问题,但是如果你真的希望使用你的ModelBinder(关于DefaultModelBinder.ResourceClassKey = "MyResource";以及viewmodel类的数据注释中指示的资源),< strong>控制器甚至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);
    }

修改

实际上有一种更好的方法是使用自定义路由处理程序根据网址设置文化,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);
    }
}

答案 2 :(得分: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;
    }

答案 3 :(得分: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>

答案 4 :(得分:5)

你的问题最好的地方是什么?最佳位置在 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 ,您可以基本上完​​成任何解析。

答案 5 :(得分:4)

如果使用Subdomains,例如像“pt.mydomain.com”来设置portuguese,则使用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);
    }
    
  5. 在非缓存调用上调用函数Application_AcquireRequestState,这允许生成和缓存内容。在缓存的调用上调用GetVaryByCustomString来检查内容是否在缓存中可用,在这种情况下,我们再次检查传入的主机域值,而不是仅仅依赖当前的文化信息,这些信息可能已针对新请求进行了更改(因为我们正在使用子域名。)

答案 6 :(得分:3)

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

答案 7 :(得分: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;
        }