【问题标题】:ASP.NET MVC 5 localization resources freeze and do not change language despite changing CurrentThread.CurrentCulture尽管更改了 CurrentThread.CurrentCulture,但 ASP.NET MVC 5 本地化资源冻结并且不更改语言
【发布时间】:2016-07-08 19:51:39
【问题描述】:

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

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

我在视图中使用了一个隐藏文本来跟踪 CurrentThread.CurrentCulture 是什么以及正在按预期变化所以问题一定出在其他地方。

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

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

在运行时,当语言或 resx 被冻结/卡在应用程序级别时,该值确实是正确的并被选中,但它与视图中显示的语言不匹配,并且在我制作例如 a 之前它不会正常工作更改 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 将它们公开以能够编译并可供控制器、视图等访问的做法是一致的

资源文件夹

谁能给我另一种选择来尝试或思考我可能做错了什么,或者我还可以尝试什么来使资源始终如一地工作。

【问题讨论】:

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


    【解决方案1】:

    我可以确保获得正确资源的唯一方法是解决直接覆盖资源的 CurrentThreadUICulture 属性,用于我的 CultureFilter 末尾的所有资源查找

    Resources.Resource.Culture = cultureInfo;
    

    这成功了!利用只有单个文件作为全局资源,所以现在一切都按预期工作,但我知道这对于多个资源文件和命名空间来说是不方便的。尽管如此,在尝试使用基本控制器和 Iauthorization actionfilter 属性或在 Global.asax.cs 中使用 Application_AcquireRequestState 三种方法都没有的情况下,我没有其他方法可以确保语言更改能够正常工作并且不会冻结尽管 Thread.CurrentThread.CurrentUICulture 始终通过三种方法正确更改,但由于冻结,始终可以正确更改 UI 语言。

    【讨论】:

      【解决方案2】:

      我相信这里发生的事情是您将文化设置在与实际使用它的位置不同的线程中。您在异步方法BeginExecuteCore 中设置文化,这可能不会始终如一地将文化转移到应用程序的 UI 工作线程。这可以解释间歇性行为 - 当应用程序线程运行缓慢时,您可能会在重负载期间看到它“冻结”。

      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。如果你这样做,选择文化就像选择一个新的 URL 一样简单,它会自动“坚持”,因为 MVC 重用路由值。即使用户将 URL 发送给其他人,它也永远不会过期。最重要的是,您将网站翻译成多种语言所做的所有工作都将被搜索引擎看到,因此会有更大的回报。

      有关基于 URL 的方法的工作示例,请参阅 ASP.NET MVC 5 culture in route and url

      【讨论】:

      • 我完全同意你关于从 SEO 角度从 URL 获取语言的观点,但这是一个完全不需要 SEO 的私有应用程序。我实际上是这样做的,因为我的地图路线是这样工作的:“{tenant}/{controller}/{action}/{id}”每个租户都是一个数据库,其中存储了我从数据库查询中读取的语言默认首选项我在 CultureHelper 中拥有所有这些逻辑,但每个租户都有不同的用户,他们有不同的语言偏好,可以推翻他们的租户父偏好。
      • 而且我以前使用过你建议的过滤器,但我仍然保持冻结状态,但如果我只使用它,即使没有冻结,也不是所有内容都能正确本地化,我还必须在 Global 中使用 Application_AcquireRequestState .asax,cs。我没有做的是在使用过滤器的同时删除 BaseController 逻辑(这可以使您的理论正确)然后我只尝试使用基本控制器,现在我只留下了 Action 过滤器和 AcquireRequestState 。跨度>
      • 就像我在帖子中所说的那样,语言的选择和阅读一直都很好,因为我在视图隐藏文本中读取了正确的值非常感谢您的帮助,但是让我们看看这是否作品。然后我只能保留一种方法,即动作过滤器或 global.asax 逻辑。
      • 单独使用动作过滤器并不能解决问题,尽管 CurrentCulture 的价值,应用程序有时仍会冻结所有会话的语言,我仍在努力解决这个问题,我将非常感谢任何其他尝试的帮助或想法类型
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多