2013-05-30 16 views
8

Pracuję nad internacjonalizacją wprowadzonych przez użytkownika danych w dość dużym kliencie/serwerze (aplikacja HTTP (Hessian) służy do komunikacji), która jest przechowywana w bazie danych. Użytkownicy mogą wybrać język, który chcą zobaczyć, oraz domyślny język używany w przypadku braku tłumaczenia w żądanym języku.Czy można używać ThreadLocal do przechowywania wymaganych ustawień regionalnych?

Obecnie klasa dane mogą wyglądać następująco:

class MyDataClass { 
    private Long id; 
    private String someText; 
    /* getters and setters */ 
} 

Po internacjonalizacji może to wyglądać tak:

class MyDataClass { 
    private Long id; 
    private Set<LocalizedStrings> localizedStrings; 
    /* getters and setters */ 
} 
class LocalizedStrings { 
    private Locale locale; 
    private String someText; 
    /* getters and setters */ 
} 

Oczywiście może to być interesujące, aby utworzyć getter powierzać MyDataClass który trwa dbałość o uzyskanie tekstu w odpowiednim języku:

public String getSomeText(Locale locale) { 
    for(LocalizedString localized : localizedStrings) { 
    if (localized.getLocale().equals(locale)) { 
     return localized.getSomeText(); 
    } 
    } 
} 

W moim zespole były jednak pewne obawy dotyczące konieczności przekazywania ustawień regionalnych przez cały czas, dopóki nie dotrą do klasy danych. Ponieważ wszystkie te rzeczy dzieje się na serwerze, a każde żądanie do serwera jest obsługiwany w dedykowanym wątku, niektórzy sugerują, aby zapisać żądane ustawienia regionalne w ThreadLocal obiektu i stworzenie wstecznie kompatybilny getter no-argument:

public String getSomeText() { 
    return getSomeText(myThreadLocalLocale.get()); 
} 

Wątek ThreadLocal musi być zmienną globalną (statyczną gdzieś) lub musi być wstrzyknięty do MyDataClass przy każdym tworzeniu pojedynczej instancji (używamy sprężyny, abyśmy mogli ją wprowadzić, jeśli sprawimy, że nasze klasy danych będą zarządzane w sposób wiosenny (co jest niewłaściwe dla mnie)).

Używanie właściwości ThreadLocal dla lokalizacji jest dla mnie złe. Mogę niejasno twierdzić, że nie podoba mi się niewidzialna magia w geterze i zależność od zmiennej globalnej (w klasie danych!). Jednak posiadanie "złego przeczucia" nie jest dobrym sposobem na dyskusję z moimi kolegami na ten temat. Aby pomóc muszę odpowiedź z jednego z następujących powodów:

  1. Powiedz mi, że moje uczucie do bani, a rozwiązanie jest idealne dla powodów X, Y i Z.
  2. dać mi kilka dobrych zacytowania argumentów można użyć do spierać się z moimi kolegami i mówić mi, jak to zrobić lepiej (po prostu zawsze przekazywać lokalizację lub jakikolwiek inny pomysł?)
+1

Dlaczego używasz zestawu dla 'localizedStrings', a nie mapy? –

+0

Czy rozważałeś użycie Spring [LocaleContextHolder] (http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/context/i18n/LocaleContextHolder.html). Jego źródła mają własny sposób przechowywania bieżących ustawień narodowych (przechowywanych przez DispatcherServlet) dla wątku. – sbk

+0

Jeśli zasięg zmiennej lokalizacyjnej jest pojedynczym wątkiem, wydaje się, że jest to rozwiązanie inteligentne (i proste). Jeśli wybierasz się na wiele wątków, to oczywiście wymaga to czegoś więcej. Jeśli twój interfejs API zawiera informacje o lokalizacji w każdym żądaniu, jest to całkowicie poprawne rozwiązanie. Pocałuj, to najlepszy argument, jaki możesz zrobić. –

Odpowiedz

1

To podejście jest całkowicie poprawne. Na przykład Spring udostępnia lokalizację za pomocą ThreadLocal do RequestContextListener i LocaleContextHolder.

Jeśli utworzysz niestandardową implementację, upewnij się, że poprawnie zarządzasz wątkiem ThreadLocal (ustaw/usuń).

1

Używanie wątku lokalnego, jak opisujesz, jest bardzo powszechnym wzorem w aplikacjach internetowych. Zobacz tę klasę wiosną API jako przykład:

org.springframework.web.context.request.RequestContextHolder 

Użyj filtru serwletu (lub podobny) do obu ustaw locale w wątku lokalny, a następnie wyczyścić wartość lokalizacji serwera po wykonaniu każdego żądania. Zamiast wstrzykiwać go w każdym miejscu, w którym jest używany, użyj statycznej metody factory/accessor podobnej do metody RequestContextHolder: RequestContextHolder.getRequestAttributes().

2

Mimo że powszechna praktyka nie lubię lokalizować "głęboko" w aplikacji.

intead to:

public String getSomeText() { 
    return getSomeText(myThreadLocalLocale.get()); 
} 

Robimy to:

public LocalizableText getSomeText() { 
    return new LocalizableText(resourceBundle, "someText"); 
} 

A potem zrobić, na przykład w JSP lub w warstwie wyjściowej:

<%= localizable.getString(locale) %> 

Sama logika jest agnostyczna. Mamy przypadki, w których po pewnym przetworzeniu aplikacja wysyła wynik pocztą, rejestruje go i przedstawia użytkownikowi sieci we wszystkich językach. Zatem przetwarzanie wraz z generowaniem wyniku, a następnie lokalizacją musi być oddzielne.

0

ThreadLocal to zła praktyka. To globalne zmienne i jest mnóstwo artykułów o tym, jak źle to jest, w jakimkolwiek języku. Fakt, że Spring go używa, nie usprawiedliwia korzystania z niego. Podoba mi się rozwiązanie, które podał nam cruftex. Unikaj przekazywania danych za pośrednictwem zmiennych globalnych.

Powiązane problemy