请求的 QueryString 本地化区域性在具有 AJAX 请求的 ASP.NET 核心 Razor 页面模型中不起作用
Requested QueryString localized culture is not working in ASP.NET Core Razor Page models with AJAX Request
我们在 ASP.NET 基于核心 Razor 页面的应用程序中的多语言开发中出现奇怪的行为,我们在 .cshtml 页面中使用的 IStringLocalizer 对象正在识别当前选择的文化并正确翻译所有元素根据所选区域性的资源文件位于 .cshtml 文件中,但在页面模型(即 .cshtml.cs)文件中使用的相同对象总是在通过 AJAX 请求时翻译成浏览器默认语言 GET 或 POST 方法 。它无法识别当前选择的文化。使用了 QueryStringRequestCultureProvider。
这是在 .cshtml 页面中工作的内容,它根据在 String
中选择的区域性进行翻译
@page
@inject IStringLocalizer<IndexModel> localizer
@model IndexModel
@{
ViewData["Title"] = "Home page";
var requestCultureFeature = HttpContext.Features.Get<IRequestCultureFeature>();
var requestCulture = requestCultureFeature.RequestCulture;
}
<div class="text-center">
<h1 class="display-4">@localizer["Welcome"]</h1>
</div>
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.min.js"></script>
<script lang="javascript">
$(document).ready(function () {
$("#btnnsubmit").click(function () {
var obj = {};
obj.name = "name";
//url
$.ajax({
type: "Get",
url: '/Index?handler=callingAjax',
data: obj,
dataType: "html",
contentType: "application/json; charset=utf-8",
success: function () {
debugger;
},
error: function () {
}
});
});
});
</script>
这是我在页面模型(即 .cshtml.cs 页面)中所做的,它总是翻译成浏览器的默认语言。
public class IndexModel : PageModel
{
public string Message { get; set; }
private readonly ILogger log;
private readonly IStringLocalizer<IndexModel> localizer;
public IndexModel(ILogger<IndexModel> log, IStringLocalizer<IndexModel> _localizer)
{
this.log = log;
localizer = _localizer;
}
public void OnGet()
{
// Called on page load without AJAX
//Localization is working here, gives translation based on selected
//language
string str = _localizer["InvalidLoginAttempt"];
}
public IActionResult OnGetCallingAjax()
{
int MaxThreads = 1;
// Called using AJAX
//Localization is not working here, always gives translation based on
//browsers default language,
string str1 = _localizer["InvalidLoginAttempt"];
string message = "SUCCESS";
return new JsonResult(new
{
Error = false,
Message = message,
// lstlang = lstlanguage
});
}
}
以下是启动文件设置。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[]
{
new CultureInfo("de"),
new CultureInfo("fr"),
new CultureInfo("en"),
new CultureInfo("nl"),
};
options.DefaultRequestCulture = new RequestCulture("de");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
options.RequestCultureProviders = new List<IRequestCultureProvider>
{
new QueryStringRequestCultureProvider(),
new CookieRequestCultureProvider()
};
});
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(30);
});
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(30); //default is 20 min
});
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMemoryCache();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix);
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddLog4Net();
Log4NetFilters.Set( // filter out the standard log messages with source "Microsoft"
source: "Microsoft",
filteredLogLevel: LogLevel.Information);
ILogger logger = loggerFactory.CreateLogger<Program>();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStaticFiles();
//app.UseRouting();
app.UseSession();
app.UseCookiePolicy();
app.UseHttpsRedirection();
var localizationOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>().Value;
app.UseRequestLocalization(localizationOptions);
app.UseMvc();
}
从评论来看,问题似乎在于您只是在初始请求的查询字符串中提供了文化,而没有将其存储在任何地方,因此在后续请求中没有使用该文化,AJAX 包括在内。
将区域性放入查询字符串中无处不在,但可能并不理想。
在您的 Startup
中,您正在设置 CookieRequestCultureProvider
,因此您可以利用它而不是每次都将内容放入查询字符串中。
例如,您可以创建一个可调用的操作以将文化首选项存储在 cookie 中:
public class CultureController : Controller
{
[HttpPost]
public IActionResult SelectCulture(string culture, string returnUrl)
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
);
return LocalRedirect(returnUrl);
}
}
这样做,CookieRequestCultureProvider
会选择用户偏好。
我们在 ASP.NET 基于核心 Razor 页面的应用程序中的多语言开发中出现奇怪的行为,我们在 .cshtml 页面中使用的 IStringLocalizer 对象正在识别当前选择的文化并正确翻译所有元素根据所选区域性的资源文件位于 .cshtml 文件中,但在页面模型(即 .cshtml.cs)文件中使用的相同对象总是在通过 AJAX 请求时翻译成浏览器默认语言 GET 或 POST 方法 。它无法识别当前选择的文化。使用了 QueryStringRequestCultureProvider。
这是在 .cshtml 页面中工作的内容,它根据在 String
中选择的区域性进行翻译@page
@inject IStringLocalizer<IndexModel> localizer
@model IndexModel
@{
ViewData["Title"] = "Home page";
var requestCultureFeature = HttpContext.Features.Get<IRequestCultureFeature>();
var requestCulture = requestCultureFeature.RequestCulture;
}
<div class="text-center">
<h1 class="display-4">@localizer["Welcome"]</h1>
</div>
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.min.js"></script>
<script lang="javascript">
$(document).ready(function () {
$("#btnnsubmit").click(function () {
var obj = {};
obj.name = "name";
//url
$.ajax({
type: "Get",
url: '/Index?handler=callingAjax',
data: obj,
dataType: "html",
contentType: "application/json; charset=utf-8",
success: function () {
debugger;
},
error: function () {
}
});
});
});
</script>
这是我在页面模型(即 .cshtml.cs 页面)中所做的,它总是翻译成浏览器的默认语言。
public class IndexModel : PageModel
{
public string Message { get; set; }
private readonly ILogger log;
private readonly IStringLocalizer<IndexModel> localizer;
public IndexModel(ILogger<IndexModel> log, IStringLocalizer<IndexModel> _localizer)
{
this.log = log;
localizer = _localizer;
}
public void OnGet()
{
// Called on page load without AJAX
//Localization is working here, gives translation based on selected
//language
string str = _localizer["InvalidLoginAttempt"];
}
public IActionResult OnGetCallingAjax()
{
int MaxThreads = 1;
// Called using AJAX
//Localization is not working here, always gives translation based on
//browsers default language,
string str1 = _localizer["InvalidLoginAttempt"];
string message = "SUCCESS";
return new JsonResult(new
{
Error = false,
Message = message,
// lstlang = lstlanguage
});
}
}
以下是启动文件设置。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[]
{
new CultureInfo("de"),
new CultureInfo("fr"),
new CultureInfo("en"),
new CultureInfo("nl"),
};
options.DefaultRequestCulture = new RequestCulture("de");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
options.RequestCultureProviders = new List<IRequestCultureProvider>
{
new QueryStringRequestCultureProvider(),
new CookieRequestCultureProvider()
};
});
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(30);
});
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(30); //default is 20 min
});
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMemoryCache();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix);
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddLog4Net();
Log4NetFilters.Set( // filter out the standard log messages with source "Microsoft"
source: "Microsoft",
filteredLogLevel: LogLevel.Information);
ILogger logger = loggerFactory.CreateLogger<Program>();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStaticFiles();
//app.UseRouting();
app.UseSession();
app.UseCookiePolicy();
app.UseHttpsRedirection();
var localizationOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>().Value;
app.UseRequestLocalization(localizationOptions);
app.UseMvc();
}
从评论来看,问题似乎在于您只是在初始请求的查询字符串中提供了文化,而没有将其存储在任何地方,因此在后续请求中没有使用该文化,AJAX 包括在内。
将区域性放入查询字符串中无处不在,但可能并不理想。
在您的 Startup
中,您正在设置 CookieRequestCultureProvider
,因此您可以利用它而不是每次都将内容放入查询字符串中。
例如,您可以创建一个可调用的操作以将文化首选项存储在 cookie 中:
public class CultureController : Controller
{
[HttpPost]
public IActionResult SelectCulture(string culture, string returnUrl)
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
);
return LocalRedirect(returnUrl);
}
}
这样做,CookieRequestCultureProvider
会选择用户偏好。