2010-10-14 22 views
13

Estoy trabajando con un cliente que quiere que las URL en nuestra aplicación web estén en francés. Soy un desarrollador inglés y también tenemos clientes ingleses. Este es un problema interesante, pero no creo que sea algo que el ASP.NET MVC Framework pueda soportar.¿Es posible localizar una URL/enrutamiento en ASP.NET MVC?

Aquí está el escenario. La ruta ...

ejemplo específico
Inglés URL
www.stackoverflow.com/questions/ask

también apoyaría

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

Genérico EJEMPLO
Inglés URL
http://clientA.product.com/AreaNameEnglish/ControllerNameEnglish/ActionNameEnglish/params

también tiene que apoyar

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

Así que en mi área MVC, controlador, y acciones todos tenemos que tener tanto traducciones Inglés y Francés.

Obviamente, la capacidad de mantenimiento sería un ENORME problema si tuviera que codificar todos mis controladores, vistas y nombres de acción en francés. ¿Hay alguna forma de localizar la ruta que se presenta en el navegador sin hacer esto? Teniendo en cuenta que hay muchas rutas diferentes en la aplicación. Un par de áreas, cada una con un puñado de controladores, cada uno con muchas acciones.

Gracias,
Justin

EDITAR
Gracias a @womp aquí es lo que he encontrado hasta el momento ... Aunque al final tomé el enfoque que he publicado como respuesta .

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. Si localizo solo ControllerName y ActionName en la URL, encontrará y mostrará la Vista correcta. Sin embargo, tengo los siguientes problemas.

Nombre del área, no se puede traducir
La localización de la Zona significa el método Controller.View() no encuentra Vistas. Aunque he reemplazado el nombre del Área en el contexto de la solicitud, el método ViewEngineCollection.Find() no parece recogerlo. En cualquier parte de mi clase Controller que devuelva "View()" no puede encontrar la vista predeterminada para su acción. Si no localizo el Área entonces los otros pasos funcionan.

RedirectToAction o Html.ActionLink
Cada vez que la aplicación llama RedirectToAction o si uso un ayudante Html.ActionLink o algo similar generan las URL son las inglesas. Parece que tendré que agregar lógica en algún lugar posiblemente en varios lugares para convertir una URL en inglés a la francesa (u otro idioma).

Respuesta

14

El siguiente blog contiene una solución completa a este problema exacto. Es realmente una solución muy elegante que recomiendo encarecidamente.

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

Nota para conseguir que funcione para las áreas he tenido que añadir el siguiente método de extensión de su clase "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; 
    } 

Sin embargo, incluso con este una ruta traducida con un AREA puede leerse e interpretarse que las rutas generadas siempre parecen incluir un nombre de ÁREA en inglés pero localizan todo lo demás.

Me dirigieron a un blog a través de la misma pregunta hecha en el ASP.NET MVC Forums

+0

Buen hallazgo. Es esencialmente el mismo concepto, excepto encapsulado en el enrutamiento, en lugar de en la lógica de creación de instancias del controlador. Estoy de acuerdo, es probable que sea un poco más elegante, parece ser un dominio más apropiado para la solución. – womp

+0

Sé que esta es una publicación anterior, pero ustedes simplemente me salvan la piel. ¡Gran respuesta! –

3

La estructura MVC es compatible con casi cualquier escenario de enrutamiento que se pueda imaginar, pero no necesariamente con las clases de enrutamiento predeterminadas.

La mayoría de las soluciones de localización que he encontrado implican el uso de los mismos nombres de Controlador y Método de acción, pero especificando un parámetro de cultura en la ruta que dictamine qué versión traducida de la Vista se presenta. Por ejemplo,

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

Si realmente debe haber traducido la URL del embargo, no veo otra opción mucho después de mantener una tabla de asignación en alguna parte. Si entiendo su pregunta correctamente, necesita poder mapear todas las diferentes traducciones de "preguntas" (controlador) y "preguntar" (acción) a la misma combinación de controlador/método de acción.

Sin embargo, una vez que haya creado esta tabla en alguna parte (¿archivos de recursos?), Puede anular fácilmente la DefaultControllerFactory que está utilizando la infraestructura e implementar su propia lógica para determinar el controlador para crear instancias. Entonces, en lugar de simplemente hacer coincidir el token {controller} de la URL como una simple comparación de cadenas, puede implementar la lógica para compararla con su tabla de asignación para seleccionar el controlador correcto.

Para ver una guía de cómo crear una fábrica de controlador personalizado, check this great blog post. En realidad, también es un ejemplo de localización, pero se basa en la configuración cultural del usuario, en lugar del idioma de la URL.

+0

muy interesante. Estoy investigando. Gracias por el puntapié inicial. – Justin

+0

De nada, buena suerte con eso. Si mi respuesta fue de alguna ayuda, no te olvides de votar :) – womp

+0

Bueno, estoy atrapado en el Request.Header. Estoy investigando, pero si me puedes decir cómo configurar la parte "ex-MX" para que se interprete como un encabezado de solicitud, sería útil. No pude encontrar la publicación de blog de muestra. Hubiera esperado que fuera parte de la RouteTable en el archivo Global.asax.cs pero no ir. – Justin

Cuestiones relacionadas