尽管更改了CurrentThread.CurrentCulture

时间:2016-07-08 19:51:40

标签: asp.net asp.net-mvc razor localization currentculture

我有一个在IIS 8中的网站下运行的ASP.NET MVC 5 WEB应用程序,我需要在运行时通过用户首选项以编程方式更改语言,我从数据库读取并存储在会话变量中,此值可以在运行时通过下拉列表进行更改。

问题是语言资源有时候不会改变,尽管CurrentCulture会这样做而且更糟糕的是他们在应用程序级别冻结,使得所有其他用户会话也冻结到该语言并且仅在所有用户中显示应用程序会话使用相同的语言,无论他们的偏好如何,或者他们甚至尝试在运行时更改语言,正常工作时强制更改CurrentThread.CurrentCulture。

我在视图中使用隐藏文字来跟踪CurrentThread.CurrentCulture的内容并且正在按预期更改,因此问题必须是其他地方。

这是我用来检查CurrentCulture的剃刀块

<div style="display:none; visibility: hidden;">Thread  
@System.Threading.Thread.CurrentThread.CurrentCulture</div>

在运行时,当语言或resx在应用程序级别冻结/停留时,该值确实是正确的并且已选中,但它与视图中显示的语言不匹配,并且在我做出示例之前它不会正常工作更改web.config或重新启动应用程序,然后它可以正常工作一段时间但不会持续太多时间。

<div style="display:none; visibility: hidden;">Thread en-US</div>

问题是,这发生在所有用户会话的应用程序级别,并且当应用程序正常工作时,CurrentCulture实际上正在按预期更改,并且无论何时重新启动,这都不会发生但是在某些时候某些时间某些事情会在用户会话期间触发冻结所有当前会话甚至新会话的语言

我已经尝试将文化逻辑的更改放在剃刀视图,动作过滤器和基本控制器中,但这并没有解决问题,之前我有一个基本控制器,我的所有控制器都继承了Global.asax的逻辑。 cs改变和决定文化。

我目前的做法是删除基本控制器并在Global.asax.cs中使用Application_AcquireRequestState(我也尝试使用Application_BeginRequest,但在生命周期中某种程度上太早了)和动作过滤器。

这是在Global.asax.cs

    protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        if (Context.Session != null && Context.Session["CultureName"] != null)
        {
             string cultureName = Context.Session["CultureName"].ToString();

             CultureInfo cultureInfo = new System.Globalization.CultureInfo(cultureName);

            Thread.CurrentThread.CurrentCulture = cultureInfo;
            Thread.CurrentThread.CurrentUICulture = cultureInfo;

        }
    }

我的动作过滤器就像这样

public class CultureActionAttribute : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        string cultureName = null;

        string tenant = filterContext.RouteData.Values["Tenant"] as string;
        if (!String.IsNullOrWhiteSpace(tenant)) 
        {
            TenantService tenantService = new TenantService(tenant);

            cultureName = CultureHelper.GetImplementedCulture(tenantService.Tenant.LanguageCulture);

            tenantService.Unit.Dispose();

        }
        //Logic to override cultureName value by getting the user preference language

        CultureInfo cultureInfo = new System.Globalization.CultureInfo(cultureName);

        Thread.CurrentThread.CurrentCulture = cultureInfo;
        Thread.CurrentThread.CurrentUICulture = cultureInfo;
    }
}

我在FilterConfig

添加了它
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new CultureActionAttribute());
        //
    }

当用户更改文化时,我甚至尝试使用ClearCachedData。

Thread.CurrentThread.CurrentCulture.ClearCachedData();

我认为这可能与缓存或池回收有关,但我不知道接下来要尝试什么。

我没有在web.config上使用任何全球化标签。

CurrentThread.CurrentCulture视图中跟踪的值始终是正确的,使用资源冻结是错误的。

资源与将它们放在单独的专用标准文件夹中的做法一致,并使用PublicResXFileCodeGenerator使它们公开以便能够编译并可供控制器,视图等访问

资源文件夹

enter image description here

任何人都可以请我提供另一种选择,尝试或考虑我可能做错了什么,或者我还能尝试使资源始终如一地工作。

2 个答案:

答案 0 :(得分:0)

我相信这里发生的事情是你在不同的线程中设置文化,而不是实际使用它。您正在以异步方法BeginExecuteCore设置文化,这可能无法将文化一致地转移到应用程序的UI工作线程。这可以解释间歇性行为 - 你可能正在看到它&#34;冻结&#34;在重负载期间应用程序线程运行缓慢。

Never use a base controller in MVC - 这绝不是一个好主意。相反,对于本地化等跨领域问题,您可以使用global filter。这不仅可以解决您的异步问题,还可以促进更松散耦合的设计。以下是从当前路线获取文化的过滤器示例:

using System.Globalization;
using System.Threading;
using System.Web.Mvc;

public class CultureFilter : IAuthorizationFilter
{
    private readonly string defaultCulture;

    public CultureFilter(string defaultCulture)
    {
        this.defaultCulture = defaultCulture;
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var values = filterContext.RouteData.Values;

        string culture = (string)values["culture"] ?? this.defaultCulture;

        CultureInfo ci = new CultureInfo(culture);

        Thread.CurrentThread.CurrentCulture = ci;
        Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(ci.Name);
    }
}

用法

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CultureFilter(defaultCulture: "nl"));
        filters.Add(new HandleErrorAttribute());
    }
}

我还会争辩说using session state for localization is not a great idea - 毕竟,当会议到期时,你会陷入黑洞。

区域设置不是个性化。区域设置是内容。提供独特内容的正确方法是为其提供唯一的网址。如果你这样做,那么选择文化就像选择一个新的URL一样简单,它会“#34;坚持&#34;自动因为MVC重新使用路由值。即使用户将URL发送给其他人,它也永远不会过期。最重要的是,您将网站翻译成多种语言所做的所有工作都将由搜索引擎显示,因此它将获得更大的收益。

有关基于网址的方法的实例,请参阅ASP.NET MVC 5 culture in route and url

答案 1 :(得分:0)

我能确保获得正确资源的唯一方法是围绕直接覆盖资源,直接覆盖我的CultureFilter结尾处所有资源查找的CurrentThreadUICulture属性

your_list = list(data.values())
# (don't use 'list' as the variable name
# since it's already a standard function)

print(your_list)
# [79, 77, 89, 92, 85]

这就是诀窍!利用只有单个文件作为全局资源,所以一切都按预期工作,但我知道这对于多个资源文件和命名空间来说是不方便的。然而,对于我来说,没有其他方法可以确保语言更改可以工作,并且在尝试使用基本控制器和Iauthorization actionfilter属性之后不会冻结,或者在Global.asax.cs中使用Application_AcquireRequestState,其中没有三种方法尽管Thread.CurrentThread.CurrentUICulture总是通过三种方法正确更改,但由于冻结,所以总是可以正确地更改UI语言。