Let me have a comprehensive response for this question and describe how I came up with a solution after reading .net core source code.
before any further description, first install this package using NuGet Microsoft.Extensions.Localization
as you guys might remember in asp.net full framework, switching between cultures was pretty straightforward
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
but in .net core, the culture is not tied to the current thread anymore. The asp.net core engine has a pipeline and we can add different MiddleWares to this pipeline, so the RequestLocalizationMiddleware is the middleware class for handling localization, we might have multiple providers so it will iterate through all the culture providers like QueryStringRequestCultureProvider, CookieRequestCultureProvider, AcceptLanguageHeaderRequestCultureProvider, …
as soon as the request localization middleware can get the current locale from the first provider then it will ignore others and pass the request to the next middleware in the pipeline, so the order of providers in the list really matters.
i personally prefer to store the culture in the browser cookie, so since the CookieRequestCultureProvider is not the first culture provider in the list i move it to the top of the list, the configuration of this part in Startup.cs > ConfigureServices is as below
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("fa-IR"),
new CultureInfo("de-DE")
};
options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
var defaultCookieRequestProvider =
options.RequestCultureProviders.FirstOrDefault(rcp =>
rcp.GetType() == typeof(CookieRequestCultureProvider));
if (defaultCookieRequestProvider != null)
options.RequestCultureProviders.Remove(defaultCookieRequestProvider);
options.RequestCultureProviders.Insert(0,
new CookieRequestCultureProvider()
{
CookieName = ".AspNetCore.Culture",
Options = options
});
});
let me describe the above code, our app default culture is en-US and we only support English, Farsi, Germany so if the browser has different locale or you setting language is other than these 3 languages then the app must switch to default culture. in above code i just remove CookieCultureProvider from the list and add it as the first provider in the list (* I already described the reason why it must be the first one*). The default CookieName is working for me, you can change it if you want.
don't forget to add the below code beneath of Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs
var options = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
app.UseRequestLocalization(options.Value);
so far so good. To have resource files you must specify the path for the resources, I separately specify the resource for Controller, Views, ViewModels and SharedResources in web project root path, the hierarchy is like below
|-Resoures
|---Controllers
|---Views
|---Models
|---SharedResource.resx
|---SharedResource.fa-IR.resx
|---SharedResource.de-DE.resx
keep in mind that for the SharedResource create an empty class with the same name in the Web Project root path i mean a SharedResource.cs with a class with the same name inside.
Add the following code snippet inside the Startup.cs for specifying the resource path and the rest.
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMvc()
AddViewLocalization(
LanguageViewLocationExpanderFormat.Suffix,
opts => { opts.ResourcesPath = "Resources/Views"; }) // this line is not required, it is just for more clarification
.AddDataAnnotationsLocalization();
with this configurations for example when you inject IStringLocalizer inside the controller you have to have corresponding resource files in Resources > Controllers folder (which are HomeController.resx, HomeController.fa-IR.resx, HomeController.de-DE.resx) we can also separate the path by .(say dot) in file name i mean Resources/Controllers/HomeController.resx can be a file in Resources/Controllers.HomeController.resx, you have to inject IViewLocalizer into views to have the localization inside views correspondingly you have to have resources files for Views inside Resources/Views folder, for the ViewModels since we named the folder Models please put all ViewModels inside a pre-created Folder in the Web project root path with the name Models, if the folder has another name or you prefer another name don't forget to rename the Models folder under Resource folder. then you need to do nothing but annotating models, for example, annotate ViewModel properties with [DisplayName("User Emailaddress")] then create resources in the corresponding resource files inside the model (the name of the files must match the model class name) with the same key ("User Emailaddress").
let's finish with where we already started, I mean CookieRequestCultureProvider. as I said earlier I prefer to store it in a cookie, but it is a bit tricky, because cookie parsing is a bit different from what you might expected, just add below code where you want to change the culture, only replace preferedCulture with your culture preference
var preferedCulture = "fa-IR"; // get the culture from user, i just mock it here in a variable
if (HttpContext.Response.Cookies.ContainsKey(".AspNetCore.Culture"))
{
HttpContext.Response.Cookies.Delete(".AspNetCore.Culture");
}
HttpContext.Response.Cookies.Append(".AspNetCore.Culture",
$"c={preferedCulture}|uic={preferedCulture}", new CookieOptions {Expires = DateTime.UtcNow.AddYears(1)});
here we go, our sp.net core web app is now localized :)