2013-05-30 17 views
8

Sto lavorando all'internazionalizzazione dei dati immessi dall'utente in un client/server piuttosto grande (l'HTTP (Hessian) è usato per la comunicazione) un'applicazione che è memorizzata in un database. Gli utenti possono scegliere la lingua che vogliono vedere e c'è una lingua predefinita che viene utilizzata quando non è presente una traduzione nella lingua richiesta.È OK utilizzare ThreadLocal per memorizzare le impostazioni internazionali richieste?

Attualmente una classe di dati può apparire come segue:

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

Dopo internazionalizzazione che potrebbe assomigliare a questo:

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

Naturalmente può essere interessante per creare un getter delegato in MyDataClass che prende cura di ottenere il testo nella locale corretta:

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

Nella mia squadra c'erano alcuni dubbi riguardo alla necessità di passare le impostazioni locali per tutto il tempo fino a quando non raggiungono la classe dati. Dal momento che tutta questa roba avviene sul server e ogni richiesta al server viene gestita in un thread dedicato, alcune persone hanno suggerito di memorizzare le impostazioni internazionali richieste in un oggetto ThreadLocal e creare un retrocompatibile senza argomenti getter:

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

Il ThreadLocal deve quindi essere una variabile globale (statica da qualche parte) o deve essere iniettata in MyDataClass su ogni singola creazione di istanza (stiamo usando molla, quindi potremmo iniettarla se rendiamo gestite le nostre classi di dati (cosa che non va me)).

L'utilizzo di un ThreadLocal per le impostazioni internazionali mi sembra in qualche modo sbagliato. Posso vagamente affermare che non mi piace la magia invisibile nel getter e la dipendenza da una variabile globale (in una classe di dati!). Tuttavia, avere un "brutto presentimento" a riguardo non è un buon modo per discutere con i miei colleghi a riguardo. Per aiutare ho bisogno di una risposta con uno dei seguenti:

  1. Dimmi che la mia sensazione fa schifo e la soluzione è grande per ragioni X, Y e Z.
  2. darmi alcune buone argomentazioni citabili che posso utilizzare per discutere con i miei colleghi e dirmi come farlo meglio (passare sempre locale o altra idea?)
+1

Perché usi un Set per 'localizedStrings' e non una Mappa? –

+0

Avete considerato l'utilizzo di Spring [LocaleContextHolder] (http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/context/i18n/LocaleContextHolder.html). Le molle possiedono il modo di memorizzare le impostazioni locali correnti (memorizzate da DispatcherServlet) per un thread. – sbk

+0

Se l'ambito della variabile di localizzazione è un singolo thread, questa sembra una soluzione intelligente (e semplice). Se ti stai avventurando in più thread, ovviamente, questo richiede qualcosa in più. Se la tua API ha informazioni di localizzazione in ogni richiesta, allora è una soluzione completamente valida. BACIO, questo è il miglior argomento che puoi fare. –

risposta

1

Questo approccio è perfettamente valido. Ad esempio, Spring rende disponibili le impostazioni locali utilizzando ThreadLocal tramite RequestContextListener e LocaleContextHolder.

Se si crea un'implementazione personalizzata, assicurarsi di gestire correttamente ThreadLocal (set/remove).

1

L'utilizzo di un thread locale come descritto è un modello molto comune nelle applicazioni Web. Vedere questa classe nelle API Spring come esempio:

org.springframework.web.context.request.RequestContextHolder 

utilizzare un filtro servlet (o simile) sia impostare la locale in un thread locale, e quindi cancellare il valore locale dopo che il server finito ogni richiesta. Invece di iniettarlo in ogni posto in cui viene utilizzato, utilizzare un metodo factory/accessor statico simile a RequestContextHolder: RequestContextHolder.getRequestAttributes().

2

Anche se, pratica comune non mi piace fare localizzare "in profondità" all'interno dell'applicazione.

intead di questo:

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

Lo facciamo:

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

E poi fare, ad esempio, in un JSP o uscita strato:

<%= localizable.getString(locale) %> 

La logica stessa è linguaggio agnostico. Abbiamo casi in cui, dopo qualche elaborazione, l'applicazione invia il risultato per posta, lo registra e lo presenta all'utente web in tutte le lingue. Quindi l'elaborazione insieme alla generazione dei risultati e quindi alla localizzazione deve essere separata.

0

ThreadLocal è una cattiva pratica. Sono variabili globali e ci sono molti articoli su quanto è male, in qualsiasi lingua. Il fatto che Spring lo usi non giustifica l'uso. Mi piace la soluzione che cruftex ha dato. Evita il passaggio di dati tramite variabili globali.

Problemi correlati