Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it OK to use ThreadLocal for storing the requested Locale?

I am working on internationalizing user entered data in a rather large Client/Server (HTTP (Hessian) is used for communication) application which is stored in a database. Users can choose the language they want to see and there is a default language which is used when a translation in the requested language is not present.

Currently a data class may look like this:

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

After internationalization it could look like this:

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

Of course it may be interesting to create a delegate getter in MyDataClass which takes care of getting the text in the correct locale:

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

In my team there were some concerns though about the need to pass the locale around all the time until they reach the data class. Since all this stuff happens on the server and every request to the server is handled in a dedicated Thread, some people suggested to store the requested locale in a ThreadLocal object and create a backward compatible no-argument getter:

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

The ThreadLocal then needs to be a global variable (static somewhere) or it needs to be injected into MyDataClass on every single instance creation (we are using spring, so we could inject it if we make our data classes spring managed (which feels wrong to me)).

Using a ThreadLocal for the locale somehow feels wrong to me. I can vaguely argue that I don't like the invisible magic in the getter and the dependency on a global variable (in a data class!). However, having a "bad feeling" about this is not really a good way to argue with my colleagues about it. To help I need an answer with one of the following:

  1. Tell me that my feeling sucks and the solution is great for reasons X,Y and Z.
  2. Give me some good quotable arguments I can use to argue with my colleagues and tell me how to do it better (just always pass locale around or any other idea?)
like image 510
yankee Avatar asked May 30 '13 14:05

yankee


3 Answers

ThreadLocal is bad practice. It's global variables and there are plenty of articles about how bad that is, in any language. The fact that Spring uses it does not justify using it. I like the solution cruftex has given. Avoid passing data via global variables.

like image 184
inor Avatar answered Oct 11 '22 15:10

inor


Although, common practise I don't like to do localizing "deep" within the application.

Intead of this:

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

We do this:

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

And then do, e.g. in a JSP or output layer:

<%= localizable.getString(locale) %>

The logic itself is language agnostic. We have cases where, after some processing, the application sends out the result by mail, logs it and presents it to the web user in all different languages. So processing together with result generation and then localization must be separate.

like image 29
cruftex Avatar answered Oct 11 '22 15:10

cruftex


This approach is perfectly valid. For example, Spring makes Locale available using ThreadLocal through RequestContextListener and LocaleContextHolder.

If you create a custom implementation, make sure you handle your ThreadLocal (set/remove) properly.

like image 45
Guillaume Darmont Avatar answered Oct 11 '22 17:10

Guillaume Darmont