Spring MVC Internationalisierung (i18n) und Lokalisierung (L10n) Beispiel

Willkommen zum Frühling Internationalisierungs (i18n) Tutorial. Jede Webanwendung mit Benutzern aus der ganzen Welt, Internationalisierung (i18n) oder Lokalisierung (L10n) ist sehr wichtig für eine bessere Benutzerinteraktion. Die meisten Webanwendungs-Frameworks bieten einfache Möglichkeiten, die Anwendung basierend auf den Benutzerlokalisierungseinstellungen zu lokalisieren. Spring folgt ebenfalls diesem Muster und bietet umfassende Unterstützung für Internationalisierung (i18n) durch die Verwendung von Spring-Interceptoren, Lokale Auflöser und Ressourcenbündel für verschiedene Lokalisierungen. Einige frühere Artikel über i18n in Java.

Spring-Internationalisierung i18n

Lassen Sie uns ein einfaches Spring MVC-Projekt erstellen, bei dem wir den Anforderungsparameter verwenden, um die Benutzerlokalisierung zu erhalten, und basierend darauf die Antwortseitenbeschriftungswerte aus lokalspezifischen Ressourcenbündeln setzen. Erstellen Sie ein Spring MVC-Projekt in der Spring Tool Suite, um den Basiscode für unsere Anwendung zu haben. Wenn Sie mit der Spring Tool Suite oder Spring MVC-Projekten nicht vertraut sind, lesen Sie bitte Spring MVC-Beispiel. Unser endgültiges Projekt mit Lokalisierungsänderungen sieht wie das folgende Bild aus. Wir werden uns alle Teile der Anwendung nacheinander ansehen.

Spring i18n Maven-Konfiguration

Unser Spring MVC pom.xml sieht wie folgt aus.

<?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>

Der größte Teil des Codes wird automatisch von STS generiert, außer dass ich die Spring-Version auf die neueste Version 4.0.2.RELEASE aktualisiert habe. Wir können Abhängigkeiten entfernen oder die Versionen anderer Abhängigkeiten ebenfalls aktualisieren, aber ich habe sie für die Einfachheit so gelassen.

Spring-Ressourcenbündel

Für Einfachheit nehmen wir an, dass unsere Anwendung nur zwei Sprachumgebungen unterstützt – Englisch und Französisch. Wenn keine Benutzersprachumgebung angegeben ist, verwenden wir Englisch als Standardsprachumgebung. Wir erstellen Spring-Ressourcenbündel für beide Sprachumgebungen, die auf der JSP-Seite verwendet werden. Der Code für messages_en.properties lautet:

label.title=Login Page
label.firstName=First Name
label.lastName=Last Name
label.submit=Login

Der Code für messages_fr.properties lautet:

label.title=Connectez-vous page
label.firstName=Pr\u00E9nom
label.lastName=Nom
label.submit=Connexion

Beachten Sie, dass ich Unicode für Sonderzeichen in den Ressourcenbündeln der französischen Sprachumgebung verwende, damit es ordnungsgemäß in der Antwort-HTML interpretiert wird, die an Clientanfragen gesendet wird. Ein weiterer wichtiger Punkt ist, dass sich beide Ressourcenbündel im Klassenpfad der Anwendung befinden und ihr Name das Muster „messages_{locale}.properties“ hat. Wir werden später sehen, warum dies wichtig ist.

Spring i18n Controller Klasse

Unsere Controller-Klasse ist sehr einfach, sie protokolliert nur die Benutzersprachumgebung und gibt die home.jsp-Seite als Antwort zurück.

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 Seite

Unser home.jsp-Seitencode sieht wie folgt aus.

<%@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>

Die einzige erwähnenswerte Komponente ist die Verwendung von spring:message, um die Nachricht mit dem angegebenen Code abzurufen. Stellen Sie sicher, dass die Spring-Tag-Bibliotheken mit der taglib jsp directive konfiguriert sind. Spring kümmert sich um das Laden des entsprechenden Ressourcen-Bundle für Nachrichten und stellt es den JSP-Seiten zur Verfügung.

Spring Internationalization i18n – Bean Configuration File

Die Spring Bean-Konfigurationsdatei ist der Ort, an dem alles passiert. Das ist das Schöne am Spring-Framework, da es uns ermöglicht, uns mehr auf die Geschäftslogik zu konzentrieren, anstatt für triviale Aufgaben zu codieren. Schauen wir uns an, wie unsere Spring Bean-Konfigurationsdatei aussieht, und wir werden uns jeden der Beans einzeln ansehen. Der Code für servlet-context.xml lautet:

