2010-10-14 1 views
13

Je travaille avec un client qui veut que les URL de notre application web soient en français. Je suis un développeur anglais et nous avons aussi des clients anglais. C'est un problème intéressant mais je ne pense pas que ce soit quelque chose que le framework ASP.NET MVC supporterait.Est-il possible de localiser une URL/un routage dans ASP.NET MVC?

Voici le scénario. La route ...

Exemple spécifique
Anglais URL
www.stackoverflow.com/questions/ask

soutiendrait également

URL Français
www.stackoverflow.com/problème/colle

exemple générique
Anglais URL
http://clientA.product.com/AreaNameEnglish/ControllerNameEnglish/ActionNameEnglish/params

doit également soutenir

URL Français
http://clientB.product.com/AreaNameFrench/ControllerNameFrench/ActionNameFrench/params

Donc dans MVC ma région, contrôleur et actions doivent tous avoir à la fois l'anglais et les traductions françaises.

Évidemment, la maintenabilité serait un énorme problème si je devais aller coder en dur tous mes noms de contrôleurs, de vues et d'actions en français. Est-il possible de localiser la route qui est présentée dans le navigateur sans le faire? En gardant à l'esprit il y a beaucoup de routes différentes dans l'application. Un couple zones avec une poignée de contrôleurs chacun avec de nombreuses actions?

Merci,
Justin

EDIT
Merci à @womp voici ce que je suis venu avec jusqu'à présent ... Même si en fin de compte, je pris l'approche que je posté en réponse .

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

Un démarrage décent. Si je localise juste le ControllerName et ActionName dans l'Url il trouvera et rendra la vue appropriée. Cependant, j'ai les problèmes suivants.

Nom de la zone ne peut pas être traduit
zone Décentraliser les moyens de la méthode Controller.View() ne trouve pas vues. Même si j'ai remplacé le nom de la zone dans le contexte de la requête, la méthode ViewEngineCollection.Find() ne semble pas l'attraper. N'importe où dans ma classe de contrôleur qui fait "return View()" ne parvient pas à trouver l'affichage par défaut pour son action. Si je ne localise pas la zone, les autres étapes fonctionnent.

RedirectToAction ou Html.ActionLink
Chaque fois que l'application appelle RedirectToAction ou si j'utilise une aide de Html.ActionLink ou quelque chose de similaire génèrent les urls sont les Anglais. Il semble que je vais devoir ajouter de la logique quelque part dans plusieurs endroits pour convertir un Url anglais en français (ou autre langue).

Répondre

14

Le blog suivant contient une solution complète à ce problème. C'est en fait une solution très élégante que je recommande fortement.

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

Remarque pour le faire fonctionner pour AREAs je devais ajouter la méthode d'extension suivante à sa classe « 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; 
    } 

Cependant, même avec cette voie traduit avec un AREA peut être lu et interprété les routes générées semblent toujours inclure un nom AREA anglais mais localisé tout le reste.

J'ai été dirigé vers un blog via la même question posée sur le ASP.NET MVC Forums

+0

Belle trouvaille. C'est essentiellement le même concept, sauf encapsulé dans le routage, plutôt que dans la logique d'instanciation du contrôleur. Je suis d'accord, c'est probablement un peu plus élégant, cela semble être un domaine plus approprié pour la solution. – womp

+0

Je sais que c'est un vieux message, mais vous venez de sauver ma peau. Très bonne réponse! –

3

Le framework MVC supporte à peu près tous les scénarios de routage auxquels vous pouvez penser, mais pas nécessairement avec les classes de routage par défaut.

La plupart des solutions de localisation que j'ai utilisées impliquent l'utilisation des mêmes noms de méthodes Controller et Action, mais en spécifiant un paramètre de culture dans la route qui dicte quelle version traduite de la vue est présentée. Par exemple,

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

Si vous voyez devez vraiment avoir traduit URL bien, je ne suis pas beaucoup d'autre choix que de maintenir une table de correspondance quelque part. Si je comprends bien votre question, vous devez être capable de mapper toutes les traductions de "questions" (contrôleur) et "demander" (action) à la même combinaison contrôleur/méthode d'action.

Cependant, une fois que vous avez construit cette table quelque part (fichiers de ressources?), Vous pouvez facilement remplacer le DefaultControllerFactory utilisé par le framework et implémenter votre propre logique pour déterminer le contrôleur à instancier. Ainsi, au lieu de simplement faire correspondre le jeton {controller} de l'URL comme une simple comparaison de chaîne, vous pouvez implémenter la logique pour la comparer à votre table de mappage afin de choisir le bon contrôleur.

Pour une procédure pas à pas de création d'une fabrique de contrôleurs personnalisée, check this great blog post. Il s'agit également d'un exemple de localisation, mais basé sur les paramètres de culture de l'utilisateur plutôt que sur la langue de l'URL.

+0

Très intéressant. Je regarde dedans. Merci pour le coup d'envoi. – Justin

+0

De rien, bonne chance avec ça. Si ma réponse a aidé du tout, n'oubliez pas d'upvote :) – womp

+0

Eh bien, je suis coincé sur le Request.Header. J'y regarde mais si vous pouviez me dire comment configurer la partie "ex-MX" pour qu'elle soit interprétée comme un en-tête de requête, cela aiderait. Je ne pouvais pas trouver l'exemple de blog. Je m'attendais à ce que cela fasse partie de RouteTable dans le fichier Global.asax.cs mais pas d'aller. – Justin