Welkom bij de Spring Internationalization (i18n) tutorial. Elke webapplicatie met gebruikers over de hele wereld vindt internationalisatie (i18n) of lokalisatie (L10n) zeer belangrijk voor een betere gebruikersinteractie. De meeste webapplicatie-frameworks bieden eenvoudige manieren om de applicatie te lokaliseren op basis van de locale instellingen van de gebruiker. Spring volgt ook dit patroon en biedt uitgebreide ondersteuning voor internationalisatie (i18n) door het gebruik van Spring-interceptors, Locale Resolvers en Resource Bundles voor verschillende locales. Enkele eerdere artikelen over i18n in Java.
Spring Internationalization i18n
Laten we een eenvoudig Spring MVC-project maken waarbij we een verzoekparameter gebruiken om de gebruikerslocale te verkrijgen en op basis daarvan de labelwaarden van het responspagina in te stellen vanuit taal-specifieke resource-bundels. Maak een Spring MVC-project in de Spring Tool Suite om de basiscode voor onze toepassing te hebben. Als je niet bekend bent met Spring Tool Suite of Spring MVC-projecten, lees dan Spring MVC Voorbeeld. Ons uiteindelijke project met lokalisatiewijzigingen ziet eruit zoals de onderstaande afbeelding. We zullen elk onderdeel van de toepassing stap voor stap bekijken.
Spring i18n Maven-configuratie
Onze Spring MVC pom.xml ziet er als volgt uit.
<?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>
Het meeste van de code is automatisch gegenereerd door STS, behalve dat ik de Spring-versie heb bijgewerkt naar de nieuwste, namelijk 4.0.2.RELEASE. We kunnen afhankelijkheden verwijderen of de versies van andere afhankelijkheden ook bijwerken, maar ik heb ze zoals ze zijn gelaten voor eenvoud.
Spring Resource Bundle
Voor eenvoud, laten we aannemen dat onze applicatie slechts twee taalinstellingen ondersteunt – Engels en Frans. Als er geen gebruikerstaal is gespecificeerd, zullen we Engels gebruiken als de standaardtaal. Laten we Spring-bronbundels maken voor beide talen die zullen worden gebruikt in de JSP-pagina. Code voor messages_en.properties:
label.title=Login Page
label.firstName=First Name
label.lastName=Last Name
label.submit=Login
Code voor messages_fr.properties:
label.title=Connectez-vous page
label.firstName=Pr\u00E9nom
label.lastName=Nom
label.submit=Connexion
Merk op dat ik unicode gebruik voor speciale tekens in de Franse taalbronbundels, zodat deze correct worden geïnterpreteerd in de HTML-respons die naar clientverzoeken wordt gestuurd. Een ander belangrijk punt om op te merken is dat beide bronbundels zich in het classpath van de applicatie bevinden en dat hun naam het patroon “messages_{locale}.properties” volgt. We zullen later zien waarom dit belangrijk is.
Spring i18n Controller Class
Onze controllerklasse is erg eenvoudig, deze logt alleen de gebruikerstaal en geeft de home.jsp-pagina als antwoord terug.
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";
}
}
Spring i18n JSP-pagina
Onze home.jsp-paginacode ziet er als volgt uit.
<%@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>
Het enige vermeldenswaardige deel is het gebruik van spring:message om het bericht met de opgegeven code op te halen. Zorg ervoor dat de Spring-tagbibliotheken zijn geconfigureerd met behulp van taglib jsp directive. Spring zorgt voor het laden van de juiste resource bundelberichten en maakt deze beschikbaar voor de JSP-pagina’s om te gebruiken.
Spring Internationalization i18n – Bean Configuration File
Het Spring Bean-configuratiebestand is de plek waar al het magische gebeurt. Dit is de kracht van het Spring-framework, omdat het ons helpt ons meer te richten op de bedrijfslogica in plaats van te coderen voor triviale taken. Laten we kijken naar hoe ons Spring Bean-configuratiebestand eruitziet, en we zullen elk van de bonen één voor één bekijken. servlet-context.xml code:
<?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 maakt de Controller-programmeermodel mogelijk. Zonder deze herkent Spring onze HomeController niet als handler voor clientverzoeken.
-
context:component-scan geeft de package op waar Spring de geannoteerde componenten zal zoeken en ze automatisch zal registreren als Spring-bean.
-
messageSource bean is geconfigureerd om i18n mogelijk te maken voor onze applicatie. basename eigenschap wordt gebruikt om de locatie van resource bundles op te geven.
classpath:messages
betekent dat resource bundles zich bevinden in de classpath en het naampatroon volgen zoalsmessages_{locale}.properties
. defaultEncoding eigenschap wordt gebruikt om de codering te definiëren die wordt gebruikt voor de berichten. -
localeResolver bean van het type
org.springframework.web.servlet.i18n.CookieLocaleResolver
wordt gebruikt om een cookie in het clientverzoek in te stellen, zodat verdere verzoeken gemakkelijk de gebruikerslocatie kunnen herkennen. Bijvoorbeeld, we kunnen de gebruiker vragen om de locatie te selecteren wanneer hij de webtoepassing voor de eerste keer start en met behulp van een cookie kunnen we de gebruikerslocatie identificeren en automatisch een locatiespecifieke reactie verzenden. We kunnen ook de standaardlocatie, cookienaam en maximale leeftijd van de cookie specificeren voordat deze door de clientbrowser verloopt en wordt verwijderd. Als uw toepassing gebruikerssessies bijhoudt, kunt u ookorg.springframework.web.servlet.i18n.SessionLocaleResolver
als localeResolver gebruiken om een locatieattribuut in de gebruikerssessie te gebruiken. De configuratie is vergelijkbaar met CookieLocaleResolver.<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"> <property name="defaultLocale" value="en" /> </bean>
Als we geen “localeResolver” registreren, zal standaard AcceptHeaderLocaleResolver worden gebruikt, die de gebruikerslocatie oplost door de
accept-language
-header in het client-HTTP-verzoek te controleren. -
org.springframework.web.servlet.i18n.LocaleChangeInterceptor
-interceptor is geconfigureerd om het gebruikersverzoek te onderscheppen en de gebruikerslocatie te identificeren. De parameter naam is configureerbaar en we gebruiken de verzoekparameter naam voor de locatie als “locale”. Zonder deze interceptor kunnen we de gebruikerslocatie niet wijzigen en geen reactie verzenden op basis van de nieuwe locatie-instellingen van de gebruiker. Het moet onderdeel zijn van het interceptors-element anders configureert Spring het niet als een interceptor.
Als je je afvraagt over de configuratie die het Spring framework vertelt om onze contextconfiguraties te laden, deze is aanwezig in het deployment descriptor van onze MVC applicatie.
<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>
We kunnen de locatie of naam van het contextbestand wijzigen door de configuratie van web.xml aan te passen. Onze Spring i18n-toepassing is klaar, je hoeft het alleen maar te implementeren in een willekeurige servlet-container. Meestal exporteer ik het als een WAR-bestand in de webapps-map van een zelfstandige Tomcat-webserver. Hier zijn de schermafbeeldingen van onze startpagina van de applicatie met verschillende talen. Standaard startpagina (Engelse locatie): Doorgeven van locatie als parameter (Franse locatie):
Verdere verzoeken zonder locatie:
Zoals je kunt zien in de bovenstaande afbeelding, geven we geen locatie-informatie door in het clientverzoek, maar onze applicatie identificeert nog steeds de locatie van de gebruiker. Je hebt waarschijnlijk al geraden dat dit komt door de CookieLocaleResolver bean die we hebben geconfigureerd in ons Spring bean configuratiebestand. Je kunt echter je browsercookiegegevens controleren om dit te bevestigen. Ik gebruik Chrome en de onderstaande afbeelding toont de cookiegegevens die zijn opgeslagen door de applicatie.
Let op dat de vervaltijd van de cookie één uur is, dat wil zeggen 3600 seconden, zoals geconfigureerd door de cookieMaxAge-eigenschap. Als je de serverlogs controleert, kun je zien dat de locatie wordt gelogd.
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.
Dat is alles voor het voorbeeld van de Spring i18n-toepassing, download het voorbeeldproject vanaf onderstaande link en speel ermee om meer te leren.