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

时间:2018-08-10 11:00:47

标签: model-view-controller asp.net-core .net-core asp.net-core-mvc 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>

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

编辑2:

关于亚当·西蒙的回答: 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}");
    }
}

关于CookieRequestCultureProvider.MakeCookieValue(new RequestCulture("pl-PL", "pl-PL")),我尝试将其放在RequestLocalizationOptions之前和之后的ConfigureServices中的ConfigureUseRequestLocalization中。所有结果都相同。

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

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

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

  • 在不使用查询的情况下,默认情况下我的本地化工作正常 字符串或cookie来提供应用文化,但这是 不是我希望它如何工作,因为我对此没有任何控制权

1 个答案:

答案 0 :(得分:4)

您必须以某种方式将选定的区域性与用户联系起来,因此,如果您不想在URL中随身携带该区域性,则必须找到另一种方法来在请求之间保留此信息。您的选择:

  • cookie
  • 会话
  • 数据库
  • 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使用了在选项中将语言设置为PL的美国文化(尽管注释了options.DefaultRequestCulture =   新的RequestCulture(defaultCulutreName,defaultCulutreName)行)

我怀疑Chrome浏览器在“接受语言”标头中发送了语言偏好设置,而FF则没有。

  

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

我重复:

  

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

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