Thymeleaf + Spring で i18n ~1~

2015.02.19

Thymeleaf + Spring の構成で、多言語化対応 Web アプリケーションのサンプルを作成してみました。

プロジェクト構成

ビルドツールには Gradle を使いました。

example1
│  build.gradle
│  gradlew
│  gradlew.bat
│  
├─gradle
└─src
    └─main
        ├─java
        │  └─example
        │      └─controller
        │              RootController.java
        │              
        ├─resources
        │  │  logback.xml
        │  │  
        │  └─i18n
        │          Messages_en.properties
        │          Messages_ja.properties
        │          
        └─webapp
            ├─css
            ├─images
            └─WEB-INF
                │  web.xml
                │  
                ├─spring
                │  │  root-context.xml
                │  │  
                │  └─appServlet
                │          servlet-context.xml
                │          
                └─templates
                        index.html

サンプルの確認

Gradle タスクに jetty プラグインを指定してあるので jettyRun タスクの実行でサンプルを確認できます。

$ cd example1
$ ./gradlew jettyRun

http://localhost:8080/example1

WS000000

ブラウザの言語を切り替えてみます。Chrome では「設定」の「言語と入力の設定」で「英語 (アメリカ合衆国)」を最上位へ持ってきます。

WS000001 WS000002 WS000003

Web アプリケーションをリロードします。

http://localhost:8080/example1

WS000004

表示が英語へ切り替わりました。

サンプルの説明

Thymeleaf テンプレート

example1/src/main/webapp/WEB-INF/templates/index.html

<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      th:with="lang=${#locale.language}"
      th:lang="${lang}">
  <head>
    <title>Example1</title>
    <meta charset="UTF-8" />
  </head>
  <body>
    <h1 th:text="#{hello.world}">FooBar</h1>
  </body>
</html>

外部ファイルに定義されたメッセージをテキストと紐付けるには#{...}を用います。サンプルではhello.worldという文字列をキーとして指定しています。 では、この hello.world というキーはどこに定義されているかというと example1/src/main/resources/i18n に用意されています。

example1/src/main/resources/i18n/Messages_ja.properties

hello.world = こんにちは世界!

example1/src/main/resources/i18n/Messages_en.properties

hello.world = Hello, world!

このままでは、Thymeleaf は外部メッセージファイルの場所を知りませんので Spring の設定ファイルで指定してあげます。

example1/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
                           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">


  <!-- **************************************************************** -->
  <!--  RESOURCE FOLDERS CONFIGURATION                                  -->
  <!--  Dispatcher configuration for serving static resources           -->
  <!-- **************************************************************** -->
  <mvc:resources location="/images/" mapping="/images/**" />
  <mvc:resources location="/css/" mapping="/css/**" />


  <!-- **************************************************************** -->
  <!--  SPRING ANNOTATION PROCESSING                                    -->
  <!-- **************************************************************** -->
  <mvc:annotation-driven />
  <context:component-scan base-package="example" />


  <!-- **************************************************************** -->
  <!--  MESSAGE EXTERNALIZATION/INTERNATIONALIZATION                    -->
  <!--  Standard Spring MessageSource implementation                    -->
  <!-- **************************************************************** -->
  <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="i18n/Messages" />
    <property name="defaultEncoding" value="ISO-8859-1" />
  </bean>


  <!-- **************************************************************** -->
  <!--  THYMELEAF-SPECIFIC ARTIFACTS                                    -->
  <!--  TemplateResolver <- TemplateEngine <- ViewResolver              -->
  <!-- **************************************************************** -->

  <bean id="templateResolver"
        class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
    <property name="prefix" value="/WEB-INF/templates/" />
    <property name="suffix" value=".html" />
    <property name="templateMode" value="HTML5" />
  </bean>

  <bean id="templateEngine"
        class="org.thymeleaf.spring4.SpringTemplateEngine">
    <property name="templateResolver" ref="templateResolver" />
  </bean>

  <bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
    <property name="templateEngine" ref="templateEngine" />
    <property name="characterEncoding" value="UTF-8" />
  </bean>


</beans>

ハイライトした箇所が外部メッセージファイルの場所を指定している箇所になります。(この設定はコチラを参考にしました。)

id をmessageSourceとしてResourceBundleMessageSourceを Spring へ登録しています。外部メッセージファイルはResourceBundleとしてクラスパス内に配置していますので、basenameプロパティはクラスパスで指定します。ここではbasenameとしてi18n/Messagesと指定していますので、i18n パッケージ内の Messages で始まるファイル(即ち前段のプロパティファイル)が対象となっています。

後は参照するメッセージリソースを Spring がブラウザから渡されたロケールで切り替え、Thymeleaf がそこに定義されたメッセージでテンプレートを置換することで言語切替が実現します。

まとめ

Thymeleaf + Spring を用いた Web アプリケーションを一から作成するのが大変だったのでミニマム構成のサンプルを作成してみました。 ご覧頂いた方がこれから作成されるアプリケーションの一助となれば幸いです。

今回はブラウザの言語値で表示が切り替わりましたが、次回は任意に言語を切り替えることが出来るようにしてみたいと思います。