SpringBoot + Thymeleaf + i18n (Internacionalização)

Olá novamente.
Esta semana eu tive que estudar como implementar i18n em uma aplicação de CRM em Spring Boot + Thymeleaf que estou desenvolvendo. O Spring Boot realmente facilita muito a configuração mas achei muitas informações desencontradas na Web a respeito do assunto e, por isto, decidi criar mais um tutorial aqui.
Como sempre, você pode encontrar o projeto completo no Github em: https://github.com/mtrojahn/springboot-thymeleaf-i18n.
Vamos começar pelo pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.marcelustrojahn</groupId> <artifactId>springboot-thymeleaf-i18n</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>springboot-thymeleaf-i18n</name> <description>Example on how to implement i18n with Spring Boot and Thymeleaf</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.codehaus.gmavenplus</groupId> <artifactId>gmavenplus-plugin</artifactId> <version>1.5</version> <executions> <execution> <goals> <goal>addSources</goal> <goal>addTestSources</goal> <goal>generateStubs</goal> <goal>compile</goal> <goal>testGenerateStubs</goal> <goal>testCompile</goal> <goal>removeStubs</goal> <goal>removeTestStubs</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
Application.groovy
package com.marcelustrojahn.boot import org.springframework.boot.SpringApplication import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.context.annotation.ComponentScan @SpringBootApplication @ComponentScan(basePackages = ["com.marcelustrojahn.controllers"]) class Application { static void main(String[] args) { SpringApplication.run(Application, args) } }
Como você pode ver, meu Application.groovy é basicamente o padrão de inicialização de uma aplicação Spring. Apenas configurei o @ComponentScan para buscar no meu pacote com o HelloWorldController.groovy mas poderia ter facilmente deixado os 2 arquivos no mesmo pacote para evitar esta configuração. Mas vamos fazer as coisas organizadamente, certo? 🙂
Meu HelloWorldController.groovy é muito simples, apenas direciona a URL /helloworld para sua respectiva view.
package com.marcelustrojahn.controllers import org.springframework.stereotype.Controller import org.springframework.web.bind.annotation.GetMapping @Controller class HelloWorldController { @GetMapping("/helloworld") helloWorld() { "helloworld" } }
E aqui está view:
<!DOCTYPE html> <html th:lang="${#locale.language}" xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/xhtml"> <head> <title>i18n Example!</title> <meta charset="utf-8"/> </head> <body> <div th:text="#{helloworld}"></div> </body> </html>
Aqui cabe uma breve explicação. Na tag <html> eu inseri a seguinte instrução do Thymeleaf: th:lang=”${#locale.language}”. Isto fará com que o Thymeleaf, na hora da renderização, insira corretamente a linguagem que foi detectada e assim você evita que seu navegador cause algum problema inesperado com a exibição da página.
Mais abaixo você verá a o #{helloworld}. As tags #{} do Thymeleaf são referentes a mensagens que ele vai buscar nos arquivos de i18n. Você não precisa se preocupar em configurar nada disto pois o Spring Boot já fará isto por você.
message.properties
helloworld=Hello World!
O message.properties é o arquivo que terá as mensagens para o idioma padrão do seu site. Qualquer outro idioma deve ter seu próprio arquivo com o nome devidamente formatado.
message_pt_BR.properties
helloworld=Olá Mundo!
Se você usa uma IDE como o Intellij IDEA, lidar com estes arquivos é muito fácil. A IDE entende este tipo de arquivo e os agrupa para edição em lote.
Para que suas mensagens sejam encontradas, configure seu application.yml.
spring: messages: basename: i18n/messages
Com tudo isto feito, basta rodar seu projeto. No meu caso, meu sistema operacional está em Inglês e tenho Português como linguagem adicional no Firefox. Por padrão, quando eu acessar a aplicação ela me exibirá o texto em Inglês, o código resultante será este:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="en"> <head> <title>i18n Example!</title> <meta charset="utf-8" /> </head> <body> <div>Hello World!</div> </body> </html>
Mas se eu mudar a linguagem default do meu navegador, um simples refresh já altera a linguagem da aplicação e o resultado é este:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="pt"> <head> <title>i18n Example!</title> <meta charset="utf-8" /> </head> <body> <div>Olá Mundo!</div> </body> </html>
Para alterar a linguagem no Firefox entre em Opções / Conteúdo e clique em Idiomas:
Espero ter ajudado! Se precisar, deixe seu comentário abaixo e até a próxima!