티스토리 뷰

반응형

.NET Core 인증을 구현했을 때 에러메시지가 다음과 같이 영어로 나타날 때 한글화 하는 방법을 소개합니다.

  • Passwords must have at least one non alphanumeric character.
  • Passwords must have at least one lowercase ('a'-'z').
  • Passwords must have at least one uppercase ('A'-'Z').

방법1: IdentityErrorDescriber 재정의하기

직접 원하는 오류 메시지를 작성하고 싶다면 IdentityErrorDescriber Class를 Override해야 합니다.

  1. IdentityErrorDescriber를 상속하여 원하는 에러메시지로 Override합니다. 아래는 영문으로 작성된 코드이므로 적절히 한글화 하여 프로젝트 추가해주세요.
    영문 IdentityErrorDescriber
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class CustomIdentityErrorDescriber : 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 = $"Passwords must be at least {length} characters."  }; }
        public override IdentityError PasswordRequiresNonAlphanumeric() { return new IdentityError { Code = nameof(PasswordRequiresNonAlphanumeric), Description = "Passwords 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')." }; }
        public override IdentityError RecoveryCodeRedemptionFailed() { return new IdentityError { Code = nameof(RecoveryCodeRedemptionFailed), Description = "Recovery code was not redeemed." }; }
        public override IdentityError PasswordRequiresUniqueChars(int uniqueChars) { return new IdentityError { Code = nameof(PasswordRequiresUniqueChars), Description = "Passwords must use at least {0} different characters." }; }
    }
    한글화 후 IdentityErrorDescriber 예시
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class IdentityErrorDescriberKr : IdentityErrorDescriber
    {
        public override IdentityError DefaultError() { return new IdentityError { Code = nameof(DefaultError), Description = $"알 수 없는 오류가 발생했습니다." }; }
        public override IdentityError ConcurrencyFailure() { return new IdentityError { Code = nameof(ConcurrencyFailure), Description = "동시성 문제가 발생했습니다. 데이터가 다른 곳에서 수정된 것 같습니다." }; }
        public override IdentityError PasswordMismatch() { return new IdentityError { Code = nameof(PasswordMismatch), Description = "잘못된 비밀번호입니다." }; }
        public override IdentityError InvalidToken() { return new IdentityError { Code = nameof(InvalidToken), Description = "유효하지 않은 토큰입니다." }; }
        public override IdentityError LoginAlreadyAssociated() { return new IdentityError { Code = nameof(LoginAlreadyAssociated), Description = "로그인하려는 이 사용자는 이미 다른 계정과 연결되어있습니다." }; }
        public override IdentityError InvalidUserName(string userName) { return new IdentityError { Code = nameof(InvalidUserName), Description = $"사용자명 '{userName}'는 유효하지 않습니다. 허용되지 않은 문자가 포함되어 있습니다." }; }
        public override IdentityError InvalidEmail(string email) { return new IdentityError { Code = nameof(InvalidEmail), Description = $"이메일 '{email}'는 유효하지 않습니다." }; }
        public override IdentityError DuplicateUserName(string userName) { return new IdentityError { Code = nameof(DuplicateUserName), Description = $"사용자명 '{userName}'는 이미 사용 중입니다." }; }
        public override IdentityError DuplicateEmail(string email) { return new IdentityError { Code = nameof(DuplicateEmail), Description = $"이메일 '{email}'는 이미 사용 중 입니다." }; }
        public override IdentityError InvalidRoleName(string role) { return new IdentityError { Code = nameof(InvalidRoleName), Description = $"역할 '{role}'는 유효하지 않습니다." }; }
        public override IdentityError DuplicateRoleName(string role) { return new IdentityError { Code = nameof(DuplicateRoleName), Description = $"역할 '{role}'는 이미 사용 중 입니다." }; }
        public override IdentityError UserAlreadyHasPassword() { return new IdentityError { Code = nameof(UserAlreadyHasPassword), Description = "사용자가 이미 비밀번호를 설정했습니다." }; }
        public override IdentityError UserLockoutNotEnabled() { return new IdentityError { Code = nameof(UserLockoutNotEnabled), Description = "이 사용자는 Lockout이 설정되지 않았습니다." }; }
        public override IdentityError UserAlreadyInRole(string role) { return new IdentityError { Code = nameof(UserAlreadyInRole), Description = $"역할 '{role}'에 존재하는 사용자입니다." }; }
        public override IdentityError UserNotInRole(string role) { return new IdentityError { Code = nameof(UserNotInRole), Description = $"역할 '{role}'에 포함되지 않은 사용자입니다." }; }
        public override IdentityError PasswordTooShort(int length) { return new IdentityError { Code = nameof(PasswordTooShort), Description = $"비밀번호는 최소 {length}자 이상이어야 합니다." }; }
        public override IdentityError PasswordRequiresNonAlphanumeric() { return new IdentityError { Code = nameof(PasswordRequiresNonAlphanumeric), Description = "비밀번호에는 영문자, 숫자가 아닌 문자가 하나 이상 있어야 합니다." }; }
        public override IdentityError PasswordRequiresDigit() { return new IdentityError { Code = nameof(PasswordRequiresDigit), Description = "비밀번호에는 적어도 하나의 숫자가 있어야합니다." }; }
        public override IdentityError PasswordRequiresLower() { return new IdentityError { Code = nameof(PasswordRequiresLower), Description = "비밀번호에는 영어 소문자가 하나 이상 있어야합니다." }; }
        public override IdentityError PasswordRequiresUpper() { return new IdentityError { Code = nameof(PasswordRequiresUpper), Description = "비밀번호에는 영어 대문자가 하나 이상 있어야합니다." }; }
        public override IdentityError RecoveryCodeRedemptionFailed() { return new IdentityError { Code = nameof(RecoveryCodeRedemptionFailed), Description = "복구 코드 사용에 실패했습니다." }; }
        public override IdentityError PasswordRequiresUniqueChars(int uniqueChars) { return new IdentityError { Code = nameof(PasswordRequiresUniqueChars), Description = "비밀번호는 최소한 {0}개의 다른 문자를 사용해야합니다." }; }
    }
  2. Startup.cs의 ConfigureServices를 열고 AddIdentity 메서드 다음에 아래와 같이 AddErrorDescriber 메서드를 추가해주시면 됩니다.
    1
    2
    services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddErrorDescriber<IdentityErrorDescriberKr>();

