I am trying to build an ASP.NET Core application that should be available in English and German. My problem is IViewLocalizer
always returns the German text, even with culture set to English. How do I get the right text for the current culture?
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddLocalization(opt => opt.ResourcesPath = "Resources");
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var cultures = new[] { new CultureInfo("en"), new CultureInfo("de") };
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en"),
SupportedCultures = cultures,
SupportedUICultures = cultures
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
HomeController.cs
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
Index.cshtml
<!DOCTYPE html>
@using Microsoft.AspNetCore.Mvc.Localization;
@inject IViewLocalizer Localizer
<html>
<body>
<h1>@Localizer["Hello, World!"]</h1>
<ul>
<li>CurrentCulture: @System.Globalization.CultureInfo.CurrentCulture</li>
<li>CurrentUICulture: @System.Globalization.CultureInfo.CurrentUICulture</li>
</ul>
</body>
</html>
The Resource File is located at Resources\Views.Home.Index.de.resx
Expected output:
Hello, World! CurrentCulture: en CurrentUICulture: en
Page output:
Hallo Welt! CurrentCulture: en CurrentUICulture: en
Request Headers:
GET / HTTP/1.1
Host: localhost:61904
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,es;q=0.6,de;q=0.4
One way you can do it is to have the drop down just redirect the page to a language specific URL (this is quite nice as you can send around language specific links) then in a base class on your controller, set the Thread's locale. This example has . aspx pages not razor . cshtml.
Localization is the process of customizing your application for a given culture and locale. Cultures and Locales. The language needs to be associated with the particular region where it is spoken, and this is done by using locale (language + location).
Declaration of supported languages is defined in Configure() of your Startup class (see example). If you still need all accepted languages as a simple string[] like the older Request. UserLanguages property, then use the HeaderDictionaryTypeExtensions. GetTypedHeaders() extension defined in the Microsoft.
Statically set the culture For more information on Blazor startup, see ASP.NET Core Blazor startup. An alternative to setting the culture Blazor's start option is to set the culture in C# code. Set CultureInfo. DefaultThreadCurrentCulture and CultureInfo.
I had the same problem since yesterday.
It looks like that the LocalizationProvider fails to set the correct culture.
After implementing a custom LocalizationProvider, the IViewLocalizer
started to work perfectly for Views
and ViewComponents
as well.
Also I had to implement the language into the url in the following way:
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 the CultureToUrlActionFilter()
. It retrieves the 2-letter language code from url and sets the correct CultureInfo
. If the Url contains a not allowed 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 set up the custom localization provider and inserted it as first in the provider 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 contains no culture code after the domain name, I do a Redirect to http://somedomain/<2-letter-culture-code>
(to the default culture). Urls always must have culture specified.
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 do the redirect it in 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" }));
}
}
}
}
The resx files are all 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With