Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IViewLocalizer returns the wrong language

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
like image 826
Christian Held Avatar asked Jul 01 '16 13:07

Christian Held


People also ask

How do I change the language of a resource file in ASP NET MVC?

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.

What is Localization C#?

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).

How can I get browser language in asp net core?

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.

How do I change my Blazor culture?

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.


1 Answers

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

like image 90
Victor Papp Avatar answered Sep 21 '22 12:09

Victor Papp