7.11. メッセージ管理

メッセージとは、画面の固定文言(項目タイトルなど)やエラーメッセージのことを指す。

画面の固定文言は、国際化の要件がなければJSPに直接埋め込んでも問題ない。

補足

メッセージは、安易に共通化せずに出来るだけ個別に定義すること。

安易に共通化を行った場合、以下の問題が発生する可能性がある。

例えば、他業務のメッセージに使えそうなメッセージがあるからとそのメッセージを使用したとする。 他業務の仕様変更でそのメッセージが変更されると、そのメッセージを使っていた箇所に関係のないメッセージが表示される。

7.11.1. 機能概要

7.11.1.1. メッセージの定義場所を指定できる

メッセージは、データベースやプロパティファイルで管理できる。デフォルトでは、プロパティファイルでの管理となる。

プロパティファイルをデフォルトとしている理由は以下のとおり。

プロパティファイルで管理した場合、メッセージの追加・変更や確認を簡単に行える。 例えば、メッセージを追加する際にデータベースへinsertするよりも、プロパティファイルに行追加するほうがはるかに楽である。

プロパティファイルでの管理の詳細は以下を参照。

補足

メッセージの定義場所に関わらず、本機能では、アプリケーションの実行中に、メッセージを更新する機能は提供していない。 メッセージを更新する場合は、アプリケーションの再起動が必要となる。

7.11.1.2. メッセージをフォーマットすることが出来る

メッセージは java.text.MessageFormat の拡張機能を使用してフォーマットする。 実行時に保持している値をメッセージに埋め込みたい場合は、 埋め込み文字を使用する に従いパターン文字列を定義する。

7.11.2. モジュール一覧

<dependency>
  <groupId>com.nablarch.framework</groupId>
  <artifactId>nablarch-core</artifactId>
</dependency>
<dependency>
  <groupId>com.nablarch.framework</groupId>
  <artifactId>nablarch-core-message</artifactId>
</dependency>

<!-- メッセージをデータベースで管理する場合のみ -->
<dependency>
  <groupId>com.nablarch.framework</groupId>
  <artifactId>nablarch-common-jdbc</artifactId>
</dependency>

7.11.3. 使用方法

7.11.3.1. プロパティファイルの作成単位

アプリケーション単位に作成する。 1つのシステムであっても、社内向けとコンシューマ向けのアプリケーションがある場合は、それぞれにプロパティファイルを作成する。

アプリケーション単位に作成することで、メッセージの影響範囲をアプリケーション内に限定できるメリットがある。 (よくある、「そのアプリケーションで使っているとは思ってませんでした」による、障害を未然に防ぐことができる)

コンシューマ向けアプリケーション
consumer/main/resources/messages.properties
社員向けアプリケーション
intra/main/resources/messages.properties

7.11.3.2. プロパティファイルにメッセージを定義する

デフォルトの設定では、プロパティファイルのパスは classpath:messages.properties となる。

メッセージは、 java.util.Properties を使用してロードする。 なお、NablarchはJava6以上を想定しているため、 UTF-8 で作成すればよくユニコード変換(native2ascii)は必要ない。

プロパティファイルの例
label.user.register.title=ユーザ登録画面
errors.login.alreadyExist=入力されたログインIDは既に登録されています。別のログインIDを入力してください。
errors.login=ログインに失敗しました。ログインIDまたはパスワードが誤っています。
errors.compare.date={0}は{1}より後の日付を入力してください。
success.delete.project=プロジェクトの削除が完了しました。
success.update.project=プロジェクトの更新が完了しました。

7.11.3.3. 多言語化対応

メッセージの多言語化を行う場合には、言語ごとのプロパティファイルを用意し、サポートする言語を PropertiesStringResourceLoader.locales に設定する。 なお、デフォルトのロケールに対応する言語については、サポートする言語に追加しなくても良い。

重要

デフォルトのロケールは、PropertiesStringResourceLoader.defaultLocale (デフォルトの言語)で設定する。設定しなかった場合、デフォルトのロケールは Locale.getDefault().getLanguage() の値が採用される。

Locale.getDefault().getLanguage() の値はOSの設定によって変化するため、この値をデフォルトのロケールとして使用すると実行する環境に応じて値が変わり障害の原因になる可能性がある。必ずデフォルトの言語を設定すること。

メッセージ取得時にどの言語が使用されるかは、 ThreadContext#getLanguage が返すロケールによって決定される。 もし、 ThreadContext#getLanguage からロケールが取得できない場合は Locale.getDefault() が使用される。

PropertiesStringResourceLoaderへの言語設定

サポートする言語として、 enzhde を設定する場合の例を示す。

<component class="nablarch.core.cache.BasicStaticDataCache" name="messageCache">
  <property name="loader">
    <!-- 多言語化したPropertiesStringResourceLoaderの定義 -->
    <component class="nablarch.core.message.PropertiesStringResourceLoader">
      <!-- サポートする言語 -->
      <property name="locales">
        <list>
          <value>en</value>
          <value>zh</value>
          <value>de</value>
        </list>
      </property>

      <!-- デフォルトの言語 -->
      <property name="defaultLocale" value="ja" />
    </component>
  </property>
