Подтвердить что ты не робот

Локализация Identity Asp.Net PublicKeyToken

Я пытаюсь получить локализованные сообщения об ошибках для шведского языка для Asp.Net Identity, используя советы из этого сообщения: Как локализовать сообщения об ошибках ASP.NET Identity UserName и Password?

Используя NuGet, я загрузил пакет немецкого языка, а затем открыл пакет \packages\Microsoft.AspNet.Identity.Core.2.0.0\lib\net45\de\Microsoft.AspNet.Identity.Core.resources.dll в dotPeek, а затем экспортировал это в новый проект VS:

https://github.com/nielsbosma/AspNet.Identity.Resources.Swedish/

Я скопировал сгенерированный файл \Microsoft.AspNet.Identity.Core.resources.dll в новую папку в папке \packages\Microsoft.AspNet.Identity.Core.2.0.0\lib\net45\se.

Когда я запускаю свой сайт локально, я вижу, что файл Microsoft.AspNet.Identity.Core.resources.dll был скопирован на MySite\bin\sv\

Но я не могу заставить его работать: (

Если я установил в свой Web.config:

<system.web>
    ...
    <globalization culture="sv-SE" uiCulture="sv" />
</system.web>

Я по-прежнему получаю английские сообщения об ошибках по умолчанию. Но если я перейду на немецкий язык, который я включил в NuGet, я получаю сообщения об ошибках в Германии.

Используя dotPeek, я сравнил свою dll с немецким, и они одинаковы, за исключением того, что у меня есть PublicKeyToken = null, а для немецкого - 31bf3856ad364e35. Может быть, поэтому я не могу загрузить свою DLL? Есть ли способ установить PublicKeyToken для dll? Любое обходное решение?

Спасибо за любые указатели.

4b9b3361

Ответ 1

Нет, если у вас есть секретный ключ, который Microsoft использует для подписи dll.

Обновлено: как обходной путь до тех пор, пока мы не добавим поддержку для подключения ваших собственных ресурсов, вы, вероятно, можете просто обернуть все сообщения об ошибках по умолчанию с явным переключением на данный момент, должно быть только около 10-20 ошибок, связанных с пользователем.

Что-то вроде:

public static string Localize(string error) {
     switch (error) {
          case "<english error>": return "<localized version";
     }
}

Ответ 2

Вдохновленный Питером Ответом Я придумал дерьмовое, но быстрое решение.

Мне нужно было локализовать ошибки в шаблоне AccountController по умолчанию, поэтому я написал свой собственный метод AddLocalizedErrors. Я использую ресурсы для локализации ошибок.

    //Original method
    private void AddErrors(IdentityResult result)
    {
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError("", error);
        }
    }
    //My method
    private void AddLocalizedErrors(IdentityResult result, ApplicationUser user)
    {
        foreach (var error in result.Errors)
        {
            var localizedError = error;
            string userName = "";
            string email = "";
            if (user != null) 
            {
                userName = user.UserName;
                email = user.Email;
            }
            //password errors
            localizedError = localizedError.Replace("Passwords must have at least one uppercase ('A'-'Z').", AspNetValidationMessages.password_uppercase);
            localizedError = localizedError.Replace("Passwords must have at least one digit ('0'-'9').", AspNetValidationMessages.password_digit);
            localizedError = localizedError.Replace("Passwords must have at least one lowercase ('a'-'z').", AspNetValidationMessages.password_lowercase);
            localizedError = localizedError.Replace("Passwords must have at least one non letter or digit character.", AspNetValidationMessages.password_nonletter_nondigit);
            localizedError = localizedError.Replace("Passwords must have at least one non letter or digit character.", AspNetValidationMessages.password_nonletter_nondigit); 
            localizedError = localizedError.Replace("Passwords must have at least one non letter or digit character.", AspNetValidationMessages.password_nonletter_nondigit); 
            //register errors
            localizedError = localizedError.Replace("Name "+userName+" is already taken.", AspNetValidationMessages.name_taken.Replace("{0}", userName));
            localizedError = localizedError.Replace("Email '" + email + "' is already taken.", AspNetValidationMessages.email_taken.Replace("{0}", email)); 

            ModelState.AddModelError("", localizedError);
        }
    }

