I had the same problem since yesterday. It seems that LocalizationProvider cannot set the correct culture. After implementing a custom LocalizationProvider, IViewLocalizer began to work fine for Views and ViewComponents .
I also had to implement the language in the URL as follows:
http://somedomain.com/<2-letter-iso-language-name>/controller/action
Here is what I did:
Startup.cs
public static string defaultCulture = "uk-UA"; public static List<CultureInfo> supportedCultures { get { return new List<CultureInfo> { new CultureInfo("en-US"), new CultureInfo("uk-UA"), new CultureInfo("de-DE") }; } }
In the ConfigureServices method, I added CultureToUrlActionFilter() . It extracts the 2 letter language code from the url and sets the correct CultureInfo . If Url contains an invalid culture code, it redirects to the default culture.
public void ConfigureServices(IServiceCollection services) { services .AddLocalization(options => options.ResourcesPath = "Resources") .AddMvc(options => { options.Filters.Add(new CultureToUrlActionFilter()); }) .AddViewLocalization() .AddDataAnnotationsLocalization(); }
In the Configure method, I configured a custom localization provider and inserted it as the first provider in the queue:
var options = new RequestLocalizationOptions() { DefaultRequestCulture = new RequestCulture(defaultCulture), SupportedCultures = supportedCultures, SupportedUICultures = supportedCultures }; options.RequestCultureProviders.Insert(0, new UrlCultureProvider()); app.UseRequestLocalization(options);
If the URL does not contain the culture code after the domain name, I am redirecting to http://somedomain/<2-letter-culture-code> (by default). Urls must always indicate culture.
Routes
app.UseMvc(routes => { routes.MapRoute("home_empty", "", defaults: new { culture="", controller = "Home", action = "Redirect" }); routes.MapRoute("home", "{culture}", defaults: new { controller = "Home", action = "Index" });
I redirect it to HomeController, Redirect action
HomeController.cs
public IActionResult Redirect() { return RedirectToAction("Index", new { culture = Startup.defaultCulture }); }
UrlCultureProvider.cs
using Microsoft.AspNetCore.Localization; using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using System.Globalization; namespace astika.Models { public class UrlCultureProvider: IRequestCultureProvider { public Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext) { if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); var pathSegments = httpContext.Request.Path.Value.Trim('/').Split('/'); string url_culture = pathSegments[0].ToLower(); CultureInfo ci = Startup.supportedCultures.Find(x => x.TwoLetterISOLanguageName.ToLower() == url_culture); if (ci == null) ci = new CultureInfo(Startup.defaultCulture); CultureInfo.CurrentCulture = CultureInfo.CurrentUICulture = ci; var result = new ProviderCultureResult(ci.Name, ci.Name); return Task.FromResult(result); } } }
CultureToUrlActionFilter.cs
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Routing; using System; using System.Globalization; namespace astika.Models { public class CultureToUrlActionFilter : IActionFilter { public void OnActionExecuted(ActionExecutedContext context) { } public void OnActionExecuting(ActionExecutingContext context) { bool redirect = false; string culture = context.RouteData.Values["culture"].ToString().ToLower(); redirect = String.IsNullOrEmpty(culture); if (!redirect) { try { CultureInfo ci = new CultureInfo(culture); redirect = Startup.supportedCultures.FindIndex(x => x.TwoLetterISOLanguageName.ToLower() == culture) < 0; } catch { redirect = true; } } if (redirect) { CultureInfo cid = new CultureInfo(Startup.defaultCulture); context.Result = new RedirectToRouteResult(new RouteValueDictionary(new { culture = cid.TwoLetterISOLanguageName, controller = "Home", action = "Index" })); } } } }
Resx files are located in the same folder /Resources/Views.Home.Index.en.resx /Resources/Views.Home.Index.de.resx /Resources/Views.Shared.Components.Navigation.Default.en.resx , etc. .