Bienvenue dans le tutoriel sur l’internationalisation du printemps (i18n). Pour toute application Web avec des utilisateurs du monde entier, l’internationalisation (i18n) ou la localisation (L10n) est très importante pour une meilleure interaction utilisateur. La plupart des frameworks d’application Web fournissent des moyens faciles pour localiser l’application en fonction des paramètres régionaux de l’utilisateur. Spring suit également ce modèle et fournit un support étendu pour l’internationalisation (i18n) grâce à l’utilisation d’intercepteurs Spring, de résolveurs de localisation et de paquets de ressources pour différentes régions. Voici quelques articles précédents sur l’i18n en Java.
Internationalisation Spring i18n
Créons un projet Spring MVC simple où nous utiliserons le paramètre de requête pour obtenir la locale de l’utilisateur et, en fonction de cela, définir les valeurs d’étiquette de la page de réponse à partir des paquets de ressources spécifiques à la locale. Créez un projet Spring MVC dans Spring Tool Suite pour avoir le code de base de notre application. Si vous n’êtes pas familier avec Spring Tool Suite ou les projets Spring MVC, veuillez lire Exemple Spring MVC. Notre projet final avec les modifications de localisation ressemble à l’image ci-dessous. Nous examinerons toutes les parties de l’application une par une.
Configuration Maven Spring i18n
Notre fichier pom.xml de Spring MVC ressemble à ceci.
<?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>
La plupart du code est généré automatiquement par STS, sauf que j’ai mis à jour la version de Spring pour utiliser la dernière, 4.0.2.RELEASE. Nous pouvons supprimer des dépendances ou mettre à jour les versions d’autres dépendances aussi, mais je les ai laissées telles quelles pour des raisons de simplicité.
Bundle de ressources Spring
Pour simplifier, supposons que notre application ne prend en charge que deux paramètres régionaux – en et fr. Si aucun paramètre régional utilisateur n’est spécifié, nous utiliserons l’anglais comme paramètre régional par défaut. Créons des bundles de ressources Spring pour ces deux paramètres régionaux qui seront utilisés dans la page JSP. Code du fichier messages_en.properties:
label.title=Login Page
label.firstName=First Name
label.lastName=Last Name
label.submit=Login
Code du fichier messages_fr.properties:
label.title=Connectez-vous page
label.firstName=Pr\u00E9nom
label.lastName=Nom
label.submit=Connexion
Notez que j’utilise l’unicode pour les caractères spéciaux dans les bundles de ressources du paramètre régional français, afin qu’il soit interprété correctement dans le HTML de réponse envoyé aux demandes des clients. Un autre point important à noter est que les deux bundles de ressources sont dans le chemin de classe de l’application et leur nom suit le modèle « messages_{locale}.properties ». Nous verrons pourquoi cela est important plus tard.
Classe de contrôleur Spring i18n
Notre classe de contrôleur est très simple, elle se contente de journaliser le paramètre régional utilisateur et de renvoyer la page home.jsp en réponse.
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";
}
}
Page JSP Spring i18n
Le code de notre page home.jsp ressemble à ce qui suit.
<%@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>
La seule partie digne de mention est l’utilisation de spring:message pour récupérer le message avec le code donné. Assurez-vous que les bibliothèques de balises Spring sont configurées à l’aide de la directive jsp taglib. Spring se charge de charger les messages du bundle de ressources approprié et de les rendre disponibles pour que les pages JSP les utilisent.
Internationalisation Spring i18n – Fichier de configuration de bean
Le fichier de configuration de bean Spring est l’endroit où toute la magie opère. C’est la beauté du framework Spring car il nous aide à nous concentrer davantage sur la logique métier plutôt que sur la programmation de tâches triviales. Voyons à quoi ressemble notre fichier de configuration de bean Spring et examinons chacun des beans un par un. Code 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>
-
La balise annotation-driven permet le modèle de programmation du contrôleur, sans quoi Spring ne reconnaîtra pas notre HomeController comme gestionnaire des demandes client.
-
La balise context:component-scan fournit le package où Spring recherchera les composants annotés et les enregistrera automatiquement en tant que bean Spring.
-
messageSource bean est configuré pour activer l’internationalisation pour notre application. La propriété basename est utilisée pour fournir l’emplacement des paquets de ressources.
classpath:messages
signifie que les paquets de ressources sont situés dans le classpath et suivent le modèle de nommagemessages_{locale}.properties
. La propriété defaultEncoding est utilisée pour définir l’encodage utilisé pour les messages. - Le bean localeResolver de type
org.springframework.web.servlet.i18n.CookieLocaleResolver
est utilisé pour définir un cookie dans la requête du client afin que les demandes ultérieures puissent facilement reconnaître la locale de l’utilisateur. Par exemple, nous pouvons demander à l’utilisateur de sélectionner la locale lorsqu’il lance l’application web pour la première fois et, avec l’utilisation du cookie, nous pouvons identifier la locale de l’utilisateur et envoyer automatiquement une réponse spécifique à la locale. Nous pouvons également spécifier la locale par défaut, le nom du cookie et la durée de vie maximale du cookie avant qu’il n’expire et ne soit supprimé par le navigateur du client. Si votre application conserve des sessions utilisateur, vous pouvez également utiliserorg.springframework.web.servlet.i18n.SessionLocaleResolver
comme localeResolver pour utiliser un attribut de locale dans la session de l’utilisateur. La configuration est similaire à celle de CookieLocaleResolver.<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"> <property name="defaultLocale" value="en" /> </bean>
Si nous ne définissons aucun « localeResolver », AcceptHeaderLocaleResolver sera utilisé par défaut, ce qui résout la locale de l’utilisateur en vérifiant l’en-tête
accept-language
dans la requête HTTP du client. -
L’intercepteur
org.springframework.web.servlet.i18n.LocaleChangeInterceptor
est configuré pour intercepter la demande de l’utilisateur et identifier la locale de l’utilisateur. Le nom du paramètre est configurable et nous utilisons le nom du paramètre de requête pour la locale comme « locale ». Sans cet intercepteur, nous ne pourrions pas changer la locale de l’utilisateur et envoyer la réponse en fonction des nouveaux paramètres de locale de l’utilisateur. Il doit faire partie de l’élément interceptors sinon Spring ne le configurera pas en tant qu’intercepteur.
Si vous vous demandez quelle est la configuration qui indique au framework Spring de charger nos configurations de contexte, elle est présente dans le descripteur de déploiement de notre application 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>
Nous pouvons changer l’emplacement ou le nom du fichier de contexte en modifiant la configuration web.xml. Notre application Spring i18n est prête, il suffit de la déployer dans n’importe quel conteneur de servlets. Habituellement, je l’exporte en tant que fichier WAR dans le répertoire webapps d’un serveur web Tomcat autonome. Voici les captures d’écran de la page d’accueil de notre application avec différentes locales. Page d’accueil par défaut (locale en) : Passage de la locale en tant que paramètre (locale fr) :
Autres demandes sans locale :
Comme vous pouvez le voir sur l’image ci-dessus, nous ne transmettons pas d’informations de localisation dans la demande du client, mais notre application identifie quand même la locale de l’utilisateur. Vous devez avoir deviné à présent que c’est grâce au bean CookieLocaleResolver que nous avons configuré dans notre fichier de configuration des beans Spring. Cependant, vous pouvez vérifier les données des cookies de votre navigateur pour le confirmer. J’utilise Chrome et l’image ci-dessous montre les données des cookies stockées par l’application.
Remarquez que le temps d’expiration du cookie est d’une heure, c’est-à-dire 3600 secondes, comme configuré par la propriété cookieMaxAge. Si vous consultez les journaux du serveur, vous verrez que la locale est enregistrée.
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.
Voici tout pour l’application exemple de localisation Spring, téléchargez le projet exemple depuis le lien ci-dessous et amusez-vous à l’explorer pour en apprendre davantage.