웹사이트 검색

Spring MVC 국제화(i18n) 및 지역화(L10n) 예제


Spring Internationalization(i18n) 튜토리얼에 오신 것을 환영합니다. 전 세계 사용자가 있는 모든 웹 애플리케이션, 국제화(i18n) 또는 현지화(L10n)는 더 나은 사용자 상호 작용을 위해 매우 중요합니다. 대부분의 웹 애플리케이션 프레임워크는 사용자 로케일 설정을 기반으로 애플리케이션을 지역화하는 쉬운 방법을 제공합니다. Spring은 또한 패턴을 따르고 다양한 로케일에 대한 Spring 인터셉터, Locale Resolver 및 리소스 번들을 사용하여 국제화(i18n)에 대한 광범위한 지원을 제공합니다. Java의 i18n에 대한 이전 기사.

  • Java 국제화 예
  • Struts2 국제화 예

스프링 국제화 i18n

Spring i18n Maven 구성

Spring MVC pom.xml은 아래와 같습니다.

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

최신 버전을 4.0.2.RELEASE로 사용하도록 Spring 버전을 업데이트한 것을 제외하면 대부분의 코드는 STS에서 자동으로 생성됩니다. 종속성을 제거하거나 다른 종속성의 버전도 업데이트할 수 있지만 단순성을 위해 그대로 두었습니다.

스프링 리소스 번들

간단히 하기 위해 애플리케이션이 en과 fr의 두 가지 로케일만 지원한다고 가정해 보겠습니다. 사용자 로케일을 지정하지 않으면 영어를 기본 로케일로 사용합니다. JSP 페이지에서 사용될 이 두 로케일에 대한 스프링 리소스 번들을 생성해 보겠습니다. messages_en.properties 코드:

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

messages_fr.properties 코드:

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

프랑스어 로캘 리소스 번들의 특수 문자에 대해 유니코드를 사용하고 있으므로 클라이언트 요청에 보낸 응답 HTML에서 올바르게 해석됩니다. 주목해야 할 또 다른 중요한 점은 두 리소스 번들이 애플리케이션의 클래스 경로에 있고 이름에 "messages_{locale}.properties\라는 패턴이 있다는 것입니다. 나중에 이것이 왜 중요한지 살펴보겠습니다.

Spring i18n 컨트롤러 클래스

컨트롤러 클래스는 매우 간단합니다. 사용자 로케일을 기록하고 home.jsp 페이지를 응답으로 반환합니다.

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";
	}
	
}

스프링 i18n JSP 페이지

home.jsp 페이지 코드는 아래와 같습니다.

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

언급할 가치가 있는 유일한 부분은 spring:message를 사용하여 주어진 코드로 메시지를 검색하는 것입니다. Spring 태그 라이브러리가 taglib jsp 지시문을 사용하여 구성되었는지 확인하십시오. Spring은 적절한 리소스 번들 메시지를 로드하고 JSP 페이지에서 사용할 수 있도록 합니다.

Spring 국제화 i18n - Bean 구성 파일

Spring Bean 구성 파일은 모든 마법이 일어나는 곳입니다. 이것은 사소한 작업을 코딩하는 것보다 비즈니스 로직에 더 집중할 수 있도록 도와주는 Spring 프레임워크의 아름다움입니다. 스프링 빈 구성 파일이 어떻게 보이는지 살펴보고 각 빈을 하나씩 살펴보겠습니다. 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>
  1. annotation-driven tag enables the Controller programming model, without it Spring won’t recognize our HomeController as handler for client requests.

  2. context:component-scan provides the package where Spring will look for the annotated components and register them automatically as Spring bean.

  3. messageSource bean is configured to enable i18n for our application. basename property is used to provide the location of resource bundles. classpath:messages means that resource bundles are located in the classpath and follows name pattern as messages_{locale}.properties. defaultEncoding property is used to define the encoding used for the messages.

  4. localeResolver bean of type org.springframework.web.servlet.i18n.CookieLocaleResolver is used to set a cookie in the client request so that further requests can easily recognize the user locale. For example, we can ask user to select the locale when he launches the web application for the first time and with the use of cookie, we can identify the user locale and automatically send locale specific response. We can also specify the default locale, cookie name and maximum age of the cookie before it gets expired and deleted by the client browser. If your application maintains user sessions, then you can also use org.springframework.web.servlet.i18n.SessionLocaleResolver as localeResolver to use a locale attribute in the user’s session. The configuration is similar to CookieLocaleResolver.

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

    If we don’t register any “localeResolver”, AcceptHeaderLocaleResolver will be used by default, which resolves user locale by checking the accept-language header in the client HTTP request.

  5. org.springframework.web.servlet.i18n.LocaleChangeInterceptor interceptor is configured to intercept the user request and identify the user locale. The parameter name is configurable and we are using request parameter name for locale as “locale”. Without this interceptor, we won’t be able to change the user locale and send the response based on the new locale settings of the user. It needs to be part of interceptors element otherwise Spring won’t configure it as an interceptor.

컨텍스트 구성을 로드하도록 Spring 프레임워크에 지시하는 구성이 궁금하다면 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>
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.

여기까지가 Spring i18n 예제 애플리케이션의 전부입니다. 아래 링크에서 예제 프로젝트를 다운로드하고 이를 가지고 놀면서 자세히 알아보세요.

Spring i18n 프로젝트 다운로드