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.