6

I was looking for a solution to internationalize/localize routes on an ASP.NET MVC website. I stumbled upon the blog post Translating routes (ASP.NET MVC and Webforms) by Maarten Balliauw. He present a very nice solution, which works great - until there's a Html.RenderAction("...") in the view.

Basically he introduces a TranslatedRoute inheriting from System.Web.Routing.Route, that than does the translation using a Dictionary with the translations on the fly.

Any idea why this behaves differently when calling Html.RenderAction("...")? Also it seems like if the error only occurs, if the action that should be rendered is in the same controller.

And here's the exact error:

"The controller for path '/MyTranslatedControllerName' was not found or does not implement IController."

Update:

Here's my routes configuration (taken from Maarten's sample project, added routes for Contact, which is the partial to be rendered):

    public static void RegisterRoutes(RouteCollection routes)
        {
            CultureInfo cultureEN = CultureInfo.GetCultureInfo("en-US");
            CultureInfo cultureDE = CultureInfo.GetCultureInfo("de-DE");
            CultureInfo cultureFR = CultureInfo.GetCultureInfo("fr-FR");

            DictionaryRouteValueTranslationProvider translationProvider = new DictionaryRouteValueTranslationProvider(
                new List<RouteValueTranslation> {
                    new RouteValueTranslation(cultureEN, "Home", "Home"),
                    new RouteValueTranslation(cultureEN, "About", "About"),
                    new RouteValueTranslation(cultureEN, "Contact", "Contact"),
                    new RouteValueTranslation(cultureDE, "Home", "Home"),
                    new RouteValueTranslation(cultureDE, "About", "About"),
                    new RouteValueTranslation(cultureDE, "Contact", "Kontakt"),
                    new RouteValueTranslation(cultureFR, "Home", "Demarrer"),
                    new RouteValueTranslation(cultureFR, "About", "Infos"),
                    new RouteValueTranslation(cultureFR, "Contact", "Contact")
                }
            );

            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapTranslatedRoute(
                "TranslatedRoute",
                "{controller}/{action}/{id}",
                new { controller = "Home", action = "Index", id = "" },
                new { controller = translationProvider, action = translationProvider },
                true
            );

            routes.MapRoute(
                "Default",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
            );
davehauser
  • 5,844
  • 4
  • 30
  • 45
  • You can try the awesome [AttributeRouting](https://github.com/mccalltd/AttributeRouting/wiki/Localization) project that I just found! You can get it through [NuGet](https://nuget.org/packages/AttributeRouting). – Leniel Maccaferri Mar 11 '12 at 17:08

2 Answers2

4

This will depend on your route configuration. Can you share it?

EDIT: It seems the RenderAction method is using "GetVirtualPath" on the route to determine a URL for the MvcHandler. However, that one does not call back into the "GetRouteData" method.

There is a method of detecting this: routeData.DataTokens["ParentActionViewContext"] Problem is it comes to late in the pipeline for the TranslatedRoute to know about this...

In short: I'm affraid this is impossible by the current implementation of ChildActionExtensions.ActionHelper in System.Web.Mvc...

I do have a (dirty, self-disrespecting) workaround (which you can also wrap in a custom RenderAction() method): - In your view, do this:

<%
RouteData.DataTokens.Add("GoingToRenderChildAction",
true); %> <%
Html.RenderAction("Contact", "Home");
%> <%
RouteData.DataTokens["GoingToRenderChildAction"]
= false; %>

Razor syntax:

@{
    ViewContext.RequestContext.RouteData.DataTokens.Add("GoingToRenderChildAction",
                                                         true);
}

@Html.Action("Action", "Controller" )

@{
    ViewContext.RequestContext.RouteData.DataTokens["GoingToRenderChildAction"]=false;
}

In TranslatedRoute.cs GetVirtualPath, add the following as the first statement:

if(requestContext.RouteData.DataTokens.ContainsKey("GoingToRenderChildAction") &&
(bool)requestContext.RouteData.DataTokens["GoingToRenderChildAction"] == true)
{
    return base.GetVirtualPath(requestContext, values);
}

This will work, but may have unwanted side effects.

Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
maartenba
  • 3,344
  • 18
  • 31
  • I just used your sample project, added a Contact action to `HomeController` and then did a `Html.RenderAction("Contact")` on `About.aspx`. Also added new `new RouteValueTranslation(cultureEN, "Contact", "Contact")` (for the other cultures as well) – davehauser Jun 03 '11 at 08:57
  • 1
    Thanks! So I'll think about messing my code with this solution ;-) Too bad, your approach to localizing routes was the cleanest solution I ever found... – davehauser Jun 03 '11 at 13:51
  • @maartenba: Great solution. I just updated your answer with the corresponding Razor syntax. I'm also using your MVC SiteMap provider: http://mvcsitemap.codeplex.com/ You helped a lot with these implementations... Thanks! – Leniel Maccaferri Mar 10 '12 at 20:27
0

In your view(Razor syntax, VB):

@Html.Action("Index", "Home", New With {.childAction = True})

Then in TranslatedRoute.cs GetVirtualPath as the first statement:

if (values.ContainsKey("childAction") &&
   (bool)values["childAction"] == true)
{
  return base.GetVirtualPath(requestContext, values);
}

This way is much lighter and only for a desired action.

Kdefthog
  • 161
  • 1
  • 4