Grails: Internacionalização em banco de dados e arquivos

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.

Depois de ver esse e esse post, pude fazer esse esquema funcionar perfeitamente.

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

comments powered by Disqus