It's common to provide a toString
implementation on a domain object that may well end up being used in the view. However, this doesn't allow for internationalization in the view. A good solution that I've used a number of times is to have classes implement Spring's MessageSourceResolvable interface. Consider this domain class that represents an image file:
grails-app/domain/Image.groovy
class Image {
String name
String path
User uploadedBy
org.joda.time.DateTime dateCreated
static transients = ['file']
File getFile() {
new File(ConfigurationHolder.config.image.base.dir, path)
}
String toString() {
"$name uploaded by $uploadedBy.username on $dateCreated - ${file.size()} bytes"
}
}
The toString
implementation is all well and good if we're targeting an English-speaking audience but with some simple changes we can make it i18n compliant:
grails-app/domain/Image.groovy
class Image implements org.springframework.context.MessageSourceResolvable {
// properties as above
static transients = ["codes", "arguments", "defaultMessage"]
Object[] getArguments() {
[name, uploadedBy.username, dateCreated.toDate(), file.size()] as Object[]
}
String[] getCodes() {
['image.info'] as String[]
}
String getDefaultMessage() {
"$name uploaded by $uploadedBy.username on $dateCreated - ${file.size()} bytes"
}
}
In our message properties file we can add:
grails-app/i18n/messages.properties
image.info={0} uploaded by {1} on {2,date} - ${3,number,integer} bytes
In the view we can display our object like this:
<g:message error="${imageInstance}"/>
Yes, that is the error
attribute we're passing to the message tag! Grails intends the attribute to be used for outputting validation errors but the underlying mechanism is the same - Spring's ObjectError
implements MessageSourceResolvable
and that's how Grails' message tag resolves the displayed error message. Rather than passing separate code
, args
and default
attributes to the tag we can pass the single MessageSourceResolvable
instance and its implementation will take care of supplying those values.
Note: I added a message
attribute to the message tag to avoid the confusion caused by using error
. This is in Grails from version 1.2-M1 onwards.
We can now add translations of our object description. For example:
grails-app/i18n/messages_af.properties
image.info={0} opgelaai deur {1} op {2,date} - ${3,number,integer} grepe
It's worth noting that the format of the dateCreated
property will be automatically determined by the request locale so the value will be formatted correctly for the user.
I've found this technique can also be very useful on enum classes. For example:
src/groovy/com/mycompany/Season.groovy
package com.mycompany
enum Season implements org.springframework.context.MessageSourceResolvable {
SPRING, SUMMER, AUTUMN, WINTER
Object[] getArguments() { [] as Object[] }
String[] getCodes() {
["${getClass().name}.${name()}"] as String[]
}
String getDefaultMessage() { name() }
}
grails-app/i18n/messages.properties
com.mycompany.Season.SPRING=Spring
com.mycompany.Season.SUMMER=Summer
com.mycompany.Season.AUTUMN=Autumn
com.mycompany.Season.WINTER=Winter
grails-app/i18n/messages_en_US.properties
com.mycompany.Season.AUTUMN=Fall