Я использую string.Replace(), потому что, например, ошибки пароля только что соединены строками одного требования к паролю.

Когда дело доходит до сбора ролей, я должен быть более креативным. Возможно использование string.Contains().

Ответ 3

На данный момент это действительно дерьмовое решение, но решение обходное решение тем не менее. Чтобы сэкономить время для тех из нас, кто нуждается в локализации и не работает в Microsoft... вот что я сделал в качестве обходного пути для голландского языка.

public class Demo {
    private string LocalizeIdentityError(string error, IdentityUser user)
    {
        if (error == "User already in role.") return "De gebruiker zit reeds in deze rol.";
        else if (error == "User is not in role.") return "De gebruiker zit niet in deze rol.";
        //else if (error == "Role {0} does not exist.") return "De rol bestaat nog niet";
        //else if (error == "Store does not implement IUserClaimStore&lt;TUser&gt;.") return "";
        //else if (error == "No IUserTwoFactorProvider for '{0}' is registered.") return "";
        //else if (error == "Store does not implement IUserEmailStore&lt;TUser&gt;.") return "";
        else if (error == "Incorrect password.") return "Ongeldig wachtwoord";
        //else if (error == "Store does not implement IUserLockoutStore&lt;TUser&gt;.") return "";
        //else if (error == "No IUserTokenProvider is registered.") return "";
        //else if (error == "Store does not implement IUserRoleStore&lt;TUser&gt;.") return "";
        //else if (error == "Store does not implement IUserLoginStore&lt;TUser&gt;.") return "";
        else if (error == "User name {0} is invalid, can only contain letters or digits.") return "De gebruikersnaam '"+user.UserName+"' kan alleen letters of cijfers bevatten.";
        //else if (error == "Store does not implement IUserPhoneNumberStore&lt;TUser&gt;.") return "";
        //else if (error == "Store does not implement IUserConfirmationStore&lt;TUser&gt;.") return "";
        else if (error.StartsWith("Passwords must be at least ")) return "Een wachtwoord moet minstens {0} karakters bevatten.";
        //else if (error == "{0} cannot be null or empty.") return "";
        else if (user != null && error == "Name "+user.UserName+" is already taken.") return "De gebruikersnaam '" + user.UserName + "' is reeds in gebruik.";
        else if (error == "User already has a password set.") return "Deze gebruiker heeft reeds een wachtwoord ingesteld.";
        //else if (error == "Store does not implement IUserPasswordStore&lt;TUser&gt;.") return "";
        else if (error == "Passwords must have at least one non letter or digit character.") return "Wachtwoorden moeten minstens een ander karakter dan een letter of cijfer bevatten.";
        else if (error == "UserId not found.") return "De gebruiker kon niet gevonden worden.";
        else if (error == "Invalid token.") return "Ongeldig token.";
        else if (user != null && error == "Email '" + user.Email + "' is invalid.") return "Het emailadres '" + user.Email + "' is ongeldig.";
        else if (user != null && error == "User " + user.UserName + " does not exist.") return "De gebruiker '" + user.UserName + "' bestaat niet.";
        else if (error == "Store does not implement IQueryableRoleStore&lt;TRole&gt;.") return "";
        else if (error == "Lockout is not enabled for this user.") return "Lockout is niet geactiveerd voor deze gebruiker.";
        //else if (error == "Store does not implement IUserTwoFactorStore&lt;TUser&gt;.") return "";
        else if (error == "Passwords must have at least one uppercase ('A'-'Z').") return "Wachtwoorden moeten minstens één hoofdletter bevatten. (A-Z)";
        else if (error == "Passwords must have at least one digit ('0'-'9').") return "Wachtwoorden moeten minstens één getal bevatten. (0-9)";
        else if (error == "Passwords must have at least one lowercase ('a'-'z').") return "Wachtwoorden moeten minstens één kleine letter bevatten. (a-z)";
        //else if (error == "Store does not implement IQueryableUserStore&lt;TUser&gt;.") return "";
        else if (user != null && error == "Email '" + user.Email + "' is already taken.") return "Het emailadres '" + user.Email + "' is reeds in gebruik. Probeer aan te melden.";
        //else if (error == "Store does not implement IUserSecurityStampStore&lt;TUser&gt;.") return "";
        else if (error == "A user with that external login already exists.") return "Een gebruiker met deze externe login bestaat reeds.";
        else if (error == "An unknown failure has occured.") return "Een onbekende fout is opgetreden. Probeer het later opnieuw.";

        return error;
    }
}

