【问题标题】:Multilingual website using OWIN and asynchronous methods使用 OWIN 和异步方法的多语言网站
【发布时间】:2015-08-20 21:31:37
【问题描述】:

背景

我正在使用 IIS 上的 ASP.NET 4.6、C#、OWIN 管道 (Microsoft.Owin.Host.SystemWeb)、大量异步方法调用和标准的全局资源文件 (*.resx in App_GlobalResources) 创建一个简单的多语言网站.该网站使用 MVC5、WebAPI2 和 Autofac 作为依赖解析器。

问题

我无法正确更改生成页面的区域设置/区域性,因为异步方法在每个请求中使用多个线程,并且我找不到为与给定请求关联的每个线程设置 Thread.Current[UI]Culture 的方法,因为这些属性不是t 同步。我还想保持干净的代码,而不用“异步/等待文化配置”弄乱有用的代码。

代码

Startup.cs

public void Configuration(IAppBuilder app)
{
    ...

    app.UseAutofacMiddleware(container);
    app.UseAutofacMvc();
    app.UseAutofacWebApi(httpConfiguration);
    app.UseWebApi(httpConfiguration);

    ...

    app.Use(async (ctx, next) =>
    {
        /* in production, detect based on current URL and/or cookie */
        var culture = new CultureInfo("pl_PL");

        CultureInfo.CurrentCulture = CultureInfo.CurrentUICulture = culture;

        await next.Invoke();
    });
}

SampleController.cs

public async Task<ActionResult> SayHello()
{
    // returns pl_PL message
    var msgA = Resources.Messages.HelloWorld; 

    await someService.doSthAsync();

    // returns system default (en_US) message
    var msgB = Resources.Messages.HelloWorld;

    return Content(msgA + " - " + msgB);
}

  1. 我应该按照in this SO answer 的建议创建一个自定义[AspNet]SynchronizationContext 吗?如果是这种情况,我应该怎么做?
  2. 我应该放弃全球资源作为翻译来源并使用其他方法吗?如果是这样,我可以使用什么(库?)?

【问题讨论】:

  • 您可以在操作方法的开头保存文化。 SampleController 不是派生自 Controller 吗?您可以从那里获得当前的 HTTP 上下文和文化?
  • 我可以在操作方法开始时检测到文化,但是我应该在哪里分配它以便在用于服务单个 HTTP 响应的多个线程中保留它?
  • 您查看过您发布的链接上的代码吗?
  • 是的,我做到了,但它需要额外的“脏”代码,我想避免。它还建议更改同步上下文的实现,但我不太确定如何正确执行此操作。
  • 代码不用于同步上下文的实现。该代码类似于 ASP.NET Identity 2 的 EntityFramework sore 使用的代码。

标签: c# asynchronous internationalization async-await owin


【解决方案1】:

the Owing Globalization Library怎么样,好像就是为了这个目的而创建的。

您仍然可以使用 resx 来本地化您的资源,并且它具有强大的自定义功能:

public void Configuration(IAppBuilder app)
{
    ...
    app.UseGlobalization(new OwinGlobalizationOptions("en-US",true)
       .DisablePaths("Content", "bundles")
       .Add("fr-FR", true).AddCustomSeeker(new CultureFromPreferences()));
}

我已经测试了 async/await 的使用并保留了文化:

    public async Task<ActionResult> About()
    {
        var msgA = Resources.Resources.About;

        await Task.Delay(1000);

        var msgB = Resources.Resources.About;

        ViewBag.Message = msgA + " - " + msgB;

        return View();
    }

注意:我不是这个库的作者,我之前碰巧用过。

【讨论】:

    【解决方案2】:

    The answer by Joe Enzmiger looks appropriate here:

    public override Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext     controllerContext, CancellationToken cancellationToken)
    {
    if (controllerContext.Request.Headers.AcceptLanguage != null && 
        controllerContext.Request.Headers.AcceptLanguage.Count > 0)
    {
        string language = controllerContext.Request.Headers.AcceptLanguage.First().Value;
        var culture = CultureInfo.CreateSpecificCulture(language);
        HttpContext.Current.Items["Culture"] = culture;
        //Thread.CurrentThread.CurrentCulture = culture;
        //Thread.CurrentThread.CurrentUICulture = culture;
    }
    
    base.ExecuteAsync(controllerContext, cancellationToken); 
    }
    

    然后,在任何任务中你都需要文化:

    var culture = HttpContext.Current != null ? HttpContext.Current.Items["Culture"] as CultureInfo : Thread.CurrentThread.CurrentCulture;
    

    【讨论】:

    • 当你抄袭其他用户的答案时,礼貌的做法是链接到原始答案
    猜你喜欢
    • 1970-01-01
    • 2010-09-16
    • 2013-03-22
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 2016-07-01
    • 2010-09-22
    相关资源
    最近更新 更多