【问题标题】:How to translate Identity Password validation messages如何翻译身份密码验证消息
【发布时间】:2020-12-22 15:21:12
【问题描述】:

到目前为止,我已经能够翻译 ASP.Net Core 2.1 Web 应用程序中的所有内容。

事实证明这是一个小挑战,因为脚手架的帐户页面需要一些设置。

但我找不到翻译密码验证消息的方法。此外,翻译模型绑定消息是一个小挑战(感谢 stackoverflow)。

有什么想法吗?

我包含了我的Startup.cs 文件的相关部分:

public void ConfigureServices(IServiceCollection services)
{
     ...

     services.AddMvc(options =>
     {
         var type = typeof(SharedResources);
         var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
         var factory = services.BuildServiceProvider().GetService<IStringLocalizerFactory>();
         var L = factory.Create("SharedResources", assemblyName.Name);

         options.ModelBindingMessageProvider.SetValueIsInvalidAccessor(x => L["The value '{0}' is invalid.", x]);
         options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(x => L["The value '{0}' is invalid.", x]);
         options.ModelBindingMessageProvider.SetValueMustBeANumberAccessor(x => L["The field {0} must be a number.", x]);
         options.ModelBindingMessageProvider.SetMissingBindRequiredValueAccessor(x => L["A value for the '{0}' property was not provided.", x]);
         options.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor((x, y) => L["The value '{0}' is not valid for {1}.", x, y]);
         options.ModelBindingMessageProvider.SetMissingKeyOrValueAccessor(() => L["A value is required."]);
         options.ModelBindingMessageProvider.SetUnknownValueIsInvalidAccessor(x => L["The supplied value is invalid for {0}.", x]);
         options.ModelBindingMessageProvider.SetMissingRequestBodyRequiredValueAccessor(() => L["A non-empty request body is required."]);
         options.ModelBindingMessageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor(x => L["The value '{0}' is not valid.", x]);
         options.ModelBindingMessageProvider.SetNonPropertyUnknownValueIsInvalidAccessor(() => L["The supplied value is invalid."]);
         options.ModelBindingMessageProvider.SetNonPropertyValueMustBeANumberAccessor(() => L["NonPropertyValueMustBeNumber"]);
     })
     .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
     .AddDataAnnotationsLocalization(options =>
        {
            options.DataAnnotationLocalizerProvider = (type, factory) =>
            {
                // This is for Account scaffolded pages data annotations
                return factory.Create(typeof(SharedResources));
            };
        });

     ...

}

我不能在Register.cshtml.csInputModel 中添加这样的内容,因为ErrorMessage 会被忽略(反正我不会这样做,因为我不想硬编码密码策略描述):

        [Required(ErrorMessage = "The {0} field is required.")]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password, ErrorMessage = "Password must be nice")]
        [Display(Name = "Password")]
        public string Password { get; set; }