<?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>
  1. annotation-driven-Tag aktiviert das Controller-Programmiermodell. Ohne es würde Spring unseren HomeController nicht als Handler für Clientanfragen erkennen.

  2. context:component-scan gibt das Paket an, in dem Spring nach annotierten Komponenten suchen und sie automatisch als Spring Bean registrieren wird.

  3. messageSource Bean ist konfiguriert, um i18n für unsere Anwendung zu aktivieren. Die Eigenschaft basename wird verwendet, um den Speicherort der Ressourcenbündel anzugeben. classpath:messages bedeutet, dass sich die Ressourcenbündel im Klassenpfad befinden und dem Namensmuster messages_{locale}.properties folgen. Die Eigenschaft defaultEncoding wird verwendet, um die Codierung für die Nachrichten festzulegen.

  4. localeResolver Bean vom Typ org.springframework.web.servlet.i18n.CookieLocaleResolver wird verwendet, um ein Cookie im Client-Anfrage zu setzen, damit weitere Anfragen leicht die Benutzerlokalität erkennen können. Zum Beispiel können wir den Benutzer bitten, die Lokalität auszuwählen, wenn er die Webanwendung zum ersten Mal startet, und mit Hilfe des Cookies können wir die Benutzerlokalität identifizieren und automatisch eine lokalisierte Antwort senden. Wir können auch die Standardlokalität, den Cookienamen und das maximale Alter des Cookies angeben, bevor er vom Client-Browser abläuft und gelöscht wird. Wenn Ihre Anwendung Benutzersitzungen beibehält, können Sie auch org.springframework.web.servlet.i18n.SessionLocaleResolver als localeResolver verwenden, um ein Locale-Attribut in der Benutzersitzung zu verwenden. Die Konfiguration ist ähnlich wie bei CookieLocaleResolver.

    <bean id="localeResolver"
    	class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
    	<property name="defaultLocale" value="en" />
    </bean>
    

    Wenn wir keinen „localeResolver“ registrieren, wird standardmäßig AcceptHeaderLocaleResolver verwendet, das die Benutzerlokalität durch Überprüfen des accept-language-Headers in der Client-HTTP-Anfrage auflöst.

  5. Der org.springframework.web.servlet.i18n.LocaleChangeInterceptor-Interceptor ist so konfiguriert, dass er die Benutzeranfrage abfängt und die Benutzerlokalisierung identifiziert. Der Parametername ist konfigurierbar, und wir verwenden den Anforderungsparametername für die Lokalisierung als „locale“. Ohne diesen Interceptor können wir die Benutzerlokalisierung nicht ändern und die Antwort basierend auf den neuen Lokalisierungseinstellungen des Benutzers senden. Er muss Teil des Interceptors-Elements sein, ansonsten konfiguriert Spring ihn nicht als Interceptor.

Wenn Sie sich über die Konfiguration Gedanken machen, die dem Spring-Framework mitteilt, unsere Kontextkonfigurationen zu laden, finden Sie diese im Bereitstellungsdescriptor unserer MVC-Anwendung.

<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>

Wir können den Speicherort oder den Namen der Kontextdatei ändern, indem wir die web.xml-Konfiguration ändern. Unsere Spring i18n-Anwendung ist bereit, setzen Sie sie einfach in einem Servlet-Container ein. Normalerweise exportiere ich sie als WAR-Datei in das Webapps-Verzeichnis des eigenständigen Tomcat-Webservers. Hier sind Screenshots unserer Anwendungsstartseite mit verschiedenen Sprachumgebungen. Standard-Startseite (en locale): `` Übergabe der Sprachumgebung als Parameter (fr locale): `` Weitere Anfragen ohne Sprachumgebung: `` Wie Sie auf dem obigen Bild sehen können, übermitteln wir keine Sprachinformationen in der Client-Anfrage, aber unsere Anwendung erkennt trotzdem die Benutzersprachumgebung. Sie haben wahrscheinlich bereits erraten, dass dies am CookieLocaleResolver Bean liegt, den wir in unserer Spring Bean-Konfigurationsdatei konfiguriert haben. Sie können jedoch Ihre Browser-Cookie-Daten überprüfen, um dies zu bestätigen. Ich benutze Chrome, und das folgende Bild zeigt die vom Programm gespeicherten Cookie-Daten. `` Beachten Sie, dass die Cookie-Verfallzeit eine Stunde beträgt, d. h. 3600 Sekunden, wie durch die cookieMaxAge-Eigenschaft konfiguriert. Wenn Sie die Serverprotokolle überprüfen, sehen Sie, dass die Sprachumgebung protokolliert wird.

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.

Das ist alles für die Frühjahrs-i18n-Beispielanwendung. Laden Sie das Beispielprojekt über den unten stehenden Link herunter und probieren Sie es aus, um mehr zu erfahren.

Frühjahrs-i18n-Projekt herunterladen

Source:
https://www.digitalocean.com/community/tutorials/spring-mvc-internationalization-i18n-and-localization-l10n-example