Thymeleafではよく使用するテキスト(以降、メッセージテキストと呼びます)を外部ファイル(以降、プロパティファイル)で管理することができます。今回はその管理されたメッセージテキストをhtmlに出力する際に使用するThymeleafの メッセージ式 について紹介します。
開発環境
・Spring Boot:3.5.0
・Java:17
メッセージ式について
まずメッセージ式とは何か、そしてどんなメリットがあるのかを説明します。
メッセージ式は単純式のひとつ
メッセージ式はThymeleafで用意されている 単純式(Simple Expressions) の一つです。
単純式はhtmlの属性に値やオブジェクトを渡す際に使用する式で、メッセージ式の場合は最初に述べたように「外部ファイルにて管理されているメッセージテキスト」を渡します。
またメッセージ式の他に、下記の単純式が用意されています。
- 変数式(Variable Expressions)
- 選択変数式(Selection Variable Expressions)
- リンクURL式(Link URL Expressions)
- フラグメント式(Fragment Expressions)
詳細は各記事に記載しています。
メッセージ式のメリット①
メリットの一つ目は、保守性が高いことです。
例として、下図のように複数ページに同じような文言が表示されている場合を考えてみます。もしその文言に修正が入った際には、該当箇所の洗い出しと該当箇所分の修正が発生してしまいますが、プロパティファイルで一元管理している場合は洗い出しが不要で、修正箇所も一カ所になります。

message.greeting=おはようございます
またソースファイルを直接触ることがなくなるため、修正時に誤って処理を変更してしまうといったヒューマンエラーも避けられます。
メッセージ式のメリット②
メリットの二つ目は、多言語に対応したページを作る場合であってもhtmlが複雑にならないことです。
Thymeleafはメッセージ式を用いるとロケールに合わせて参照するプロパティファイルを自動で判別してくれます。試しに自国の挨拶を表示するページを作成して、ロケールを日本と英語圏に設定した際にどのようにページが変化するのか確認してみます。
htmlには通常のメッセージ式のみ記載しました。プロパティファイルの名前はmessage_ja.propertiesとmessage_en.propertiesとしてあります。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<p th:text="#{message.title}"></p>
<p th:text="#{greeting.first}"></p>
<p th:text="#{greeting.second}"></p>
<p th:text="#{greeting.third}"></p>
</body>
</html>
message.title=メッセージ式(日本語)
greeting.first=おはよう
greeting.second=こんにちは
greeting.third=こんばんは
message.title=メッセージ式(英語)
greeting.first=good morning
greeting.second=good afternoon
greeting.third=good evening
上記のファイルで出力される画面は下の通りになります。

ロケールを変更すると表示内容も変更されていることがわかります(ロケールはウェブブラウザの言語で設定)。このようにThymeleafのメッセージ式はシンプルな記載方法で複数言語のメッセージテキストに対応することができます。
メッセージ式の記載方法
メッセージ式を使用するための記載方法を説明します。
基本
メッセージ式は先頭に#をつけて{}でプロパティファイルに記載したキー名を囲みます。
今回は渡した値を画面に表示するためにThymeleafで用意された独自の属性であるtextを使用しています。Thymeleafの各属性を使用する際には、下記の2行目のようにxmlネームスペース(xmlns)を設定する必要があるため書き忘れに気を付けましょう。
<!-- prefixには"th"がよく使用される -->
<html xmlns:th="http://www.thymeleaf.org" >
<!-- headタグやbodyタグは省略 -->
<!-- 記載方法 prefix:属性="#{キー名}" -->
<p th:text="#{message.login}"></p>
次はプロパティファイルについてです。プロパティファイルはデフォルトの設定でsrc/main/resources直下にmessages.propertiesというファイル名で保存します。
記載方法はキー名と値をイコールで結んだものを1セットとし、1行に1つだけ記載します。
# 記載方法 キー名=値
message.login=Hello!
上記のファイルで出力される画面は下の通りです。

パラメータ渡し
プロパティファイルで管理するメッセージテキストにはパラメータを渡すこともでき、メッセージテキストの一部を動的に変更することができます。まずは理解しやすいように固定値taroを渡してみましょう。
パラメータを渡したい場合、プロパティファイルには{0}と記載し、htmlには()の中に渡したいパラメータを記載します。今回は固定値であるためtaroと記載しました。
# 記載方法 パラメータを渡したいところに{0}
message.login=Hello, {0}!
<html xmlns:th="http://www.thymeleaf.org" >
<!-- headタグやbodyタグは省略 -->
<!-- 記載方法 prefix:属性="#{キー名(パラメータ)}" -->
<p th:text="#{message.login('taro')}"></p>
結果は下図のようになります。

無事、メッセージテキストに固定値を渡すことができました。次は変数式の値を渡してみましょう。変数式を渡す場合は、htmlの引数部分に変数式をそのまま記載します。
# 記載方法 パラメータを渡したいところに{0}
message.login=Hello, {0}!
<html xmlns:th="http://www.thymeleaf.org" >
<!-- headタグやbodyタグは省略 -->
<!-- 記載方法 prefix:属性="#{キー名(パラメータ)}"(変数式のuser.nameには'hanako'を格納済み)-->
<p th:text="#{message.login(${user.name})}"></p>

