2010-10-14 20 views
13

Sto lavorando con un cliente che vuole che gli URL nella nostra applicazione web siano in francese. Sono uno sviluppatore inglese e abbiamo anche clienti inglesi. Questo è un problema interessante, ma non penso che sia qualcosa che ASP.NET MVC Framework supporterà.È possibile localizzare un URL/routing in ASP.NET MVC?

Ecco lo scenario. Il percorso ...

esempio specifico
Inglese URL
www.stackoverflow.com/questions/ask

sarebbe anche sostenere

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

Generico ESEMPIO
Inglese URL
http://clientA.product.com/AreaNameEnglish/ControllerNameEnglish/ActionNameEnglish/params

ha anche bisogno di supportare

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

Così nel MVC mia zona, Controller e azioni tutto necessario avere sia traduzioni inglese e francese.

Ovviamente, la manutenibilità sarebbe un problema ENORME se dovessi andare e codificare tutti i miei controller, visualizzazioni e nomi di azioni in francese. Esiste comunque la possibilità di localizzare il percorso presentato nel browser senza farlo? Tenendo a mente ci sono molti percorsi diversi nell'applicazione. Un paio di aree ciascuna con una manciata di controller ciascuno con molte azioni?

Grazie,
Justin

EDIT
Grazie alla @womp qui è ciò che mi è venuta in mente finora ... Anche se alla fine ho preso l'approccio che ho postato come una risposta .

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 buon inizio. Se localizzo solo ControllerName e ActionName nell'URL, troverà e renderà la Vista corretta. Tuttavia ho i seguenti problemi.

Area Nome non può essere tradotto
Localizzazione della zona, il metodo in Controller.View() non riesce a trovare Visualizzazioni. Anche se ho sostituito il nome dell'area nel contesto della richiesta, sembra che il metodo ViewEngineCollection.Find() non lo raccolga. Ovunque nella mia classe Controller che esegue "return View()" non riesce a trovare la vista predefinita per la sua azione. Se non localizzo l'Area, gli altri passaggi funzionano.

RedirectToAction o Html.ActionLink
Ogni volta che l'applicazione chiama RedirectToAction o se uso un aiutante Html.ActionLink o qualcosa di simile gli URL generano sono quelle inglesi. Sembra che dovrò aggiungere la logica da qualche parte eventualmente in più punti per convertire un URL inglese in uno francese (o in un'altra lingua).

risposta

14

Il seguente blog contiene una soluzione completa questo problema esatto. È in realtà una soluzione molto elegante che consiglio vivamente.

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

Nota per farlo funzionare per le aree che ho dovuto aggiungere il seguente metodo di estensione alla sua "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; 
    } 

Tuttavia, anche con questo un percorso tradotto con una AREA può essere letto e interpretato i percorsi generati sembrano sempre includere un nome AREA inglese ma localizzato tutto il resto.

sono stato diretto a un blog tramite la stessa domanda fatta sulla ASP.NET MVC Forums

+0

Bella scoperta. È essenzialmente lo stesso concetto, tranne che incapsulato nel routing, piuttosto che nella logica di istanziazione del controller. Sono d'accordo, probabilmente è un po 'più elegante, sembra un dominio più appropriato per la soluzione. – womp

+0

So che questo è un vecchio post, ma voi ragazzi avete appena salvato la mia pelle. Bella risposta! –

3

Il framework MVC supporta praticamente qualsiasi scenario di routing che si possa pensare, ma non necessariamente con le classi di routing predefinite.

La maggior parte delle soluzioni di localizzazione che ho incontrato implicano l'utilizzo degli stessi nomi dei metodi Controller e Action, ma specificando un parametro cultura nella route che determina quale versione tradotta della Vista è presentata. Ad esempio,

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

Se davvero necessario aver tradotto URL però, non vedo molta altra scelta quindi di mantenere una tabella di mappatura da qualche parte. Se comprendo correttamente la tua domanda, devi essere in grado di mappare tutte le diverse traduzioni linguistiche di "domande" (controller) e "ask" (azione) alla stessa combinazione di controller/metodo di azione.

Tuttavia, una volta creata questa tabella da qualche parte (file di risorse?), È possibile sovrascrivere facilmente lo DefaultControllerFactory utilizzato dal framework e implementare la propria logica per determinare il controller da istanziare. Pertanto, anziché un semplice abbinamento del token {controller} dall'URL come semplice confronto tra stringhe, è possibile implementare la logica per verificarla rispetto alla tabella di mapping per selezionare il controller corretto.

Per una procedura dettagliata di creazione di una fabbrica di controller personalizzata, check this great blog post. In realtà è anche un esempio di localizzazione, ma è basato sulle impostazioni della cultura dell'utente, piuttosto che sulla lingua dell'URL.

+0

Molto interessante. Ci sto guardando. Grazie per l'inizio. – Justin

+0

Prego, buona fortuna. Se la mia risposta ha aiutato affatto, non dimenticare di upvote :) – womp

+0

Beh, sono bloccato su Request.Header. Ci sto lavorando, ma se puoi dirmi come configurare la parte "ex-MX" da interpretare come intestazione di richiesta, sarebbe utile. Non sono riuscito a trovare il post del blog di esempio. Mi sarei aspettato che facesse parte del RouteTable nel file Global.asax.cs ma non ci sono riuscito. – Justin

Problemi correlati