【问题标题】:MVC Core set application culture from controller actionMVC Core 从控制器操作中设置应用程序文化
【发布时间】:2019-01-17 23:36:08
【问题描述】:

我正在为我的 Web 应用程序添加本地化。我已经配置了IStringLocalizer,它可以从两个不同的 resx 文件中正确读取字符串资源,具体取决于浏览器设置。然后它将这些字符串资源映射到 ViewData,我的 View 从中获取正确语言的文本(不确定这是否是最好的方法,但现在我不想花更多时间在这上面)。

问题是我的 UI 中还有一个下拉列表,允许用户手动切换语言。我正在读取用户在我的控制器操作中设置的值并将其添加到 cookie,但现在我还想将我的应用程序的文化设置为与 cookie 中的字符串匹配的文化。

是否可以通过 MVC Core 中的控制器操作设置应用程序文化?如果是,那么如何正确执行此操作?

编辑:

我刚刚了解到我可以这样做:

<a class="nav-item nav-link" asp-route-culture="en-US">English</a>

它会将?culture=en-US 添加到我的路线中,这将为我设置页面的文化。有什么方法可以做到这一点而不必将其保存在地址栏中?

编辑 2:

关于亚当西蒙的回答: CookieRequestCultureProvider 是我想在我的应用程序中使用的,但问题是它没有产生任何 cookie。文档说 .net 核心将通过检查哪个提供者提供有效的解决方案来解析使用哪个提供者,从 QueryStringRequestCultureProvider 开始,然后转到 CookieRequestCultureProvider,然后是其他提供者。

我当前的 Startup 是这样的:

public class Startup
{
    private const string defaultCulutreName = "en-US";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddLocalization(); 
        services.Configure<RequestLocalizationOptions>(options =>
        {
            var supportedCultures = new[]
            {
                new CultureInfo(defaultCulutreName),
                new CultureInfo("pl-PL")
             };
            options.DefaultRequestCulture = new RequestCulture(defaultCulutreName, defaultCulutreName);

            options.SupportedCultures = supportedCultures;
            options.SupportedUICultures = supportedCultures;
        });

        services.AddMvc()
            .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix);
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseStaticFiles();

        //TRIED PLACING IT BEFORE AND AFTER UseRequestLocalization
        //CookieRequestCultureProvider.MakeCookieValue(new RequestCulture("pl-PL", "pl-PL"));

        app.UseRequestLocalization(app.ApplicationServices
            .GetService<IOptions<RequestLocalizationOptions>>().Value);

        CookieRequestCultureProvider.MakeCookieValue(new RequestCulture("pl-PL", "pl-PL"));

        app.UseMvc(ConfigureRoutes);
    }

    private void ConfigureRoutes(IRouteBuilder routeBuilder)
    {
        routeBuilder.MapRoute("Default", "{controller=About}/{action=About}");
    }
}

关于CookieRequestCultureProvider.MakeCookieValue(new RequestCulture("pl-PL", "pl-PL")),我尝试将其放入ConfigureServices 中的RequestLocalizationOptions,在UseRequestLocalization 之前和之后的Configure 中。结果都一样。

此解决方案出现以下“问题”:

  • MakeCookieValue 方法未生成任何 .AspNetCore.Culture cookie

  • 语言设置为 PL 的 Chrome 浏览器正在使用 pl-PL 文化 正确,但 Firefox 使用 en-US 文化,语言设置为 PL 在选项中(尽管注释掉了options.DefaultRequestCulture = new RequestCulture(defaultCulutreName, defaultCulutreName) 行)

  • 不知何故,我的本地化默认情况下可以正常工作,而无需使用查询 字符串或 cookie 为应用程序提供文化,但这是 不是我想要的,因为我无法控制它

