在ASP.NET MVC中是否有可能本地化URL /路由?

15

我正在与一个客户合作,该客户希望我们Web应用程序中的URL为法语。 我是一名英语开发人员,我们还有英语客户。 这是一个有趣的问题,但我不认为ASP.NET MVC框架支持这种功能。

以下是具体情况...

特定示例
英语URL
www.stackoverflow.com/questions/ask

还应当支持

法语URL
www.stackoverflow.com/problème/poser

通用示例
英语URL
http://clientA.product.com/AreaNameEnglish/ControllerNameEnglish/ActionNameEnglish/params

也需要支持

法语URL
http://clientB.product.com/AreaNameFrench/ControllerNameFrench/ActionNameFrench/params

因此,在MVC中,我的区域(Area)、控制器(Controller)和操作(Actions)都需要有英文和法文翻译。

显然,如果我要将所有控制器、视图和操作名称硬编码成法语,那么可维护性将成为一个巨大的问题。有没有办法本地化在浏览器中呈现的路由而不需要这样做?要记住,应用程序中有许多不同的路由。 一些区域每个都有几个控制器,每个控制器都有许多操作?

谢谢,
Justin

编辑
感谢@womp,这是我目前想到的...虽然最终我采取了我发布为答案的方法。

public class LocalizedControllerFactory : DefaultControllerFactory
{
    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        if (string.IsNullOrEmpty(controllerName))
            throw new ArgumentNullException("controllerName");

        if (CultureInfo.CurrentCulture.TwoLetterISOLanguageName == "fr")
        {
            controllerName = this.ReplaceControllerName(requestContext, controllerName);
            this.ReplaceActionName(requestContext);
            this.ReplaceAreaName(requestContext);
        }

        return base.CreateController(requestContext, controllerName);
    }

    private string ReplaceControllerName(RequestContext requestContext, string controllerName)
    {
        // would use the language above to pick the propery controllerMapper.  For now just have french
        Dictionary<string, string> controllerMapper = new Dictionary<string, string>()
        {
            {"frenchControllerA", "englishControllerA"},
            {"frenchControllerB", "englishControllerB"}
        };

        return this.ReplaceRouteValue(requestContext, "controller", controllerMapper);
    }

    private void ReplaceAreaName(RequestContext requestContext)
    {
        // would use the language above to pick the propery areaMapper.  For now just have french
        Dictionary<string, string> areaMapper = new Dictionary<string, string>()
        {
            {"frenchAreaX", "englishAreaX"},
            {"frenchAreaY", "englishAreaY"}
        };

        this.ReplaceRouteValue(requestContext, "area", areaMapper);
    }

    private void ReplaceActionName(RequestContext requestContext)
    {
        // would use the language above to pick the propery actionMapper.  For now just have french
        Dictionary<string, string> actionMapper = new Dictionary<string, string>()
        {
            {"frenchAction1", "englishAction1"},
            {"frenchAction2", "englishAction2"}
        };

        this.ReplaceRouteValue(requestContext, "action", actionMapper);
    }

    private string ReplaceRouteValue(RequestContext requestContext, string paramName, Dictionary<string, string> translationLookup)
    {
        if (requestContext.RouteData.Values[paramName] == null)
        {
            return null;
        }

        string srcRouteValue = requestContext.RouteData.Values[paramName] as string;
        if (srcRouteValue != null && translationLookup.ContainsKey(srcRouteValue))
        {
            requestContext.RouteData.Values[paramName] = translationLookup[srcRouteValue];
        }

        return requestContext.RouteData.Values[paramName] as string;
    }
}

起步还不错。如果我只是本地化Url中的ControllerName和ActionName,则可以找到并呈现正确的视图。但是我遇到了以下问题。

无法翻译区域名称
本地化区域意味着Controller.View()方法无法找到视图。 尽管我已经在请求上下文中替换了Area名称,但ViewEngineCollection.Find()方法似乎没有使用它。我的任何控制器类中执行“return View()”的位置都无法找到其操作的默认视图。如果我不本地化Area,则其他步骤将正常工作。

RedirectToAction或Html.ActionLink
每当应用程序调用RedirectToAction或者我使用Html.ActionLink助手或类似的内容时,生成的Url都是英文的。看起来我需要在某个地方添加逻辑,可能会在多个位置将英文Url转换为法语(或其他语言)。

