Возможно ли локализовать URL/маршрутизацию в ASP.NET MVC?
Я работаю с клиентом, который хочет, чтобы URL-адреса в нашем веб-приложении были на французском языке. Я английский разработчик, и у нас также есть английские клиенты. Это интересная проблема, но я не думаю, что ее поддерживает ASP.NET MVC Framework.
Вот сценарий. Маршрут...
Конкретный пример
Английский URL
www.stackoverflow.com/info/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 все должны иметь как английский, так и французский перевод.
Очевидно, что ремонтопригодность была бы ОГРОМНОЙ проблемой, если бы я должен был пойти и переписать все мои имена контроллеров, представлений и действий на французский язык. Есть ли способ локализовать маршрут, который представлен в браузере, не делая этого? Помните, что в приложении есть много разных маршрутов. Пара Области, каждая из которых имеет несколько контроллеров, каждый со многими действиями?
Спасибо,
Джастин
ИЗМЕНИТЬ
Благодаря @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. Однако у меня есть следующие проблемы.
Имя области не может быть переведено
Локализация области означает, что метод Controller.View() не может найти Views.
Несмотря на то, что я заменил имя Area в контексте запроса, метод ViewEngineCollection.Find(), похоже, не забирает его. Anywhere в моем классе Controller, который возвращает return(), не может найти представление по умолчанию для своего действия. Если я не локализую область, тогда выполняются другие шаги.
RedirectToAction или Html.ActionLink
В любое время, когда приложение вызывает RedirectToAction, или если я использую помощник Html.ActionLink или что-то похожее на создание Urls, это английские. Похоже, мне придется добавить логику где-нибудь, возможно, в нескольких местах, чтобы преобразовать английский язык на французский (или другой язык).
Ответы
Ответ 1
Следующий блог содержит полное решение этой точной проблемы. Это действительно очень элегантное решение, которое я очень рекомендую.
https://blog.maartenballiauw.be/post/2010/01/26/translating-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 Forums
Ответ 2
Структура MVC поддерживает практически любой сценарий маршрутизации, о котором вы можете думать, но не обязательно с классами маршрутизации по умолчанию.
Большинство решений локализации, с которыми я столкнулся, включают использование тех же имен метода Controller и Action, но указание параметра культуры на маршруте, который определяет, какая переведенная версия View была представлена. Например,
http://clientA.product.com/AreaName/Controller/Action //en-US
http://clientB.product.com/es-MX/AreaName/Controller/Action // spanish
Если вы действительно должны перевести URL-адрес, я не вижу другого выбора, чем когда-либо поддерживать таблицу сопоставления. Если я правильно понял ваш вопрос, вам нужно будет сопоставить все языковые переводы "вопросов" (контроллер) и "спросить" (действие) с той же комбинацией методов управления/действия.
Однако, как только вы построили эту таблицу где-нибудь (файлы ресурсов?), вы можете легко переопределить DefaultControllerFactory
, который использует инфраструктура, и реализовать свою собственную логику для определения контроллера для создания экземпляра. Поэтому вместо того, чтобы просто сопоставлять токен {controller}
с URL-адресом в качестве простого сравнения строк, вы можете реализовать логику, чтобы проверить его на соответствие таблице сопоставления, чтобы выбрать правильный контроллер.
Для пошагового создания пользовательского контроллера factory, проверьте этот отличный пост в блоге. Это на самом деле пример локализации, но он основан на настройках пользовательской культуры, а не на языке URL.