可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
ASP.Net core features new support for localization.
In my project I need only one language. For most of the text and annotations I can specify things in my language, but for text coming from ASP.Net Core itself the language is English.
Examples:
- Passwords must have at least one uppercase ('A'-'Z').
- Passwords must have at least one digit ('0'-'9').
- User name 'x@x.com' is already taken.
- The E-post field is not a valid e-mail address.
- The value '' is invalid.
I've tried setting the culture manually, but the language is still English.
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") }
});
How can I change the language of ASP.Net Core, or override its default text?
回答1:
The listed error messages are defined in ASP.NET Core Identity and provided by the IdentityErrorDescriber
. I did not found translated resource files so far and I think they are not localized. On my system (German locale) they are not translated as well although the CultureInfo
is set correctly.
You can configure a custom IdentityErrorDescriber to return your own messages as well as their translations. It is described e.g. in
How to change default error messages of MVC Core ValidationSummary?
In the Configure
method of the Startup.cs
you can wire up your Error Describer class inherited from IdentityErrorDescriber
like
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddErrorDescriber<TranslatedIdentityErrorDescriber>();
For other default model error messages (like invalid number) you can provide own accessor functions in the ModelBindingMessageProvider
. This provider is used by ASP.NET Core MVC and can be configured in the Startup.cs
as well.
services.AddMvc(
options =>
options.ModelBindingMessageProvider.ValueIsInvalidAccessor = s => $"My not valid text for {s}");
回答2:
Based on rboe's and Tedd Hansen's excellent answers, I have written an IStringLocalizer
based IdentityErrorDescriber
for my own use, thought I'd share it here in case anyone needs multiple language support :)
The basic idea is to create a resource file for your default language and any other languages in the usual way.
(https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization)
Located at (If you keep my class name):
/Resources/yournamespace.LocalizedIdentityErrorDescriber.resx
/Resources/yournamespace.LocalizedIdentityErrorDescriber.fr.resx
etc...
Within these you will use the error codes (ex: DefaultError, ConcurrencyError) as the key.
Then add the class below
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's 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's 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))
};
}
}
And initialize everything:
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:
Norwegian IdentityErrorDescriber, in case someone needs it.
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)."
};
}
}
回答4:
and Russian IdentityErrorDescriber, in case someone needs it.
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:
If you are looking for polish one, here it is:
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)."
};
}
}
回答6:
Let me have a comprehensive response for this question and describe how I came up with a solution after reading .net core source code.
before any further description, first install this package using NuGet Microsoft.Extensions.Localization
as you guys might remember in asp.net full framework, switching between cultures was pretty straightforward
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
but in .net core, the culture is not tied to the current thread anymore. The asp.net core engine has a pipeline and we can add different MiddleWares to this pipeline, so the RequestLocalizationMiddleware is the middleware class for handling localization, we might have multiple providers so it will iterate through all the culture providers like QueryStringRequestCultureProvider, CookieRequestCultureProvider, AcceptLanguageHeaderRequestCultureProvider, …
as soon as the request localization middleware can get the current locale from the first provider then it will ignore others and pass the request to the next middleware in the pipeline, so the order of providers in the list really matters.
i personally prefer to store the culture in the browser cookie, so since the CookieRequestCultureProvider is not the first culture provider in the list i move it to the top of the list, the configuration of this part in Startup.cs > ConfigureServices is as below
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
});
});
let me describe the above code, our app default culture is en-US and we only support English, Farsi, Germany so if the browser has different locale or you setting language is other than these 3 languages then the app must switch to default culture. in above code i just remove CookieCultureProvider from the list and add it as the first provider in the list (* I already described the reason why it must be the first one*). The default CookieName is working for me, you can change it if you want.
don't forget to add the below code beneath of Configure(IApplicationBuilder app, IHostingEnvironment env) in Startup.cs
var options = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
app.UseRequestLocalization(options.Value);
so far so good. To have resource files you must specify the path for the resources, I separately specify the resource for Controller, Views, ViewModels and SharedResources in web project root path, the hierarchy is like below
|-Resoures
|---Controllers
|---Views
|---Models
|---SharedResource.resx
|---SharedResource.fa-IR.resx
|---SharedResource.de-DE.resx
keep in mind that for the SharedResource create an empty class with the same name in the Web Project root path i mean a SharedResource.cs with a class with the same name inside.
Add the following code snippet inside the Startup.cs for specifying the resource path and the rest.
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMvc()
AddViewLocalization(
LanguageViewLocationExpanderFormat.Suffix,
opts => { opts.ResourcesPath = "Resources/Views"; }) // this line is not required, it is just for more clarification
.AddDataAnnotationsLocalization();
with this configurations for example when you inject IStringLocalizer inside the controller you have to have corresponding resource files in Resources > Controllers folder (which are HomeController.resx, HomeController.fa-IR.resx, HomeController.de-DE.resx) we can also separate the path by .(say dot) in file name i mean Resources/Controllers/HomeController.resx can be a file in Resources/Controllers.HomeController.resx, you have to inject IViewLocalizer into views to have the localization inside views correspondingly you have to have resources files for Views inside Resources/Views folder, for the ViewModels since we named the folder Models please put all ViewModels inside a pre-created Folder in the Web project root path with the name Models, if the folder has another name or you prefer another name don't forget to rename the Models folder under Resource folder. then you need to do nothing but annotating models, for example, annotate ViewModel properties with [DisplayName("User Emailaddress")] then create resources in the corresponding resource files inside the model (the name of the files must match the model class name) with the same key ("User Emailaddress").
let's finish with where we already started, I mean CookieRequestCultureProvider. as I said earlier I prefer to store it in a cookie, but it is a bit tricky, because cookie parsing is a bit different from what you might expected, just add below code where you want to change the culture, only replace preferedCulture with your culture preference
var preferedCulture = "fa-IR"; // get the culture from user, i just mock it here in a variable
if (HttpContext.Response.Cookies.ContainsKey(".AspNetCore.Culture"))
{
HttpContext.Response.Cookies.Delete(".AspNetCore.Culture");
}
HttpContext.Response.Cookies.Append(".AspNetCore.Culture",
$"c={preferedCulture}|uic={preferedCulture}", new CookieOptions {Expires = DateTime.UtcNow.AddYears(1)});
here we go, our sp.net core web app is now localized :)
回答7:
For those who are wondering, if it is possible to localize the messages for currently chosen culture, yes it is possible. In asp.net core 2.2 you can create a class LocalizedIdentityErrorDescriber where IdentityResources is the name of your dummy class (it is described here: Globalization and localization):
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's 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's 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))
};
}
}
Then create resource files for your languages as followed: Resources\(name of dummy class described above)(.culture).resx. For your default language culture should be left blank. And don't forget about the "." before culture name if it is not default. Your IdentityResources files should look like this:
In Startup.cs add the following lines:
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?}"
);
});
}
By now you are almost done but you will need to set Culture cookie and there is a code example in here: Globalization and localization.
Hope it helps someone.