到目前为止,我已经能够翻译ASP.Net Core 2.1 Web应用程序中的所有内容。
事实证明这是一个小挑战,因为脚手架的“帐户页面”需要一些设置。
但是我找不到一种翻译密码验证消息的方法。另外,翻译模型绑定消息是一个小挑战(感谢stackoverflow)。
有什么想法吗?
我将Startup.cs
文件的相关部分包括在内:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc(options =>
{
var type = typeof(SharedResources);
var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
var factory = services.BuildServiceProvider().GetService<IStringLocalizerFactory>();
var L = factory.Create("SharedResources", assemblyName.Name);
options.ModelBindingMessageProvider.SetValueIsInvalidAccessor(x => L["The value '{0}' is invalid.", x]);
options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(x => L["The value '{0}' is invalid.", x]);
options.ModelBindingMessageProvider.SetValueMustBeANumberAccessor(x => L["The field {0} must be a number.", x]);
options.ModelBindingMessageProvider.SetMissingBindRequiredValueAccessor(x => L["A value for the '{0}' property was not provided.", x]);
options.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor((x, y) => L["The value '{0}' is not valid for {1}.", x, y]);
options.ModelBindingMessageProvider.SetMissingKeyOrValueAccessor(() => L["A value is required."]);
options.ModelBindingMessageProvider.SetUnknownValueIsInvalidAccessor(x => L["The supplied value is invalid for {0}.", x]);
options.ModelBindingMessageProvider.SetMissingRequestBodyRequiredValueAccessor(() => L["A non-empty request body is required."]);
options.ModelBindingMessageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor(x => L["The value '{0}' is not valid.", x]);
options.ModelBindingMessageProvider.SetNonPropertyUnknownValueIsInvalidAccessor(() => L["The supplied value is invalid."]);
options.ModelBindingMessageProvider.SetNonPropertyValueMustBeANumberAccessor(() => L["NonPropertyValueMustBeNumber"]);
})
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization(options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) =>
{
// This is for Account scaffolded pages data annotations
return factory.Create(typeof(SharedResources));
};
});
...
}
我无法在InputModel
的{{1}}中放入这样的内容,因为Register.cshtml.cs
被忽略了(我也不会这样做,因为我不想对代码进行硬编码密码策略说明):
ErrorMessage
答案 0 :(得分:3)
这可以通过本地化身份错误消息来完成,有22条消息必须本地化。
首先,创建一个共享资源文件“其密钥使用public access修饰符定义”,然后键入具有本地化版本的所有错误消息,如下图所示:
然后创建一个实现IdentityErrorDescriber
的新类,并参考共享资源文件覆盖所有默认消息;在此示例中,共享资源文件名为LocalizedIdentityErrorMessages:
public class LocalizedIdentityErrorDescriber : IdentityErrorDescriber
{
public override IdentityError DuplicateEmail(string email)
{
return new IdentityError
{
Code = nameof(DuplicateEmail),
Description = string.Format(LocalizedIdentityErrorMessages.DuplicateEmail, email)
};
}
public override IdentityError DuplicateUserName(string userName)
{
return new IdentityError
{
Code = nameof(DuplicateUserName),
Description = string.Format(LocalizedIdentityErrorMessages.DuplicateUserName, userName)
};
}
public override IdentityError InvalidEmail(string email)
{
return new IdentityError
{
Code = nameof(InvalidEmail),
Description = string.Format(LocalizedIdentityErrorMessages.InvalidEmail, email)
};
}
public override IdentityError DuplicateRoleName(string role)
{
return new IdentityError
{
Code = nameof(DuplicateRoleName),
Description = string.Format(LocalizedIdentityErrorMessages.DuplicateRoleName, role)
};
}
public override IdentityError InvalidRoleName(string role)
{
return new IdentityError
{
Code = nameof(InvalidRoleName),
Description = string.Format(LocalizedIdentityErrorMessages.InvalidRoleName, role)
};
}
public override IdentityError InvalidToken()
{
return new IdentityError
{
Code = nameof(InvalidToken),
Description = LocalizedIdentityErrorMessages.InvalidToken
};
}
public override IdentityError InvalidUserName(string userName)
{
return new IdentityError
{
Code = nameof(InvalidUserName),
Description = string.Format(LocalizedIdentityErrorMessages.InvalidUserName, userName)
};
}
public override IdentityError LoginAlreadyAssociated()
{
return new IdentityError
{
Code = nameof(LoginAlreadyAssociated),
Description = LocalizedIdentityErrorMessages.LoginAlreadyAssociated
};
}
public override IdentityError PasswordMismatch()
{
return new IdentityError
{
Code = nameof(PasswordMismatch),
Description = LocalizedIdentityErrorMessages.PasswordMismatch
};
}
public override IdentityError PasswordRequiresDigit()
{
return new IdentityError
{
Code = nameof(PasswordRequiresDigit),
Description = LocalizedIdentityErrorMessages.PasswordRequiresDigit
};
}
public override IdentityError PasswordRequiresLower()
{
return new IdentityError
{
Code = nameof(PasswordRequiresLower),
Description = LocalizedIdentityErrorMessages.PasswordRequiresLower
};
}
public override IdentityError PasswordRequiresNonAlphanumeric()
{
return new IdentityError
{
Code = nameof(PasswordRequiresNonAlphanumeric),
Description = LocalizedIdentityErrorMessages.PasswordRequiresNonAlphanumeric
};
}
public override IdentityError PasswordRequiresUniqueChars(int uniqueChars)
{
return new IdentityError
{
Code = nameof(PasswordRequiresUniqueChars),
Description = string.Format(LocalizedIdentityErrorMessages.PasswordRequiresUniqueChars, uniqueChars)
};
}
public override IdentityError PasswordRequiresUpper()
{
return new IdentityError
{
Code = nameof(PasswordRequiresUpper),
Description = LocalizedIdentityErrorMessages.PasswordRequiresUpper
};
}
public override IdentityError PasswordTooShort(int length)
{
return new IdentityError
{
Code = nameof(PasswordTooShort),
Description = string.Format(LocalizedIdentityErrorMessages.PasswordTooShort, length)
};
}
public override IdentityError UserAlreadyHasPassword()
{
return new IdentityError
{
Code = nameof(UserAlreadyHasPassword),
Description = LocalizedIdentityErrorMessages.UserAlreadyHasPassword
};
}
public override IdentityError UserAlreadyInRole(string role)
{
return new IdentityError
{
Code = nameof(UserAlreadyInRole),
Description = string.Format(LocalizedIdentityErrorMessages.UserAlreadyInRole, role)
};
}
public override IdentityError UserNotInRole(string role)
{
return new IdentityError
{
Code = nameof(UserNotInRole),
Description = string.Format(LocalizedIdentityErrorMessages.UserNotInRole, role)
};
}
public override IdentityError UserLockoutNotEnabled()
{
return new IdentityError
{
Code = nameof(UserLockoutNotEnabled),
Description = LocalizedIdentityErrorMessages.UserLockoutNotEnabled
};
}
public override IdentityError RecoveryCodeRedemptionFailed()
{
return new IdentityError
{
Code = nameof(RecoveryCodeRedemptionFailed),
Description = LocalizedIdentityErrorMessages.RecoveryCodeRedemptionFailed
};
}
public override IdentityError ConcurrencyFailure()
{
return new IdentityError
{
Code = nameof(ConcurrencyFailure),
Description = LocalizedIdentityErrorMessages.ConcurrencyFailure
};
}
public override IdentityError DefaultError()
{
return new IdentityError
{
Code = nameof(DefaultError),
Description = LocalizedIdentityErrorMessages.DefaultIdentityError
};
}
}
最后,将本地化的错误描述器添加到启动类中的ConfigureServices方法下的身份设置中:
services.AddIdentity<AppUser, AppRole>()
// localize identity error messages
.AddErrorDescriber<LocalizedIdentityErrorDescriber>()
.AddEntityFrameworkStores()
.AddDefaultTokenProviders();
资源:http://www.ziyad.info/en/articles/20-Localizing_Identity_Error_Messages
此外,您可能需要阅读分步本地化文章: http://www.ziyad.info/en/articles/10-Developing_Multicultural_Web_Application
答案 1 :(得分:1)
这些错误消息是使用IdentityErrorDescriber
生成的。这是类本身的示例:
public class IdentityErrorDescriber
{
...
public virtual IdentityError PasswordTooShort(int length)
{
return new IdentityError
{
Code = nameof(PasswordTooShort),
Description = Resources.FormatPasswordTooShort(length)
};
}
...
}
如果您要自定义特定消息,则可以创建自己的IdentityErrorDescriber
实现。这是一个示例:
public class MyIdentityErrorDescriber : IdentityErrorDescriber
{
public override IdentityError PasswordTooShort(int length)
{
return new IdentityError
{
Code = nameof(PasswordTooShort),
Description = "Your description goes here."
};
}
}
要使用此新实现,需要将其添加到Startup.ConfigureServices
中的DI容器中:
services.AddScoped<IdentityErrorDescriber, MyIdentityErrorDescriber>();
答案 2 :(得分:0)
你可以使用 ViewModel
而不是你可以只使用 DataAnnotations
这里是 Controller
、ViewModel
和 View
在这种情况下的样子(翻译是克罗地亚语).
[HttpGet]
public IActionResult Login()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
var result = await signInManager.PasswordSignInAsync(
model.Email, model.Password, model.RememberMe, false);
if (result.Succeeded)
{
return RedirectToAction("index", "home");
}
ModelState.AddModelError(string.Empty, "Neuspješni pokušaj");
}
return View(model);
}
在 ErrorMessage
下放置您的错误信息
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace MyApplication.ViewModels
{
public class LoginViewModel
{
[Required(ErrorMessage = "Email je obavezan")]
[EmailAddress]
public string Email { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Lozinka")]
[Required(ErrorMessage = "Lozinka je obavezna")]
public string Password { get; set; }
[Display(Name = "Zapamti me")]
public bool RememberMe { get; set; }
}
}
@model LoginViewModel
@{
ViewBag.Title = "Prijava";
}
<h2>Prijava</h2>
<div class="row">
<div class="col-md-12">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<div class="checkbox">
<label asp-for="RememberMe">
<input asp-for="RememberMe" />
@Html.DisplayNameFor(m => m.RememberMe)
</label>
</div>
</div>
<button type="submit" class="btn btn-primary">Prijava</button>
</form>
</div>
</div>