Создание веб-приложения с несколькими языками с помощью Spring Boot
1. Цель статьи
Иногда вам нужно создать многоязычный вебсайт, многоязычный вебсайт помогает вам иметь доступ к большему количеству пользователей. Многоязычный вебсайт так же называется Internationalization (i18n) (Интернационализированный), напротив Localization (L10n) (Локализированный).
Примечание: Internationalization это слово с 18 символами, первый символ это i и последний символ это n, поэтому оно обычно пишется аббревиатурой i18n.
Spring предоставляет поддержку расширения для интернационализации (Internationalization) (i18n) через использование Spring Interceptor, Locale Resolvers и Resource Bundles для разных регионов.
В данной статье я покажу вам как создать простой многоязычный вебсайт, используя Spring Boot.
Вы можете предварительно посмотреть пример ниже:
В данном примере, локальная информация (Locale) находится в параметре URL. Информация Locale сохранится в Cookie, и в следующих страницах пользователю не понадобится еще раз выбирать язык.
- http://localhost:8080/SomeContextPath/login1?lang=vi
- http://localhost:8080/SomeContextPath/login1?lang=fr
Другой пример с информацией Locale на URL:
- http://localhost:8080/SomeContextPath/vi/login2
- http://localhost:8080/SomeContextPath/en/login2
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. Interceptor & LocaleResolver
Вам нужно объявить 2 Spring BEAN это localeResolver и messageResource.
localeResolver - Определяет способ получения информации Locale, которым будет пользователься пользователь. CookieLocaleResolver возьмет информацию Locale из Cookie, чтобы узнать на каком языке раньше пользователь смотрел вебсайт.
messageResource - Загружает содержание файлов properties.
localeResolver - Определяет способ получения информации Locale, которым будет пользователься пользователь. CookieLocaleResolver возьмет информацию Locale из Cookie, чтобы узнать на каком языке раньше пользователь смотрел вебсайт.
messageResource - Загружает содержание файлов properties.
Перед тем как запрос обрабатывается Controller-ом, он должен пройти через Interceptors, здесь вам нужно зарегистрировать LocaleChangeInterceptor, Interceptor обрабатывает изменения Locale со стороны пользователя.
WebMvcConfig.java
package org.o7planning.sbi18n.config;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Bean(name = "localeResolver")
public LocaleResolver getLocaleResolver() {
CookieLocaleResolver resolver= new CookieLocaleResolver();
resolver.setCookieDomain("myAppLocaleCookie");
// 60 minutes
resolver.setCookieMaxAge(60*60);
return resolver;
}
@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;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
localeInterceptor.setParamName("lang");
registry.addInterceptor(localeInterceptor).addPathPatterns("/*");
}
}
5. Controller & Views
MainController.java (Locale on Parameter)
package org.o7planning.sbi18n.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 staticResource(Model model) {
return "login1";
}
}
login1.html (Thymeleaf View)
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:utext="#{label.title}"></title>
</head>
<body>
<div style="text-align: right;padding:5px;margin:5px 0px;background:#ccc;">
<a th:href="@{/login1?lang=en}">Login (English)</a>
|
<a th:href="@{/login1?lang=fr}">Login (French)</a>
|
<a th:href="@{/login1?lang=vi}">Login (Vietnamese)</a>
</div>
<form method="post" action="">
<table>
<tr>
<td>
<strong th:utext="#{label.userName}"></strong>
</td>
<td><input name="userName" /></td>
</tr>
<tr>
<td>
<strong th:utext="#{label.password}"></strong>
</td>
<td><input name="password" /></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" th:value="#{label.submit}" />
</td>
</tr>
</table>
</form>
</body>
</html>
В случае если вы используете технологию JSP на уровне View.Смотрите так же:src/main/webapp/WEB-INF/jsp/login1.jsp (JSP View)<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%> <%@ page contentType="text/html; charset=UTF-8" %> <%@ 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>
6. Информация Locale на URL
В случае если вы хотите создать многоязычный вебсайт, у которого информация Locale находится на URL. Вам нужно изменить некоторые конфигурации:
- http://localhost:8080/SomeContextPath/vi/login2
- http://localhost:8080/SomeContextPath/en/login2
Создать 2 класса UrlLocaleInterceptor и UrlLocaleResolver.
UrlLocaleInterceptor.java
package org.o7planning.sbi18n.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.sbi18n.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) {
// ==> /SomeContextPath/en/...
// ==> /SomeContextPath/fr/...
// ==> /SomeContextPath/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
}
}
Изменить кофигурацию Interceptor в WebMvcConfig:
WebMvcConfig.java
package org.o7planning.sbi18n.config;
import org.o7planning.sbi18n.interceptor.UrlLocaleInterceptor;
import org.o7planning.sbi18n.resolver.UrlLocaleResolver;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@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:
// /SomeContextPath/en/login2
// /SomeContextPath/vi/login2
// /SomeContextPath/fr/login2
@Bean(name = "localeResolver")
public LocaleResolver getLocaleResolver() {
LocaleResolver resolver = new UrlLocaleResolver();
return resolver;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
UrlLocaleInterceptor localeInterceptor = new UrlLocaleInterceptor();
registry.addInterceptor(localeInterceptor).addPathPatterns("/en/*", "/fr/*", "/vi/*");
}
}
Controller:
MainController2.java (Locale on URL)
package org.o7planning.sbi18n.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MainController2 {
@RequestMapping(value = "/{locale:en|fr|vi}/login2")
public String login2(Model model) {
return "login2";
}
}
index2.html (Thymeleaf View)
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:utext="#{label.title}"></title>
</head>
<body>
<div style="text-align: right;padding:5px;margin:5px 0px;background:#ccc;">
<a th:href="@{/en/login2}">Login (English)</a>
|
<a th:href="@{/fr/login2}">Login (French)</a>
|
<a th:href="@{/vi/login2}">Login (Vietnamese)</a>
</div>
<form method="post" action="">
<table>
<tr>
<td>
<strong th:utext="#{label.userName}"></strong>
</td>
<td><input name="userName" /></td>
</tr>
<tr>
<td>
<strong th:utext="#{label.password}"></strong>
</td>
<td><input name="password" /></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" th:value="#{label.submit}" />
</td>
</tr>
</table>
</form>
</body>
</html>
login2.jsp (JSP View)<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%> <%@ page contentType="text/html; charset=UTF-8" %> <%@ 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>
Запуск приложения:
7. Многоязычный вебсайт с содержанием сохраненным в базе данных
Возможно вы не довольны с примером про многоязычный вебсайт выше. Вы хотите иметь вебсайт новостей на разных языках, и содержание будет храниться в Базе данных. Вы можете использовать такое решение, как использование разных Datasource. И каждый datasource это база данных, хранящая содержание определенного языка.
Смотрите так же:
- TODO
Руководства Spring Boot
- Установите Spring Tool Suite для Eclipse
- Руководство Spring для начинающих
- Руководство Spring Boot для начинающих
- Общие свойства Spring Boot
- Руководство Spring Boot и Thymeleaf
- Руководство Spring Boot и FreeMarker
- Руководство Spring Boot и Groovy
- Руководство Spring Boot и Mustache
- Руководство Spring Boot и JSP
- Руководство Spring Boot, Apache Tiles, JSP
- Используйте Logging в Spring Boot
- Мониторинг приложений с помощью Spring Boot Actuator
- Создание веб-приложения с несколькими языками с помощью Spring Boot
- Используйте несколько ViewResolver в Spring Boot
- Используйте Twitter Bootstrap в Spring Boot
- Руководство Spring Boot Interceptor
- Руководство Spring Boot, Spring JDBC и Spring Transaction
- Руководство Spring JDBC
- Руководство Spring Boot, JPA и Spring Transaction
- Руководство Spring Boot и Spring Data JPA
- Руководство Spring Boot, Hibernate и Spring Transaction
- Интеграция Spring Boot, JPA и H2 Database
- Руководство Spring Boot и MongoDB
- Используйте несколько DataSources с Spring Boot и JPA
- Используйте несколько DataSource с Spring Boot и RoutingDataSource
- Создайте приложение для входа с Spring Boot, Spring Security, Spring JDBC
- Создайте приложение для входа с Spring Boot, Spring Security, JPA
- Создайте приложение регистрации пользователей с помощью Spring Boot, Spring Form Validation
- Пример OAuth2 Social Login в Spring Boot.
- Запускать фоновые запланированные задачи в Spring
- Пример CRUD Restful Web Service c Spring Boot
- Пример Spring Boot Restful Client c RestTemplate
- Пример CRUD с Spring Boot, REST и AngularJS
- Защита Spring Boot RESTful Service используя Basic Authentication
- Защита Spring Boot RESTful Service используя Auth0 JWT
- Пример Upload file c Spring Boot
- Пример Download file c Spring Boot
- Пример Upload file c Spring Boot и jQuery Ajax
- Пример Upload file c Spring Boot и AngularJS
- Создание веб-приложения для корзины покупок с помощью Spring Boot, Hibernate
- Руководство Spring Email
- Создайте простое приложение Chat с Spring Boot и Websocket
- Разверните приложение Spring Boot на Tomcat Server
- Развертывание приложения Spring Boot на Oracle WebLogic Server
- Установите бесплатный сертификат Let's Encrypt SSL для Spring Boot
- Настройте Spring Boot для перенаправления HTTP на HTTPS
Show More