Можно ли локализовать URL / маршрутизацию в ASP.NET MVC? - PullRequest
14 голосов
/ 14 октября 2010

Я работаю с клиентом, который хочет, чтобы URL-адреса в нашем веб-приложении были на французском языке. Я английский разработчик, и у нас также есть английские клиенты. Это интересная проблема, но я не думаю, что это то, что ASP.NET MVC Framework поддержит.

Вот сценарий. Маршрут ...

Конкретный ПРИМЕР
Английский 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 мой Регион, Контроллер и Действия должны иметь переводы на английский и французский языки.

Очевидно, что ремонтопригодность была бы ОГРОМНОЙ проблемой, если бы я перешел и жестко запрограммировал все свои имена контроллеров, представлений и действий на французский. Есть ли способ локализовать маршрут, представленный в браузере, без этого? Помните, что в приложении много разных маршрутов. Пара областей, каждая из которых имеет несколько контроллеров, каждая из которых имеет множество действий?

Спасибо,
Justin

EDIT
Благодаря @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;
    }
}

Достойное начало. Если я локализую только ControllerName и ActionName в URL, он найдет и отобразит правильный View. Однако у меня есть следующие проблемы.

Название области не может быть переведено
Локализация Area означает, что метод Controller.View () не может найти Views. Несмотря на то, что я заменил имя области в контексте запроса, метод ViewEngineCollection.Find (), похоже, не подхватил его. В любом месте моего класса Controller, который "return View ()" не может найти представление по умолчанию для своего действия. Если я не локализую Район, другие шаги сработают.

RedirectToAction или Html.ActionLink
Каждый раз, когда приложение вызывает RedirectToAction или если я использую помощник Html.ActionLink или что-то похожее на URL-адреса, генерируемые на английском языке. Похоже, мне придется добавить логику где-то, возможно, в нескольких местах, чтобы преобразовать английский URL во французский (или другой язык).

Ответы [ 2 ]

14 голосов
/ 15 октября 2010

Следующий блог содержит полное решение этой точной проблемы.Это на самом деле очень элегантное решение, которое я очень рекомендую.

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

Примечание , чтобы заставить его работать для областей, мне пришлось добавить следующий метод расширения к его "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;
    }

Однако даже при этом можно считать и интерпретировать переведенный маршрут с AREA, сгенерированные маршруты всегда включают английское имя AREA, но локализуют все остальное.

Меня направили в блог по тому же вопросу, заданному на ASP.NET MVC Форумы

3 голосов
/ 14 октября 2010

Среда MVC поддерживает практически любой сценарий маршрутизации, о котором вы только можете подумать, но не обязательно с классами маршрутизации по умолчанию.

Большинство решений по локализации, с которыми я сталкивался, включают использование одних и тех же имен методов Controller и Action, но указание параметра маршрута в маршруте, который определяет, какая переведенная версия представления представлена. Например,

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

Если вы действительно перевели URL, я не вижу другого выбора, кроме как поддерживать таблицу сопоставления где-нибудь. Если я правильно понимаю ваш вопрос, вы должны иметь возможность отобразить все языковые переводы «вопросы» (контроллер) и «спросить» (действие) в одну и ту же комбинацию «контроллер / метод действия».

Однако, как только вы где-то построили эту таблицу (файлы ресурсов?), Вы можете легко переопределить DefaultControllerFactory, который использует инфраструктура, и реализовать собственную логику для определения экземпляра контроллера, который нужно создать. Таким образом, вместо простого сопоставления токена {controller} из URL-адреса в качестве простого сравнения строк вы можете реализовать логику, чтобы сравнить ее с таблицей сопоставления для выбора правильного контроллера.

Для пошагового руководства по созданию собственной фабрики контроллеров, посмотрите это великолепное сообщение в блоге . На самом деле это также пример локализации, но он основан на настройках культуры пользователя, а не на языке URL.

...