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



基本的には[この記事](https://blogs.msdn.microsoft.com/mvpawardprogram/2017/05/09/aspnetcore-mvc-error-message/)の通りにやれば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に載ってました](https://stackoverflow.com/questions/19961648/how-to-localize-asp-net-identity-username-and-password-error-messages)
以下のクラスを作成して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();

```

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