Brendan Green
Brendan Green

Reputation: 11914

MVC5 Localization with culture in route - issues with having an Area

I've had a look at the following blog post, and implemented the code as has been laid out:

ASP.NET MVC 5 Internationalization

Without areas in my MVC5 application, it works really well. A user navigates to http://localhost and with the default language of my browser set to Italian (it) I end up with http://localhost/it. Perfect.

With an area registered in my application, everything breaks down. A user navigating to http://localhost as per above ends up trying to hit http://localhost/it/myarea/Home, which doesn't exist.

As per the referenced blog post, here's what I have:

My route configuration

namespace MyWebsite
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            //routing to include culture
            routes.MapRoute(
                name: "DefaultWithCulture",
                url: "{culture}/{controller}/{action}/{id}",
                defaults: new { culture = string.Empty, controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

My area registration

namespace MyWebsite.Areas.WindowShop
{
    public class MyAreaRegistration : AreaRegistration 
    {
        public override string AreaName 
        {
            get 
            {
                return "myarea";
            }
        }

        public override void RegisterArea(AreaRegistrationContext context) 
        {
            context.MapRoute(
                "myarea_default",
                "{culture}/myarea/{controller}/{action}/{id}",
                new { action = "Index", culture = string.Empty, id = UrlParameter.Optional }
            );
        }
    }
}

I have a BaseController that should inject the culture of the browser if it hasn't already been supplied in the route.

namespace MyWebsite.Code
{
    public class BaseController : Controller
    {
        protected override IAsyncResult BeginExecuteCore(AsyncCallback callback, object state)
        {
            string cultureName = RouteData.Values["culture"] as string;

            if (string.IsNullOrEmpty(cultureName))
                cultureName = Request.UserLanguages != null && Request.UserLanguages.Length > 0 ? Request.UserLanguages[0] : null; // obtain it from HTTP header AcceptLanguages

            // some validation
            cultureName = CultureHelper.GetImplementedCulture(cultureName);

            if (RouteData.Values["culture"] as string != cultureName)
            {
                RouteData.Values["culture"] = cultureName.ToLowerInvariant();

                Response.RedirectToRoute(RouteData.Values);
            }

            Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName);
            Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;

            return base.BeginExecuteCore(callback, state);
        }
    }
}

I suspect that something is getting screwed up with the routing, because we seem (with the Area enabled) to be attempting to hit a HomeController within the area, but no controller of that name exists within that area (there is a root HomeController).

I really don't understand why, when the area is in play, we end up trying to redirect to a controller that doesn't exist.

I'm looking to get this working, as I'd like to keep the areas, and I am also trying to avoid using cookies (I'd rather have the culture in the URL).

Upvotes: 1

Views: 2165

Answers (2)

Rafael Ruiz
Rafael Ruiz

Reputation: 36

I had exaclty the same problem following this internationalization post and solved it by adding area = AreaName as a parameter of the default area route and area = "" as a parameter of the default route:

Area route configuration:

    public override void RegisterArea(AreaRegistrationContext context) 
    {
        context.MapRoute(
            "myarea_default",
            "{culture}/myarea/{controller}/{action}/{id}",
            new { action = "Index", culture = string.Empty, area = AreaName, id = UrlParameter.Optional }
        );
    }

Route configuration:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        //routing to include culture
        routes.MapRoute(
            name: "DefaultWithCulture",
            url: "{culture}/{controller}/{action}/{id}",
            defaults: new { culture = string.Empty, controller = "Home", action = "Index", area = "", id = UrlParameter.Optional }
        );
    }

If you have the same controller/action in distinct areas, you must add the namespaces too.

Hope it works for you too.

Upvotes: 2

loau
loau

Reputation: 103

After struggling with the same Problem for some time i found an answer working for me. In your Global.asax just make sure to register the Routes before registering the Areas:

protected void Application_Start()
{
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    AreaRegistration.RegisterAllAreas();
    // ...
}

Upvotes: 2

Related Questions