Thymeleafではよく使用するテキスト(以降、テンプレートと呼びます)を外部ファイル(以降、プロパティファイル)で管理することができます。今回はその管理されたテンプレートをhtmlに出力する際に使用するThymeleafの”メッセージ式“について紹介します。
開発環境
・Spring Boot:2.6.2
・Thymeleaf:3.0.14.RELEASE
・Java:1.8
メッセージ式について
まずメッセージ式とは何か、そしてどんなメリットがあるのかを説明します。
メッセージ式は単純式のひとつ
メッセージ式はThymeleafで用意されている”単純式(Simple Expressions)“の一つです。
単純式はhtmlの属性に値やオブジェクトを渡す際に使用する式で、メッセージ式の場合は最初に述べたように「外部ファイルにて管理されているテンプレート」を渡します。
またメッセージ式の他に、下記の単純式が用意されています。
- 変数式(Variable Expressions)
- 選択変数式(Selection Variable Expressions)
- リンクURL式(Link URL Expressions)
- フラグメント式(Fragment Expressions)
詳細は各記事に記載しています。
メッセージ式のメリット①
メリットの一つ目は、保守性が高いことです。
例として、下図のように複数ページに同じような文言が表示されている場合を考えてみます。もしその文言に修正が入った際には、該当箇所の洗い出しと該当箇所分の修正が発生してしまいますが、プロパティファイルで一元管理している場合は洗い出しが不要で、修正箇所も一カ所になります。
# message_ja.properties
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_ja.properties
message.title=メッセージ式(日本語)
greeting.first=おはよう
greeting.second=こんにちは
greeting.third=こんばんは
# message_en.properties
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つだけ記載します。
# messages.properties
# 記載方法 キー名=値
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”で変更可能です。
# application.properties
# spring.messages.basenameをキーとしファイルパスを記載することで
# メッセージ用のプロパティファイルを指定可能(拡張子の.propertiesは書かない)
spring.messages.basename=errors
# errors.properties
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行にカンマ区切りで記載します。
# application.properties
# 複数指定したい場合はカンマ区切りでファイルパスを記載する
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>
# messages.properties
message.login=Hello, {0}!
# errors.properties
error.no-content=コンテンツがありません。
参照している複数のプロパティファイル間でキー名が重複していた場合、エラーは発生せずに先にキー名と一致したテンプレートが使用されます。
# application.properties
spring.messages.basename=message/messages,error/errors
# messages.properties
message.output=メッセージ
# errors.properties
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桁)とする。
メッセージ式を使えばテンプレートの保守がとても楽になるので、マスターしましょう!
コメント