Fala, pessoal!
Estive pesquisando uma forma de gravar internacionalização em banco de dados para permitir que alguns textos da minha aplicação sejam alterados sem a necessidade de precisar subir uma nova versão no servidor, mas continuando a usar as chaves definidas nos arquivos messages_*.properties
caso o texto não esteja definido no banco.
Primeiro de tudo, é preciso criar uma classe de domínio para armazenar os valores no banco:
class Mensagem {
String code
Locale locale
String text
static mapping = {
cache true
columns {
code index: 'idx_message_key'
locale index: 'idx_message_key'
}
}
String toString() {
text
}
}
Criei um índice para tornar a busca das mensagens mais rápida.
Depois, foi preciso criar a classe DatabaseMessageSource
estendendo a classe AbstractMessageSource
, que será a classe responsável por fazer a pesquisa no banco de dados e, caso não encontre um valor, retornar a chave definida em arquivo:
class DatabaseMessageSource extends AbstractMessageSource {
Ehcache messageCache
def messageBundleMessageSource
@Override
protected MessageFormat resolveCode(String code, Locale locale) {
def key = new MessageKey(code, locale)
def format = messageCache.get(key)?.value
if (!format) {
Mensagem mensagem = Mensagem.findByCodeAndLocale(code, locale)
if (mensagem ) {
format = new MessageFormat(mensagem.text, mensagem.locale)
} else {
format = messageBundleMessageSource.resolveCode(code, locale)
}
messageCache.put(new Element(key, format))
}
return format
}
}
Usei o EhCache para evitar que a cada exibição de página seja feita uma consulta no banco retornando os valores já encontrados em execuções anteriores, poupando assim uma quantidade razoável de recursos do servidor. Para configurar esse cache corretamente, primeiro é preciso criar a classe MessageKey
:
@Immutable
class MessageKey implements Serializable {
String code
Locale locale
}
Depois, é preciso dizer ao Spring para carregar os beans que acabamos de criar no resources.groovy
:
messageCache(EhCacheFactoryBean) {
timeToLive = 500
// outras configurações de cache
}
messageSource(DatabaseMessageSource) {
messageCache = messageCache
messageBundleMessageSource = ref("messageBundleMessageSource")
}
messageBundleMessageSource(org.codehaus.groovy.grails.context.support.PluginAwareResourceBundleMessageSource) {
basenames = "WEB-INF/grails-app/i18n/messages"
}
Usamos a classe PluginAwareResourceBundleMessageSource
porque, segundo este comentário, algumas mensagens de plugins podem ser ignoradas caso usemos alguma outra classe aqui.
Depois de fazer essa configuração, basta fazer o teste. Eu gerei o controlador e páginas para a classe Mensagem
usando o comando grails generate-all pacote.Mensagem
. Depois é só fazer os testes usando a tag <g:message>
nas GSPs e a action message(args)
nos controladores, e pronto! Sempre que for preciso sobrescrever um texto no sistema, basta criar um registro na tabela Mensagem
e imediatamente a mensagem estará disponível, sem a necessidade de precisar subir uma nova versão da aplicação!// = Your Blog title