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.
- views
 
- controllers
 
- services
 
- Filters
 
- Utility files(e.g. in util package or generalised exception message  handling)
 
- Special files e.g. in Shiro security rest realms 
 
Below are the elaborate usage scenarios:
Views:- we have taglib available with message tag. Use this on views.
 
controllers :- message method is by default available here and locale conversion is automatically handled. See this.
 
service: we may call taglibs inside services as below:
def myCustomTaglib = grailsApplication.mainContext.getBean('com.custom.MyCustomTagLib');
 
Or inject messageSource bean as
def messageSource 
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.