betacode

Создание многоязычного веб-приложения с использованием Spring MVC

  1. Цель статьи
  2. Создать Maven Project
  3. Message Resources
  4. Конфигурация Spring MVC
  5. Controller & Views
  6. Конфигурация и запуск приложения
  7. Информация Locale на URL
  8. Многоязычный вебсайт с содержанием в базе данных 

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.
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>
       &nbsp;&nbsp;
       <a href="${pageContext.request.contextPath}/login1?lang=fr">Login (French)</a>
       &nbsp;&nbsp;
       <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. Конфигурация и запуск приложения

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>
       &nbsp;&nbsp;
       <a href="${pageContext.request.contextPath}/fr/login2">Login (French)</a>
       &nbsp;&nbsp;
       <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