【问题讨论】:

    标签: model-view-controller asp.net-core .net-core asp.net-core-mvc asp.net-core-2.0


    【解决方案1】:

    您必须以某种方式将所选文化与用户联系起来,因此如果您不想在 URL 中携带它,则必须找到另一种方法来在请求之间保留这条信息。您的选择:

    • 饼干
    • 会话
    • 数据库
    • HTTP 标头
    • 隐藏输入

    一般情况下使用cookie来存储语言偏好是一个完美的选择

    在 ASP.NET Core 为当前请求检索和设置区域性的最佳位置是中间件。幸运的是,该框架包含一个,可以通过在您的 Startup.Configure 方法中调用 app.UseRequestLocalization(...) 将其放入请求管道中。默认情况下,此中间件将尝试按此顺序从请求 URL、cookie 和 Accept-Language HTTP 标头中获取当前文化。

    因此,总结一下:您需要使用请求本地化中间件,将用户的文化偏好存储在格式为 c=%LANGCODE%|uic=%LANGCODE%(例如 c=en-US|uic=en-US)的 cookie 中,然后就完成了。

    您可以在this MSDN article 中找到所有详细信息。

    奖金:

    然后它将这些字符串资源映射到 ViewData,我的 View 就是从该数据中获取的 以正确的语言获取文本(不确定这是否是最好的 方法,但现在我不想花更多时间在这上面)。

    将本地化文本传递给 ViewData 中的视图既麻烦又容易出错。在 ASP.NET Core 中,我们有 view localization 用于此目的。您只需将 IViewLocalizer 组件注入到您的视图中,即可获得一种访问本地化文本资源的便捷方式。 (在引擎盖下 IViewLocalizer 使用 IStringLocalizer。)

    关于 EDIT 2

    MakeCookieValue 方法不产生任何 .AspNetCore.Culture cookie

    CookieRequestCultureProvider.MakeCookieValue 方法只是生成正确格式的 cookie 值的助手。它只返回一个字符串,仅此而已。但是,即使它打算将 cookie 添加到响应中,在 Startup.Configure 中调用它也是完全错误的,因为您在那里配置了请求管道。 (在我看来你对 ASP.NET Core 中的请求处理和中间件有点困惑,所以我建议学习this topic。)

    所以正确的请求管道的设置是这样的:

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
    
            #region Localization
            // REMARK: you may refactor this into a separate method as it's better to avoid long methods with regions
            var supportedCultures = new[]
            {
                new CultureInfo(defaultCultureName),
                new CultureInfo("pl-PL")
            };
            var localizationOptions = new RequestLocalizationOptions
            {
                DefaultRequestCulture = new RequestCulture(defaultCultureName, defaultCultureName),
                SupportedCultures = supportedCultures,
                SupportedUICultures = supportedCultures,
                // you can change the list of providers, if you don't want the default behavior
                // e.g. the following line enables to pick up culture ONLY from cookies
                RequestCultureProviders = new[] { new CookieRequestCultureProvider() }
            };
            app.UseRequestLocalization(localizationOptions);
            #endregion
    
            app.UseStaticFiles();
    
            app.UseMvc(ConfigureRoutes);
        }
    

    (以上备注:不需要在DI容器中注册RequestLocalizationOptions。)

    然后你可以有一些控制器操作设置文化cookie:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult SetCulture(string culture, string returnUrl)
    {
        HttpContext.Response.Cookies.Append(
            CookieRequestCultureProvider.DefaultCookieName,
            CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
            // making cookie valid for the actual app root path (which is not necessarily "/" e.g. if we're behind a reverse proxy)
            new CookieOptions { Path = Url.Content("~/") });
    
        return Redirect(returnUrl);
    }
    

    最后,一个如何从视图调用它的示例:

    @using Microsoft.AspNetCore.Localization
    @using Microsoft.AspNetCore.Http.Extensions
    
    @{
        var httpContext = ViewContext.HttpContext;
        var currentCulture = httpContext.Features.Get<IRequestCultureFeature>().RequestCulture.UICulture;
        var currentUrl = UriHelper.BuildRelative(httpContext.Request.PathBase, httpContext.Request.Path, httpContext.Request.QueryString);
    }
    <form asp-action="SetCulture" method="post">
        Culture: <input type="text" name="culture" value="@currentCulture">
        <input type="hidden" name="returnUrl" value="@currentUrl">
        <input type="submit" value="Submit">
    </form>
    

    语言设置为 PL 的 Chrome 浏览器正确使用 pl-PL 文化,但 Firefox 使用的是 en-US 文化,并且选项中的语言设置为 PL(尽管注释掉了 options.DefaultRequestCulture = 新的 RequestCulture(defaultCulutreName, defaultCulutreName) 行)

    我怀疑 Chrome 浏览器在 Accept-Language 标头中发送语言首选项,而 FF 没有。

    默认情况下,我的本地化在不使用查询字符串或 cookie 为应用程序提供文化的情况下以某种方式工作,但这不是我希望它的工作方式,因为我无法控制它

    我再说一遍:

    默认情况下,此中间件将尝试从请求 URL、cookie 和 Accept-Language HTTP 标头中获取当前文化,按此顺序。

    您可以通过更改或替换 RequestLocalizationOptions.RequestCultureProviders 列表来配置此行为。

    【讨论】:

    • 感谢您的回答。你已经为我阐明了这个话题,但我仍然在努力。我曾尝试使用 CookieRequestCultureProvider,但遗憾的是没有成功……我已更新我的问题以提供更多信息。
    • 再次感谢。你是对的,我仍然对所有这些 ASP.Net Core 的东西感到困惑。那是我的第一个“沙盒”Web 应用程序,经过几年的桌面开发,转移到 Web 并不是那么容易……我在这个问题上开始了赏金,只是为了奖励你帮助我解决这个问题。我已经为此苦苦挣扎了几天,所以你完全应得的:) 从现在起 24 小时后,我将能够将其标记为悬赏的答案
    • 嘿,你真是太好了,谢谢!可以肯定的是,Web 开发是一个完全不同的动物,一个需要多种技术专业知识的巨大主题(尤其是在当今所有 JS 伪造的情况下)。如果 Silverlight 成功了……至少通过 WebAssembly,我们重拾了希望(有一些非常令人兴奋的项目,例如 Uno Platform)。在那之前,我们已经有了 ASP.NET Core,这是一个相当不错的框架。继续阅读(写得很好)official docs,你很快就会熟悉它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-25
    • 1970-01-01
    • 2020-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-09
    相关资源
    最近更新 更多