2个回答

14
以下博客包含了完整的解决方案,非常优雅,强烈推荐。

https://blog.maartenballiauw.be/post/2010/01/26/translating-routes-(aspnet-mvc-and-webforms).html

注意:为了使其适用于区域,我必须将以下扩展方法添加到他的“TranslatedRouteCollectionExtensions.cs”类中:

    public static Route MapTranslatedRoute(this AreaRegistrationContext areaContext, string name, string url, object defaults, object routeValueTranslationProviders, bool setDetectedCulture)
    {
        TranslatedRoute route = new TranslatedRoute(
            url,
            new RouteValueDictionary(defaults),
            new RouteValueDictionary(routeValueTranslationProviders),
            setDetectedCulture,
            new MvcRouteHandler());

        route.DataTokens["area"] = areaContext.AreaName;

        // disabling the namespace lookup fallback mechanism keeps this areas from accidentally picking up
        // controllers belonging to other areas
        bool useNamespaceFallback = (areaContext.Namespaces == null || areaContext.Namespaces.Count == 0);
        route.DataTokens["UseNamespaceFallback"] = useNamespaceFallback;

        areaContext.Routes.Add(route);

        return route;
    }

然而,即使有这个,使用AREA翻译的路由也可以被阅读和解释,但生成的路由似乎总是包含一个英文AREA名称,但其他内容都本地化了。
我通过在ASP.NET MVC论坛上提出相同的问题被引导到一个博客。

不错的发现。本质上是相同的概念,只是封装在路由中,而不是在控制器实例化逻辑中。我同意,这可能更加优雅,似乎是解决方案更合适的领域。 - womp
我知道这是一个旧帖子,但你们刚刚救了我的命。非常棒的答案! - Matt L

4

MVC框架支持几乎任何您可以想到的路由方案,但默认的路由类可能不支持。

我遇到的大多数本地化解决方案都涉及使用相同的控制器和操作方法名称,但在路由中指定一个文化参数,该参数决定呈现哪个翻译版本的视图。例如,

http://clientA.product.com/AreaName/Controller/Action    //en-US
http://clientB.product.com/es-MX/AreaName/Controller/Action   // spanish

如果你确实需要翻译URL,那么我认为唯一的选择就是维护一个映射表。如果我正确理解了你的问题,你需要能够将“questions”(控制器)和“ask”(操作)的所有不同语言翻译映射到相同的控制器/操作方法组合。
但是,一旦你在某个地方建立了这个表(资源文件?),你就可以轻松地覆盖框架正在使用的DefaultControllerFactory,并实现自己的逻辑来确定要实例化的控制器。因此,你可以实现逻辑来检查它是否与你的映射表匹配以选择正确的控制器,而不仅仅是将URL中的{controller}标记与字符串进行简单的比较。
有关创建自定义控制器工厂的详细步骤,请参见这篇优秀的博客文章。它实际上也是一个本地化示例,但是它基于用户的文化设置,而不是URL的语言。

非常有趣。我正在研究它。感谢您的启动。 - Justin
不客气,祝你好运。如果我的回答有帮助,请别忘了点赞 :) - womp
我卡在了Request.Header上。我正在研究它,但如果您能告诉我如何配置“ex-MX”部分以被解释为请求头,那会很有帮助。我在示例博客文章中找不到它。我本来期望它是Global.asax.cs文件中RouteTable的一部分,但没有成功。 - Justin
抱歉,我觉得我可能让你感到困惑了。那篇博客文章是一个独立的方法,与我在前三段中描述的不同。该博客文章展示了如何根据用户的浏览器文化设置选择控制器。我描述的第一种方法与使用自定义控制器工厂不同。在第一种方法中,您将使用像{culture}/{area}/{{controller}/{action}这样的路由,并根据“culture”标记简单地选择您的视图。但在这种情况下,所有URL都将是英文的。 - womp
好的,所谓的“Accept-Language”来自浏览器语言。我已经按照你的建议解决了语言问题。目前为止,我取得了一定的成功:) 我还有两个问题需要解决。我会更新问题描述,请稍后查看。 - Justin
虽然这是一个很好的想法,但我认为我从Maarten Balliauw的博客中发布的那个更干净地解决了问题。但还是感谢你的帮助!是的,我仍然给了你一个赞! - Justin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接