The Grails web development framework is a very powerful tool for quickly creating web applications ranging from simple to enterprise ready. For that, it probably provides everything a developer ever needs. And if it’s not delivered by Grails itself there are many useful plugins and tons of existing Java libraries (including the JDK, of course, the underlying SpringSource Framework and the GDK).
However, there is one thing I have stumbled across several times now and a quick search on the internet shows that I’m not alone with this problem: how can I use the internationalization features from a regular Java or Groovy class?
User centric artifacts
Just a quick overview of how Grails lets you access your message bundles (see docs).
- GSP files provide the Grails tag
- Controllers basically do the same, only as a method call
message(code: 'some.code')
- The same applies for tag-libs, with the only exception that the method call needs to be prefixed with the Grails namespace
g.message(code='some.code')
.
Application internal artifacts
What about services? In a service you have to inject an instance of MessageSource by adding def messageSource
to your service class. This allows you to call getMessage
in a similar way Grails provides in controllers. There’s one caveat though: you have to supply the locale for which you want to retrieve a message. More on that later.
Now, this was still easy but maybe you want to translate text in, let’s say, your own Groovy class (in the “src/groovy” folder). There’s no dependency injection available in that case. What do you do? For one, you could simply create a field or a local variable of type MessageSource
. But that ain’t Groovy, ya now! The cool way is to add a getter method to your class at runtime which returns such a message-source. And where’s the best place to do this? Bootstrap!
import typicalnerd.TranslationHelper class BootStrap { def messageSource def init = { servletContext -> TranslationHelper.class.metaClass.getMessageSource = TranslationHelper.class.metaClass.static.getMessageSource = { messageSource } } def destroy = { } }
I’ve created myself a Groovy class named TranslationHelper
which can be used by any other class that needs to fetch a translated string. Using Groovy’s Meta-Class system I add a static and non-static getMessageSource
closure to it which captures the dependency injected messageSource
of the BootStrap
class. This gives me access to a message-source from within my translation helper, for both, static and instance methods.
The Locale
As already mentioned in context with services, if you make use of the MessageSource
then you are responsible for supplying a locale. Apart from the need to have translation in non-user-visible artifacts I do not fancy having this boilerplate code litter my carefully styled code wherever I work with a message-source. This is another good reason for this helper class. Alright, where can one get the locale?
- The current implicit context of a request
- The HTTP request
- You create one yourself
The last one is not very useful unless you want to restrict your application to one language – which can implicitly be achieved by not using the message bundles at all. I’ll focus on the first two.
Implicit context
The SpringSource framework provides LocaleContextHolder which can be found in the package org.springframework.context.i18n
. You’ll only need an import and call the static method getLocale()
on it. Here’s how it is done in my helper class.
package typicalnerd import org.springframework.context.i18n.LocaleContextHolder as LCH class TranslationHelper { static String getMessage(String code) { return getMessageSource().getMessage( code, null, '', LCH.getLocale()) } }
Here you can see two things:
- The use of the static
getMessageSource()
that was added in the bootstrap - I created an alias
LCH
forLocaleContextHolder
to shorten the actual code that uses this class.
There’s one important thing to keep in mind. getLocale()
is bound to the context of the current thread. For classes used in the context of an invocation of one of your controller’s actions this is the locale of the request. Taken from the Grails docs (section “Scoped Controllers):
By default, a new controller instance is created for each request. In fact, because the controller is prototype scoped, it is thread-safe since each request happens on its own thread.
In this case there’s nothing to worry about. Even if your code branches into a service it belongs to the context of the calling thread. However, if you create your own threads then they are outside of the scope of any request. In this case getLocale()
returns a string based on the default system locale (unless you set it explicitly before). As a quick demo (I did not want to fiddle with threads and all that code that goes with it) I put a call to my helper class in the bootstrap, and, who’d have thought, the message returned is in German, always. Calls to actions, on the other hand, return the value based on the context of the request.
HTTP request
What LocaleContextHolder
is doing implicitly you can also do explicitly. The RequestContextUtils allow you to extract the locale of the request. Again, see my helper class:
package typicalnerd import javax.servlet.http.HttpServletRequest import org.springframework.context.i18n.LocaleContextHolder as LCH import org.springframework.web.servlet.support.RequestContextUtils class TranslationHelper { static String getMessage(String code) { return getMessageSource().getMessage( code, null, '', LCH.getLocale()) } static String getMessage(String code, HttpServletRequest request) { if (request) { return getMessageSource().getMessage( code, null, '', RequestContextUtils.getLocale(request)) } else { return getMessage(code) } } }
With every call to an action you have access to a variable named request
within this action. This instance is of type HttpServletRequest and can be used to extract the locale by passing it to RequestContextUtils.getLocale()
. That’s basically all there is to it.