【问题讨论】:

    标签: c# asp.net-core asp.net-core-2.1 asp.net-core-identity


    【解决方案1】:

    这可以通过本地化身份错误消息来完成,有 22 条消息需要本地化。

    首先,创建一个共享资源文件“其键定义为公共访问修饰符”,并键入所有错误消息以及本地化版本,如下图所示:

    然后创建一个实现IdentityErrorDescriber的新类,并参考共享资源文件覆盖所有默认消息;在此示例中,共享资源文件名为 LocalizedIdentityErrorMessages:

    public class LocalizedIdentityErrorDescriber : IdentityErrorDescriber
        {
            public override IdentityError DuplicateEmail(string email)
            {
                return new IdentityError
                {
                    Code = nameof(DuplicateEmail),
                    Description = string.Format(LocalizedIdentityErrorMessages.DuplicateEmail, email)
                };
            }
    
            public override IdentityError DuplicateUserName(string userName)
            {
                return new IdentityError
                {
                    Code = nameof(DuplicateUserName),
                    Description = string.Format(LocalizedIdentityErrorMessages.DuplicateUserName, userName)
                };
            }
    
            public override IdentityError InvalidEmail(string email)
            {
                return new IdentityError
                {
                    Code = nameof(InvalidEmail),
                    Description = string.Format(LocalizedIdentityErrorMessages.InvalidEmail, email)
                };
            }
    
            public override IdentityError DuplicateRoleName(string role)
            {
                return new IdentityError
                {
                    Code = nameof(DuplicateRoleName),
                    Description = string.Format(LocalizedIdentityErrorMessages.DuplicateRoleName, role)
                };
            }
    
            public override IdentityError InvalidRoleName(string role)
            {
                return new IdentityError
                {
                    Code = nameof(InvalidRoleName),
                    Description = string.Format(LocalizedIdentityErrorMessages.InvalidRoleName, role)
                };
            }
    
            public override IdentityError InvalidToken()
            {
                return new IdentityError
                {
                    Code = nameof(InvalidToken),
                    Description = LocalizedIdentityErrorMessages.InvalidToken
                };
            }
    
            public override IdentityError InvalidUserName(string userName)
            {
                return new IdentityError
                {
                    Code = nameof(InvalidUserName),
                    Description = string.Format(LocalizedIdentityErrorMessages.InvalidUserName, userName)
                };
            }
    
            public override IdentityError LoginAlreadyAssociated()
            {
                return new IdentityError
                {
                    Code = nameof(LoginAlreadyAssociated),
                    Description = LocalizedIdentityErrorMessages.LoginAlreadyAssociated
                };
            }
    
            public override IdentityError PasswordMismatch()
            {
                return new IdentityError
                {
                    Code = nameof(PasswordMismatch),
                    Description = LocalizedIdentityErrorMessages.PasswordMismatch
                };
            }
    
            public override IdentityError PasswordRequiresDigit()
            {
                return new IdentityError
                {
                    Code = nameof(PasswordRequiresDigit),
                    Description = LocalizedIdentityErrorMessages.PasswordRequiresDigit
                };
            }
    
            public override IdentityError PasswordRequiresLower()
            {
                return new IdentityError
                {
                    Code = nameof(PasswordRequiresLower),
                    Description = LocalizedIdentityErrorMessages.PasswordRequiresLower
                };
            }
    
            public override IdentityError PasswordRequiresNonAlphanumeric()
            {
                return new IdentityError
                {
                    Code = nameof(PasswordRequiresNonAlphanumeric),
                    Description = LocalizedIdentityErrorMessages.PasswordRequiresNonAlphanumeric
                };
            }
    
            public override IdentityError PasswordRequiresUniqueChars(int uniqueChars)
            {
                return new IdentityError
                {
                    Code = nameof(PasswordRequiresUniqueChars),
                    Description = string.Format(LocalizedIdentityErrorMessages.PasswordRequiresUniqueChars, uniqueChars)
                };
            }
    
            public override IdentityError PasswordRequiresUpper()
            {
                return new IdentityError
                {
                    Code = nameof(PasswordRequiresUpper),
                    Description = LocalizedIdentityErrorMessages.PasswordRequiresUpper
                };
            }
    
            public override IdentityError PasswordTooShort(int length)
            {
                return new IdentityError
                {
                    Code = nameof(PasswordTooShort),
                    Description = string.Format(LocalizedIdentityErrorMessages.PasswordTooShort, length)
                };
            }
    
            public override IdentityError UserAlreadyHasPassword()
            {
                return new IdentityError
                {
                    Code = nameof(UserAlreadyHasPassword),
                    Description = LocalizedIdentityErrorMessages.UserAlreadyHasPassword
                };
            }
    
            public override IdentityError UserAlreadyInRole(string role)
            {
                return new IdentityError
                {
                    Code = nameof(UserAlreadyInRole),
                    Description = string.Format(LocalizedIdentityErrorMessages.UserAlreadyInRole, role)
                };
            }
    
            public override IdentityError UserNotInRole(string role)
            {
                return new IdentityError
                {
                    Code = nameof(UserNotInRole),
                    Description = string.Format(LocalizedIdentityErrorMessages.UserNotInRole, role)
                };
            }
    
            public override IdentityError UserLockoutNotEnabled()
            {
                return new IdentityError
                {
                    Code = nameof(UserLockoutNotEnabled),
                    Description = LocalizedIdentityErrorMessages.UserLockoutNotEnabled
                };
            }
    
            public override IdentityError RecoveryCodeRedemptionFailed()
            {
                return new IdentityError
                {
                    Code = nameof(RecoveryCodeRedemptionFailed),
                    Description = LocalizedIdentityErrorMessages.RecoveryCodeRedemptionFailed
                };
            }
    
            public override IdentityError ConcurrencyFailure()
            {
                return new IdentityError
                {
                    Code = nameof(ConcurrencyFailure),
                    Description = LocalizedIdentityErrorMessages.ConcurrencyFailure
                };
            }
    
            public override IdentityError DefaultError()
            {
                return new IdentityError
                {
                    Code = nameof(DefaultError),
                    Description = LocalizedIdentityErrorMessages.DefaultIdentityError
                };
            }
        }
    

    最后,将本地化错误描述器添加到启动类的ConfigureServices方法下的身份设置中:

    services.AddIdentity<AppUser, AppRole>()
            // localize identity error messages
            .AddErrorDescriber<LocalizedIdentityErrorDescriber>()
            .AddEntityFrameworkStores()
            .AddDefaultTokenProviders();
    

    资源:http://www.ziyad.info/en/articles/20-Localizing_Identity_Error_Messages

    此外,您可能需要阅读分步本地化文章: http://www.ziyad.info/en/articles/10-Developing_Multicultural_Web_Application

    更新 - 2020 年 12 月

    最近我开发了一个新的 nuget 包(XLocalizer),它简化了 Asp.Net Core Web 应用程序的本地化设置,它支持自动在线翻译和自动资源创建。此外,所有身份错误、模型绑定错误和验证错误都可以在 json 文件中轻松自定义。

    参考资料:

    【讨论】:

    【解决方案2】:

    这些错误消息是使用IdentityErrorDescriber 生成的。下面是类本身的样例:

    public class IdentityErrorDescriber
    {
        ...
    
        public virtual IdentityError PasswordTooShort(int length)
        {
            return new IdentityError
            {
                Code = nameof(PasswordTooShort),
                Description = Resources.FormatPasswordTooShort(length)
            };
        }
    
    
        ...
    }
    

    要自定义特定消息,请创建您自己的 IdentityErrorDescriber 实现。这是一个例子:

    public class MyIdentityErrorDescriber : IdentityErrorDescriber
    {    
        public override IdentityError PasswordTooShort(int length)
        {
            return new IdentityError
            {
                Code = nameof(PasswordTooShort),
                Description = "Your description goes here."
            };
        }
    }
    

    要使用这个新实现,请将其添加到Startup.ConfigureServices 中的 DI 容器中:

    services.AddScoped<IdentityErrorDescriber, MyIdentityErrorDescriber>();
    

    【讨论】:

      【解决方案3】:

      您可以使用ViewModel 而不是只使用DataAnnotations 这是ControllerViewModelView 在这种情况下的外观(翻译为克罗地亚语)。

      控制器

          [HttpGet]
          public IActionResult Login()
          {
              return View();
          }
      
          [HttpPost]
          public async Task<IActionResult> Login(LoginViewModel model)
          {
              if (ModelState.IsValid)
              {
                  var result = await signInManager.PasswordSignInAsync(
                      model.Email, model.Password, model.RememberMe, false);
      
                  if (result.Succeeded)
                  {
                      return RedirectToAction("index", "home");
                  }
      
                  ModelState.AddModelError(string.Empty, "Neuspješni pokušaj");
              }
      
              return View(model);
          }
      

      视图模型

      ErrorMessage下面放上你的错误信息

      using System;
      using System.Collections.Generic;
      using System.ComponentModel.DataAnnotations;
      using System.Linq;
      using System.Threading.Tasks;
      
      namespace MyApplication.ViewModels
      {
          public class LoginViewModel
          {
              [Required(ErrorMessage = "Email je obavezan")]
              [EmailAddress]
              public string Email { get; set; }
      
              [DataType(DataType.Password)]
              [Display(Name = "Lozinka")]
              [Required(ErrorMessage = "Lozinka je obavezna")]
              public string Password { get; set; }
      
              [Display(Name = "Zapamti me")]
              public bool RememberMe { get; set; }
          }
      }
      

      登录查看

      @model LoginViewModel
      
      @{
          ViewBag.Title = "Prijava";
      }
      
      <h2>Prijava</h2>
      
      <div class="row">
          <div class="col-md-12">
              <form method="post">
                  <div asp-validation-summary="All" class="text-danger"></div>
                  <div class="form-group">
                      <label asp-for="Email"></label>
                      <input asp-for="Email" class="form-control" />
                      <span asp-validation-for="Email" class="text-danger"></span>
                  </div>
                  <div class="form-group">
                      <label asp-for="Password"></label>
                      <input asp-for="Password" class="form-control" />
                      <span asp-validation-for="Password" class="text-danger"></span>
                  </div>
                  <div class="form-group">
                      <div class="checkbox">
                          <label asp-for="RememberMe">
                              <input asp-for="RememberMe" />
                              @Html.DisplayNameFor(m => m.RememberMe)
                          </label>
                      </div>
                  </div>
                  <button type="submit" class="btn btn-primary">Prijava</button>
              </form>
          </div>
      </div>
      

      【讨论】:

        猜你喜欢
        • 2021-04-27
        • 1970-01-01
        • 2020-09-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多