Ustawianie kultury aplikacji na podstawie preferencji przeglądarki

10

Wielojęzyczność aplikacji www można rozwiązać na kilka sposobów. Jedne strony mają rysuneczki flag symbolizujących język, w jakim chcemy widzieć teksty (i nie tylko) i pamiętają to w cookie. Inne pozwalają to ustawić w profilu użytkownika i pamiętają ustawienie w bazie.

Ostatnio pisałem rozwiązanie, które ustawia odpowiednią kulturę aplikacji na podstawie informacji wysyłanych przez przeglądarkę podczas żądania. Sam szkielet rozwiązania jest banalny:

  1:  public static class CultureUtils
  2:  {
  3:      public static void SetCurrentThreadCultureToBrowserPreferences()
  4:      {
  5:          var langs = HttpContext.Current.Request.UserLanguages;
  6:  
  7:          CultureInfo userCulture = CultureInfo.GetCultureInfo(langs[0]);
  8:  
  9:          Thread.CurrentThread.CurrentCulture = userCulture;
 10:          Thread.CurrentThread.CurrentUICulture = userCulture;
 11:      }
 12:  }

Pobieramy język wybrany jako pierwszy w ustawieniach przeglądarki… i już.

Jednak podczas działania aplikacji raz po raz coś zaczęło się wywalać. Problemem okazał się POST wysyłany przez flashowy uploadify – takie żądanie nie wysyłało UserLanguages. Więc pierwsza modyfikacja…

  1:  public static void SetCurrentThreadCultureToBrowserPreferences()
  2:  {
  3:      var langs = HttpContext.Current.Request.UserLanguages;
  4:  
  5:      if (langs != null)
  6:      {
  7:          CultureInfo userCulture = CultureInfo.GetCultureInfo(langs[0]);
  8:  
  9:          //....

Nie trwało jednak długo, zanim znalazłem kolejny problem. Otóż w przeglądarce użytkownik może przecież wybrać tzw "neutral culture", czyli kulturę nie określającą regionu, do którego się odnosi (więcej info w MSDN). Przy próbie ustawienia takiej kultury jako UICulture dostajemy wyjątek:

"Culture ‘en’ is a neutral culture. It cannot be used in formatting and parsing and therefore cannot be set as the thread’s current culture"

I słusznie…

Niektóre aplikacje mogłyby sobie pozwolić na wyświetlenie w tym momencie komunikatu "wybierz w przeglądarce ‘specific’ culture". Jest jedno ALE… nie we wszystkich przeglądarkach da się to zrobić! Na przykład domyślnie w FF 4.0 nie ma kultury "pl-PL", jest tylko "pl". Wymaganie od użytkownika żeby sam definiował nową kulturę na poziomie przeglądarki to trochę przegięcie.

Wystarczyło jednak kilka chwil zastanowienia i taką sytuację rozwiązałem w następujący sposób:

  1:  public static class CultureUtils
  2:  {
  3:      private static readonly CultureInfo[] _allCultures = CultureInfo.GetCultures(CultureTypes.FrameworkCultures);
  4:  
  5:      public static void SetCurrentThreadCultureToBrowserPreferences()
  6:      {
  7:          var langs = HttpContext.Current.Request.UserLanguages;
  8:  
  9:          if (langs != null)
 10:          {
 11:              CultureInfo userCulture = CultureInfo.GetCultureInfo(langs[0]);
 12:              if (userCulture.IsNeutralCulture)
 13:              {
 14:                  // browser sent neutral culture that cannot be used for parsing and formatting
 15:                  // using first non-neutral culture inheriting from this culture
 16:                  userCulture = _allCultures.Where(x => x.Parent.LCID == userCulture.LCID)
 17:                      .First();
 18:              }
 19:              Thread.CurrentThread.CurrentCulture = userCulture;
 20:              Thread.CurrentThread.CurrentUICulture = userCulture;
 21:          }
 22:      }
 23:  }

Po prostu w przypadku otrzymania neutralnej kultury wyszukuję pierwszą lepszą kulturę specific będącą jej dzieckiem. A że nie znalazłem innego sposobu niż ten przedstawiony wyżej, to zostawiam go tu ku pamięci.

Share.

About Author

Programista, trener, prelegent, pasjonat, blogger. Autor podcasta programistycznego: DevTalk.pl. Jeden z liderów Białostockiej Grupy .NET i współorganizator konferencji Programistok. Od 2008 Microsoft MVP w kategorii .NET. Więcej informacji znajdziesz na stronie O autorze. Napisz do mnie ze strony Kontakt. Dodatkowo: Twitter, Facebook, YouTube.

10 Comments

  1. Hej,

    nie tylko user może wyłączyć domyślny język przeglądarki, googlebot pewnie też nie ma ustawionego domyślnego języka ;)

    Sądzę, że warto także sprawdzić czy HttpContext.Current nie jest przypadkiem null, tak dla pewności.
    Inna rzecz to podejżewam, że nie masz testu na ten kod ;)

    Pozdrawiam,
    Marek

  2. marek,
    To prawda, na pewno w niektórych sytuacjach trzeba do tego coś dopisać. Akurat aplikacja w której zastosowałem powyższy kod nie jest wystawiana publicznie, więc google (czy cokolwiek innego poza przeglądarką) mnie nie interesuje.

    A co do testu – to prawda, nie mam i w obecnej postaci raczej ciężko byłoby takowy dopisać. Ale ten kod ma pokazać technikę ustawiania kultury aplikacji na podstawie danych z przeglądarki, a nie testowania aplikacji webowych:).
    Jakbym dorzucił tu przekazywanie kontekstu w parametrze konstruktora to trzeba by było dodatkowo napisać skąd instancja tej klasy się bierze, gdzie wpiąć kontener DI, jaki to ma wpływ na ControllerFactory i wiele innych rzeczy, o których tutaj pisać nie chciałem.

  3. Michal,
    Ustawiając to w web.config decyduję się na jedną stałą kulturę… czy czegoś nie wiem?

  4. procent,

    Można go ustawić tak: <globalization culture="auto" uiCulture="auto"/> i wtedy pobierze sobie to z przeglądarki użytkownika. Moim zdaniem zadziała to dokładnie tak jak Twój kod, czyli odpowiednio pominie netural-culture dla CurrentCulture.

  5. Można go ustawić tak: <globalization culture="auto" uiCulture="auto"/>
    Chyba nawet z tego skorzystać ,niż powyższej klasy ,gdyż można łatwo podczepić
    culture z web.config np.pod scriptmenagera ustawiając
    EnableScriptGlobalization="true" EnableScriptLocalization="true"

  6. Michal, xRidx,
    Dzięki za info, nie zdawałem sobie z tego sprawy. Jeśli tak jest to faktycznie lepiej skorzystać z auto niż z tego co napisałem:)

  7. Ja to tak ogólnie nigdy nie zmuszam użytkowników do danego języka, zdarza się, że pod ręką jest przeglądarka z innym domyślnym językiem niż pożądany albo ktoś po prostu woli przeglądać w innym języku, tak więc zawsze dorzucam te flagi języka, tak na wszelki wypadek, lepsze to niż przełączać priorytety kultury w przeglądarce, przynajmniej dla użytkownika końcowego.

  8. endrju,
    Tak jak napisałem nie jest to aplikacja publiczna i było to konkretne "feature request" od klienta. W aplikacji publicznej z pewnością znalazłby się inny mechanizm do tego.

Newsletter: devstyle weekly!
Dołącz do 1000 programistów!
  Zero spamu. Tylko ciekawe treści.
Dzięki za zaufanie!
Do przeczytania w najbliższy piątek!
Niech DEV będzie z Tobą!