Создание многоязычного веб-приложения с использованием Spring MVC
1. Цель статьи
Иногда вам нужно создать многоязычный вебсайт, многоязычный вебсайт помогает вам иметь доступ к большему количетсву пользователей. Многоязычный вебсайт так же называется Internationalization (i18n) (Интернационализированный), напротив Localization (L10n) (Локализированный).
Примечание: Internationalization это слово с 18 символами, первый символ это i и последний символ это n, поэтому он обычно пишется аббревиатурой i18n.
Spring предоставляет поддержку для интернационализации (Internationalization) (i18n) через использование Spring Interceptor, Locale Resolvers и Resource Bundles для разных регионов.
В данной статье я покажу вам как создать простой многоязычный вебсайт используя Spring MVC.
Вы можете предварительно посмотреть пример ниже:
В данном примере, локальное расположение (Locale) находится в параметре URL. Информация Locale сохранится в Cookie, и в следующих страницаъ пользователю не понадобится еще раз выбирать язык.
- http://localhost:8080/SpringMVCInternationalization/login1?lang=vi
- http://localhost:8080/SpringMVCInternationalization/login1?lang=fr
Другой пример с информацией Locale на URL:
- http://localhost:8080/SpringMVCInternationalization/vi/login2
- http://localhost:8080/SpringMVCInternationalization/en/login2
2. Создать Maven Project
- File/New/Other..
- Group Id: org.o7planning
- Artifact Id: SpringMVCInternationalization
- Package: org.o7planning.springmvcinternationalization
Project создан:
Удостовеьртесь что ваш project использует Java >= 6.
Project Properties:
3. Message Resources
Здесь я создал 3 файла properties для английского, французского и вьетнамского языков. Эти файлы будут загружены (load), и управляемы с помощью messageResource Bean.
i18n/messages_en.properties
#Generated by Eclipse Messages Editor (Eclipse Babel)
label.password = Password
label.submit = Login
label.title = Login Page
label.userName = User Name
i18n/messages_fr.properties
#Generated by Eclipse Messages Editor (Eclipse Babel)
label.password = Mot de passe
label.submit = Connexion
label.title = Connectez-vous page
label.userName = Nom d'utilisateur
i18n/messages_vi.properties
#Generated by Eclipse Messages Editor (Eclipse Babel)
label.password = M\u1EADt kh\u1EA9u
label.submit = \u0110\u0103ng nh\u1EADp
label.title = Trang \u0111\u0103ng nh\u1EADp
label.userName = T\u00EAn ng\u01B0\u1EDDi d\u00F9ng
Eclipse поддерживает вас в редактировании содержания этих файлов, используя "Message Editor".
4. Конфигурация Spring MVC
Вы создаете многоязычный вебсайт, поэтому вам нужно использовать кодирование UTF-8.
SpringWebAppInitializer.java
package org.o7planning.springmvcinternationalization.config;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.DispatcherServlet;
public class SpringWebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(ApplicationContextConfig.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("SpringDispatcher",
new DispatcherServlet(appContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
// UtF8 Charactor Filter.
FilterRegistration.Dynamic fr = servletContext.addFilter("encodingFilter", CharacterEncodingFilter.class);
fr.setInitParameter("encoding", "UTF-8");
fr.setInitParameter("forceEncoding", "true");
fr.addMappingForUrlPatterns(null, true, "/*");
}
}
Вам нужно объявить 2 Spring BEAN это localeResolver и messageResource.
localeResolver - Определяет способ получения информации Locale, которым будет использовать пользователь. CookieLocaleResolver возьмет информацию Locale из Cookie, чтобы узнать на каком языке раньше пользователь смотрел вебсайт.
messageResource - Загружает содержание файлов properties.
localeResolver - Определяет способ получения информации Locale, которым будет использовать пользователь. CookieLocaleResolver возьмет информацию Locale из Cookie, чтобы узнать на каком языке раньше пользователь смотрел вебсайт.
messageResource - Загружает содержание файлов properties.
ApplicationContextConfig.java
package org.o7planning.springmvcinternationalization.config;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@ComponentScan("org.o7planning.springmvcinternationalization.*")
public class ApplicationContextConfig {
@Bean(name = "viewResolver")
public InternalResourceViewResolver getViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Bean(name = "messageSource")
public MessageSource getMessageResource() {
ReloadableResourceBundleMessageSource messageResource= new ReloadableResourceBundleMessageSource();
// Read i18n/messages_xxx.properties file.
// For example: i18n/messages_en.properties
messageResource.setBasename("classpath:i18n/messages");
messageResource.setDefaultEncoding("UTF-8");
return messageResource;
}
@Bean(name = "localeResolver")
public LocaleResolver getLocaleResolver() {
CookieLocaleResolver resolver= new CookieLocaleResolver();
resolver.setCookieDomain("myAppLocaleCookie");
// 60 minutes
resolver.setCookieMaxAge(60*60);
return resolver;
}
}
Перед тем как запрос обрабатывается Controller-ом, он должен пройти через Interceptors, здесь вам нужно зарегистрировать LocaleChangeInterceptor, Interceptor обрабатывает изменения Locale со стороны пользователя.
WebMvcConfig.java
package org.o7planning.springmvcinternationalization.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
// Static Resource Config
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Default..
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
localeInterceptor.setParamName("lang");
registry.addInterceptor(localeInterceptor).addPathPatterns("/*");
}
}
5. Controller & Views
MainController.java
package org.o7planning.springmvcinternationalization.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MainController {
@RequestMapping(value = "/login1")
public String login1(Model model) {
return "login1";
}
}
login1.jsp
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ page session="false"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><spring:message code="label.title" /></title>
</head>
<body>
<div style="text-align: right;padding:5px;margin:5px 0px;background:#ccc;">
<a href="${pageContext.request.contextPath}/login1?lang=en">Login (English)</a>
<a href="${pageContext.request.contextPath}/login1?lang=fr">Login (French)</a>
<a href="${pageContext.request.contextPath}/login1?lang=vi">Login (Vietnamese)</a>
</div>
<form method="post" action="">
<table>
<tr>
<td>
<strong>
<spring:message code="label.userName" />
</strong>
</td>
<td><input name="userName" /></td>
</tr>
<tr>
<td>
<strong>
<spring:message code="label.password" />
</strong>
</td>
<td><input name="password" /></td>
</tr>
<tr>
<td colspan="2">
<spring:message code="label.submit" var="labelSubmit"></spring:message>
<input type="submit" value="${labelSubmit}" />
</td>
</tr>
</table>
</form>
</body>
</html>
7. Информация Locale на URL
В случае если вы хотите создать многоязычный вебсайт, у которого информация Locale находится на URL. Вам нужно изменить некоторые конфигурации:
- http://localhost:8080/SpringMVCInternationalization/vi/login2
- http://localhost:8080/SpringMVCInternationalization/en/login2
Создать 2 класса UrlLocaleInterceptor и UrlLocaleResolver.
UrlLocaleInterceptor.java
package org.o7planning.springmvcinternationalization.interceptor;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.support.RequestContextUtils;
public class UrlLocaleInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
if (localeResolver == null) {
throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
}
// Get locale from LocaleResolver.
Locale locale = localeResolver.resolveLocale(request);
localeResolver.setLocale(request, response, locale);
return true;
}
}
UrlLocaleResolver.java
package org.o7planning.springmvcinternationalization.resolver;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.LocaleResolver;
public class UrlLocaleResolver implements LocaleResolver {
private static final String URL_LOCALE_ATTRIBUTE_NAME = "URL_LOCALE_ATTRIBUTE_NAME";
@Override
public Locale resolveLocale(HttpServletRequest request) {
// ==> /SpringMVCInternationalization/en/...
// ==> /SpringMVCInternationalization/fr/...
// ==> /SpringMVCInternationalization/WEB-INF/pages/...
String uri = request.getRequestURI();
System.out.println("URI=" + uri);
String prefixEn = request.getServletContext().getContextPath() + "/en/";
String prefixFr = request.getServletContext().getContextPath() + "/fr/";
String prefixVi = request.getServletContext().getContextPath() + "/vi/";
Locale locale = null;
// English
if (uri.startsWith(prefixEn)) {
locale = Locale.ENGLISH;
}
// French
else if (uri.startsWith(prefixFr)) {
locale = Locale.FRANCE;
}
// Vietnamese
else if (uri.startsWith(prefixVi)) {
locale = new Locale("vi", "VN");
}
if (locale != null) {
request.getSession().setAttribute(URL_LOCALE_ATTRIBUTE_NAME, locale);
}
if (locale == null) {
locale = (Locale) request.getSession().getAttribute(URL_LOCALE_ATTRIBUTE_NAME);
if (locale == null) {
locale = Locale.ENGLISH;
}
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
// Nothing
}
}
Изменить класс ApplicationContextConfig:
ApplicationContextConfig.java
package org.o7planning.springmvcinternationalization.config;
import org.o7planning.springmvcinternationalization.resolver.UrlLocaleResolver;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@ComponentScan("org.o7planning.springmvcinternationalization.*")
public class ApplicationContextConfig {
@Bean(name = "viewResolver")
public InternalResourceViewResolver getViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Bean(name = "messageSource")
public MessageSource getMessageResource() {
ReloadableResourceBundleMessageSource messageResource= new ReloadableResourceBundleMessageSource();
// Read i18n/messages_xxx.properties file.
// For example: i18n/messages_en.properties
messageResource.setBasename("classpath:i18n/messages");
messageResource.setDefaultEncoding("UTF-8");
return messageResource;
}
// To solver URL like:
// /SpringMVCInternationalization/en/login2
// /SpringMVCInternationalization/vi/login2
// /SpringMVCInternationalization/fr/login2
@Bean(name = "localeResolver")
public LocaleResolver getLocaleResolver() {
LocaleResolver resolver= new UrlLocaleResolver();
return resolver;
}
}
Изменить кофигурацию Interceptor в WebMvcConfig:
WebMvcConfig.java
package org.o7planning.springmvcinternationalization.config;
import org.o7planning.springmvcinternationalization.interceptor.UrlLocaleInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Default..
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
UrlLocaleInterceptor localeInterceptor = new UrlLocaleInterceptor();
registry.addInterceptor(localeInterceptor).addPathPatterns("/en/*", "/fr/*", "/vi/*");
}
}
Controller:
MainController.java
package org.o7planning.springmvcinternationalization.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MainController {
@RequestMapping(value = "/{locale:en|fr|vi}/login2")
public String login2(Model model) {
return "login2";
}
}
login2.jsp
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ page session="false"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><spring:message code="label.title" /></title>
</head>
<body>
<div style="text-align: right;padding:5px;margin:5px 0px;background:#ccc;">
<a href="${pageContext.request.contextPath}/en/login2">Login (English)</a>
<a href="${pageContext.request.contextPath}/fr/login2">Login (French)</a>
<a href="${pageContext.request.contextPath}/vi/login2">Login (Vietnamese)</a>
</div>
<form method="post" action="">
<table>
<tr>
<td>
<strong>
<spring:message code="label.userName" />
</strong>
</td>
<td><input name="userName" /></td>
</tr>
<tr>
<td>
<strong>
<spring:message code="label.password" />
</strong>
</td>
<td><input name="password" /></td>
</tr>
<tr>
<td colspan="2">
<spring:message code="label.submit" var="labelSubmit"></spring:message>
<input type="submit" value="${labelSubmit}" />
</td>
</tr>
</table>
</form>
</body>
</html>
Запуск приложения:
8. Многоязычный вебсайт с содержанием в базе данных
No ADS
Возможно вы не довольны с примером про многоязычный вебсайт выше. Вы хотите иметь вебсайт новостей на разных языках, и содержание будет храниться в Базе данных. Вы можете использовать такое решение, как использование разныъ Datasource. И каждыйi datasource это база данных, хранящая содержание определенного языка.
Смотрите так же:
No ADS
Руководства Spring MVC
- Руководство Spring для начинающих
- Установите Spring Tool Suite для Eclipse
- Руководство Spring MVC для начинающих
- Настроить статические ресурсы в Spring MVC
- Руководство Spring MVC Interceptor
- Создание многоязычного веб-приложения с использованием Spring MVC
- Руководство Spring MVC File Upload/Download
- Простой веб-приложение Java с использованием Spring MVC, Spring Security и Spring JDBC
- Социальный вход в Spring MVC с Spring Social Security
- Руководство Spring MVC и FreeMarker
- Используйте Template в Spring MVC с Apache Tiles
- Используйте несколько DataSources в Spring MVC
- Руководство Spring MVC Form и Hibernate
- Запускать фоновые запланированные задачи в Spring
- Создание Java Корзина веб-приложения с использованием Spring MVC и Hibernate
- Простой пример CRUD с Spring MVC RESTful Web Service
- Развертывание Spring MVC на сервере Oracle WebLogic Server
Show More