Bem-vindo ao tutorial de Internacionalização (i18n) da Primavera. Qualquer aplicação web com usuários ao redor do mundo, a internacionalização (i18n) ou localização (L10n) é muito importante para uma melhor interação do usuário. A maioria dos frameworks de aplicação web fornece maneiras fáceis de localizar a aplicação com base nas configurações de localidade do usuário. A Primavera também segue esse padrão e oferece suporte extensivo para internacionalização (i18n) por meio do uso de interceptadores da Primavera, resolutores de localidade e pacotes de recursos para diferentes localidades. Alguns artigos anteriores sobre i18n em Java.
Internacionalização i18n na Primavera
Vamos criar um projeto simples do Spring MVC onde vamos usar o parâmetro de solicitação para obter a localidade do usuário e, com base nisso, definir os valores de rótulo da página de resposta a partir dos pacotes de recursos específicos da localidade. Crie um projeto do Spring MVC no Spring Tool Suite para ter o código base para nossa aplicação. Se você não estiver familiarizado com o Spring Tool Suite ou projetos do Spring MVC, por favor, leia Exemplo do Spring MVC. Nosso projeto final com as alterações de localização se parece com a imagem abaixo. Vamos analisar todas as partes da aplicação uma por uma.
Configuração do Maven do Spring i18n
O nosso pom.xml do Spring MVC se parece com o seguinte.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev</groupId>
<artifactId>spring</artifactId>
<name>Springi18nExample</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>1.6</java-version>
<org.springframework-version>4.0.2.RELEASE</org.springframework-version>
<org.aspectj-version>1.7.4</org.aspectj-version>
<org.slf4j-version>1.7.5</org.slf4j-version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
A maior parte do código é gerada automaticamente pelo STS, exceto que atualizei a versão do Spring para usar a mais recente, que é a 4.0.2.RELEASE. Podemos remover dependências ou atualizar as versões de outras dependências também, mas deixei como está para simplificar.
Pacote de Recursos do Spring
Para simplificar, vamos supor que nossa aplicação suporta apenas duas localidades – inglês e francês. Se nenhuma localidade do usuário for especificada, usaremos inglês como localidade padrão. Vamos criar pacotes de recursos Spring para ambas essas localidades que serão usados na página JSP. Código messages_en.properties:
label.title=Login Page
label.firstName=First Name
label.lastName=Last Name
label.submit=Login
Código messages_fr.properties:
label.title=Connectez-vous page
label.firstName=Pr\u00E9nom
label.lastName=Nom
label.submit=Connexion
Note que estou usando unicode para caracteres especiais nos pacotes de recursos da localidade francesa, para que seja interpretado corretamente no HTML de resposta enviado às solicitações do cliente. Outro ponto importante a notar é que ambos os pacotes de recursos estão no classpath da aplicação e seus nomes seguem o padrão “messages_{localidade}.properties”. Veremos por que esses são importantes mais tarde.
Classe Controladora Spring i18n
Nossa classe controladora é muito simples, ela apenas registra a localidade do usuário e retorna a página home.jsp como resposta.
package com.journaldev.spring;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
return "home";
}
}
Página JSP Spring i18n
O código de nossa página home.jsp é como abaixo.
<%@taglib uri="https://www.springframework.org/tags" prefix="spring"%>
<%@ page session="false"%>
<html>
<head>
<title><spring:message code="label.title" /></title>
</head>
<body>
<form method="post" action="login">
<table>
<tr>
<td><label> <strong><spring:message
code="label.firstName" /></strong>
</label></td>
<td><input name="firstName" /></td>
</tr>
<tr>
<td><label> <strong><spring:message
code="label.lastName" /></strong>
</label></td>
<td><input name="lastName" /></td>
</tr>
<tr>
<spring:message code="label.submit" var="labelSubmit"></spring:message>
<td colspan="2"><input type="submit" value="${labelSubmit}" /></td>
</tr>
</table>
</form>
</body>
</html>
A única parte que vale a pena mencionar é o uso de `spring:message` para recuperar a mensagem com o código fornecido. Certifique-se de que as bibliotecas de tags do Spring estão configuradas usando `taglib jsp directive`. O Spring cuida de carregar os pacotes de mensagens de recursos apropriados e torná-los disponíveis para as páginas JSP usarem. `
Internacionalização i18n do Spring – Arquivo de Configuração de Beans
O arquivo de configuração de beans do Spring é o lugar onde toda a mágica acontece. Esta é a beleza do framework Spring, pois nos ajuda a nos concentrar mais na lógica de negócios do que na codificação de tarefas triviais. Vamos ver como nosso arquivo de configuração de bean do Spring se parece e vamos analisar cada um dos beans um por um. Código servlet-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="https://www.springframework.org/schema/mvc"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:beans="https://www.springframework.org/schema/beans"
xmlns:context="https://www.springframework.org/schema/context"
xsi:schemaLocation="https://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing
infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving
up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources
in the /WEB-INF/views directory -->
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<beans:bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basename" value="classpath:messages" />
<beans:property name="defaultEncoding" value="UTF-8" />
</beans:bean>
<beans:bean id="localeResolver"
class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<beans:property name="defaultLocale" value="en" />
<beans:property name="cookieName" value="myAppLocaleCookie"></beans:property>
<beans:property name="cookieMaxAge" value="3600"></beans:property>
</beans:bean>
<interceptors>
<beans:bean
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<beans:property name="paramName" value="locale" />
</beans:bean>
</interceptors>
<context:component-scan base-package="com.journaldev.spring" />
</beans:beans>
-
annotation-driven tag permite o modelo de programação do Controller, sem ele o Spring não reconhecerá nosso HomeController como manipulador de solicitações de cliente.
-
context:component-scan fornece o pacote onde o Spring procurará os componentes anotados e os registrará automaticamente como bean do Spring.
-
messageSource bean está configurado para habilitar a internacionalização em nossa aplicação. A propriedade basename é usada para fornecer o local dos pacotes de recursos.
classpath:messages
significa que os pacotes de recursos estão localizados no classpath e seguem o padrão de nome comomessages_{locale}.properties
. A propriedade defaultEncoding é usada para definir a codificação usada para as mensagens. -
localeResolver bean do tipo
org.springframework.web.servlet.i18n.CookieLocaleResolver
é usado para definir um cookie na solicitação do cliente, facilitando o reconhecimento do local do usuário em futuras solicitações. Por exemplo, podemos solicitar ao usuário que selecione o local ao iniciar a aplicação web pela primeira vez e, com o uso do cookie, podemos identificar o local do usuário e enviar automaticamente uma resposta específica para esse local. Também podemos especificar o local padrão, o nome do cookie e a idade máxima do cookie antes que seja expirado e excluído pelo navegador do cliente. Se sua aplicação mantiver sessões de usuário, você também pode usarorg.springframework.web.servlet.i18n.SessionLocaleResolver
como localeResolver para usar um atributo de local na sessão do usuário. A configuração é semelhante à do CookieLocaleResolver.<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"> <property name="defaultLocale" value="en" /> </bean>
Se não registrarmos nenhum “localeResolver”, AcceptHeaderLocaleResolver será usado por padrão, o que resolve o local do usuário verificando o cabeçalho
accept-language
na solicitação HTTP do cliente. -
O interceptor
org.springframework.web.servlet.i18n.LocaleChangeInterceptor
é configurado para interceptar a solicitação do usuário e identificar a localidade do usuário. O nome do parâmetro é configurável e estamos utilizando o nome do parâmetro da solicitação como “locale”. Sem este interceptor, não seremos capazes de alterar a localidade do usuário e enviar a resposta com base nas novas configurações de localidade do usuário. Ele precisa fazer parte do elemento interceptadores caso contrário, o Spring não o configurará como um interceptor.
Se você estiver se perguntando sobre a configuração que diz ao framework Spring para carregar nossas configurações de contexto, ela está presente no descritor de implantação de nossa aplicação MVC.
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Podemos alterar a localização ou o nome do arquivo de contexto alterando a configuração do web.xml. Nossa aplicação Spring i18n está pronta, basta implantá-la em qualquer contêiner de servlet. Normalmente, eu a exporto como arquivo WAR no diretório webapps de um servidor web Tomcat independente. Aqui estão as capturas de tela da página inicial de nossa aplicação com diferentes locais. Página Inicial Padrão (localização em inglês): Passando Localização como parâmetro (localização em francês):
Outras solicitações sem localização:
Como você pode ver na imagem acima, não estamos passando informações de localização na solicitação do cliente, mas ainda assim nossa aplicação identifica o local do usuário. Você já deve ter deduzido que isso ocorre por causa do bean CookieLocaleResolver que configuramos em nosso arquivo de configuração de beans do Spring. No entanto, você pode verificar os dados dos cookies do seu navegador para confirmar isso. Estou usando o Chrome e a imagem abaixo mostra os dados do cookie armazenados pela aplicação.
Observe que o tempo de expiração do cookie é de uma hora, ou seja, 3600 segundos, conforme configurado pela propriedade cookieMaxAge. Se você verificar os logs do servidor, verá que o local está sendo registrado.
INFO : com.journaldev.spring.HomeController - Welcome home! The client locale is en.
INFO : com.journaldev.spring.HomeController - Welcome home! The client locale is fr.
INFO : com.journaldev.spring.HomeController - Welcome home! The client locale is fr.
Aqui está tudo para o exemplo de aplicação Spring i18n. Faça o download do projeto de exemplo a partir do link abaixo e explore-o para aprender mais.