2010-10-14 10 views
13

Pracuję z klientem, który chce, aby adresy URL w naszej aplikacji internetowej były w języku francuskim. Jestem angielskim programistą i mamy również klientów z Polski. Jest to interesujący problem, ale nie sądzę, aby jego działanie wspierało środowisko ASP.NET MVC.Czy można zlokalizować URL/routing w ASP.NET MVC?

Oto scenariusz. Trasa ...

konkretnym przykładzie
English URL
www.stackoverflow.com/questions/ask

będzie również wspierać

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

Ogólny PRZYKŁAD
English URL
http://clientA.product.com/AreaNameEnglish/ControllerNameEnglish/ActionNameEnglish/params

musi również obsługiwać

francuski Adres
http://clientB.product.com/AreaNameFrench/ControllerNameFrench/ActionNameFrench/params

Więc w mojej okolicy MVC, kontroler i wszystkie działania muszą mieć po angielsku i tłumaczenia Francuski.

Oczywiście łatwość konserwacji byłaby ogromnym problemem, gdybym poszedł i zakodował wszystkie moje kontrolery, widoki i nazwy akcji na francuski. Czy istnieje sposób, aby zlokalizować trasę, która jest prezentowana w przeglądarce, nie robiąc tego? Pamiętaj, że w aplikacji jest wiele różnych tras. Kilka obszarów, z których każdy ma garstkę kontrolera, z których każdy ma wiele akcji?

Dzięki,
Justin

EDIT
Dzięki @womp tutaj jest to, co mam wymyślić tak daleko ... Chociaż w końcu wziąłem podejście, które napisałem w odpowiedzi .

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

Przyzwoity początek. Jeśli zlokalizuję tylko nazwę ControllerName i ActionName w Url, odnajdzie on i wyrenderuje odpowiedni widok. Mam jednak następujące problemy.

Obszar Nazwa nie może być tłumaczone
zlokalizowaniu oznacza metodę Controller.View() nie znajdzie Widoki. Mimo że nazwa obszaru została zastąpiona w kontekście żądania, metoda ViewEngineCollection.Find() nie wydaje się go podnosić. W dowolnym miejscu w klasie kontrolera, która "zwraca widok()", nie można znaleźć domyślnego widoku dla jego działania. Jeśli nie zlokalizuję obszaru, pozostałe kroki będą działać.

RedirectToAction lub Html.ActionLink
Anytime aplikacja wywołuje RedirectToAction lub jeśli używam pomocnika Html.ActionLink lub coś podobnego generowania adresów URL są te angielskie. Wygląda na to, że będę musiał dodać logikę gdzieś możliwie w wielu miejscach, aby przekształcić angielski URL na francuski (lub inny język).

Odpowiedz

14

Poniższy blog zawiera pełne rozwiązanie tego problemu. To naprawdę bardzo eleganckie rozwiązanie, które bardzo polecam.

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

Uwaga aby dostać pracę na obszarach musiałem dodać następującą metodę rozszerzenia do swojej klasy „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; 
    } 

Jednak nawet z tym przetłumaczony trasa Z OBSZAREM można odczytać i zinterpretować generowane trasy zawsze wydają się zawierać angielską nazwę AREA, ale zlokalizowane wszystko inne.

zostałem skierowany na blogu przez ten sam pytanie zadane na ASP.NET MVC Forums

+0

Miłe znalezisko. Jest to zasadniczo ta sama koncepcja, z wyjątkiem zamkniętego w routingu, a nie w logice instancji kontrolera. Zgadzam się, jest to prawdopodobnie nieco bardziej eleganckie, wydaje się, że jest to bardziej odpowiednia domena dla rozwiązania. – womp

+0

Wiem, że to stary post, ale ludzie właśnie uratowali moją skórę. Świetna odpowiedź! –

3

Środowisko MVC obsługuje prawie każdy scenariusz routingu, jaki można sobie wyobrazić, ale niekoniecznie z domyślnymi klasami routingu.

Większość rozwiązań lokalizacyjnych, na które natknąłem się, dotyczy tych samych nazw kontrolerów i metod działania, ale określa parametr kultury na trasie, który określa, która wersja przetłumaczona widoku jest prezentowana. Na przykład,

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

Jeśli naprawdę musi mieć przetłumaczone URL choć nie widzę innego wyjścia wtedy dużo, aby utrzymać gdzieś tabeli odwzorowania. Jeśli dobrze rozumiem twoje pytanie, musisz umieć odwzorować wszystkie tłumaczenia w różnych językach "pytania" (kontroler) i "pytanie" (czynność) na tę samą kombinację kontrolera/metody działania.

Jednak po zbudowaniu gdzieś tej tabeli (pliki zasobów?), Można łatwo zastąpić DefaultControllerFactory używaną przez framework i zaimplementować własną logikę do określenia kontrolera do utworzenia instancji. Dlatego zamiast dopasowywania tokenu {controller} z adresu URL jako prostego porównania ciągów, możesz zaimplementować logikę, aby sprawdzić ją w tabeli odwzorowania, aby wybrać odpowiedni kontroler.

Aby zapoznać się z instrukcją tworzenia niestandardowej fabryki kontrolerów, check this great blog post. W rzeczywistości jest to również przykład lokalizacji, ale opiera się na ustawieniach kultury użytkownika, a nie na języku adresu URL.

+0

Bardzo interesująca. Zajmuję się tym. Dzięki za rozpoczęcie gry. – Justin

+0

Nie ma za co, powodzenia z tym. Jeśli moja odpowiedź w ogóle pomogła, nie zapomnij o awansie :) – womp

+0

Cóż utknąłem na Request.Header. Zajmuje się tym, ale jeśli możesz mi powiedzieć, jak skonfigurować część "ex-MX", która zostanie zinterpretowana jako nagłówek żądania, to pomoże. Nie mogłem znaleźć tego przykładowego wpisu na blogu. Spodziewałbym się, że będzie częścią RouteTable w pliku Global.asax.cs, ale nie ma go. – Justin