Following this blog article I enabled my application to load i18n messages from the database. It works great. However, I don\'t want to manage all messages in the database
You may extend ReloadableResourceBundleMessageSource (the original grails message bundle, which does not seem to be final) instead and then apply this code:
class DatabaseMessageSource extends ReloadableResourceBundleMessageSource {
protected MessageFormat resolveCode(String code, Locale locale) {
Message msg = Message.findByCodeAndLocale(code, locale)
def format = null
if (msg) {
format = new MessageFormat(msg.text, msg.locale)
}else{
format = super.resolveCode(code,locale)
}
return format;
}
}
You may also reconfigure your message resources in Resources.groovy:
beans = {
messageSource(com.mycompany.DatabaseMessageSource) {
basename = "WEB-INF/grails-app/i18n/messages"
}
}
I strongly encourage you to use Copycopter
(http://copycopter.com) to manage i18n keys content. It will allow your team to update keys on the fly easily, using a friendly interface, and you will be able to set the default as you wanted. This way no one will come to you when a key needs to be updated.
ThoughtBot, a strong web development consulting company is behind that product. It is solid.
Your code will look like this then:
mail :to => member.email,
:subject => I18n.translate("member_mailer.reminder_to_sign_in.#{underscored_number_of_days}.subject",
:default => %{You have not signed in for #{humanized_number_of_days}})
The i18n keys are generated on the fly on copytcopter, the first time you use it. It is really easy to use.
Check it out, it is worth it.
I would propose to keep one bundle-message-source in a new bean and inject it into your DatabaseMessageSource
.
resources.groovy:
// Place your Spring DSL code here
beans = {
messageSource(DatabaseMessageSource) {
messageBundleMessageSource = ref("messageBundleMessageSource")
}
messageBundleMessageSource(org.codehaus.groovy.grails.context.support.PluginAwareResourceBundleMessageSource) {
basenames = "WEB-INF/grails-app/i18n/messages"
}
}
DatabaseMessageSource.groovy:
class DatabaseMessageSource extends AbstractMessageSource {
def messageBundleMessageSource
protected MessageFormat resolveCode(String code, Locale locale) {
Message msg = Message.findByCodeAndLocale(code, locale)
def format
if(msg) {
format = new MessageFormat(msg.text, msg.locale)
}
else {
format = messageBundleMessageSource.resolveCode(code, locale)
}
return format;
}
}
This way, in fallback solution, the message will be read from the appropriate messages_*.properties
file, by just requesting it from one resource bundle message source. Note that you should use the PluginAwareResourceBundleMessageSource
, otherwise you could miss some important messages from your plugins.
Although it's not Grails specific, we at Griffon created CompositeResourceBundle and DelegatingResourceBundle as parts of our own i18n plugin (source found at http://svn.codehaus.org/griffon/plugins/griffon-i18n/trunk/src/main/griffon/plugins/i18n/).
ExtendedResourceBundleMessageSource shows how these classes can be used.
resolveCode(code, locale) in AbstractMessageSource is an abstract method so you will not be able to call it. Try extending ResourceBundleMessageSource or ReloadableResourceBundleMessageSource instead.