방법2: Resources(.resx) 파일 사용하기

여러 국가의 언어로 서비스할 예정이라면 Resources(.resx)파일을 이용하는 방법이 권장됩니다. Microsoft에서도 이와 같이 구현하고 있으며 Resources.rexs(Github)IdentityErrorDescriber.cs(Github)에서 확인할 수 있습니다.

  1. 먼저 프로젝트에 Resources 폴더를 추가하고 .resx 파일을 추가합니다.
    Resources에 추가된 .resx 지역화 파일
    파일 명 뒤에 .ko-KR, .ja-JP는 .NET의 언어-국가 코드입니다. C#언어-국가 코드ISO 언어코드, MSDN(CultureInfo)를 참고하셔서 필요한 언어별로 파일을 추가하시면 됩니다.
  2. IdentityErrorMessages.resx의 파일을 텍스트 에디터로 열고 아래 코드를 추가해주세요.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    <data name="ConcurrencyFailure" xml:space="preserve">
        <value>Optimistic concurrency failure, object has been modified.</value>
        <comment>Error when optimistic concurrency fails</comment>
    </data>
    <data name="DefaultError" xml:space="preserve">
        <value>An unknown failure has occurred.</value>
        <comment>Default identity result error message</comment>
    </data>
    <data name="DuplicateEmail" xml:space="preserve">
        <value>Email '{0}' is already taken.</value>
        <comment>Error for duplicate emails</comment>
    </data>
    <data name="DuplicateRoleName" xml:space="preserve">
        <value>Role name '{0}' is already taken.</value>
        <comment>Error for duplicate roles</comment>
    </data>
    <data name="DuplicateUserName" xml:space="preserve">
        <value>User name '{0}' is already taken.</value>
        <comment>Error for duplicate user names</comment>
    </data>
    <data name="InvalidEmail" xml:space="preserve">
        <value>Email '{0}' is invalid.</value>
        <comment>Invalid email</comment>
    </data>
    <data name="InvalidRoleName" xml:space="preserve">
        <value>Role name '{0}' is invalid.</value>
        <comment>Error for invalid role names</comment>
    </data>
    <data name="InvalidToken" xml:space="preserve">
        <value>Invalid token.</value>
        <comment>Error when a token is not recognized</comment>
    </data>
    <data name="InvalidUserName" xml:space="preserve">
        <value>User name '{0}' is invalid, can only contain letters or digits.</value>
        <comment>User names can only contain letters or digits</comment>
    </data>
    <data name="LoginAlreadyAssociated" xml:space="preserve">
        <value>A user with this login already exists.</value>
        <comment>Error when a login already linked</comment>
    </data>
    <data name="PasswordMismatch" xml:space="preserve">
        <value>Incorrect password.</value>
        <comment>Error when a password doesn't match</comment>
    </data>
    <data name="PasswordRequiresDigit" xml:space="preserve">
        <value>Passwords must have at least one digit ('0'-'9').</value>
        <comment>Error when passwords do not have a digit</comment>
    </data>
    <data name="PasswordRequiresLower" xml:space="preserve">
        <value>Passwords must have at least one lowercase ('a'-'z').</value>
        <comment>Error when passwords do not have a lowercase letter</comment>
    </data>
    <data name="PasswordRequiresNonAlphanumeric" xml:space="preserve">
        <value>Passwords must have at least one non alphanumeric character.</value>
        <comment>Error when password does not have enough non alphanumeric characters</comment>
    </data>
    <data name="PasswordRequiresUpper" xml:space="preserve">
        <value>Passwords must have at least one uppercase ('A'-'Z').</value>
        <comment>Error when passwords do not have an uppercase letter</comment>
    </data>
    <data name="PasswordTooShort" xml:space="preserve">
        <value>Passwords must be at least {0} characters.</value>
        <comment>Error message for passwords that are too short</comment>
    </data>
    <data name="RecoveryCodeRedemptionFailed" xml:space="preserve">
        <value>Recovery code redemption failed.</value>
        <comment>Error when a recovery code is not redeemed.</comment>
    </data>
    <data name="UserAlreadyHasPassword" xml:space="preserve">
        <value>User already has a password set.</value>
        <comment>Error when AddPasswordAsync called when a user already has a password</comment>
    </data>
    <data name="UserAlreadyInRole" xml:space="preserve">
        <value>User already in role '{0}'.</value>
        <comment>Error when a user is already in a role</comment>
    </data>
    <data name="UserLockoutNotEnabled" xml:space="preserve">
        <value>Lockout is not enabled for this user.</value>
        <comment>Error when lockout is not enabled</comment>
    </data>
    <data name="UserNotInRole" xml:space="preserve">
        <value>User is not in role '{0}'.</value>
        <comment>Error when a user is not in the role</comment>
    </data>
    <data name="PasswordRequiresUniqueChars" xml:space="preserve">
        <value>Passwords must use at least {0} different characters.</value>
        <comment>Error message for passwords that are based on similar characters</comment>
    </data>
    저장 후 IdentityErrorMessages.resx 파일을 VisualStudio에서 파일을 열면 아래와 같이 나타납니다.
    IdentityErrorMessages.rexs 파일내용
  3. IdentityErrorMessages.ko-KR.rexs과 IdentityErrorMessages.ja-JP.rexs도 동일하게 텍스트 편집기로 열고 코드를 추가하신 후 내용을 한국어, 일어로 번역해주세요.
  4. IdentityErrorMessage.resx 파일을 Visual Studio에서 열어서 Access modifiers를 Public로 변경합니다.
    .resx파일의 Access modifier를 Public로 설정
  5. IdentityErrorDescriber를 Orverride하여 Resources를 사용하도록 작성하면 됩니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class LocalizedIdentityErrorDescriber : IdentityErrorDescriber
    {
        public override IdentityError DuplicateEmail(string email) { return new IdentityError { Code = nameof(DuplicateEmail), Description = string.Format(IdentityErrorMessages.DuplicateEmail, email) }; }
        public override IdentityError DuplicateUserName(string userName) { return new IdentityError { Code = nameof(DuplicateUserName), Description = string.Format(IdentityErrorMessages.DuplicateUserName, userName) }; }
        public override IdentityError InvalidEmail(string email) { return new IdentityError { Code = nameof(InvalidEmail), Description = string.Format(IdentityErrorMessages.InvalidEmail, email) }; }
        public override IdentityError DuplicateRoleName(string role) { return new IdentityError { Code = nameof(DuplicateRoleName), Description = string.Format(IdentityErrorMessages.DuplicateRoleName, role) }; }
        public override IdentityError InvalidRoleName(string role) { return new IdentityError { Code = nameof(InvalidRoleName), Description = string.Format(IdentityErrorMessages.InvalidRoleName, role) }; }
        public override IdentityError InvalidToken() { return new IdentityError { Code = nameof(InvalidToken), Description = IdentityErrorMessages.InvalidToken }; }
        public override IdentityError InvalidUserName(string userName) { return new IdentityError { Code = nameof(InvalidUserName), Description = string.Format(IdentityErrorMessages.InvalidUserName, userName) }; }
        public override IdentityError LoginAlreadyAssociated() { return new IdentityError { Code = nameof(LoginAlreadyAssociated), Description = IdentityErrorMessages.LoginAlreadyAssociated }; }
        public override IdentityError PasswordMismatch() { return new IdentityError { Code = nameof(PasswordMismatch), Description = IdentityErrorMessages.PasswordMismatch }; }
        public override IdentityError PasswordRequiresDigit() { return new IdentityError { Code = nameof(PasswordRequiresDigit), Description = IdentityErrorMessages.PasswordRequiresDigit }; }
        public override IdentityError PasswordRequiresLower() { return new IdentityError { Code = nameof(PasswordRequiresLower), Description = IdentityErrorMessages.PasswordRequiresLower }; }
        public override IdentityError PasswordRequiresNonAlphanumeric() { return new IdentityError { Code = nameof(PasswordRequiresNonAlphanumeric), Description = IdentityErrorMessages.PasswordRequiresNonAlphanumeric }; }
        public override IdentityError PasswordRequiresUniqueChars(int uniqueChars) { return new IdentityError { Code = nameof(PasswordRequiresUniqueChars), Description = string.Format(IdentityErrorMessages.PasswordRequiresUniqueChars, uniqueChars) }; }
        public override IdentityError PasswordRequiresUpper() { return new IdentityError { Code = nameof(PasswordRequiresUpper), Description = IdentityErrorMessages.PasswordRequiresUpper }; }
        public override IdentityError PasswordTooShort(int length) { return new IdentityError { Code = nameof(PasswordTooShort), Description = string.Format(IdentityErrorMessages.PasswordTooShort, length) }; }
        public override IdentityError UserAlreadyHasPassword() { return new IdentityError { Code = nameof(UserAlreadyHasPassword), Description = IdentityErrorMessages.UserAlreadyHasPassword }; }
        public override IdentityError UserAlreadyInRole(string role) { return new IdentityError { Code = nameof(UserAlreadyInRole), Description = string.Format(IdentityErrorMessages.UserAlreadyInRole, role) }; }
        public override IdentityError UserNotInRole(string role) { return new IdentityError { Code = nameof(UserNotInRole), Description = string.Format(IdentityErrorMessages.UserNotInRole, role) }; }
        public override IdentityError UserLockoutNotEnabled() { return new IdentityError { Code = nameof(UserLockoutNotEnabled), Description = IdentityErrorMessages.UserLockoutNotEnabled }; }
        public override IdentityError RecoveryCodeRedemptionFailed() { return new IdentityError { Code = nameof(RecoveryCodeRedemptionFailed), Description = IdentityErrorMessages.RecoveryCodeRedemptionFailed }; }
        public override IdentityError ConcurrencyFailure() { return new IdentityError { Code = nameof(ConcurrencyFailure), Description = IdentityErrorMessages.ConcurrencyFailure }; }
        public override IdentityError DefaultError() { return new IdentityError { Code = nameof(DefaultError), Description = IdentityErrorMessages.DefaultError }; }
    }
  6. 마지막으로 Startup.cs의 ConfigureServices를 열고 AddIdentity 메서드 다음에 아래와 같이 AddErrorDescriber 메서드를 추가해주시면 됩니다.
    1
    2
    services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddErrorDescriber<LocalizedIdentityErrorDescriber>();

방법3: Nuget-Package에서 Identity.Core 한국어 설치

누겟에서 검색어 Microsoft.AspNet.Identity.Core를 입력하시면 Microsoft.AspNet.Identity.Core.ko 패키지가 나타납니다. 이 패키지를 설치하면 지역화가 된다고 합니다. (주의: 아직 테스트 되지 않은 방법입니다. 실제로 동작하지 않을 수도 있습니다.)

Nuget에서 한국어 Identity.Core 설치

테스트 방법

브라우저를 열고 설정에서 선호 언어를 변경하시면 해당 언어로 변경되어 나타납니다. 예를들어 IE에서는 [인터넷 옵션]의 [언어]에서 변경하실 수 있습니다.

IE의 언어설정 방법

'Web > ASP.NET Core' 카테고리의 다른 글

ASP.NET Core에 Vue앱 추가하기  (0) 2023.11.15
[ASP.NET Core] 배포 프로필 (.pubxml)  (1) 2022.04.01
Application State vs 전역 Static 변수  (0) 2019.08.19
댓글