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:
"Th开发者_运维技巧e 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
);
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.
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.
精彩评论