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

发布于 2024-09-27 20:07:34 字数 4223 浏览 1 评论 0原文

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

这是场景。路线...

具体示例
英文网址
www.stackoverflow.com/questions/ask

也将支持

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

通用示例
英文网址
http://clientA.product.com/AreaNameEnglish/ControllerNameEnglish/ActionNameEnglish/params

还需要支持

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

所以在 MVC 我的区域,Controller和Actions都需要有英文和法文翻译。

显然,如果我要将所有控制器、视图和操作名称硬编码为法语,那么可维护性将是一个巨大的问题。是否有办法在不执行此操作的情况下本地化浏览器中显示的路线?请记住,应用程序中有许多不同的路线。几个区域,每个区域都有少量控制器,每个区域都有许多操作?

谢谢,
贾斯汀

编辑
感谢@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() 方法无法找到视图。 尽管我已经替换了请求上下文中的区域名称,但 ViewEngineCollection.Find() 方法似乎没有选择它。在我的 Controller 类中任何执行“return View()”的地方都无法找到其操作的默认视图。如果我不本地化该区域,则其他步骤有效。

RedirectToAction 或 Html.ActionLink
每当应用程序调用 RedirectToAction 或者我使用 Html.ActionLink 帮助程序或类似的东西时,生成的 URL 都是英文的。看来我必须在多个位置的某个地方添加逻辑,才能将英语 Url 转换为法语(或其他语言)Url。

I'm working with a client that wants the URLs in our web application to be in French. I'm an English developer and we also have English clients. This is an interesting problem but I don't think its something the ASP.NET MVC Framework would support.

Here's the scenario. The route...

Specific EXAMPLE
English URL
www.stackoverflow.com/questions/ask

would also support

French URL
www.stackoverflow.com/problème/poser

Generic EXAMPLE
English URL
http://clientA.product.com/AreaNameEnglish/ControllerNameEnglish/ActionNameEnglish/params

also needs to support

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

So in MVC my Area, Controller and Actions all need to have both English and French translations.

Obviously maintainability would be a HUGE issue if I were to go and hardcode all my Controllers, Views and Action names to French. Is there anyway to localize the route that is presented in the browser without doing this? Keeping in mind there are lots of different routes in the application. A couple Areas each with a handful of Controller each with many Actions?

Thanks,
Justin

EDIT
Thanks to @womp here is what I've come up with so far... Although in the end I took the approach which I posted as an answer.

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;
    }
}

A decent start. If I localize just the ControllerName and ActionName in the Url it will find and render the proper View. However I have the following problems.

Area Name can't be translated
Localizing the Area means the Controller.View() method fails to find Views.
Even though I've replaced the Area name in the request context the ViewEngineCollection.Find() method doesn't seem to pick it up. Anywhere in my Controller class that does "return View()" fails to find the default view for its action. If I don't localize the Area then the other steps work.

RedirectToAction or Html.ActionLink
Anytime the application calls RedirectToAction or if I use an Html.ActionLink helper or something similiar the Urls generate are the English ones. It looks like I'm going to have to add logic somewhere possibly in multiple spots to convert an English Url to the French (or other language) one.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

残疾 2024-10-04 20:07:34

以下博客包含此问题的完整解决方案。它实际上是一个非常优雅的解决方案,我强烈推荐。

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

注意 要使其适用于 AREA,我必须将以下扩展方法添加到他的“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 论坛上提出的相同问题被引导至博客

The following blog contains a complete solution this exact problem. Its actually a very elegant solution which I highly recommend.

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

Note to get it working for AREAs I had to add the following extension method to his "TranslatedRouteCollectionExtensions.cs" class:

    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;
    }

However, even with this a translated route with an AREA can be read and interpreted the routes generated always seem to include an English AREA name but localized everything else.

I was directed to a blog via the same question asked on the ASP.NET MVC Forums

无力看清 2024-10-04 20:07:34

MVC 框架几乎支持您能想到的任何路由场景,但不一定支持默认路由类。

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

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

如果您确实必须翻译 URL,那么除了在某处维护映射表之外,我没有看到太多其他选择。如果我正确理解您的问题,您需要能够将“问题”(控制器)和“询问”(操作)的所有不同语言翻译映射到相同的控制器/操作方法组合。

但是,一旦您在某处(资源文件?)构建了此表,您就可以轻松覆盖框架正在使用的 DefaultControllerFactory,并实现您自己的逻辑来确定要实例化的控制器。因此,您可以实现逻辑来根据映射表检查它以选择正确的控制器,而不是仅将 URL 中的 {controller} 标记作为简单的字符串比较进行匹配。

有关创建自定义控制器工厂的演练,查看这篇精彩的博客文章。它实际上也是一个本地化示例,但它基于用户的文化设置,而不是 URL 的语言。

The MVC framework supports pretty much any routing scenario you can think of, but not necessarily with the default routing classes.

Most localization solutions I've run across involve using the same Controller and Action method names, but specifying a culture parameter in the route which dictates which translated version of the View is presented. For example,

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

If you really must have translated URL's though, I don't see much other choice then to maintain a mapping table somewhere. If I understand your question correctly, you need to be able to map all the different language translations of "questions" (controller) and "ask" (action) to the same controller/action method combination.

However, once you've built this table somewhere (resource files?), you can easily override the DefaultControllerFactory that the framework is using, and implement your own logic for determining the controller to instantiate. So instead of just matching the {controller} token from the URL as a simple string comparison, you can implement logic to check it against your mapping table to pick the right controller.

For a walkthrough of creating a custom controller factory, check this great blog post. It's actually a localization example as well, but it's based on the user's culture settings, rather than the language of the URL.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文