20
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

ASP.NET Coreでエラーメッセージを日本語で表示する

はじめに

ASP.NET Coreを使ったことがある人は皆経験したであろう、(したよね?)
「Validationのエラーメッセージが英語で表示される・・・だと」問題。
調べたところ、日本語で書かれた資料が見つけらなかったので、まとめておきます。

※多言語化の話ではありませんのでご注意ください
※もっと簡単な方法があったら教えてください(appsettingsにLocale設定するとか)

環境
Windows10 Pro 64bit
VisualStudio Pro 2017 Version 15.3.4
ASP.NET Core 2.0 Razor Pages

基本的にはこの記事の通りにやればOKです。

resxファイルの作成

まずは適当にフォルダ切るなりして、お馴染みのresxファイルを作成します。
私はプロジェクト直下に「Resources」というフォルダを作成して
そこにDefaultValidationResource.resxという名前でいれておきました

名前 値  
RequiredAttribute {0}は必須項目です
MaxLengthAttribute {0}は{1}文字以内で入力してください
EmailAddressAttribute {0}の形式が正しくありません。

こんな感じで追加していきます。

ValidationMetadataProviderを作る

以下のクラスも追加します。
元記事のソースだと何故かEmailAddressAttributeだけ取りこぼすので条件を追加してあります。

using System;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Resources;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;

namespace SampleProject.Resources
{
    public class ValidationMetadataProviderJp : IValidationMetadataProvider
    {
        private ResourceManager resourceManager; private Type resourceType;
        public ValidationMetadataProviderJp(string baseName, Type type)
        {
            resourceType = type;
            resourceManager = new ResourceManager(baseName,
                type.GetTypeInfo().Assembly);
        }
        public void CreateValidationMetadata(
            ValidationMetadataProviderContext context)
        {
            if (context.Key.ModelType.GetTypeInfo().IsValueType &&
                context.ValidationMetadata.ValidatorMetadata.Where(m => m.GetType() == typeof(RequiredAttribute)).Count() == 0)
                context.ValidationMetadata.ValidatorMetadata.Add(new RequiredAttribute());

            foreach (var attribute in context.ValidationMetadata.ValidatorMetadata)
            {
                ValidationAttribute tAttr = attribute as ValidationAttribute;
                if (tAttr != null&& tAttr.ErrorMessageResourceName == null)
                {
                    //何故かEmailAddressAttributeはErrorMessageがデフォルトでnullにならない。
                    if (tAttr.ErrorMessage == null || (attribute as EmailAddressAttribute != null && !string.IsNullOrEmpty(tAttr.ErrorMessage)))
                    {
                        var name = tAttr.GetType().Name;
                        if (resourceManager.GetString(name) != null)
                        {
                            tAttr.ErrorMessageResourceType = resourceType;
                            tAttr.ErrorMessageResourceName = name;
                            tAttr.ErrorMessage = null;
                        }
                    }
                }
            }
        }

    }
}

Startup.csを変更する

ConfigureServicesメソッド内に以下を追加

Startup.cs
services.AddMvc(o =>
{
    o.ModelMetadataDetailsProviders.Add(
        new ValidationMetadataProviderJp (
            "SampleProject.Resources.DefaultValidationResource",
            typeof(DefaultValidationResource)));
});

これでresxファイルに定義されているAttributeのエラーメッセージは日本語化されるはずです。

ログイン画面とかのエラーメッセージがまだ英語なんだけど!

StackOverFlowに載ってました
以下のクラスを作成してDescriptionを日本語にしてください

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')." }; }
}

Startup.csをさらに変更

Startup.cs
    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddErrorDescriber<Resources.IdentityErrorDescriberJP>() //←これ
        .AddDefaultTokenProviders();

おわりに

これで出来たんだけど、何かつらい・・・。
絶対他にもっと簡単な方法あるだろと思ってるので誰か教えてください(切実)

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
20
Help us understand the problem. What are the problem?