So far I have been able to translate everything in an ASP.Net Core 2.1 Web Application.
It has proven a little challenge, since the scaffolded Account Pages needed a little setup.
But what I cannot find is a way to translate the Password validation messages. Also, translating the model binding messages were a little challenge (thanks stackoverflow).
Any ideas?
I include the relevant parts of my Startup.cs
file:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc(options =>
{
var type = typeof(SharedResources);
var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
var factory = services.BuildServiceProvider().GetService<IStringLocalizerFactory>();
var L = factory.Create("SharedResources", assemblyName.Name);
options.ModelBindingMessageProvider.SetValueIsInvalidAccessor(x => L["The value '{0}' is invalid.", x]);
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(x => L["The value '{0}' is invalid.", x]);
options.ModelBindingMessageProvider.SetValueMustBeANumberAccessor(x => L["The field {0} must be a number.", x]);
options.ModelBindingMessageProvider.SetMissingBindRequiredValueAccessor(x => L["A value for the '{0}' property was not provided.", x]);
options.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor((x, y) => L["The value '{0}' is not valid for {1}.", x, y]);
options.ModelBindingMessageProvider.SetMissingKeyOrValueAccessor(() => L["A value is required."]);
options.ModelBindingMessageProvider.SetUnknownValueIsInvalidAccessor(x => L["The supplied value is invalid for {0}.", x]);
options.ModelBindingMessageProvider.SetMissingRequestBodyRequiredValueAccessor(() => L["A non-empty request body is required."]);
options.ModelBindingMessageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor(x => L["The value '{0}' is not valid.", x]);
options.ModelBindingMessageProvider.SetNonPropertyUnknownValueIsInvalidAccessor(() => L["The supplied value is invalid."]);
options.ModelBindingMessageProvider.SetNonPropertyValueMustBeANumberAccessor(() => L["NonPropertyValueMustBeNumber"]);
})
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization(options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) =>
{
// This is for Account scaffolded pages data annotations
return factory.Create(typeof(SharedResources));
};
});
...
}
I cannot put something like this in the InputModel
of Register.cshtml.cs
because the ErrorMessage
gets ignored (and I won't do it anyways, since I don't want to hardcode the password policy description):
[Required(ErrorMessage = "The {0} field is required.")]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password, ErrorMessage = "Password must be nice")]
[Display(Name = "Password")]
public string Password { get; set; }
This can be done by localizing identity error messages, there are 22 message that has to be localized.
First, create a shared resource file "its keys defined with public access modifier" and type all error messages with localized versions as in the below image:
then create a new class that implements IdentityErrorDescriber
and override all default messages with reference to the shared resource file; in this sample the shared resource file name is LocalizedIdentityErrorMessages:
public class LocalizedIdentityErrorDescriber : IdentityErrorDescriber
{
public override IdentityError DuplicateEmail(string email)
{
return new IdentityError
{
Code = nameof(DuplicateEmail),
Description = string.Format(LocalizedIdentityErrorMessages.DuplicateEmail, email)
};
}
public override IdentityError DuplicateUserName(string userName)
{
return new IdentityError
{
Code = nameof(DuplicateUserName),
Description = string.Format(LocalizedIdentityErrorMessages.DuplicateUserName, userName)
};
}
public override IdentityError InvalidEmail(string email)
{
return new IdentityError
{
Code = nameof(InvalidEmail),
Description = string.Format(LocalizedIdentityErrorMessages.InvalidEmail, email)
};
}
public override IdentityError DuplicateRoleName(string role)
{
return new IdentityError
{
Code = nameof(DuplicateRoleName),
Description = string.Format(LocalizedIdentityErrorMessages.DuplicateRoleName, role)
};
}
public override IdentityError InvalidRoleName(string role)
{
return new IdentityError
{
Code = nameof(InvalidRoleName),
Description = string.Format(LocalizedIdentityErrorMessages.InvalidRoleName, role)
};
}
public override IdentityError InvalidToken()
{
return new IdentityError
{
Code = nameof(InvalidToken),
Description = LocalizedIdentityErrorMessages.InvalidToken
};
}
public override IdentityError InvalidUserName(string userName)
{
return new IdentityError
{
Code = nameof(InvalidUserName),
Description = string.Format(LocalizedIdentityErrorMessages.InvalidUserName, userName)
};
}
public override IdentityError LoginAlreadyAssociated()
{
return new IdentityError
{
Code = nameof(LoginAlreadyAssociated),
Description = LocalizedIdentityErrorMessages.LoginAlreadyAssociated
};
}
public override IdentityError PasswordMismatch()
{
return new IdentityError
{
Code = nameof(PasswordMismatch),
Description = LocalizedIdentityErrorMessages.PasswordMismatch
};
}
public override IdentityError PasswordRequiresDigit()
{
return new IdentityError
{
Code = nameof(PasswordRequiresDigit),
Description = LocalizedIdentityErrorMessages.PasswordRequiresDigit
};
}
public override IdentityError PasswordRequiresLower()
{
return new IdentityError
{
Code = nameof(PasswordRequiresLower),
Description = LocalizedIdentityErrorMessages.PasswordRequiresLower
};
}
public override IdentityError PasswordRequiresNonAlphanumeric()
{
return new IdentityError
{
Code = nameof(PasswordRequiresNonAlphanumeric),
Description = LocalizedIdentityErrorMessages.PasswordRequiresNonAlphanumeric
};
}
public override IdentityError PasswordRequiresUniqueChars(int uniqueChars)
{
return new IdentityError
{
Code = nameof(PasswordRequiresUniqueChars),
Description = string.Format(LocalizedIdentityErrorMessages.PasswordRequiresUniqueChars, uniqueChars)
};
}
public override IdentityError PasswordRequiresUpper()
{
return new IdentityError
{
Code = nameof(PasswordRequiresUpper),
Description = LocalizedIdentityErrorMessages.PasswordRequiresUpper
};
}
public override IdentityError PasswordTooShort(int length)
{
return new IdentityError
{
Code = nameof(PasswordTooShort),
Description = string.Format(LocalizedIdentityErrorMessages.PasswordTooShort, length)
};
}
public override IdentityError UserAlreadyHasPassword()
{
return new IdentityError
{
Code = nameof(UserAlreadyHasPassword),
Description = LocalizedIdentityErrorMessages.UserAlreadyHasPassword
};
}
public override IdentityError UserAlreadyInRole(string role)
{
return new IdentityError
{
Code = nameof(UserAlreadyInRole),
Description = string.Format(LocalizedIdentityErrorMessages.UserAlreadyInRole, role)
};
}
public override IdentityError UserNotInRole(string role)
{
return new IdentityError
{
Code = nameof(UserNotInRole),
Description = string.Format(LocalizedIdentityErrorMessages.UserNotInRole, role)
};
}
public override IdentityError UserLockoutNotEnabled()
{
return new IdentityError
{
Code = nameof(UserLockoutNotEnabled),
Description = LocalizedIdentityErrorMessages.UserLockoutNotEnabled
};
}
public override IdentityError RecoveryCodeRedemptionFailed()
{
return new IdentityError
{
Code = nameof(RecoveryCodeRedemptionFailed),
Description = LocalizedIdentityErrorMessages.RecoveryCodeRedemptionFailed
};
}
public override IdentityError ConcurrencyFailure()
{
return new IdentityError
{
Code = nameof(ConcurrencyFailure),
Description = LocalizedIdentityErrorMessages.ConcurrencyFailure
};
}
public override IdentityError DefaultError()
{
return new IdentityError
{
Code = nameof(DefaultError),
Description = LocalizedIdentityErrorMessages.DefaultIdentityError
};
}
}
finally, add the localized error describer to identity setup under ConfigureServices method in startup class:
services.AddIdentity<AppUser, AppRole>()
// localize identity error messages
.AddErrorDescriber<LocalizedIdentityErrorDescriber>()
.AddEntityFrameworkStores()
.AddDefaultTokenProviders();
resource: http://www.ziyad.info/en/articles/20-Localizing_Identity_Error_Messages
Additionally you may need to read the step-by-step localization articles: http://www.ziyad.info/en/articles/10-Developing_Multicultural_Web_Application
Recently I've developed a new nuget package (XLocalizer), it simplifies the localization setup of Asp.Net Core web apps, it supports auto online translation and auto resource creating. Additionaly, all identity errors, model binding errors and validation errors can be customized easily in a json file.
References:
These error messages are generated using IdentityErrorDescriber
. Here's a sample of what the class itself looks like:
public class IdentityErrorDescriber
{
...
public virtual IdentityError PasswordTooShort(int length)
{
return new IdentityError
{
Code = nameof(PasswordTooShort),
Description = Resources.FormatPasswordTooShort(length)
};
}
...
}
To customise a specific message, create your own IdentityErrorDescriber
implementation. Here's an example:
public class MyIdentityErrorDescriber : IdentityErrorDescriber
{
public override IdentityError PasswordTooShort(int length)
{
return new IdentityError
{
Code = nameof(PasswordTooShort),
Description = "Your description goes here."
};
}
}
To use this new implementation, add it to the DI container in Startup.ConfigureServices
:
services.AddScoped<IdentityErrorDescriber, MyIdentityErrorDescriber>();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With