user3714598
user3714598

Reputation: 1773

Grails: Most preferred way to get message from i18n message properties

I've already found a way to get message from i18n message properties (outside the controller)

def messageSource = Holders.grailsApplication.mainContext.getBean 'messageSource'
....
errorMessages << [field: "transactionDate", message: messageSource.getMessage("transactionDate.require.message", null ,null)]

But then while reviewing my colleauges work, I found out that they use a different way..

def g = new ApplicationTagLib()
errorMessages.add([field: "exchangeRate", message: g.message(code: "exchangeRate.max.message")])

Obviously, this a simpler version but I want to know which way is preferred (best practice) and what is the difference of the one to the other.

Upvotes: 3

Views: 2768

Answers (2)

Vinay Prajapati
Vinay Prajapati

Reputation: 7505

Just as an addition to above answers, there could be following places where you need to implement internationalisation or fetch the message from message bundles.

  1. views
  2. controllers
  3. services
  4. Filters
  5. Utility files(e.g. in util package or generalised exception message handling)
  6. Special files e.g. in Shiro security rest realms

Below are the elaborate usage scenarios:

  1. Views:- we have taglib available with message tag. Use this on views.

  2. controllers :- message method is by default available here and locale conversion is automatically handled. See this.

  3. service: we may call taglibs inside services as below:

    def myCustomTaglib = grailsApplication.mainContext.getBean('com.custom.MyCustomTagLib');
    

Or inject messageSource bean as

def messageSource 
  1. Filters / utility / Special files:- For these you may create something like below and then use it throughout.

    String i18nMessage(def input,String defaultMessage) {
        String[] languageCode = RequestContextHolder.currentRequestAttributes().request.getHeader("Accept-    Language").split("-")
        Locale locale = languageCode.length == 2 ? new Locale(languageCode[0], languageCode[1]) : new Locale(languageCode[0])
       String message = defaultMessage
       try {
            message = messageSource.getMessage(input.code,input?.args?.toArray(),locale)
        }catch (NoSuchMessageException nsme ){
            log.info("No such error message--> ${nsme.getMessage()}")
        }
       return message
     }
    

Also, if you get exception below:

java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.

Then, you might need to add request listener to your web.xml

 <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
</listener>

Note: web.xml is not available by default, you need to generate it from template.

These are the most common places you might need message bundle conversions. Solution at point 4 would work in almost all cases. If you notice locale has been handled here manually which we could pass after fetching from requestHeader or request params optionally.

Hope it helps.

Upvotes: 1

Sandeep Poonia
Sandeep Poonia

Reputation: 2188

Inside a Controller message and g.message are directly available from ValidationTagLib. Your colleague is assigning an object of ApplicationTagLib to variable g. I suspect ApplicationTagLib is a custom class and not provided by grails api, atleast not in 2.3.x.

If you need i18 messages anywhere except controllers in your application, then you would have to get messageSource bean first or you can create a new instance of ValidationTagLib and then call message() on that instance.

Upvotes: 1

Related Questions