ASP.NET Core: エラーメッセージの日本語化

ASP.NET Coreでは入力値を検証するための[Required]等の検証属性が提供されていますが、エラーメッセージが英語になっています。
検証属性以外でも画面の実装方法によってはモデルバインディングと言われる機能により、ユーザに英語のエラーメッセージが表示される場合があります。
ここでは、このようなユーザ向けエラーメッセージを日本語化します。

前提

  • Visual Studio 2019 + ASP.NET Core 3.1(MVC + Razor)を使用します。
  • 検証属性のユーザ向けエラーメッセージを日本語化します。
    ASP.NET Core標準の検証属性群が使用するメッセージは、ユーザが入力した値の検証に失敗した場合に使用されるものと、プログラマが検証属性に不正なパラメータを指定した場合に使用されるものに分けられます。(後者の例として、例えば[Range(100, 1)]のように大小関係を間違えて指定した場合が考えられます。)
    前者はユーザに対して表示するメッセージであるため日本語化の対象とします。後者はプログラマ向けのものなので、日本語化の対象外とします。
  • モデルバインディングのエラーメッセージも日本語化します。
    ASP.NET Coreではモデルバインディングという仕組みがあり、ユーザが入力した値はサーバ側でモデル(クラス)のプロパティに自動的に設定されます。この際、例えば「int型のプロパティに入力値”abc”を設定」などのように、変換できない場合はエラーメッセージが表示される場合があります。
    クライアント側の入力検証が有効であれば、このようなモデルバインディングでのエラーは発生しませんが、念のためのモデルバインディングのエラーメッセージも日本語化します。
  • 実行環境のカルチャに応じた使用言語の変更は行いません。
    .NET Coreのリソースマネージャは実行環境や要求に含まれるカルチャー情報に基づいて、使用する言語を変更することができます。
    日本の業務アプリでは多言語に対応するアプリは少ないことや実装を簡略化するために、固定で日本語を使用する前提とします。
  • ここで説明するサンプルは、こちらのプロジェクトに含まれています。

実現方針

  • 結構時間をかけて調べたのですが…スマートなやり方が分かりませんでした。
    結果として、他の方も参考にされている次の記事の内容をベースに対応します。

  • 検証属性のエラーメッセージ変更方式
    • 検証失敗時のエラー情報を提供するためのプロバイダクラス(IValidationMetadataProviderを実装したクラス)を作成します。検証失敗時にこのクラス(メソッド)が呼び出されるので、必要に応じてエラーメッセージを設定します。
    • 上記記事のサンプルは次の検証属性に対応していない(メッセージを上書きできない)ため、独自に条件を修正しています。
      1. CreditCardAttribute
      2. EmailAddressAttribute
      3. PhoneAttribute
      4. UrlAttribute
    • 標準の検証属性は、次の例のように属性個別にメッセージを設定できます。このように個別に設定されたメッセージがある場合、そのメッセージを優先して表示するようにします。
  • モデルバインディングのエラーメッセージ変更方式
    startupのAddMvc/AddControllers/AddControllersWithViews()使用時のMVCオプションのModelBindingMessageProviderを使用して、モデルバインディング時の各種エラーに対応するエラーメッセージを設定します。
  • 日本語メッセージのリソースはResources.resxに定義します。
    詳細は「リソース定義」節をご覧ください。

エラーメッセージの日本語化方法

検証属性のエラーメッセージを日本語化する方法と、モデルバインディングのエラーメッセージを日本語化する方法について説明します。

検証属性のエラーメッセージ

検証属性でユーザ向けに使用するエラーメッセージを日本語化します。
これは、検証失敗時のエラー情報を提供するためのプロバイダクラスの作成と、サービスへのプロバイダクラスの登録で実現します。

プロバイダクラスの実装