Ответ 4

Другой вариант - наследовать от Microsoft.AspNet.Identity.PasswordValidator, затем переопределить ValidateAsync.

Затем вы можете использовать свои собственные файлы ресурсов для локализации. Исходный файл ресурсов для английского языка можно найти с помощью DotPeek или аналогичного, тогда вы можете использовать его для английского в качестве шаблона для собственного перевода на другие языки.

Мои файлы resx:

MyLocalization/IdentityResource.resx(то же, что и в Microsoft.AspNet.Identity.Core.Resources)

MyLocalization/IdentityResource.nb-no.resx(мои норвежские переводы)

 var manager = new ApplicationUserManager(new UserStore<ApplicationUser>    (context.Get<ApplicationDbContext>()));

 // Configure validation logic for passwords
 manager.PasswordValidator = new MyCustomPasswordValidator(System.Threading.Thread.CurrentThread.CurrentUICulture)

В MyCustomPasswordValidator.cs: (также обратите внимание на исправление, которое мне также нужно было сделать в ValidateAsync)

using Resources = MyLocalization.IdentityResource;

public class MyCustomPasswordValidator : Microsoft.AspNet.Identity.PasswordValidator
{
    private readonly CultureInfo _currentUIculture;

    public MyCustomPasswordValidator(CultureInfo currentUIculture)
    {
        _currentUIculture = currentUIculture;
    }

    /// <summary>
    /// Ensures that the string is of the required length and meets the configured requirements
    /// 
    /// </summary>
    /// <param name="item"/>
    /// <returns/>
    public override Task<IdentityResult> ValidateAsync(string item)
    {
        //BUG: CurrentUICulture is not set correctly https://aspnetidentity.codeplex.com/workitem/2060
        System.Threading.Thread.CurrentThread.CurrentUICulture = _currentUIculture;
        if (item == null)
            throw new ArgumentNullException("item");
        List<string> list = new List<string>();
        if (string.IsNullOrWhiteSpace(item) || item.Length < this.RequiredLength)
            list.Add(string.Format((IFormatProvider)CultureInfo.CurrentCulture, Resources.PasswordTooShort, new object[1]
    {
      (object) this.RequiredLength
    }));
        if (this.RequireNonLetterOrDigit && Enumerable.All<char>((IEnumerable<char>)item, new Func<char, bool>(this.IsLetterOrDigit)))
            list.Add(Resources.PasswordRequireNonLetterOrDigit);
        if (this.RequireDigit && Enumerable.All<char>((IEnumerable<char>)item, (Func<char, bool>)(c => !this.IsDigit(c))))
            list.Add(Resources.PasswordRequireDigit);
        if (this.RequireLowercase && Enumerable.All<char>((IEnumerable<char>)item, (Func<char, bool>)(c => !this.IsLower(c))))
            list.Add(Resources.PasswordRequireLower);
        if (this.RequireUppercase && Enumerable.All<char>((IEnumerable<char>)item, (Func<char, bool>)(c => !this.IsUpper(c))))
            list.Add(Resources.PasswordRequireUpper);
        if (list.Count == 0)
            return Task.FromResult<IdentityResult>(IdentityResult.Success);
        return Task.FromResult<IdentityResult>(IdentityResult.Failed(new string[1]
  {
    string.Join(" ", (IEnumerable<string>) list)
  }));
    }

}

