2.

Thymeleaf メッセージ式と国際化 (i18n) 完全ガイド

編集
この記事の要点
  • メッセージ式 #{...}messages.properties 等の外部ファイルから翻訳テキストを読み込む仕組み
  • 基本: messages.propertieshome.title=ホーム を書き、テンプレートで <h1 th:text="#{home.title}"></h1>
  • 多言語対応: messages_ja.properties / messages_en.properties を用意。Accept-Language ヘッダや URL パラメータでロケール切替
  • 引数つき: greeting=こんにちは {0} さん ({1} 件のメッセージ)#{greeting(${user.name}, ${count})}
  • Spring Boot は MessageSource 経由で自動連携。spring.messages.basename=messages がデフォルト

メッセージ式とは

Thymeleaf の #{...} は、テンプレート内に直接文字列を書くのではなく、外部のプロパティファイルから読み込む仕組みです。これにより (1) 多言語対応 (i18n)(2) 翻訳作業をデザイナと分業(3) 文言変更時にテンプレート再ビルド不要といった利点があります。

最小サンプル

1. messages.properties (デフォルト)

# src/main/resources/messages.properties
home.title=ホーム
home.welcome=ようこそ
home.button.login=ログイン
home.button.signup=新規登録

article.title=記事タイトル
article.body=本文
article.created_at=作成日時

error.required={0} は必須です
error.minLength={0} は {1} 文字以上で入力してください

2. テンプレート

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
  <title th:text="#{home.title}">ホーム</title>
</head>
<body>
  <h1 th:text="#{home.welcome}">ようこそ</h1>
  <button th:text="#{home.button.login}">ログイン</button>
  <button th:text="#{home.button.signup}">新規登録</button>
</body>
</html>

多言語対応 (i18n)

ファイル命名規約

ファイル名適用条件
messages.propertiesデフォルト (どのロケールにも該当しない時)
messages_ja.properties日本語
messages_en.properties英語
messages_en_US.propertiesアメリカ英語
messages_zh_CN.properties簡体字中国語

サンプル

# messages_ja.properties
home.title=ホーム
home.welcome=ようこそ
home.button.login=ログイン

# messages_en.properties
home.title=Home
home.welcome=Welcome
home.button.login=Login

# messages_zh_CN.properties
home.title=主页
home.welcome=欢迎
home.button.login=登录

application.properties

# 既定で messages という basename を読みます。明示も可
spring.messages.basename=messages
# 複数の basename を読む場合
# spring.messages.basename=messages,errors,validation
spring.messages.encoding=UTF-8
spring.messages.fallback-to-system-locale=false
spring.messages.use-code-as-default-message=true

ロケール切替の方法

Spring MVC はロケール解決を 3 つの方法で行えます:

// 1. URL パラメータ ?lang=en で切替
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver resolver = new SessionLocaleResolver();
        resolver.setDefaultLocale(Locale.JAPANESE);
        return resolver;
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
        interceptor.setParamName("lang");   // ?lang=en
        return interceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }
}

// 2. Accept-Language HTTP ヘッダ
//    AcceptHeaderLocaleResolver (Spring Boot のデフォルト)

// 3. クッキー
//    CookieLocaleResolver

引数つきメッセージ

# messages.properties
greeting=こんにちは {0} さん、{1} 件の通知があります
error.required={0} は必須項目です
error.range={0} は {1} 以上 {2} 以下で入力してください
<!-- テンプレート -->
<p th:text="#{greeting(${user.name}, ${notificationCount})}">
  こんにちは ゲスト さん
</p>

<p th:text="#{error.required('メールアドレス')}">
  必須エラー
</p>

<p th:text="#{error.range('年齢', 0, 120)}">
  範囲エラー
</p>

属性へのメッセージ展開

<!-- placeholder にメッセージ -->
<input type="text"
       th:placeholder="#{form.name.placeholder}"
       th:title="#{form.name.title}">

<!-- 文字列連結 -->
<a th:text="|#{home.welcome}, ${user.name}!|">Welcome, taro!</a>

<!-- 複数属性の一括 -->
<input type="text"
       th:attr="placeholder=#{form.placeholder}, title=#{form.title}">

テキスト中に直接埋め込み

<!-- [[ ]] = th:text、[( )] = th:utext と同義 -->
<p>[[#{home.welcome}]] さん</p>

<!-- 条件・繰り返しと組み合わせ -->
<th:block th:if="${count > 0}">
  <p th:text="#{notifications.summary(${count})}">通知あり</p>
</th:block>

Controller で MessageSource を直接使う

@RestController
public class ApiController {
    private final MessageSource messageSource;

    public ApiController(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    @GetMapping("/api/hello")
    public String hello(Locale locale) {
        return messageSource.getMessage("home.welcome", null, locale);
    }

    @GetMapping("/api/error")
    public String error(Locale locale) {
        return messageSource.getMessage(
            "error.required",
            new Object[]{"メールアドレス"},
            locale
        );
    }
}

UTF-8 設定 (文字化け対策)

古い Spring では messages_ja.properties を Unicode エスケープ (こん...) する必要がありました。Spring Boot 1.4+ では UTF-8 がデフォルトですが、明示推奨:

# application.properties
spring.messages.encoding=UTF-8
spring.thymeleaf.encoding=UTF-8
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true

FAQ

Q: ??home.welcome_ja_JP?? と表示される
A: メッセージキー未定義。messages.properties に定義漏れか、basename 設定ミス。

Q: HTML タグを含むメッセージを出したい
A: th:utext="#{key}" でエスケープ無効化。XSS 注意。

Q: 改行をメッセージに入れたい
A: properties に \n と書き、テンプレートで style="white-space:pre-line" で改行を表示。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 導入方法と基本動作
  2. メッセージ式
  3. テンプレートフラグメント(ヘッダー等の共有化)

最近更新/作成されたページ