Raymond Holguin
Raymond Holguin

Reputation: 1122

Grails: Call a localization message from within another message

Coming from Struts/XWorks I was able to do something like this

Messages.properties

myMessage.invalid.fieldvalue=Invalid input for $'{'getText("{0}")'}'
email.label=Email Address

Application Code

getText("myMessage.invalid.fieldvalue",new String[] {"email.label"})
Output: Invalid input for Email Address

So basically the parameter I am passing into a message is actually the code for another message and I want to render them both together.

I can't seem to find a way to do this with Grails/Spring messaging. Is this possible and if so how?

EDIT: To more clearly show one of the reason I am asking for this take this example.

Lets say I have 5 Domain classes with the property emailAddress

To validate for NULL I would have to do this

myClass1.emailAddress.nullable=Email Address cannot be NULL
myClass2.emailAddress.nullable=Email Address cannot be NULL
myClass3.emailAddress.nullable=Email Address cannot be NULL
myClass4.emailAddress.nullable=Email Address cannot be NULL
myClass5.emailAddress.nullable=Email Address cannot be NULL

What I want to be able to do is simply the messaging by overriding the default validation message as such

OLD: default.null.message=Property [{0}] of class [{1}] cannot be null

NEW: default.null.message=getMessage({0}) cannot be null
emailAddress=Email Address

So now anytime any class has a property called emailAddress and it validates as NULL I will get the message Email Address cannot be null. There is no need to have 5 messages that basically say the same exact thing. If I had another class with the property emailAddress then its already handled and I dont have to copy and paste a 6th line.

Anytime I have classes with shared property names, all I have to do is add just add a single line for each property that will be applied to all classes

sharedProp1= Shared property 1
sharedProp2= Shared property 2

Upvotes: 4

Views: 346

Answers (2)

Raymond Holguin
Raymond Holguin

Reputation: 1122

I was able to get this done by extending the MessageSource and overriding the resolveArguments method.

class CustomMessageSource extends ReloadableResourceBundleMessageSource {
@Override
protected Object[] resolveArguments(Object[] args, Locale locale) {
    if (args == null) {
        return new Object[0];
    }
    List<Object> resolvedArgs = new ArrayList<Object>(args.length);
    for (Object arg : args) {
        if (arg instanceof MessageSourceResolvable) {
            resolvedArgs.add(getMessage((MessageSourceResolvable) arg, locale));
        }
        else {
            //resolvedArgs.add(arg) **REPLACED THIS LINE
            resolvedArgs.add(getMessage(arg, null, arg, locale));
        }
    }
    return resolvedArgs.toArray(new Object[resolvedArgs.size()]);
}
}

I replaced a single line within the loop that evaluates your message arguments. I basically take the argument and see if its a key to another message. If yes, then replace the argument with that message, if no then continue as normal to use the argument

Make sure to map the new messageSource in your resources.groovy file

beans = {
    messageSource(groovyUtils.CustomMessageSource) {
        basenames = "messages"
    }
}

Upvotes: 2

Coffee Monkey
Coffee Monkey

Reputation: 472

When in a controller, call message for the param you want to internationalize and store that in a local variable. Then call message for the full message and add your local variable, with the internationalized param value, as a param to that message.

def emailLabel = message(code: "email.label")
def fullMessage = message(code: 'myMessage.invalid.fieldvalue', args: [emailLabel])

Your messages.properties would contain something like

myMessage.invalid.fieldvalue=Invalid input for {0}
email.label=Email Address

Upvotes: 2

Related Questions