shuhelohelo’s blog

Xamarin.Forms多めです.

ASP.NET CoreでIdentityのバリデーションメッセージを日本語化する

ASP.NETはパスワードやEmailの入力に対するバリデーションの機能がついていて便利です。

f:id:shuhelohelo:20190822200830p:plain

便利なのですが、デフォルトでは英語です。これを日本語で表示させる必要があったりします。

今回は認証機能(Identity)のバリデーションに関するメッセージの日本語表示の方法です。

環境

参考↓

qiita.com

qiita.com

IdentityErrorDescriberをoverride

まずはこのデフォルトのメッセージがどこからきているのかを確認してみます。 Microsoft.AspNetCore.Identity名前空間の中に、IdentityErrorDescriber(こちら)クラスがあります。

これをみるとvirtualなメソッドがたくさんあって、それぞれのメソッドはIdentityErrorクラスのインスタンスのDescriptionにプロパティにResourcesという静的クラスのプロパティをセットしてリターンしています。

このResourcesこちら)を見てみると、Identity関連のメッセージが定義されていることがわかります。

つまり、IdentityErrorDescriberクラスの各メソッドをoverrideすることで任意のメッセージを設定できることがわかります。

IdentityErrorDescriberを継承したクラスを作成する

ここでは、参考のQiitaの記事にもあるように、StackOverFlowでIdentityErrorDescriberクラスを継承したクラスのひな型を使います。

プロジェクト内にファイルを追加して、以下のクラスを定義します。

public class IdentityErrorDescriberJP : IdentityErrorDescriber
{
    public override IdentityError DefaultError() { return new IdentityError { Code = nameof(DefaultError), Description = $"An unknown failure has occurred." }; }
    public override IdentityError ConcurrencyFailure() { return new IdentityError { Code = nameof(ConcurrencyFailure), Description = "Optimistic concurrency failure, object has been modified." }; }
    public override IdentityError PasswordMismatch() { return new IdentityError { Code = nameof(PasswordMismatch), Description = "Incorrect password." }; }
    public override IdentityError InvalidToken() { return new IdentityError { Code = nameof(InvalidToken), Description = "Invalid token." }; }
    public override IdentityError LoginAlreadyAssociated() { return new IdentityError { Code = nameof(LoginAlreadyAssociated), Description = "A user with this login already exists." }; }
    public override IdentityError InvalidUserName(string userName) { return new IdentityError { Code = nameof(InvalidUserName), Description = $"User name '{userName}' is invalid, can only contain letters or digits." }; }
    public override IdentityError InvalidEmail(string email) { return new IdentityError { Code = nameof(InvalidEmail), Description = $"Email '{email}' is invalid." }; }
    public override IdentityError DuplicateUserName(string userName) { return new IdentityError { Code = nameof(DuplicateUserName), Description = $"User Name '{userName}' is already taken." }; }
    public override IdentityError DuplicateEmail(string email) { return new IdentityError { Code = nameof(DuplicateEmail), Description = $"Email '{email}' is already taken." }; }
    public override IdentityError InvalidRoleName(string role) { return new IdentityError { Code = nameof(InvalidRoleName), Description = $"Role name '{role}' is invalid." }; }
    public override IdentityError DuplicateRoleName(string role) { return new IdentityError { Code = nameof(DuplicateRoleName), Description = $"Role name '{role}' is already taken." }; }
    public override IdentityError UserAlreadyHasPassword() { return new IdentityError { Code = nameof(UserAlreadyHasPassword), Description = "User already has a password set." }; }
    public override IdentityError UserLockoutNotEnabled() { return new IdentityError { Code = nameof(UserLockoutNotEnabled), Description = "Lockout is not enabled for this user." }; }
    public override IdentityError UserAlreadyInRole(string role) { return new IdentityError { Code = nameof(UserAlreadyInRole), Description = $"User already in role '{role}'." }; }
    public override IdentityError UserNotInRole(string role) { return new IdentityError { Code = nameof(UserNotInRole), Description = $"User is not in role '{role}'." }; }
    public override IdentityError PasswordTooShort(int length) { return new IdentityError { Code = nameof(PasswordTooShort), Description = $"password must be at least {length} characters." }; }
    public override IdentityError PasswordRequiresNonAlphanumeric() { return new IdentityError { Code = nameof(PasswordRequiresNonAlphanumeric), Description = "password must have at least one non alphanumeric character." }; }
    public override IdentityError PasswordRequiresDigit() { return new IdentityError { Code = nameof(PasswordRequiresDigit), Description = "Passwords must have at least one digit ('0'-'9')." }; }
    public override IdentityError PasswordRequiresLower() { return new IdentityError { Code = nameof(PasswordRequiresLower), Description = "Passwords must have at least one lowercase ('a'-'z')." }; }
    public override IdentityError PasswordRequiresUpper() { return new IdentityError { Code = nameof(PasswordRequiresUpper), Description = "Passwords must have at least one uppercase ('A'-'Z')." }; }
}

このひな型の各メソッド内のDescription="...."を書き換えれば、対応するバリデーションメッセージが変わります。

例えば以下のようにすると、「パスワードにアルファベットの大文字が含まれないとき」のメッセージが変わります。(クラスを定義しただけなので、これだけでは変わりませんが。)

        public override IdentityError PasswordRequiresUpper() { return new IdentityError { Code = nameof(PasswordRequiresUpper), Description = "アルファベットの大文字 ('A'-'Z')を1文字以上含んでください。" }; }

ミドルウェアを追加

バリデーションメッセージとして上記クラスの設定を使用するには、ミドルウェアを追加する必要があります。

Startup.csを開いて、ConfigureServicesメソッドの中で、Identityの利用を追加しているところで、先程のクラスを利用するように、AddErrorDescriberメソッドを追加します。

            services.AddDefaultIdentity<IdentityUser>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddErrorDescriber<IdentityErrorDescriberJp>();//<--これを追加

これで、IdentityErrorDescriberJpクラスが使用されます。

実行

ASP.NET Coreのプロジェクトテンプレートから認証を使用するプロジェクトを作成し、Registerページでユーザー登録を試してみるとお手軽だと思います。

小文字のみのパスワードを入力して「Register」ボタンを押すと、以下のように先程設定した日本語のメッセージが表示されます。

f:id:shuhelohelo:20190822195843p:plain