3

This is my route:

        app.UseMvc(routes =>
        {
            routes.MapRoute(
     name: "defaultWithCulture",
     template: "{culture=fa-IR}/{controller}/{action=Index}/{id?}");
        });

but localization works when I use the link:

http://localhost:1776/fa-IR/Home/About?culture=fa-ir

but not when

http://localhost:1776/fa-IR/Home/About

why doesn't the culture in the route apply?

Ashkan Mobayen Khiabani
  • 33,575
  • 33
  • 102
  • 171
  • your route template should be `template: "{culture}/{controller}/{action=Index}/{id?}");`. How are you setting the culture of the current thread by using the values coming as part of the URL? – Chetan Mar 01 '17 at 10:53
  • @ChetanRanpariya I'm not setting it. shouldn't routing do that itself? – Ashkan Mobayen Khiabani Mar 01 '17 at 10:58
  • The behavior you want is not available out of the box in asp.net mvc core. You might want to write an action filter which will retrieve the route data value for culture from the URL and set it to the current Thread. – Chetan Mar 01 '17 at 13:35
  • @ChetanRanpariya could you please add it as answer and show how to do it? – Ashkan Mobayen Khiabani Mar 01 '17 at 13:36
  • I have answered similar question previously. Please refer http://stackoverflow.com/questions/42154050/asp-mvc-resources-global-doesnt-work Let me know if you face any issue with that approach. – Chetan Mar 01 '17 at 13:55

2 Answers2

7

SSA's answer didn't solve my problem but gave me a big clue, and finally I got it to work.

The thing to do is:

  1. As SSA said asp.net core has following RequestCultureProviders, in order. First non-null will be used.
QueryStringRequestCultureProvider
CookieRequestCultureProvider
AcceptLanguageHeaderRequestCultureProvider

So we must change the order of them and add the Routing to be the first:

services.Configure<RequestLocalizationOptions>(
    options =>
    {
        var supportedCultures = new List<CultureInfo>
        {
           new CultureInfo("en-US"),
           new CultureInfo("fa-IR"),
        };
        options.DefaultRequestCulture = new RequestCulture(culture: "fa-IR", uiCulture: "fa-IR");
        options.SupportedCultures = supportedCultures;
        options.SupportedUICultures = supportedCultures;
        options.RequestCultureProviders.Clear();
        options.RequestCultureProviders.Insert(0, new RouteDataRequestCultureProvider();
        options.RequestCultureProviders.Insert(1, new QueryStringRequestCultureProvider());
        options.RequestCultureProviders.Insert(2, new CookieRequestCultureProvider());
        options.RequestCultureProviders.Insert(3, new AcceptLanguageHeaderRequestCultureProvider());                    
        services.AddSingleton(options);
    });

2- When RouteDataRequestCultureProvider Task executed, still the RoutingData is null and is not given a value yet, so it will always return null, so I changed it like Below to use the Url.Path instead of RoutingData:

public class RouteDataRequestCultureProvider : RequestCultureProvider
{

    public override Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException(nameof(httpContext));
        }

        string culture = null;
        string uiCulture = null;
         uiCulture =  culture = httpContext.Request.Path.Value.Split('/')[1]?.ToString();
        if (culture == null)
        {
            return TaskCache<ProviderCultureResult>.DefaultCompletedTask;
        }
        var providerResultCulture = new ProviderCultureResult(culture, uiCulture);

        return Task.FromResult(providerResultCulture);
    }
} 
Ashkan Mobayen Khiabani
  • 33,575
  • 33
  • 102
  • 171
  • I have the same problem as you (RoutingData is null). Is it a bug? – Fernando Mondo Oct 02 '17 at 16:21
  • @FernandoMondo nope, it is not a bug. It's because the localization middleware runs before the MVC router, so it hasn't parsed it yet. – Mark Szabo Dec 13 '17 at 23:06
  • @MarkSzabo I think it is kind of bug or misimplementation. Because this is standard aspnet library and it is not working. And thank you for great guiding about this topic. I'l l create my own RequestCultureProvider class. – Iren Saltalı Feb 10 '18 at 18:06
  • So what is the use of RouteDataRequestCultureProvider if it's not being called after routing? I mean - I could use the workaround by manually parsing the path, but I have complicated routing rules. – Dirk Boer Jun 01 '20 at 22:23
3

As far as I know, asp.net core has following RequestCultureProviders, in order. First non-null will be used.

QueryStringRequestCultureProvider
CookieRequestCultureProvider
AcceptLanguageHeaderRequestCultureProvider

and you need to add RouteDataRequestCultureProvider to support culture from route data.

taking an example from here:

public void ConfigureServices(IServiceCollection services)  
{
    // Add framework services.
    services.AddMvc();

    var supportedCultures = new[]
    {
        new CultureInfo("en-US"),
        new CultureInfo("en-GB"),
        new CultureInfo("de"),
        new CultureInfo("fr-FR"),
    };

    var options = new RequestLocalizationOptions()
    {
        DefaultRequestCulture = new RequestCulture(culture: "en-GB", uiCulture: "en-GB"),
        SupportedCultures = supportedCultures,
        SupportedUICultures = supportedCultures
    };
    options.RequestCultureProviders = new[] 
    { 
         new RouteDataRequestCultureProvider() { Options = options } 
    };

    services.AddSingleton(options);
}
SSA
  • 5,433
  • 4
  • 36
  • 50
  • 1
    I saw this solution in several places but the problem is that, for me, I get the error `The type or namespace name 'RouteDataRequestCultureProvider' could not be found (are you missing a using directive or an assembly reference?)` and I couldn't find any namespaces that would resolve the problem. do you know how can I solve it? – Ashkan Mobayen Khiabani Mar 01 '17 at 14:14
  • 1
    You need to get Microsoft.AspNetCore.Localization.Routing package. "Microsoft.AspNetCore.Localization.Routing": "1.1.0" – SSA Mar 01 '17 at 14:31