MVC核心通过控制器动作设置应用程序文化

Kac*_*ski 4 model-view-controller asp.net-core-mvc .net-core asp.net-core asp.net-core-2.0

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

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

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

编辑:

我刚刚知道我可以做这样的事情:

<a class="nav-item nav-link" asp-route-culture="en-US">English</a>
Run Code Online (Sandbox Code Playgroud)

它将添加?culture=en-US到我的路线中,从而为我设置页面的区域性。有什么方法可以不必将其保存在地址栏中?

编辑2:

关于亚当·西蒙(Adam Simon)的回答: CookieRequestCultureProvider我想在我的应用程序中使用它,但是问题是它不会产生任何cookie。文档说,.net核心将通过检查哪个将提供一个可行的解决方案来确定要使用的提供程序,该解决方案从QueryStringRequestCultureProvider(然后到CookieRequestCultureProvider)其他提供程序开始。

我当前的启动看起来像这样:

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}");
    }
}
Run Code Online (Sandbox Code Playgroud)

关于CookieRequestCultureProvider.MakeCookieValue(new RequestCulture("pl-PL", "pl-PL"))我试图把它RequestLocalizationOptionsConfigureServices,在Configure之前UseRequestLocalization和之后。所有结果相同。

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

  • MakeCookieValue方法不产生任何.AspNetCore.Culturecookie

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

  • 某种程度上,我的本地化默认情况下是在不使用查询字符串或cookie来为应用程序提供文化的情况下工作的,但这不是我希望它起作用的方式,因为我对此没有任何控制权

Ada*_*mon 9

您必须以某种方式必须将选定的区域性与用户绑定,因此,如果您不想在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中,然后就可以完成了。

您可以在此MSDN文章中找到所有详细信息。

奖金:

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

将本地化的文本传递给ViewData中的视图既麻烦又容易出错。在ASP.NET Core中,我们为此目的进行了视图本地化。您只需要在视图中注入IViewLocalizer组件即可获得访问本地化文本资源的便捷方法。(IViewLocalizer后台使用IStringLocalizer。)

关于EDIT 2

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

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

因此,请求管道的正确设置如下所示:

    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);
    }
Run Code Online (Sandbox Code Playgroud)

(上面的说明:无需在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);
}
Run Code Online (Sandbox Code Playgroud)

最后,一个示例如何从视图调用此方法:

@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>
Run Code Online (Sandbox Code Playgroud)

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

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

某种程度上,我的本地化默认情况下是在不使用查询字符串或cookie来为应用程序提供文化的情况下工作的,但这不是我希望它起作用的方式,因为我对此没有任何控制权

我重复:

默认情况下,该中间件将尝试按请求顺序从请求URL,cookie和Accept-Language HTTP标头中选择当前区域性。

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