変数式の記載方法がわかっていれば簡単に動的な出力ができますね。変数式の記載方法を確認したい人は「Thymeleafの変数式 html編」にまとめているのでこちらを参考にしていただければと思います。
またパラメータは複数渡すことができます。複数渡す場合のプロパティファイルは{0},{1},…と連番で数字を割り当てていきます。htmlには渡すパラメータを()の中にカンマ区切りで記載します。
# 記載方法 パラメータを渡したいところに{0}、{1}...と連番で記載
# 連番の値はメッセージ式の引数の順番を示す
message.login=Hello, {0}! {1}
message.introduction=I'm IT Hamasan.
<html xmlns:th="http://www.thymeleaf.org" >
<!-- headタグやbodyタグは省略 -->
<!-- 引数が複数の場合はカンマで区切る(変数式のuser.nameには'hanako'を格納済み) -->
<p th:text="#{message.login(${user.name},#{message.introduction})}"></p>

複数のパラメータを使用する場合は、引数の過不足や連番の抜けに気をつけましょう。エラーは発生しませんが、文章がおかしくなったり、情報が抜け落ちてしまうので気を付けましょう。
# 引数は2つ
message.param2=Hello, {0}! My name is {1}.
<html xmlns:th="http://www.thymeleaf.org" >
<!-- headタグやbodyタグは省略 -->
<p th:text="#{message.param2('taro')}"></p>
<p th:text="#{message.param2('taro','hanako','jiro')}"></p>

外部ファイル(プロパティファイル)について
プロパティファイルのパス指定
同記事「メッセージ式の記載方法-基本」でも軽く述べましたが、メッセージテキストはデフォルトの設定でsrc/main/resources直下のmessages.propertiesで管理しています。
この設定はapplication.propertiesのspring.messages.basenameで変更可能です。
# spring.messages.basenameをキーとしファイルパスを記載することで
# メッセージ用のプロパティファイルを指定可能(拡張子の.propertiesは書かない)
spring.messages.basename=errors
error.occurred=エラー:{0}時に異常が発生しました。
spring.messages.basenameにはsrc/main/resources直下に配置したerrors.propertiesを指定したかったため、拡張子を取り除いたerrorsを記載しています。

messages.properties以外のファイルを参照して出力することができました。パラメータ渡しも問題なくできていますね。
spring.messages.basenameを指定するとmessages.propertiesは参照されなくなります。
引き続き参照する場合は、次の「プロパティファイルの複数利用」を参考にして、参照するプロパティファイルの追加を行いましょう。
プロパティファイルの複数利用
通常処理とエラー処理でメッセージテキストを分けて管理するなど、プロパティファイルを複数利用したい時もあると思います。そんな時はapplication.propertiesのspring.messages.basenameに参照するプロパティファイルを複数指定します。複数指定は1行にカンマ区切りで記載します。
# 複数指定したい場合はカンマ区切りでファイルパスを記載する
spring.messages.basename=message/messages,error/errors
src/main/resourcesにプロパティファイルが増えてきたため、その直下に新しくmessageとerrorという名前のフォルダを追加してmessages.properties、errors.propertiesをそれぞれに保存しました。
<html xmlns:th="http://www.thymeleaf.org" >
<!-- headタグやbodyタグは省略 -->
<p th:text="#{message.login(${user.name})}"></p>
<p th:text="#{error.no-content}"></p>
message.login=Hello, {0}!
error.no-content=コンテンツがありません。

参照している複数のプロパティファイル間でキー名が重複していた場合、エラーは発生せずに先にキー名と一致したメッセージテキストが使用されます。
spring.messages.basename=message/messages,error/errors
message.output=メッセージ
message.output=エラー
プロパティファイルの参照順はspring.messages.basenameに記載されている順となるため、上記の場合はmessages.propertiesが先に参照され次の出力となります。

エラーが発生しないことにより発見が難しいため、キー名が重複しないように工夫をしましょう。(例:ファイル名をキー名の先頭につける 等)
どうしてもキー名が重複してしまう場合は、spring.messages.basenameの記載順に気を付けても良いかもしれません。
ロケールの指定
複数言語に対応する場合は、プロパティファイル名の語尾に言語を識別する言語コード(2桁)を追加します。
例:日本語:messages_ja.properties、英語圏:messages_en.properties
また国コード(2桁)も追加したい場合は言語コードにの後ろにアンダーバーをつけて繋げます。
例:ポルトガル語(ブラジル):messages_pt_BR.properties
言語コードは小文字、国コードは大文字で記載しましょう。また各コードはネットで検索できるので、対応が必要であればその都度調べて適切なファイル名で作成しましょう。
まとめ
- メッセージ式は#{キー名}を使用し、プロパティファイルに記載したメッセージテキストをhtmlに出力する単純式。
- htmlに#{キー名(パラメータ1,…)}と記載してパラメータを渡すことで、動的にメッセージテキストの一部を変更することができる。プロパティファイルにはパラメータを渡したいところに{0},{1}…と連番で記載する。
- メッセージテキストを管理するプロパティファイルは、デフォルトでsrc/main/resources直下のmessages.propertiesとなっている。
- 参照するプロパティファイルを指定したい場合はapplication.propertiesの
spring.messages.basenameにsrc/main/resources以降のパスを拡張子なしで記載する。
複数指定したい場合はファイルパスをカンマ区切りで記載する。 - ロケールによってプロパティファイルを自動選択する。その際のプロパティファイル名はプロパティファイル名_言語コード(2桁).properties、国コードを追加で指定する場合はプロパティファイル名_言語コード(小文字2桁)_国コード(大文字2桁).propertiesとする。
メッセージ式を使えばメッセージテキストの保守がとても楽になるので、マスターしましょう!
コメント