Центральная локализация ASP.Net

Основные возможности ASP.Net новая поддержка локализации.

В моем проекте мне нужен только один язык. Для большей части текста и аннотаций я могу указать вещи на своем языке, но для текста, исходящего из самого ASP.Net Core, язык является английским.

Примеры:

  • Пароли должны иметь как минимум один верхний регистр ( "A" - "Z" ).
  • Пароли должны иметь как минимум одну цифру ('0' - '9').
  • Имя пользователя '[email protected]' уже выполнено.
  • Поле E-post не является допустимым адресом электронной почты.
  • Значение '' недействительно.

Я попытался настроить культуру вручную, но язык по-прежнему остается английским.

app.UseRequestLocalization(new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture("nb-NO"),
    SupportedCultures = new List<CultureInfo> { new CultureInfo("nb-NO") },
    SupportedUICultures = new List<CultureInfo> { new CultureInfo("nb-NO") }
});

Как я могу изменить язык ASP.Net Core или переопределить его текст по умолчанию?

Ответ 1

Перечисленные сообщения об ошибках определены в ASP.NET Core Identity и предоставлены IdentityErrorDescriber. Я не нашел переведенных файлов ресурсов и думаю, что они не локализованы. В моей системе (немецкий язык) они также не переведены, хотя CultureInfo настроен правильно.

Вы можете настроить собственный IdentityErrorDescriber для возврата ваших собственных сообщений, а также их переводов. Это описано, например, в Как изменить стандартные сообщения об ошибках MVC Core ValidationSummary?

В методе Configure Startup.cs вы можете подключить свой класс Error Describer, унаследованный от IdentityErrorDescriber, как

 services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddErrorDescriber<TranslatedIdentityErrorDescriber>();

Для других сообщений об ошибках модели по умолчанию (например, недействительный номер) вы можете предоставить собственные функции доступа в ModelBindingMessageProvider. Этот провайдер используется ASP.NET Core MVC и также может быть настроен в Startup.cs.

services.AddMvc(
            options => 
            options.ModelBindingMessageProvider.ValueIsInvalidAccessor = s => $"My not valid text for {s}");

Ответ 2

Основываясь на превосходных ответах rboe и Tedd Hansen, я написал IStringLocalizer, основанный на IdentityErrorDescriber, для собственного использования, но я бы хотел поделиться этим здесь, если кому-то понадобится поддержка нескольких языков :)

Основная идея состоит в том, чтобы создать файл ресурсов для вашего языка по умолчанию и любых других языков обычным способом. (https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization)

Расположен в (если вы сохраните мое имя класса):

/Resources/yournamespace.LocalizedIdentityErrorDescriber.resx
/Resources/yournamespace.LocalizedIdentityErrorDescriber.fr.resx
и т.д...

В этих ключах вы будете использовать коды ошибок (например: DefaultError, ConcurrencyError) в качестве ключа.

Затем добавьте класс ниже

LocalizedIdentityErrorDescriber.cs

public class LocalizedIdentityErrorDescriber : IdentityErrorDescriber
{
    /// <summary> 
    /// The <see cref="IStringLocalizer{LocalizedIdentityErrorDescriber}"/>
    /// used to localize the strings
    /// </summary>
    private readonly IStringLocalizer<LocalizedIdentityErrorDescriber> localizer;

    /// <summary>
    /// Initializes a new instance of the <see cref="LocalizedIdentityErrorDescriber"/> class.
    /// </summary>
    /// <param name="localizer">
    /// The <see cref="IStringLocalizer{LocalizedIdentityErrorDescriber}"/>
    /// that we will use to localize the strings
    /// </param>
    public LocalizedIdentityErrorDescriber(IStringLocalizer<LocalizedIdentityErrorDescriber> localizer)
    {
        this.localizer = localizer;
    }