</component>

<component name="stringResourceHolder" class="nablarch.core.message.StringResourceHolder">
  <!-- 多言語化したPropertiesStringResourceLoaderを持つBasicStaticDataCacheを設定する -->
  <property name="stringResourceCache" ref="messageCache" />
</component>

<component name="initializer"
           class="nablarch.core.repository.initialization.BasicApplicationInitializer">
  <property name="initializeList">
    <list>
      <!-- BasicStaticDataCacheを初期化対象に追加する -->
      <component-ref name="messageCache" />
    </list>
  </property>
</component>
言語ごとのプロパティファイルの作成

上記の PropertiesStringResourceLoader に設定したサポート言語に対応するプロパティファイルの作成例を示す。

PropertiesStringResourceLoader に設定した言語に対応するプロパティファイルを作成する。 ファイル名は、 messages_言語.properties とする。

デフォルトのロケールに対応するプロパティファイルは、言語を入れずに messages.properties として作成する。 messages.properties が存在していない場合は、エラーとして処理を終了するので注意すること。

main/resources/messages.properties       # デフォルトの言語に対応したファイル
               messages_en.properties    # enに対応したファイル
               messages_zh.properties    # zhに対応したファイル
               messages_de.properties    # deに対応したファイル

7.11.3.4. メッセージを持つ業務例外を送出する

プロパティファイルに設定されたメッセージを持つ業務例外( ApplicationException ) を送出する例を示す。

プロパティファイルに設定されたメッセージを取得するには、 MessageUtil クラスを使用する。 MessageUtil から取得した Message を元に業務例外( ApplicationException )を生成し送出する。

プロパティファイル
errors.login.alreadyExist=入力されたログインIDは既に登録されています。別のログインIDを入力してください。
実装例
Message message = MessageUtil.createMessage(MessageLevel.ERROR, "errors.login.alreadyExist");

throw new ApplicationException(message);

7.11.3.5. 埋め込み文字を使用する

java.text.MessageFormat 形式での埋め込み文字に対応している。 メッセージに埋め込む値に Map のみを指定した場合は、 java.text.MessageFormat を使用せずに Map のキー値を元に値を埋め込む拡張機能を使用する。

埋め込み文字を使用する場合には、メッセージにパターン文字を使用し、メッセージ取得時に埋め込み文字を指定する。

埋め込み文字に Map 以外を使用した場合
プロパティファイル

java.text.MessageFormat の仕様に従い、メッセージを定義する。

success.upload.project={0}件のプロジェクトを登録しました。
実装例

projects.size()5 を返した場合、取得されるメッセージは「5件のプロジェクトを登録しました。」となる。

MessageUtil.createMessage(MessageLevel.INFO, "success.upload.project", projects.size());
埋め込み文字に Map のみを使用した場合
プロパティファイル

埋め込み文字部分には、 Map のキー名を {} で囲んで定義する。

success.upload.project={projectCount}件のプロジェクトを登録しました。
実装例

メッセージ取得時に指定する埋め込み文字に Map を指定する。

projects.size()5 を返した場合、取得されるメッセージは「5件のプロジェクトを登録しました。」となる。

Map<String, Object> options = new HashMap<>();
options.put("projectCount", projects.size());

MessageUtil.createMessage(MessageLevel.INFO, "success.upload.project", options);

重要

埋め込み文字に指定できる値は、 Map のみとなる。 複数の Map や、 Map 以外の値とセットで指定された場合は、 java.text.MessageFormat を使用した値の埋め込み処理をおこなう。

メッセージのフォーマット方法を変更したい場合は、 メッセージのフォーマット方法を変更する を参照し対応すること。

7.11.3.6. 画面の固定文言をメッセージから取得する

画面の固定文言にメッセージの値を出力したい場合は、カスタムタグライブラリの message タグを使用する。

message タグの詳細な使用方法は、 メッセージを出力する を参照。

プロパティファイル
login.title=ログイン
JSP
<div class="title-nav">
  <span><n:message messageId="login.title" /></span>
</div>
画面表示結果

プロパティファイルに定義したメッセージが固定文言として表示される。

../../../_images/jsp_title.png

7.11.3.7. メッセージレベルを使い分ける

メッセージレベルを使い分けることで、画面表示時のスタイルを切り替えることができる。 スタイルの切り替えは、カスタムタグライブラリの errors タグを使用することで実現できる。

重要

メッセージレベルとカスタムタグを使用したスタイル変更は以下の問題点がある。

  • カスタムタグライブラリが出力するDOM構造に制約があり、一般的なCSSフレームワークとの相性が悪い
  • メッセージレベルが3種類しかなくそれより細かい分類ができない
  • JSP以外のテンプレートエンジンで使用できない

このため、 errorsタグを使用したメッセージレベルに応じたスタイル切り替え を使用するのではなく以下の実装方法を推奨する。

