代码之家  ›  专栏  ›  技术社区  ›  Kacper Stachowski

来自控制器操作的MVC核心集应用程序区域性

  •  4
  • Kacper Stachowski  · 技术社区  · 6 年前

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

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

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

    编辑:

    我刚刚知道我可以这样做:

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

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

    编辑2:

    关于亚当·西蒙的回答: CookieRequestCultureProvider 是我想在我的应用程序中使用的,但问题是它没有生成任何cookie。文档说明.net core将通过检查哪个提供程序将提供一个工作解决方案来解决使用哪个提供程序的问题,从 QueryStringRequestCultureProvider ,然后转到 CookiierequestCultureProvider供应商 ,然后是其他提供者。

    我现在的创业公司是这样的:

    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 ,在 Configure 之前 UseRequestLocalization 在那之后。结果都一样。

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

    • MakeCookieValue 方法没有产生任何 .AspNetCore.Culture 饼干

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

    • 不知怎的,我的本地化在默认情况下不使用查询 字符串或cookie为应用程序提供区域性,但这是 我不想它怎么工作,因为我无法控制它

    1 回复  |  直到 6 年前
        1
  •  10
  •   Adam Simon    6 年前

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

    • 饼干
    • 阶段
    • 数据库
    • HTTP头
    • 隐藏输入

    正常情况下 使用cookie存储语言首选项是一个完美的选择 .

    在ASP.NET内核中 为当前请求检索和设置区域性的最佳位置是中间件 . 幸运的是,框架包含一个,可以通过调用 app.UseRequestLocalization(…) 在你的 启动。配置 方法。默认情况下,此中间件将尝试按此顺序从请求URL、cookies和接受语言HTTP头中获取当前区域性。

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

    你可以在 this MSDN article .

    奖金:

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

    将本地化文本传递到ViewData中的视图是繁琐且容易出错的。在ASP.NET内核中,我们有 view localization 为此目的。你只需要注射 IViewLocalizer公司 组件,以获得访问本地化文本资源的方便方法。(在引擎盖下面 IViewLocalizer公司 使用 IStringLocalizer公司 .)

    关于编辑2

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

    CookierEquiestCultureProvider.MakeCookieValue 方法只是生成正确格式的cookie值的助手。它只返回一个字符串,仅此而已。但即使它是要将cookie添加到响应中,调用它 启动。配置 当您在那里配置请求管道时将完全错误。(在我看来,你对ASP.NET内核中的请求处理和中间件有些困惑,所以我建议你研究一下 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容器中。)

    那你可以吃点 控制器动作 设置区域性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-in选项的en-US区域性(尽管注释掉了options.DefaultRequestCulture= 新的请求区域性(defaultCulutreName、defaultCulutreName)行

    我怀疑Chrome浏览器在接受语言头中发送了语言首选项,而FF没有。

    不知怎的,我的本地化在默认情况下工作,没有使用查询字符串或cookie为应用程序提供区域性,但这不是我希望它工作的方式,因为我对它没有任何控制权

    我重复:

    默认情况下,此中间件将尝试从请求URL、cookies和 接受语言HTTP头 ,按此顺序。

    您可以通过更改或替换 RequestLocalizationOptions.RequestCultureProviders 列表。