IValidationMetadataProviderを実装したクラスを定義します。
検証属性がエラーメッセージを使用する際にCreateValidationMetadata()メソッドが実行されます。主に、サーバ側での入力検証の時に実行されますが、クライアント側検証が有効になっている場合は、クライアント側で使用するエラーメッセージをページに埋め込むためにページ生成時にも実行されます。
このメソッドの処理で、各検証属性のエラーメッセージを必要に応じて変更します。検証属性のエラーメッセージ(やリソース)が既存から変更されていた場合、そのエラーメッセージを使用します。既存からの変更がなく、その検証属性に対応するエラーメッセージ(日本語)が定義されている場合、そのエラーメッセージを使用します。
日本語のエラーメッセージはResourceで定義しています。詳細は「リソース定義」節をご覧ください。

エラーメッセージが既存から変更されたかどうかの判定に関する補足です。
当初、参考記事のサンプルのように「検証属性のエラーメッセージプロパティがnullの場合はメッセージは変更されていない。」という条件で良いと考えていました。実際にやってみると、CreditCard等の一部属性ではエラーメッセージを設定していないにも関わらず、エラーメッセージプロパティにメッセージが設定されており、誤判定されます。
そのため、次のように判定しています。(といっても、どうちらのケースも59行目のようにstring.Equals()の1行で判定しています。)

対象の属性既定のエラーメッセージ
(ErrorMessageの値)
「変更なし」の判定方法
CreditCard
EmailAddress
Phone
Url
“The {0} field is not a valid credit card number.”等のようなメッセージが設定されています。検証属性のエラーメッセージが左記の既定のエラーメッセージと一致する。
Compare
Range
RegularExpression
Required
StringLength
nullが設定されています。
(検証属性側でメッセージを生成)
検証属性のエラーメッセージがnullである。

プロバイダの登録

StartupのMVC系サービス登録時のMVCオプションとして上記のプロバイダクラスを指定します。

モデルバインディングのエラーメッセージ

モデルバインディングで使用するエラーメッセージを日本語化します。

MVCオプションのModelBindingMessageProviderを使って、各ケース毎のエラーメッセージを指定します。
string.formatをいちいち記載するのが嫌だったので、ラムダ式(Func)を使っています。
日本語のエラーメッセージはResourceで定義しています。詳細は「リソース定義」節をご覧ください。

リソースの定義

前述の検証属性やモデルバインディングのエラーメッセージ日本語化で使用しているリソースは次の通りです。

  • “Resource.resx”をデザイナではなくテキストエディタで開いた例です。
    コピーする場合は同様に開けば容易かと思います。
  • 用途が分かるようリソース名は次のルールで決定しています。
    • 検証属性のリソース名: “Validator_” + (属性クラス名)
    • モデルバインディングのリソース名: “ModelBinding_”+(プロパティ名)
  • 参考としてコメントに既存のメッセージを定義しています。
    既存のメッセージの追跡方法は「参考」をご覧ください。

参考

検証属性の既定のエラーメッセージ

ASP.NET Core標準の検証属性は次のように既定のエラーメッセージを参照しています。
この仕組みを追跡して既定のエラーメッセージを列挙しています。

  1. 検証属性の各コンストラクタにて、既定で使用するメッセージとしてのDataAnnotationsResourcesリソースのプロパティ名を指定している。
  2. 上記のリソースはDataAnnotationsResourcesで定義されている。
    このリソースには、入力値の検証エラー時に使用するユーザ向けのエラーメッセージと、検証属性に対するパラメータ不正時に使用されるエラーメッセージが定義されている。
    前者のリソース名は概ねRequiredAttribute_ValidationErrorのように(属性名)_ValidationErrorというネーミングになっている。

モデルバインディングの既定のエラーメッセージ

モデルバインディングでは次のように既定のエラーメッセージを参照しています。
この仕組みを追跡して既定のエラーメッセージを列挙しています。

  1. メッセージの設定で使用したMVCオプションのModelBindingMessageProviderプロパティの実体は、DefaultModelBindingMessageProviderである。このクラスのコンストラクタで各ケースで使用する既定のエラーメッセージをResourceのプロパティ名で指定している。
  2. 上記で参照しているリソースのプロパティは、リソースのデザイナで生成されたResources.Designer.csで定義されている。
  3. 上記のプロパティで参照しているリソース名はResources.resxで定義されている。

参考URL