`

Ответ 5

Еще одним обходным путем для поля Password может быть реализация RegisterViewModel.Password с CustomValidationAttribute:

public class RegisterViewModel
{
    [CustomValidation(typeof(CustomValidations), "ValidatePassword")]
    public string Password { get; set; }
}

И иметь метод CustomValidations.ValidatePassword, который бы имитировал правила проверки пароля, которые вы настроили для PasswordValidator. То есть:.

public static class CustomValidations
{
    public static ValidationResult ValidatePassword(string password)
    {
        // Implement validation logic here, e.g. require numbers,
        // uppercase etc. and create localized ValidationResult.
        return new ValidationResult(Resources.PasswordValidation.NoNumbers);
    }
}

Здесь вы можете явно локализовать свои сообщения об ошибках в соответствии с вашими личными предпочтениями, используя стандартные файлы ресурсов resx. Итак, чтобы подвести итог, вы просто запретите любые недействительные пароли от достижения PasswordValidator.

В поле Email это станет немного уродливым, так как для проверки уникальности и т.д. потребуется округлять до DB, но это должно быть выполнимо.

Дополнительный бонус за использование этого решения заключается в том, что полученные ошибки будут "за поле", т.е. вам не нужно будет отображать все ошибки проверки с помощью @Html.ValidationSummary, но может сделать:

@Html.ValidationMessageFor(m => m.Password, null, new { @class = "text-danger" })

Ответ 6

Ниже приведен список файлов и значений файла en.-US Resources.resx. Это значения, требующие локализации. Исходный код на http://aspnetidentity.codeplex.com

Идентификатор Asp.Net {name} Validator.cs ErrorMessage Resources.resx Локализация См.: http://aspnetidentity.codeplex.com/discussions/638351

*DefaultError= An unknown failure has occured.
DuplicateEmail= Email '{0}' is already taken.
DuplicateName= Name {0} is already taken.
ExternalLoginExists= A user with that external login already exists.
InvalidEmail= Email '{0}' is invalid.
InvalidToken= Invalid token.
InvalidUserName= User name {0} is invalid, can only contain letters or digits.
LockoutNotEnabled= Lockout is not enabled for this user.
NoTokenProvider= No IUserTokenProvider is registered.
NoTwoFactorProvider= No IUserTwoFactorProvider for '{0}' is registered.
PasswordMismatch= Incorrect password.
PasswordRequireDigit= Passwords must have at least one digit ('0'-'9').
PasswordRequireLower= Passwords must have at least one lowercase ('a'-'z').
PasswordRequireNonLetterOrDigit= Passwords must have at least one non letter or digit character.
PasswordRequireUpper= Passwords must have at least one uppercase ('A'-'Z').
PasswordTooShort= Passwords must be at least {0} characters.
PropertyTooShort= {0} cannot be null or empty.
RoleNotFound= Role {0} does not exist.
StoreNotIQueryableRoleStore= Store does not implement IQueryableRoleStore<TRole>.
StoreNotIQueryableUserStore= Store does not implement IQueryableUserStore<TUser>.
StoreNotIUserClaimStore= Store does not implement IUserClaimStore<TUser>.
StoreNotIUserConfirmationStore= Store does not implement IUserConfirmationStore<TUser>.
StoreNotIUserEmailStore= Store does not implement IUserEmailStore<TUser>.
StoreNotIUserLockoutStore= Store does not implement IUserLockoutStore<TUser>.
StoreNotIUserLoginStore= Store does not implement IUserLoginStore<TUser>.
StoreNotIUserPasswordStore= Store does not implement IUserPasswordStore<TUser>.
StoreNotIUserPhoneNumberStore= Store does not implement IUserPhoneNumberStore<TUser>.
StoreNotIUserRoleStore= Store does not implement IUserRoleStore<TUser>.
StoreNotIUserSecurityStampStore= Store does not implement IUserSecurityStampStore<TUser>.
StoreNotIUserTwoFactorStore= Store does not implement IUserTwoFactorStore<TUser>.
UserAlreadyHasPassword= User already has a password set.
UserAlreadyInRole= User already in role.
UserIdNotFound= UserId not found.
UserNameNotFound= User {0} does not exist.
UserNotInRole= User is not in role.*