Having issues generating URLs for custom routes to include the users culture in the RouteValues

55 Views Asked by At

I'm trying to create a custom route which will include the users culture in the route (RouteValues). Using default routing convention everything works fine.

I have the following controller:

    public class HomeController : Controller
    {
        public HomeController()
        {
        }

        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }
    }

I believe I've configured localization in the app correctly as follows:

        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<RequestLocalizationOptions>(opts =>
            {
                opts.SupportedCultures = new[] { new CultureInfo("en"), new CultureInfo("fr") };
                opts.SupportedUICultures = opts.SupportedCultures;
                opts.SetDefaultCulture("en");
                opts.DefaultRequestCulture = new RequestCulture("en");
                opts.RequestCultureProviders.Insert(0, new RouteDataRequestCultureProvider);
                opts.ApplyCurrentCultureToResponseHeaders = opts.ApplyCurrentCultureToResponseHeaders;
            });

            services.AddControllersWithViews(opts =>
            {
                opts.Filters.Add(new CultureFilter("en"));
            });

            services.AddLocalization();
            services.AddMvc();
        }

And I have an ActionFilter that sets the users culture based on the route value.

    public class CultureFilter : IAuthorizationFilter
    {
        private readonly string defaultCulture;

        public CultureFilter(string defaultCulture)
        {
            this.defaultCulture = defaultCulture;
        }

        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var values = context.RouteData.Values;

            string culture = (string)values["culture"] ?? this.defaultCulture;

            CultureInfo ci = new CultureInfo(culture);

            Thread.CurrentThread.CurrentCulture = ci;
            Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(ci.Name);
        }
    }

Using the default routing convention I get the desired result (more or less).

Thus for the following routes:

endpoints.MapControllerRoute(
    name: "culture-default",
    pattern: "{culture=en}/{controller=Home}/{action=Index}");

endpoints.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}");

Thus while on the url "http://localhost" I get: "@Url.ActionLink("Index", "Home")" = "http://localhost" "@Url.ActionLink("Privacy", "Home")" = "http://localhost/home/privacy"

And while on the url "http://localhost/fr" I get: "@Url.ActionLink("Index", "Home")" = "http://localhost/fr" "@Url.ActionLink("Privacy", "Home")" = "http://localhost/fr/home/privacy"

Ok - so far so goood....

But when I add a custom route for the Privacy ActionMethod I can't seem to get the correct culture in the generated URL.

Thus for the following routes:

endpoints.MapControllerRoute(
    name: "culture-privacy",
    pattern: "{culture}/h/p",
    defaults: new { culture = "en", controller = "Home", action = "Privacy" });

endpoints.MapControllerRoute(
    name: "default-privacy",
    pattern: "h/p",
    defaults: new { controller = "Home", action = "Privacy" });

Thus while on the url "//localhost" I get: "@Url.ActionLink("Index", "Home")" = "//localhost" "@Url.ActionLink("Privacy", "Home")" = "//localhost/h/p"

And while on the url "//localhost/fr" I get: "@Url.ActionLink("Index", "Home")" = "//localhost/fr" "@Url.ActionLink("Privacy", "Home")" = "//localhost/en/h/p"

Presumably this is because I included the "culture = 'en'" in the default for the "culture-privacy" route, but shouldn't the default value of 'en' only be used if the culture is not otherwise specified in the route?

1

There are 1 best solutions below

1
Nathan On

You are right, the reason is that the default value "en" is overriding the value passed in the route data. If you want to use the culture which user typed, try to use the culture value passed in the route data instead of a fixed value. Here is the code sample:

endpoints.MapControllerRoute(
name: "culture-privacy",
pattern: "{culture}/h/p",
defaults: new { culture = "{culture}", controller = "Home", action = "Privacy" });