Occasionally I need to use a message from an i18n resource in a service or domain object and every time I forget how. It’s a piece of cake in a controller or GSP because the message method is available, but when it’s not what do I do?
How Do I Lookup a Message Code Without the Tag Lib?
Here’s the code I use in one of my services:
class MyService { //Autowire the grails application bean def grailsApplication private def getMessage(Locale locale) { def appCtx = grailsApplication.getMainContext() return appCtx.getMessage("message.code", ["arg1", "arg2"], "default message", locale) } }
So it’s just a case of using the GrailsApplication bean to get hold of the Spring ApplicationContext and from there you can use one of a few methods to resolve the message code.
But Don’t Pass an Empty List
There’s a potential gotcha in that you can’t pass an empty list to the getMessage methods:
appCtx.getMessage("message.code", [], "default message", locale)
fails with an error something like:
Caused by: groovy.lang.MissingMethodException: No signature of method: org.codehaus.groovy.grails.commons.spring. GrailsWebApplicationContext.getMessage() is applicable for argument types: (java.lang.String, java.util.ArrayList, java.lang.String, java.util.Locale) values: [test.message, [], default message, en_GB]
But this will work
appCtx.getMessage("message.code", null, "default message", locale)
so will this
appCtx.getMessage("message.code", new Object[0], "default message", locale)
and this
appCtx.getMessage("message.code", [] as Object[], "default message", locale)Do You Really Need to?
Having attempted this a few times I’ve found that it’s important I ask myself the question “Should I be accessing message bundles outside the controller”. Often you can’t avoid it – maybe you need to send an email or write to an internationalised log file. Other times, and this is especially true in domain objects, it’s best to pass message codes around and delegate the lookup to a piece of code that knows the user’s locale. Since the locale may vary between requests you will be sure of using the correct value if you resolve messages in the controller.
As a rule, if the message I’m resolving is destined for a view I will always resolve the message in the GSP.
6 thoughts on “Resolving i18n Messages Outside GSPs and Controllers in Grails”
Leave a Reply
This doesn’t actually work for me…
grailsApplication remains null.
What was supposed to populate it?
Hi Yuval, did you create the code in a service (in the services directory). If you did then the grailsApplication object should be injected by the container.
If you didn’t then that will be your problem!
If you need to access the grailsApplication outside of an autowired bean then you’ll have to get it from the spring application context directly, otherwise you can set your bean up using the Spring DSL and wire it up manually (more here).
Dave
This won’t be relevant anymore for you, but anyway, I am doing it as follows:
ApplicationContext applicationContext = (ApplicationContext) ServletContextHolder.getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT);
applicationContext.getMessage(“long_live_slash_msg”, null, Locale.ITALY);
Yes, that works too, nice!
Thanks, the trick including the inelegant “as Object[]” was worth posting.
And yes, i18n in the service happens.