サーバサイド

サーバサイドでメッセージ文字列を構築し、リクエストスコープに設定する。 メッセージを生成する際にはメッセージレベルが必須なため、INFOレベルを指定すれば良い。

context.setRequestScopedVar("message",
    MessageUtil.createMessage(MessageLevel.INFO, "login.message").formatMessage());
View

View(JSP等)では、リクエストスコープに設定したメッセージを出力する。 JSPを使用する場合は、 write タグを使用してリクエストスコープに設定したメッセージを出力する。

<div class="alert alert-success" role="alert">
  <n:write name="message" />
</div>
errorsタグを使用したメッセージレベルに応じたスタイル切り替え例

メッセージレベルは、 INFOWARNERROR の3種類があり、 MessageLevel に定義されている。

errorsタグを使用すると、メッセージレベルに応じて以下のcssクラスが適用される。 errors タグの詳細な使用方法は、 エラー表示を行う を参照。

INFO:nablarch_info
WARN:nablarch_warn
ERROR:nablarch_error

補足

バリデーション機能 から送出される業務例外( ApplicationException )が持つメッセージは、 全て ERROR レベルとなる。

プロパティファイル
info=インフォメーション
warn=ワーニング
error=エラー
スタイルシート

メッセージレベルに対応したスタイルを定義する。

.nablarch_info {
  color: #3333BB;
}

.nablarch_warn {
  color: #EA8128;
}

.nablarch_error {
  color: #ff0000;
}
action class

errors タグで出力するメッセージは、 WebUtil.notifyMessages を使ってリクエストスコープに格納する。

WebUtil.notifyMessages(context, MessageUtil.createMessage(MessageLevel.INFO, "info"));
WebUtil.notifyMessages(context, MessageUtil.createMessage(MessageLevel.WARN, "warn"));
WebUtil.notifyMessages(context, MessageUtil.createMessage(MessageLevel.ERROR, "error"));
JSP

errors タグを使用して、 WebUtil に格納したメッセージを画面表示する。

<n:errors />
画面表示結果

メッセージレベルに応じてスタイルが切り替わっていることがわかる。

../../../_images/message_level.png

7.11.4. 拡張例

7.11.4.1. プロパティファイル名や格納場所を変更する

PropertiesStringResourceLoader には、ファイル名やディレクトリのパスを変更するためのプロパティが用意されている。 デフォルト構成を変更したい場合は、これらのプロパティを用いて変更すること。

7.11.4.2. メッセージをデータベースで管理する

メッセージをデータベースで管理するには BasicStringResourceLoader を使用してメッセージをロードする必要がある。

以下にデータベースで管理するメッセージを使用するための設定例を示す。

<!-- データベースからメッセージをロードするコンポーネント -->
<component name="stringResourceLoader" class="nablarch.core.message.BasicStringResourceLoader">
  <property name="dbManager" ref="defaultDbManager"/>
  <property name="tableName" value="MESSAGE"/>
  <property name="idColumnName" value="ID"/>
  <property name="langColumnName" value="LANG"/>
  <property name="valueColumnName" value="MESSAGE"/>
</component>

<!-- ロードしたメッセージをキャッシュするコンポーネント -->
<component name="stringResourceCache" class="nablarch.core.cache.BasicStaticDataCache">
  <!-- ローダーには、データベースからメッセージをロードするクラスを指定する -->
  <property name="loader" ref="stringResourceLoader"/>
  <!-- 起動時に一括でロードする -->
  <property name="loadOnStartup" value="true"/>
</component>

<!--
メッセージの元となる文字リソースを保持するコンポーネント
コンポーネント名はstringResourceHolderとすること
-->
<component name="stringResourceHolder" class="nablarch.core.message.StringResourceHolder">
  <!-- メッセージをキャッシュするコンポーネントを指定する -->
  <property name="stringResourceCache" ref="stringResourceCache"/>
</component>

7.11.4.3. メッセージのフォーマット方法を変更する

メッセージのフォーマット方法は、 MessageFormatter の実装クラスを作成しコンポーネント定義するこどで変更できる。

以下に例を示す。

MessageFormatterの実装クラス
package sample;

import nablarch.core.message.MessageFormatter;

public class SampleMessageFormatter implements MessageFormatter {

    @Override
    public String format(final String template, final Object[] options) {
        return String.format(template, options);
    }
}
コンポーネント設定ファイル

コンポーネント名を messageFormatter として、 MessageFormatter の実装クラスを設定する。

<!-- コンポーネント名をmessageFormatterとして定義する。 -->
<component name="messageFormatter" class="sample.SampleMessageFormatter" />

なお、 MessageFormatter の実装としては以下のクラスを提供している。

BasicMessageFormatter:
埋め込み文字の仕様 に従いメッセージをフォーマットする。 MessageFormatter の実装クラスがコンポーネント定義されていない場合は本クラスが使用される。
JavaMessageFormatBaseMessageFormatter:
MessageFormat を使用してメッセージをフォーマットする。