    /// <summary>
    /// Returns the default <see cref="IdentityError" />.
    /// </summary>
    /// <returns>The default <see cref="IdentityError" /></returns>
    public override IdentityError DefaultError()
    {
        return this.GetErrorByCode("DefaultError");
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a concurrency failure.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a concurrency failure.</returns>
    public override IdentityError ConcurrencyFailure()
    {
        return this.GetErrorByCode("ConcurrencyFailure");
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a password mismatch.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a password mismatch.</returns>
    public override IdentityError PasswordMismatch()
    {
        return this.GetErrorByCode("PasswordMismatch");
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating an invalid token.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating an invalid token.</returns>
    public override IdentityError InvalidToken()
    {
        return this.GetErrorByCode("InvalidToken");
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating an external login is already associated with an account.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating an external login is already associated with an account.</returns>
    public override IdentityError LoginAlreadyAssociated()
    {
        return this.GetErrorByCode("LoginAlreadyAssociated");
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating the specified user <paramref name="userName" /> is invalid.
    /// </summary>
    /// <param name="userName">The user name that is invalid.</param>
    /// <returns>An <see cref="IdentityError" /> indicating the specified user <paramref name="userName" /> is invalid.</returns>
    public override IdentityError InvalidUserName(string userName)
    {
        return this.FormatErrorByCode("InvalidUserName", (object)userName);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating the specified <paramref name="email" /> is invalid.
    /// </summary>
    /// <param name="email">The email that is invalid.</param>
    /// <returns>An <see cref="IdentityError" /> indicating the specified <paramref name="email" /> is invalid.</returns>
    public override IdentityError InvalidEmail(string email)
    {
        return this.FormatErrorByCode("InvalidEmail", (object)email);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating the specified <paramref name="userName" /> already exists.
    /// </summary>
    /// <param name="userName">The user name that already exists.</param>
    /// <returns>An <see cref="IdentityError" /> indicating the specified <paramref name="userName" /> already exists.</returns>
    public override IdentityError DuplicateUserName(string userName)
    {
        return this.FormatErrorByCode("DuplicateUserName", (object)userName);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating the specified <paramref name="email" /> is already associated with an account.
    /// </summary>
    /// <param name="email">The email that is already associated with an account.</param>
    /// <returns>An <see cref="IdentityError" /> indicating the specified <paramref name="email" /> is already associated with an account.</returns>
    public override IdentityError DuplicateEmail(string email)
    {
        return this.FormatErrorByCode("DuplicateEmail", (object)email);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating the specified <paramref name="role" /> name is invalid.
    /// </summary>
    /// <param name="role">The invalid role.</param>
    /// <returns>An <see cref="IdentityError" /> indicating the specific role <paramref name="role" /> name is invalid.</returns>
    public override IdentityError InvalidRoleName(string role)
    {
        return this.FormatErrorByCode("InvalidRoleName", (object)role);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating the specified <paramref name="role" /> name already exists.
    /// </summary>
    /// <param name="role">The duplicate role.</param>
    /// <returns>An <see cref="IdentityError" /> indicating the specific role <paramref name="role" /> name already exists.</returns>
    public override IdentityError DuplicateRoleName(string role)
    {
        return this.FormatErrorByCode("DuplicateRoleName", (object)role);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a user already has a password.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a user already has a password.</returns>
    public override IdentityError UserAlreadyHasPassword()
    {
        return this.GetErrorByCode("UserAlreadyHasPassword");
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating user lockout is not enabled.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating user lockout is not enabled..</returns>
    public override IdentityError UserLockoutNotEnabled()
    {
        return this.GetErrorByCode("UserLockoutNotEnabled");
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a user is already in the specified <paramref name="role" />.
    /// </summary>
    /// <param name="role">The duplicate role.</param>
    /// <returns>An <see cref="IdentityError" /> indicating a user is already in the specified <paramref name="role" />.</returns>
    public override IdentityError UserAlreadyInRole(string role)
    {
        return this.FormatErrorByCode("UserAlreadyInRole", (object)role);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a user is not in the specified <paramref name="role" />.
    /// </summary>
    /// <param name="role">The duplicate role.</param>
    /// <returns>An <see cref="IdentityError" /> indicating a user is not in the specified <paramref name="role" />.</returns>
    public override IdentityError UserNotInRole(string role)
    {
        return this.FormatErrorByCode("UserNotInRole", (object)role);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a password of the specified <paramref name="length" /> does not meet the minimum length requirements.
    /// </summary>
    /// <param name="length">The length that is not long enough.</param>
    /// <returns>An <see cref="IdentityError" /> indicating a password of the specified <paramref name="length" /> does not meet the minimum length requirements.</returns>
    public override IdentityError PasswordTooShort(int length)
    {
        return this.FormatErrorByCode("PasswordTooShort", (object)length);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a password entered does not contain a non-alphanumeric character, which is required by the password policy.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a password entered does not contain a non-alphanumeric character.</returns>
    public override IdentityError PasswordRequiresNonAlphanumeric()
    {
        return this.GetErrorByCode("PasswordRequiresNonAlphanumeric");
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a password entered does not contain a numeric character, which is required by the password policy.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a password entered does not contain a numeric character.</returns>
    public override IdentityError PasswordRequiresDigit()
    {
        return this.GetErrorByCode("PasswordRequiresDigit");
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a password entered does not contain a lower case letter, which is required by the password policy.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a password entered does not contain a lower case letter.</returns>
    public override IdentityError PasswordRequiresLower()
    {
        return this.GetErrorByCode("PasswordRequiresLower");
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a password entered does not contain an upper case letter, which is required by the password policy.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a password entered does not contain an upper case letter.</returns>
    public override IdentityError PasswordRequiresUpper()
    {
        return this.GetErrorByCode("PasswordRequiresUpper");
    }

    /// <summary>Returns a localized <see cref="IdentityError"/> for the provided code.</summary>
    /// <param name="code">The error code.</param>
    /// <returns>A localized <see cref="IdentityError"/>.</returns>
    private IdentityError GetErrorByCode(string code)
    {
        return new IdentityError()
        {
            Code = code,
            Description = this.localizer.GetString(code)
        };
    }

    /// <summary>Formats a localized <see cref="IdentityError"/> for the provided code.</summary>
    /// <param name="code">The error code.</param>
    /// <param name="parameters">The parameters to format the string with.</param>
    /// <returns>A localized <see cref="IdentityError"/>.</returns>
    private IdentityError FormatErrorByCode(string code, params object[] parameters)
    {
        return new IdentityError
        {
            Code = code,
            Description = string.Format(this.localizer.GetString(code, parameters))
        };
    }
}

И все инициализировать:

Startup.cs

    [...]

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddErrorDescriber<LocalizedIdentityErrorDescriber>();

        services.AddLocalization(options => options.ResourcesPath = "Resources");

        // Your service configuration code

        services.AddMvc()
            .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
            .AddDataAnnotationsLocalization();

        // Your service configuration code cont.
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        // your configuration code

        app.UseRequestLocalization(
            new RequestLocalizationOptions()
                {
                    DefaultRequestCulture = new RequestCulture("fr"),
                    SupportedCultures = SupportedCultures,
                    SupportedUICultures = SupportedCultures
                });

        app.UseStaticFiles();

        app.UseIdentity();

        // your configuration code
    }

    [...]

Ответ 3

Позвольте мне дать исчерпывающий ответ на этот вопрос и опишите, как я нашел решение после прочтения исходного кода ядра .net.

перед дальнейшим описанием сначала установите этот пакет, используя NuGet Microsoft.Extensions.Localization

как вы, ребята, помните в полной структуре asp.net, переключение между культурами было довольно простым

System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");

но в ядре .net культура больше не привязана к текущему потоку. Ядро ядра asp.net имеет конвейер, и мы можем добавить в него различные MiddleWares, поэтому RequestLocalizationMiddleware является классом промежуточного программного обеспечения для обработки локализации, у нас может быть несколько провайдеров, поэтому он будет перебирать всех провайдеров культуры например, QueryStringRequestCultureProvider, CookieRequestCultureProvider, AcceptLanguageHeaderRequestCultureProvider,…

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

Я лично предпочитаю хранить культуру в cookie файлах браузера, поэтому, поскольку CookieRequestCultureProvider не является первым поставщиком культуры в списке, я перемещаю его в начало списка, конфигурация этой части в Startup.cs > ConfigureServices как показано ниже

services.Configure<RequestLocalizationOptions>(options =>
             {
                 var supportedCultures = new[]
                 {
                    new CultureInfo("en-US"),
                    new CultureInfo("fa-IR"),
                    new CultureInfo("de-DE")
                };
                options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");
                options.SupportedCultures = supportedCultures;
                options.SupportedUICultures = supportedCultures;

                var defaultCookieRequestProvider =
                    options.RequestCultureProviders.FirstOrDefault(rcp =>
                        rcp.GetType() == typeof(CookieRequestCultureProvider));
                if (defaultCookieRequestProvider != null)
                    options.RequestCultureProviders.Remove(defaultCookieRequestProvider);

                options.RequestCultureProviders.Insert(0,
                    new CookieRequestCultureProvider()
                    {
                        CookieName = ".AspNetCore.Culture",
                        Options = options
                    });
            });

позвольте мне описать приведенный выше код, культура нашего приложения по умолчанию en-US, и мы поддерживаем только английский, фарси, германию, поэтому, если в браузере используется другой язык или вы устанавливаете язык, отличный от этих 3 языков тогда приложение должно переключиться на культуру по умолчанию. в приведенном выше коде я просто удаляю CookieCultureProvider из списка и добавляю его в качестве первого поставщика в списке (* я уже описал причину, по которой он должен быть первым *). CookieName по умолчанию работает для меня, вы можете изменить его, если хотите.

не забудьте добавить приведенный ниже код под Configure (приложение IApplicationBuilder, enH IHostingEnvironment) в Startup.cs

   var options = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
   app.UseRequestLocalization(options.Value);

пока все хорошо. Чтобы иметь файлы ресурсов, вы должны указать путь к ресурсам, я отдельно указываю ресурс для Controller, Views, ViewModels и SharedResources в корневом пути веб-проекта, иерархия описана ниже

|-Resoures
|---Controllers
|---Views
|---Models
|---SharedResource.resx
|---SharedResource.fa-IR.resx
|---SharedResource.de-DE.resx

Ответ 4

и Russian IdentityErrorDescriber, если кому-то это нужно.

public class RussianIdentityErrorDescriber : IdentityErrorDescriber
{

    public override IdentityError ConcurrencyFailure()
    {
        return new IdentityError
        {
            Code = "ConcurrencyFailure",
            Description = "Сбой в параллельных запросах. Возможно объект был изменен."
        };
    }
    public override IdentityError DefaultError()
    {
    return new IdentityError
        {
            Code = "DefaultError",
            Description = "Произошла неизвестная ошибка при авторизации."
        };
    }
    public override IdentityError DuplicateEmail(string email)
    {
        return new IdentityError
        {
            Code = "DuplicateEmail",
            Description = $"E-mail '{email}' уже используется."
        };
    }
    public override IdentityError DuplicateRoleName(string role)
    {
        return new IdentityError
        {
            Code = "DuplicateRoleName",
            Description = $"Роль с именем '{role}' уже существует."
        };
    }
    public override IdentityError DuplicateUserName(string userName)
    {
        return new IdentityError
        {
            Code = "DuplicateUserName",
            Description = $"Пользователь '{userName}' уже зарегистрирован."
        };
    }
    public override IdentityError InvalidEmail(string email)
    {
        return new IdentityError
        {
            Code = "InvalidEmail",
            Description = $"E-mail '{email}' содержит неверный формат."
        };
    }
    public override IdentityError InvalidRoleName(string role)
    {
        return new IdentityError
        {
            Code = "InvalidRoleName",
            Description = $"Имя роли '{role}' задано не верно (содержит не допустимые символы либо длину)."
        };
    }
    public override IdentityError InvalidToken()
    {
        return new IdentityError
        {
            Code = "InvalidToken",
            Description = "Неправильно указан код подтверждения (token)."
        };
    }
    public override IdentityError InvalidUserName(string userName)
    {
        return new IdentityError
        {
            Code = "InvalidUserName",
            Description = $"Имя пользователя '{userName}' указано не верно (содержит не допустимые символы либо длину)."
        };
    }
    public override IdentityError LoginAlreadyAssociated()
    {
        return new IdentityError
        {
            Code = "LoginAlreadyAssociated",
            Description = "Данный пользователь уже привязан к аккаунту."
        };
    }
    public override IdentityError PasswordMismatch()
    {
        return new IdentityError
        {
            Code = "PasswordMismatch",
            Description = "Пароли не совпадают."
        };
    }
    public override IdentityError PasswordRequiresDigit()
    {
        return new IdentityError
        {
            Code = "PasswordRequiresDigit",
            Description = "Пароль должен содержать минимум одну цифру."
        };
    }
    public override IdentityError PasswordRequiresLower()
    {
        return new IdentityError
        {
            Code = "PasswordRequiresLower",
            Description = "Пароль должен содержать минимум одну строчную букву."
        };
    }
    public override IdentityError PasswordRequiresNonAlphanumeric()
    {
        return new IdentityError
        {
            Code = "PasswordRequiresNonAlphanumeric",
            Description = "Пароль должен содержать минимум один специальный символ (не буквенно-цифровой)."
        };
    }
    public override IdentityError PasswordRequiresUniqueChars(int uniqueChars)
    {
        return new IdentityError
        {
            Code = "PasswordRequiresUniqueChars",
            Description = $"Пароль должен содержать минимум '{uniqueChars}' не повторяющихся символов."
        };
    }
    public override IdentityError PasswordRequiresUpper()
    {
        return new IdentityError
        {
            Code = "PasswordRequiresUpper",
            Description = "Пароль должен содержать минимум одну заглавную букву."
        };
    }
    public override IdentityError PasswordTooShort(int length)
    {
        return new IdentityError
        {
            Code = "PasswordTooShort",
            Description = $"Пароль слишком короткий. Минимальное количество символов: '{length}'."
        };
    }
    public override IdentityError RecoveryCodeRedemptionFailed()
    {
        return new IdentityError
        {
            Code = "RecoveryCodeRedemptionFailed",
            Description = "Не удалось использовать код восстановления."
        };
    }
    public override IdentityError UserAlreadyHasPassword()
    {
        return new IdentityError
        {
            Code = "UserAlreadyHasPassword",
            Description = "Пароль для пользователя уже задан."
        };
    }
    public override IdentityError UserAlreadyInRole(string role)
    {
        return new IdentityError
        {
            Code = "UserAlreadyInRole",
            Description = $"Роль '{role}' уже привязана к пользователю."
        };
    }
    public override IdentityError UserLockoutNotEnabled()
    {
        return new IdentityError
        {
            Code = "UserLockoutNotEnabled",
            Description = "Блокировка пользователя отключена."
        };
    }
    public override IdentityError UserNotInRole(string role)
    {
        return new IdentityError
        {
            Code = "UserNotInRole",
            Description = $"Пользователь должен иметь роль: '{role}'"
        };
    }
}

In `Startup.cs` set `RussianIdentityErrorDescriber `

public void ConfigureServices(IServiceCollection services)
{
// ...   
    services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddErrorDescriber<RussianIdentityErrorDescriber>();
    // ...   
}

Ответ 5

Norwegian IdentityErrorDescriber, если кому-то это нужно.

public class NorwegianIdentityErrorDescriber : IdentityErrorDescriber
{

    public override IdentityError DefaultError()
    {
        return new IdentityError()
        {
            Code = "DefaultError",
            Description = "En ukjent feil har oppstått."
        };
    }

    public override IdentityError ConcurrencyFailure()
    {
        return new IdentityError()
        {
            Code = "ConcurrencyFailure",
            Description = "Optimistisk samtidighet feilet, objektet har blitt endret."
        };
    }

    public override IdentityError PasswordMismatch()
    {
        return new IdentityError()
        {
            Code = "PasswordMismatch",
            Description = "Feil passord."
        };
    }

    public override IdentityError InvalidToken()
    {
        return new IdentityError()
        {
            Code = "InvalidToken",
            Description = "Feil token."
        };
    }

    public override IdentityError LoginAlreadyAssociated()
    {
        return new IdentityError()
        {
            Code = "LoginAlreadyAssociated",
            Description = "En bruker med dette brukernavnet finnes allerede."
        };
    }

    public override IdentityError InvalidUserName(string userName)
    {
        IdentityError identityError = new IdentityError();
        identityError.Code = "InvalidUserName";
        string str = $"Brkernavnet '{userName}' er ikke gyldig. Det kan kun inneholde bokstaver og tall.";
        identityError.Description = str;
        return identityError;
    }

    public override IdentityError InvalidEmail(string email)
    {
        IdentityError identityError = new IdentityError();
        identityError.Code = "InvalidEmail";
        string str = $"E-post '{email}' er ugyldig.";
        identityError.Description = str;
        return identityError;
    }

    public override IdentityError DuplicateUserName(string userName)
    {
        IdentityError identityError = new IdentityError();
        identityError.Code = "DuplicateUserName";
        string str = $"Brukernavn '{userName}' er allerede tatt.";
        identityError.Description = str;
        return identityError;
    }

    public override IdentityError DuplicateEmail(string email)
    {
        IdentityError identityError = new IdentityError();
        identityError.Code = "DuplicateEmail";
        string str = $"E-post '{email}' er allerede tatt.";
        identityError.Description = str;
        return identityError;
    }

    public override IdentityError InvalidRoleName(string role)
    {
        IdentityError identityError = new IdentityError();
        identityError.Code = "InvalidRoleName";
        string str = $"Rollenavn '{role}' er ugyldig.";
        identityError.Description = str;
        return identityError;
    }

    public override IdentityError DuplicateRoleName(string role)
    {
        IdentityError identityError = new IdentityError();
        identityError.Code = "DuplicateRoleName";
        string str = $"Rollenavn '{role}' er allerede tatt.";
        identityError.Description = str;
        return identityError;
    }

    public virtual IdentityError UserAlreadyHasPassword()
    {
        return new IdentityError()
        {
            Code = "UserAlreadyHasPassword",
            Description = "Bruker har allerede passord satt."
        };
    }

    public override IdentityError UserLockoutNotEnabled()
    {
        return new IdentityError()
        {
            Code = "UserLockoutNotEnabled",
            Description = "Utestenging er ikke slått på for denne brukeren."
        };
    }

    public override IdentityError UserAlreadyInRole(string role)
    {
        IdentityError identityError = new IdentityError();
        identityError.Code = "UserAlreadyInRole";
        string str = $"Brukeren er allerede i rolle '{role}'.";
        identityError.Description = str;
        return identityError;
    }

    public override IdentityError UserNotInRole(string role)
    {
        IdentityError identityError = new IdentityError();
        identityError.Code = "UserNotInRole";
        string str = $"Bruker er ikke i rolle '{role}'.";
        identityError.Description = str;
        return identityError;
    }

    public override IdentityError PasswordTooShort(int length)
    {
        IdentityError identityError = new IdentityError();
        identityError.Code = "PasswordTooShort";
        string str = $"Passordet må være på minimum {length} tegn.";
        identityError.Description = str;
        return identityError;
    }

    public override IdentityError PasswordRequiresNonAlphanumeric()
    {
        return new IdentityError()
        {
            Code = "PasswordRequiresNonAlphanumeric",
            Description = "Passordet må inneholde minst ett spesialtegn."
        };
    }

    public override IdentityError PasswordRequiresDigit()
    {
        return new IdentityError()
        {
            Code = "PasswordRequiresDigit",
            Description = "Passordet må inneholde minst ett tall."
        };
    }

    public override IdentityError PasswordRequiresLower()
    {
        return new IdentityError()
        {
            Code = "PasswordRequiresLower",
            Description = "Passordet må inneholde minst en liten bokstav (a-z)."
        };
    }

    public override IdentityError PasswordRequiresUpper()
    {
        return new IdentityError()
        {
            Code = "PasswordRequiresUpper",
            Description = "Passordet må inneholde minst en stor bokstav (A-Z)."
        };
    }
}

Ответ 6

Если вы ищете польский, вот он:

    public class PolishLocalizedIdentityErrorDescriber : IdentityErrorDescriber
        {
            public override IdentityError DefaultError()
            {
                return new IdentityError
                {
                    Code = nameof(DefaultError),
                    Description = "Wystąpił nieznany błąd."
                };
            }

            public override IdentityError ConcurrencyFailure()
            {
                return new IdentityError
                {
                    Code = nameof(ConcurrencyFailure),
                    Description = "Błąd współbieżności, obiekt został już zmodyfikowany."
                };
            }

            public override IdentityError PasswordMismatch()
            {
                return new IdentityError
                {
                    Code = nameof(PasswordMismatch),
                    Description = "Niepoprawne hasło."
                };
            }

            public override IdentityError InvalidToken()
            {
                return new IdentityError
                {
                    Code = nameof(InvalidToken),
                    Description = "Nieprawidłowy token."
                };
            }

            public override IdentityError RecoveryCodeRedemptionFailed()
            {
                return new IdentityError
                {
                    Code = nameof(RecoveryCodeRedemptionFailed),
                    Description = "Pobranie kodu odzyskiwania nie powiodło się."
                };
            }

            public override IdentityError LoginAlreadyAssociated()
            {
                return new IdentityError
                {
                    Code = nameof(LoginAlreadyAssociated),
                    Description = "Użytkownik o tym loginie już istnieje."
                };
            }

            public override IdentityError InvalidUserName(string userName)
            {
                return new IdentityError
                {
                    Code = nameof(InvalidUserName),
                    Description =
                        $"Nazwa użytkownika '{userName}' jest nieprawidłowa, może zawierać tylko litery lub cyfry."
                };
            }

            public override IdentityError InvalidEmail(string email)
            {
                return new IdentityError
                {
                    Code = nameof(InvalidEmail),
                    Description = $"Adres e-mail '{email}' jest nieprawidłowy."
                };
            }

            public override IdentityError DuplicateUserName(string userName)
            {
                return new IdentityError
                {
                    Code = nameof(DuplicateUserName),
                    Description = $"Nazwa '{userName}' jest już zajęta."
                };
            }

            public override IdentityError DuplicateEmail(string email)
            {
                return new IdentityError
                {
                    Code = nameof(DuplicateEmail),
                    Description = $"Adres e-mail '{email}' jest już zajęty."
                };
            }

            public override IdentityError InvalidRoleName(string role)
            {
                return new IdentityError
                {
                    Code = nameof(InvalidRoleName),
                    Description = $"Nazwa roli '{role}' już niepoprawna."
                };
            }

            public override IdentityError DuplicateRoleName(string role)
            {
                return new IdentityError
                {
                    Code = nameof(DuplicateRoleName),
                    Description = $"Rola '{role}' już istnieje."
                };
            }

            public override IdentityError UserAlreadyHasPassword()
            {
                return new IdentityError
                {
                    Code = nameof(UserAlreadyHasPassword),
                    Description = "Użytkownik ma już ustawione hasło."
                };
            }

            public override IdentityError UserLockoutNotEnabled()
            {
                return new IdentityError
                {
                    Code = nameof(UserLockoutNotEnabled),
                    Description = "Blokada konta nie jest włączona dla tego użytkownika."
                };
            }

            public override IdentityError UserAlreadyInRole(string role)
            {
                return new IdentityError
                {
                    Code = nameof(UserAlreadyInRole),
                    Description = $"Użytkownik korzysta już z roli '{role}'."
                };
            }

            public override IdentityError UserNotInRole(string role)
            {
                return new IdentityError
                {
                    Code = nameof(UserNotInRole),
                    Description = $"Użytkownika nie ma w roli '{role}'."
                };
            }

            public override IdentityError PasswordTooShort(int length)
            {
                return new IdentityError
                {
                    Code = nameof(PasswordTooShort),
                    Description = $"Hasło musi składać się z co najmniej {length} znaków."
                };
            }

            public override IdentityError PasswordRequiresUniqueChars(int uniqueChars)
            {
                return new IdentityError
                {
                    Code = nameof(PasswordRequiresUniqueChars),
                    Description = $"Hasło musi składać się z co najmniej {uniqueChars} unikalnych znaków."
                };
            }

            public override IdentityError PasswordRequiresNonAlphanumeric()
            {
                return new IdentityError
                {
                    Code = nameof(PasswordRequiresNonAlphanumeric),
                    Description = "Hasło musi zawierać co najmniej jeden znak niebędący literą lub cyfrą."
                };
            }

            public override IdentityError PasswordRequiresDigit()
            {
                return new IdentityError
                {
                    Code = nameof(PasswordRequiresDigit),
                    Description = "Hasło musi zawierać co najmniej jedną cyfrę (0-9)."
                };
            }

            public override IdentityError PasswordRequiresLower()
            {
                return new IdentityError
                {
                    Code = nameof(PasswordRequiresLower),
                    Description = "Hasło musi zawierać co najmniej jedną małą literę (a-z)."
                };
            }

            public override IdentityError PasswordRequiresUpper()
            {
                return new IdentityError
                {
                    Code = nameof(PasswordRequiresUpper),
                    Description = "Hasło musi zawierać co najmniej jedną wielką literę (A-Z)."
                };
            }
        }

Ответ 7

Для тех, кто интересуется, можно ли локализовать сообщения для выбранной в данный момент культуры, да, это возможно. В asp.net core 2.2 вы можете создать класс LocalizedIdentityErrorDescriber, где IdentityResources - это имя вашего фиктивного класса (оно описано здесь: Глобализация и локализация):

public class LocalizedIdentityErrorDescriber : IdentityErrorDescriber
{
    /// <summary> 
    /// The <see cref="IStringLocalizer{LocalizedIdentityErrorDescriber}"/>
    /// used to localize the strings
    /// </summary>
    private readonly IStringLocalizer<IdentityResources> localizer;

    /// <summary>
    /// Initializes a new instance of the <see cref="LocalizedIdentityErrorDescriber"/> class.
    /// </summary>
    /// <param name="localizer">
    /// The <see cref="IStringLocalizer{LocalizedIdentityErrorDescriber}"/>
    /// that we will use to localize the strings
    /// </param>
    public LocalizedIdentityErrorDescriber(IStringLocalizer<IdentityResources> localizer)
    {
        this.localizer = localizer;
    }

    /// <summary>
    /// Returns the default <see cref="IdentityError" />.
    /// </summary>
    /// <returns>The default <see cref="IdentityError" /></returns>
    public override IdentityError DefaultError()
    {
        return this.GetErrorByCode(nameof(DefaultError));
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a concurrency failure.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a concurrency failure.</returns>
    public override IdentityError ConcurrencyFailure()
    {
        return this.GetErrorByCode(nameof(ConcurrencyFailure));
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a password mismatch.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a password mismatch.</returns>
    public override IdentityError PasswordMismatch()
    {
        return this.GetErrorByCode(nameof(PasswordMismatch));
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating an invalid token.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating an invalid token.</returns>
    public override IdentityError InvalidToken()
    {
        return this.GetErrorByCode(nameof(InvalidToken));
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating an external login is already associated with an account.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating an external login is already associated with an account.</returns>
    public override IdentityError LoginAlreadyAssociated()
    {
        return this.GetErrorByCode(nameof(LoginAlreadyAssociated));
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating the specified user <paramref name="userName" /> is invalid.
    /// </summary>
    /// <param name="userName">The user name that is invalid.</param>
    /// <returns>An <see cref="IdentityError" /> indicating the specified user <paramref name="userName" /> is invalid.</returns>
    public override IdentityError InvalidUserName(string userName)
    {
        return this.FormatErrorByCode(nameof(InvalidUserName), (object)userName);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating the specified <paramref name="email" /> is invalid.
    /// </summary>
    /// <param name="email">The email that is invalid.</param>
    /// <returns>An <see cref="IdentityError" /> indicating the specified <paramref name="email" /> is invalid.</returns>
    public override IdentityError InvalidEmail(string email)
    {
        return this.FormatErrorByCode(nameof(InvalidEmail), (object)email);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating the specified <paramref name="userName" /> already exists.
    /// </summary>
    /// <param name="userName">The user name that already exists.</param>
    /// <returns>An <see cref="IdentityError" /> indicating the specified <paramref name="userName" /> already exists.</returns>
    public override IdentityError DuplicateUserName(string userName)
    {
        return this.FormatErrorByCode(nameof(DuplicateUserName), (object)userName);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating the specified <paramref name="email" /> is already associated with an account.
    /// </summary>
    /// <param name="email">The email that is already associated with an account.</param>
    /// <returns>An <see cref="IdentityError" /> indicating the specified <paramref name="email" /> is already associated with an account.</returns>
    public override IdentityError DuplicateEmail(string email)
    {
        return this.FormatErrorByCode(nameof(DuplicateEmail), (object)email);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating the specified <paramref name="role" /> name is invalid.
    /// </summary>
    /// <param name="role">The invalid role.</param>
    /// <returns>An <see cref="IdentityError" /> indicating the specific role <paramref name="role" /> name is invalid.</returns>
    public override IdentityError InvalidRoleName(string role)
    {
        return this.FormatErrorByCode(nameof(InvalidRoleName), (object)role);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating the specified <paramref name="role" /> name already exists.
    /// </summary>
    /// <param name="role">The duplicate role.</param>
    /// <returns>An <see cref="IdentityError" /> indicating the specific role <paramref name="role" /> name already exists.</returns>
    public override IdentityError DuplicateRoleName(string role)
    {
        return this.FormatErrorByCode(nameof(DuplicateRoleName), (object)role);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a user already has a password.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a user already has a password.</returns>
    public override IdentityError UserAlreadyHasPassword()
    {
        return this.GetErrorByCode(nameof(UserAlreadyHasPassword));
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating user lockout is not enabled.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating user lockout is not enabled..</returns>
    public override IdentityError UserLockoutNotEnabled()
    {
        return this.GetErrorByCode(nameof(UserLockoutNotEnabled));
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a user is already in the specified <paramref name="role" />.
    /// </summary>
    /// <param name="role">The duplicate role.</param>
    /// <returns>An <see cref="IdentityError" /> indicating a user is already in the specified <paramref name="role" />.</returns>
    public override IdentityError UserAlreadyInRole(string role)
    {
        return this.FormatErrorByCode(nameof(UserAlreadyInRole), (object)role);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a user is not in the specified <paramref name="role" />.
    /// </summary>
    /// <param name="role">The duplicate role.</param>
    /// <returns>An <see cref="IdentityError" /> indicating a user is not in the specified <paramref name="role" />.</returns>
    public override IdentityError UserNotInRole(string role)
    {
        return this.FormatErrorByCode(nameof(UserNotInRole), (object)role);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a password of the specified <paramref name="length" /> does not meet the minimum length requirements.
    /// </summary>
    /// <param name="length">The length that is not long enough.</param>
    /// <returns>An <see cref="IdentityError" /> indicating a password of the specified <paramref name="length" /> does not meet the minimum length requirements.</returns>
    public override IdentityError PasswordTooShort(int length)
    {
        return this.FormatErrorByCode(nameof(PasswordTooShort), (object)length);
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a password entered does not contain a non-alphanumeric character, which is required by the password policy.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a password entered does not contain a non-alphanumeric character.</returns>
    public override IdentityError PasswordRequiresNonAlphanumeric()
    {
        return this.GetErrorByCode(nameof(PasswordRequiresNonAlphanumeric));
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a password entered does not contain a numeric character, which is required by the password policy.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a password entered does not contain a numeric character.</returns>
    public override IdentityError PasswordRequiresDigit()
    {
        return this.GetErrorByCode(nameof(PasswordRequiresDigit));
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a password entered does not contain a lower case letter, which is required by the password policy.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a password entered does not contain a lower case letter.</returns>
    public override IdentityError PasswordRequiresLower()
    {
        return this.GetErrorByCode(nameof(PasswordRequiresLower));
    }

    /// <summary>
    /// Returns an <see cref="IdentityError" /> indicating a password entered does not contain an upper case letter, which is required by the password policy.
    /// </summary>
    /// <returns>An <see cref="IdentityError" /> indicating a password entered does not contain an upper case letter.</returns>
    public override IdentityError PasswordRequiresUpper()
    {
        return this.GetErrorByCode(nameof(PasswordRequiresUpper));
    }

    public override IdentityError PasswordRequiresUniqueChars(int uniqueChars)
    {
        return this.FormatErrorByCode(nameof(PasswordRequiresUniqueChars), (object)uniqueChars);
    }

    public override IdentityError RecoveryCodeRedemptionFailed()
    {
        return this.GetErrorByCode(nameof(RecoveryCodeRedemptionFailed));
    }

    /// <summary>Returns a localized <see cref="IdentityError"/> for the provided code.</summary>
    /// <param name="code">The error code.</param>
    /// <returns>A localized <see cref="IdentityError"/>.</returns>
    private IdentityError GetErrorByCode(string code)
    {
        return new IdentityError()
        {
            Code = code,
            Description = this.localizer.GetString(code)
        };
    }

    /// <summary>Formats a localized <see cref="IdentityError"/> for the provided code.</summary>
    /// <param name="code">The error code.</param>
    /// <param name="parameters">The parameters to format the string with.</param>
    /// <returns>A localized <see cref="IdentityError"/>.</returns>
    private IdentityError FormatErrorByCode(string code, params object[] parameters)
    {
        return new IdentityError
        {
            Code = code,
            Description = string.Format(this.localizer.GetString(code, parameters))
        };
    }
}

Затем создайте файлы ресурсов для своих языков следующим образом: Resources\(имя класса-заглушки, описанное выше) (. culture).resx. Для вашей языковой культуры по умолчанию следует оставить пустым. И не забывайте про "." перед именем культуры, если оно не по умолчанию. Ваши файлы IdentityResources должны выглядеть так: enter image description here

В Startup.cs добавьте следующие строки:

 public void ConfigureServices(IServiceCollection services)
 {
      services.AddLocalization(opts => { opts.ResourcesPath = "Resources"; });
      services.AddIdentity<IdentityUser, IdentityRole>()
           .AddEntityFrameworkStores<nameofyourdbcontext>()
           .AddDefaultTokenProviders()
           .AddErrorDescriber<LocalizedIdentityErrorDescriber>(); // this line is important
       services.Configure<RequestLocalizationOptions>(options =>
       {
            var supportedCultures = new[]
            {
                new CultureInfo("en"), // english
                new CultureInfo("tr"), // turkish
                new CultureInfo("ru")  // russian
            };

            options.DefaultRequestCulture = new RequestCulture(culture: "en", uiCulture: "en");
            options.SupportedCultures = supportedCultures;
            options.SupportedUICultures = supportedCultures;
            options.RequestCultureProviders.Insert(0, new CookieRequestCultureProvider()); // type of CultureProvider you want.
        });
 }
 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
 {
        var localizationOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
        // IT IS IMPORTANT TO CALL UseRequestLocalization BEFORE UseMvc
        app.UseRequestLocalization(localizationOptions.Value);
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}"
            );
        });
 }

К настоящему времени вы почти закончили, но вам нужно будет установить cookie файл Culture, и здесь приведен пример кода: Глобализация и локализация. Надеюсь, это кому-нибудь поможет.