Reputation: 9123
I'm looking for something similar like the old behaviour of setting the Culture and everything in the request after that has that Culture.
Similar like ASP.NET 4.5:
Thread.CurrentThread.CurrentCulture = culture_info;
Thread.CurrentThread.CurrentUICulture = culture_info;
When I do this right now, some code seems to be constantly resetting the Culture to English at various points in the MVC pipeline.
Why do I want that? Because I want to set the culture through my extended routing logic in a very large (and customizable) platform-type of web project.
The default RouteDataRequestCultureProvider doesn't seem to work at all - see explained here:
https://irensaltali.com/en/asp-net-core-mvc-localization-by-url-routedatarequestcultureprovider/
and
Localization works when culture is set via QueryString but not when culture is in route
I don't want to use the "hardcoded" url parsing that is being presented as a solution, because my routing logic is complicated - not all routes will have a culture defined in the same spot in the URL.
I tried setting this, but that also didn't work:
httpContext.Features.Set<IRequestCultureFeature>(new RequestCultureFeature(request_culture, new DummyProvider()));
Is there any workaround for just simply setting the Culture for the rest of the request?
Upvotes: 2
Views: 4276
Reputation: 1810
I was looking for a solution similar to the OP's question for setting a culture after the request. I needed to retrieve a culture string in a query string or a request header and @Stilgar's answer provided the solution I needed. Here's a working solution of his answer. It extracts a culture key/value pair in the query string. (The code to check the request header was left out.)
For a URL request https://localhost:7181/?culture=fr
, the culture fr-FR
is set. In the OnPost
method, Thread.CurrentThread.CurrentCulture
returns fr-FR
.
public void OnPost()
{
// Display the current culture
System.Diagnostics.Debug.WriteLine("Current culture is " + Thread.CurrentThread.CurrentCulture.EnglishName);
}
program.cs
app.UseRequestLocalization(options =>
{
string defaultCulture = "en-US";
string[] supportedCultures = new string[] { "fr-FR", defaultCulture };
options.AddSupportedUICultures(supportedCultures)
.AddSupportedCultures(supportedCultures)
.AddInitialRequestCultureProvider(
new CustomRequestCultureProvider(async context =>
{
string? cultureCode =
await GetCultureFromRequest(context.Request, supportedCultures) ?? defaultCulture;
return new ProviderCultureResult(cultureCode);
}));
});
static Task<string?> GetCultureFromRequest(
HttpRequest request, string[] supportedCultures)
{
if (request.QueryString.Value is null ||
!request.QueryString.Value.Contains("culture="))
{
return Task.FromResult<string?>(default);
}
string? cultureVal =
request.Query.FirstOrDefault(q => q.Key == "culture").Value;
if (cultureVal is null || string.IsNullOrEmpty(cultureVal))
{
return Task.FromResult<string?>(default);
}
string? cultureEntry = supportedCultures
.Where(c => c.Contains(cultureVal)).FirstOrDefault();
return Task.FromResult(cultureEntry ?? default);
}
The above solution is the same as setting up a localization middleware found here:
program.cs
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
CultureInfo[] supportedCultures = new[]
{
new CultureInfo("fr"),
};
options.DefaultRequestCulture = new RequestCulture("en-US");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
options.RequestCultureProviders.Insert(0, new QueryStringRequestCultureProvider());
});
IOptions<RequestLocalizationOptions> locOptions = builder.Services.BuildServiceProvider().GetService<IOptions<RequestLocalizationOptions>>();
app.UseRequestLocalization(locOptions.Value);
Upvotes: 0
Reputation: 23571
While I too can't find a way to set the request culture for the rest of the request you can use the CustomRequestCultureProvider and use your custom routing logic to get set the culture for the request
app.UseRequestLocalization(options =>
{
options.AddSupportedUICultures(... set some cultures here...)
.AddSupportedCultures(... and here...)
.AddInitialRequestCultureProvider(new CustomRequestCultureProvider(async context =>
{
string cultureCode = await RunCustomRoutingAndGetCulture(context.Request);
return new ProviderCultureResult(cultureCode);
}));
});
If your custom logic does not require awaiting remove async and return with Task.FromResult
Upvotes: 1
Reputation: 36715
A workaround for simply setting the Culture for the rest of the request is to use default QueryStringRequestCultureProvider
.
Here is the whole demo:
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddControllersWithViews()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
//...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("de"),
new CultureInfo("fr"),
};
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en-US"),
// Formatting numbers, dates, etc.
SupportedCultures = supportedCultures,
// UI strings that we have localized.
SupportedUICultures = supportedCultures
});
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Create Resource folder and add resource file as below:
Configure resource file like below:
Controllers.HomeController.de.resx
Controllers.HomeController.fr.resx
Reference:Resource file naming
Controller:
public class HomeController : Controller
{
private readonly IStringLocalizer<HomeController> _localizer;
public HomeController(IStringLocalizer<HomeController> localizer)
{
_localizer = localizer;
}
public IActionResult Index()
{
ViewData["Title"] = _localizer["Your Title"];
return View();
}
}
Index.cshtml:
@ViewData["Title"]
Upvotes: 2