Локализация Java Spring MVC приложения. Настройка поддержки нескольких языков
Локализация и интернационализация (localization & internationalization) в Spring MVC. Настройка приложения, создание и заполнение resource bundle.
Обзор приложения Spring MVC + AngularJS + Bootstrap + HTML5
Используемые технологии и библиотеки
- Spring MVC 4.2.4.Release
- IntelliJ IDEA 15.0.2
1. Описание задачи
Рассмотрим как реализовать интернационализацию и локализацию в приложении Spring MVC. Переведем часть интерфейса и отображаемых данных на два языка — английский и русский. Добавим возможность переключения между языками по одному клику.
На сайте ранее уже была написана статья по локализации приложения — Spring MVC — локализация (выбор языка). В этой статье расписано как создать resource bundle, а так же некоторые понятия и определения. Перед прочтением этой статьи рекомендуется ознакомиться с указанной информацией для получения дополнительной информации.
2. Структура проекта
Относительно предыдущих частей в проект добавлен пакет locales и внутри него resourceBundle. В этих .properties файлах будут записаны пары ключ-значение на разных языках (английский и русский). Остальные изменения внесены в уже существующие представления и будут видны по ходу этой статьи.
3. pom.xml
Для настройки интернационализации никаких дополнительных (относительно предыдущих глав) зависимостей добавлять не нужно. Всё уже находится в базовых пакетах Spring. Полный листинг pom.xml смотрите в предыдущей части или в загруженном проекте.
4. Конфигурация Spring MVC
Все настройки по подключению локали были добавлены в файл конфигурации mvc-config.xml.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<mvc:interceptors> .... <mvc:interceptor> <mvc:mapping path="/*" /> <bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> <property name="paramName" value="languageVar" /> </bean> </mvc:interceptor> </mvc:interceptors> <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> <property name="defaultLocale" value="ru" /> <!-- cookieMaxAge in seconds. if you set it to -1, the cookie will be deleted when browser is closed) --> <property name="cookieMaxAge" value="100000"/> </bean> <!-- MessageSource ReloadableResourceBundleMessageSource configuration --> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basenames" value="classpath:/locales/messages,classpath:util"/> <property name="cacheSeconds" value="1"/> <property name="defaultEncoding" value="UTF-8" /> </bean> |
Смена локали в приложении будет реализована с помощью перехватчика LocaleChangeInterceptor (описание работы и настройки перехватчиков читайте в соответствующей статье в обзоре приложения). Также нам необходимо добавить два бина — localeResolver и messageSource. Первый необходим для определения локали приложения, а второй для настройки файла с текстом на разных языках. В приложении указанном по ссылке в начале статьи использовался класс SessionLocaleResolver, здесь мы будем использовать другой подход — CookieLocaleResolver. Названия классов поясняют разницу между этими подходами по определению локали.
Разберем по отдельности код выше. Перехватчик LocaleChangeInterceptor будет срабатывать на любой URI в нашем приложении (/*). Далее указывается имя параметра, который мы будем использовать в приложении. Его назначение будет понятнее далее по тексту.
В бине localeResolver мы указываем локаль по умолчанию (в нашем случае Ru), которая будет включаться, если в куках нет никакой информации о пришедшем пользователе (и например настройках браузера). Так же указывается срок жизни куки. Если вы хотите, чтобы они удалялись после закрытия браузера, то необходимо выставить значение -1.
Для бина messageSource задаем путь по умолчанию для файла с «источником сообщений», время кеша HTTP заголовков и кодировку по умолчанию.
Полный файл mvc-config.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <!--Spring security enabled annotations--> <security:global-method-security pre-post-annotations="enabled" secured-annotations="enabled" jsr250-annotations="enabled"/> <!-- context:component-scan This tag will scan @Component, @Repository, @Service, @Controller and also resolves @Autowired and @Qualifier --> <context:component-scan base-package="ru.javastudy.mvcHtml5Angular.mvc" /> <!-- mvc:annotation-driven configures Spring MVC annotations Support for validating @Controller inputs with @Valid, if a JSR-303 Provider is present on the classpath. HttpMessageConverter support for @RequestBody method parameters and @ResponseBody method return values from @RequestMapping or @ExceptionHandler methods. --> <mvc:annotation-driven> <!--use int RestController to produce pretty json response--> <mvc:message-converters> <bean id="jacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="prettyPrint" value="true" /> </bean> </mvc:message-converters> </mvc:annotation-driven> <!-- activate @Transactional JPA annotation --> <tx:annotation-driven/> <!-- ViewResolver bean config for mapping strings to jsp views --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' --> <property name="order" value="1" /> <property name="prefix" value="/WEB-INF/view/" /> <property name="suffix" value=".jsp" /> </bean> <!-- File Upload bean config--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- set the maximum file size in bytes --> <property name="maxUploadSize" value="1000000"/> </bean> <!--Excel and PDF xml view configuration --> <!--disabling for jUnit test. --> <bean class="org.springframework.web.servlet.view.XmlViewResolver"> <property name="order" value="0" /> <property name="location" value="/WEB-INF/config/excel-pdf-config.xml"/> </bean> <mvc:view-controller path="/" view-name="/index"/> <mvc:view-controller path="/index.html" view-name="/index"/> <mvc:view-controller path="/login.html" view-name="/form/login"/> <mvc:view-controller path="/about.html" view-name="/about/about"/> <mvc:view-controller path="/file.html" view-name="/file/file"/> <mvc:view-controller path="/jdbc.html" view-name="/jdbc/jdbc"/> <mvc:view-controller path="/email.html" view-name="/email/email"/> <mvc:view-controller path="/rest.html" view-name="/rest/rest"/> <mvc:view-controller path="/orm.html" view-name="/orm/orm"/> <mvc:view-controller path="/jstl.html" view-name="/jstl/jstl"/> <mvc:view-controller path="/scope.html" view-name="/scope/scope"/> <mvc:view-controller path="/cookie.html" view-name="/cookie/cookieView"/> <mvc:view-controller path="/security.html" view-name="/security/security"/> <mvc:view-controller path="/security/admin.html" view-name="/security/admin"/> <!-- Static Resources Configuration (get access to static sources such as CSS and JavaScript files) --> <mvc:resources mapping="/resources/**" location="/resources/" /> <!-- themes can be put in different folder such as <mvc:resources mapping="/resources/**" location="/resources/themeBlue" /> <mvc:resources mapping="/resources/**" location="/resources/themeGreen" /> --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/interceptorCall/*"/> <!--need to use ' /** ' not ' /* ' if you want to intercept all requests.--> <!--<mvc:mapping path="/**"/>--> <bean class="ru.javastudy.mvcHtml5Angular.mvc.interceptors.SiteInterceptor"/> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/*" /> <bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> <property name="paramName" value="languageVar" /> </bean> </mvc:interceptor> </mvc:interceptors> <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> <property name="defaultLocale" value="ru" /> <!-- cookieMaxAge in seconds. if you set it to -1, the cookie will be deleted when browser is closed) --> <property name="cookieMaxAge" value="100000"/> </bean> <!-- MessageSource ReloadableResourceBundleMessageSource configuration --> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basenames" value="classpath:/locales/messages,classpath:util"/> <property name="cacheSeconds" value="1"/> <property name="defaultEncoding" value="UTF-8" /> </bean> </beans> |
5. Resource Bundle
В файле messages.properties (messages_ru и messages_en) находятся пары ключ-значения, где ключ — название переменной, а значение — текст на соответствующем языке. Как создать такой файл читайте в статье указанной в начале. В IDEA можно нажать правой кнопкой и выбрать Jump to Source, чтобы увидеть следующий вид:
Как видите здесь удобно редактировать для каждого ключа сразу несколько вариантов записи сообщения.
6. Представления jsp
Локализация была добавлена в несколько представлений jsp в приложении. Рассмотрим здесь только главную страницу — index.jsp.
6.1 template.tag
Основные доработки были сделаны в header нашего приложения. Как известно из начальных статей — в приложении используется шаблон для заголовка, чтобы не повторять код на каждой jsp странице. Для того, чтобы вставить текст, который будет меняться в зависимости от выбранной локали, необходимо использовать такую запись:
1 |
<spring:message code="navMenu.login"/> |
Где пространство имен задано как:
1 |
<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %> |
navMenu.login — это переменная (ключ), указанная в resource bundle (messages.properties).
Для того, чтобы переключить локаль используется следующая запись:
1 2 |
<a href="<%=request.getContextPath()%>?languageVar=en">EN</a> <a href="<%=request.getContextPath()%>?languageVar=ru">RU</a> |
Т.е. это обычная ссылка EN, RU (ссылка находится внизу страницы приложения), по нажатию на которую происходит переключения локали. Это происходит с помощью добавления параметра ?languageVar=en (или ru). Напомню. что languageVar был прописан в mvc-config.xml в атрибуте propertyName для перехватчика localeChangeInterceptor.
6.2 Запуск приложения
Локализация Ru:
Локализация En:
Исходные коды
MVC_AngularJS_Html5 full project — полный проект Spring MVC + AngularJS + Bootstrap + HTML5.
17. Localization — код для этой части
Обзор приложения Spring MVC + AngularJS + Bootstrap + HTML5
62 thoughts on “Локализация Java Spring MVC приложения. Настройка поддержки нескольких языков”
Добавить комментарий
Для отправки комментария вам необходимо авторизоваться.
Следующий вопрос возник, если в приложении используется в web.xml
например, то при запуске приложения будет ошибка в строке подключения шаблона (<page:template>).
Если же например убрать welcome-file-list директиву, то при запуске приложения меня кидает сразу на страницу с ошибкой error/error.jsp , но приложением можно пользоваться.
Как можно данный вопрос решить?
Сам же и отвечу, у меня в mvc-config’е отсутствовал
, поэтому и возникала ошибка.
Прошу прощения.