目录
本地化ASP.NET Core 3.x或2.x或1.x Web应用程序需要大量的基础结构设置,并且要花费时间和精力。在本文中,我们将使用LazZiya.ExpressLocalization nuget包一步一步地本地化我们的Web应用程序。
- https://github.com/LazZiya/ExpressLocalizationSampleCore3(Core 3.x Razor页面和Mvc)
背景
大多数Web应用程序使用基于URL的本地化,因此我们可以在URL中看到所选的区域性,例如http://www.example.com/en/Contact。默认情况下,ASP.NET Core提供以下请求区域性提供程序:
- QueryStringRequestCultureProvider
- CookieRequestCultureProvider
- AcceptLanguageHeaderRequestCultureProvider
为了进行路由值本地化,我们将构建自定义本地化提供程序并定义一个全局路由模板,因此,基本上,我们需要完成以下本地化步骤,以完全本地化Web应用程序:
- 生成路由值请求区域性提供程序
根据{culture}路由值进行请求本地化
- 为区域性参数定义全局路由模板
在网址中添加{culture}参数,例如www.example.com/zh-CN/
- 设置数据注释本地化
数据注释的本地化,例如显示名称,必填性,字符串长度等。
DataAnnotations在System.ComponentModel.DataAnnotations命名空间中定义。
除了本地化默认的DataAnnotation之外,自定义属性还必须使用相同的逻辑进行本地化。
有关更多详细信息,请参见DataAnnotations。
- 设置模型绑定错误消息本地化
输入类型的验证在服务器端提交后,如ValueMustBeANumberAccessor,AttemptedValueIsInvalidAccessor等。
有关更多详细信息,请参见DefaultModelBindingMessageProvider。
- 设置标识描述错误消息
所有与用户和角色相关的消息的本地化,如“角色中的用户已存在”,“用户名已存在”,...等。
有关更多详细信息,请参见IdentityErrorDescriber。
- 设置OnRedirectTo ...事件
当用户被自动重定向到“登录”,“注销”或“AccessDenied”页面时,将自动触发这些事件。自动重定向不处理区域性值,因此必须手动配置。
有关更多详细信息,请参见https://github.com/LazZiya/ExpressLocalization/issues/6
- 设置视图本地化
在Razor页面中本地化text/html。
有关更多详细信息,请参见ViewLocalization。
- 设置客户端验证脚本
客户验证消息的本地化是必不可少的,表单将在提交之前进行验证。客户端验证脚本必须进行本地化,以便用户将看到本地化的消息,例如:必填字段,密码必须匹配,等等。
另一重要的事情是验证本地化的十进制数,某些区域性使用句点,而其他区域性则使用逗号作为十进制数,例如(1,2)和(1.2),这种情况在客户端验证中应谨慎处理。
而且,某些区域性可能会使用完全不同的编号系统。例如,阿拉伯区域性使用的是“٠١٢٣٤٥٦٧٨٩”之类的数字,拉丁区域性使用的是"0123456789"。如果编号系统设置不正确,则会出现验证错误。(请参阅本文,以了解如何更改编号系统以进行客户端验证。)
有关更多详细信息,请参阅GitHub中与客户端验证和本地化相关的问题。
- 为每种区域性创建本地化的资源文件
视图,数据注释,模型绑定和标识错误都需要本地化资源。此步骤耗费大量时间和精力!
所有这些步骤需要大量的工作,并且会花费太多时间。因此,这带来了LazZiya.ExpressLocalization nuget软件包的好处,它通过简单的代码行消除了本地化设置的时间和精力。
创建项目
首先创建一个基本的ASP.NET Core Web应用程序(我正在使用VS2019):
1、通过选择ASP.NET Core Web应用程序来创建一个新项目:
2、单击下一步,为该项目起一个友好的名称,然后单击创建:
3、选择Web应用程序,将目标框架设置为任何ASP.NET Core版本(3、2或1),并确保将身份验证更改为个人用户帐户。
4、单击创建,等待解决方案创建基本模板,完成后,您可以通过在解决方案资源管理器中选择项目名称,然后按(Ctrl + Shift + B)来构建项目,然后按(Ctrl + Shift + W),无需在浏览器中进行调试即可运行。
安装LazZiya.ExpressLocalization
1、在解决方案资源管理器中,在项目名称下,右键单击Dependencies,然后选择“Manage Nuget Packages ”。
2、转到“浏览”选项卡并搜索“LazZiya”,选择“LazZiya.ExpressLocalization”,然后单击“安装”,选择最新版本并进行安装:
创建本地化资源
我已经为该项目准备了本地化的资源,因此您不必浪费时间在创建本地化的资源上。
在项目根目录下,创建一个新文件夹,并将其命名为“LocalizationResources ”:
在LocalizationResources文件夹下,创建新public类并将其命名为“ViewLocalizationResource”,该类将用于对资源文件进行分组以进行视图本地化:
namespace ExpressLocalizationSample.LocalizationResources
{
public class ViewLocalizationResource
{
}
}
在LocalizationResources文件夹下,创建新public类并将其命名为“ExpressLocalizationResource”,该类将用于对资源文件进行标识,模型绑定和数据注释的分组。
namespace ExpressLocalizationSample.LocalizationResources
{
public class ExpressLocalizationResource
{
}
}
我们将使用这两个类将资源类型传递给express本地化方法。
注意:在此示例中,我将使用两个资源文件作为本地化字符串,但是您可以自由地使用一个、两个或多个本地化资源文件作为所有本地化字符串。在AddExpressLocalization的不同版本之间切换,以查看可用的可能性。在GitHub中阅读更多内容。
最后,从此存储库文件夹下载相关的区域性资源。请注意,您需要为每种区域性下载两个文件,例如(ExpressLocalizationResource.tr.resx和ViewLocalizationResource.tr.resx)。将下载的文件复制到“LocalizationResources”文件夹。
使用代码
最后,我们准备好进行本地化设置。:)
打开startup.cs文件并添加所需的名称空间:
using LazZiya.ExpressLocalization;
然后在ConfigureServices方法内部,定义支持的区域性并添加本地化设置,如下所示:
var cultures = new[]
{
new CultureInfo("tr"),
new CultureInfo("ar"),
new CultureInfo("hi"),
new CultureInfo("en"),
};
services.AddRazorPages()
.AddExpressLocalization<ExpressLocalizationResource, ViewLocalizationResource>(
ops =>
{
ops.ResourcesPath = "LocalizationResources";
ops.RequestLocalizationOptions = o =>
{
o.SupportedCultures = cultures;
o.SupportedUICultures = cultures;
o.DefaultRequestCulture = new RequestCulture("en");
};
});
注意:根据项目模式(MVC或Razor页面)和版本,服务实现可能更改为services.AddMvc()或services.AddControllersWithViews()或services.AddRazorPages().
然后在Configure方法下,将应用程序配置为使用请求本地化:
app.UseRequestLocalization();
在不同的.NET Core版本和项目类型中进行安装
不同版本的.NET Core(1、2、3)可能具有不同的服务设置步骤,ExpressLocalization可与所有版本和模式变体一起使用:
services.AddMvc().AddExpressLocalization(...);
services.AddRazorPages().AddExpressLocalization(...);
services.AddControllersWithViews().AddExpressLocalization(...);
如果您在启动时定义了路由,则需要在路由表中添加区域性参数:
app.UseRequestLocalization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{culture=en}/{controller=Home}/{action=Index}/{id?}");
});
或者,如果您在控制器上使用路由属性,请将区域性路由参数添加到路由属性:
app.UseRequestLocalization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{culture=en}/{controller=Home}/{action=Index}/{id?}");
});
添加语言导航
此步骤需要另一个nuget软件包LazZiya.TagHelpers,也可以从nuget安装。
在Pages文件夹下,打开_ViewImports.cshtml文件并添加LazZiya.TagHelpers,该文件将有助于创建语言导航:
@addTagHelper *, LazZiya.TagHelpers
然后打开Pages/Shared/_Layout.cshtml 文件,并在_LoginPartial标签下添加语言导航标签帮助器,如下所示:
<partial name="_LoginPartial" />
<language-nav></language-nav>
设置区域性Cookie
ExpressLocalization按以下顺序使用所有区域性提供程序:
- RouteSegmentCultureProvider
- QueryStringRequestCultureProvider
- CookieRequestCultureProvider
- AcceptedLanguageHeaderRequestCultureProvider
- 使用启动设置中的默认请求区域性
要设置Cookie值,我们需要在IndexPage或主页中创建一个处理程序,以设置应用程序的Cookie区域性值。
public class IndexModel : PageModel
{
// ...
public IActionResult OnGetSetCultureCookie(string cltr, string returnUrl)
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(cltr)),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
);
return LocalRedirect(returnUrl)
}
}
然后我们可以如下配置语言导航:
<language-nav cookie-handler-url="@Url.Page("/Index", "SetCultureCookie",
new { area="", cltr="{0}", returnUrl="{1}" })">
</language-nav>
参考每个列出的区域性,占位符“{0}”和“{1}”将被语言taghelper替换。
添加LanguageNav标签帮助器后,我们就可以开始第一次运行了:
很好,到目前为止,我们已经在受支持的区域性上进行了导航,但是我们仍然需要对视图文本进行本地化以查看本地化的版本。
有关更多详细信息,请访问LanguageTagHelper Wiki。
本地化视图
在下载的“ViewLocalizationResource.xx.resx”文件中已经提供了默认项目的本地化文本。如果需要为视图添加更多自定义文本,请将其添加到“ViewLocalizationResource.xx.resx”文件中。
选项1
在这里,我们将使用LazZiya.ExpressLocalization附带的LocalizeTagHelper,它提供了一个用于本地化视图的简单而清晰的HTML标记。
打开Pages/_ViewImports.cshtml文件并添加本地化标记帮助器:
@addTagHelper *, LazZiya.ExpressLocalization
使用LocalizeTagHelper非常简单,只需将“localize-contents”属性添加到任何html标签,或将任何html内容包含在“<localize>”标签中。
然后打开Pages/Index.cshtml并使用localize标签助手来本地化texts/html:
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
var locArgs = new[] { "https://docs.microsoft.com/aspnet/core" };
}
<div class="text-center">
<h1 class="display-4" localize-content>Welcome</h1>
<p localize-args="@locArgs">Learn about
<a href="{0}">building Web apps with ASP.NET Core</a>.</p>
</div>
也可以使用相同的过程本地化其他视图中的所有文本。
在实时演示页面和LocalizeTagHelper Wiki中查看更多详细信息。
选项2
打开Pages/_ViewImports.cshtml文件并注入已随ExpressLocalization提供的ISharedCultureLocalizer:
@using LazZiya.ExpressLocalization
@inject ISharedCultureLocalizer _loc
然后打开Pages/Index.cshtml并使用文本的本地化函数:
@page
@model IndexModel
@{
ViewData["Title"] = _loc.GetLocalizedString("Home page");
}
<div class="text-center">
<h1 class="display-4">@_loc.GetLocalizedString("Welcome")</h1>
<p>@_loc.GetLocalizedHtmlString("Learn about
<a href='{0}'> building Web apps with ASP.NET Core</a>",
new[]{"https://docs.microsoft.com/aspnet/core"}).</p>
</div>
也可以使用相同的过程本地化其他视图中的所有文本。
本地化URL
当页面处于默认区域之外的其他区域时,如果单击“隐私”,“登录”或“注册”链接,您会注意到我们正在丢失所选区域,这是因为我们没有将区域性路径值添加到链接中。
打开Pages/_ViewImports.cshtml并添加对System.Globalization的引用:
@using System.Globalization
然后打开Pages/_LoginPartial.cshtml并在页面顶部添加如下culture参数:
@using System.Globalization
使用此参数可以为所有链接提供culture路由值,如下所示:
<a class="nav-link text-dark"
asp-area="Identity"
asp-page="/Account/Register"
asp-route-culture="@culture"
localize-content>Register</a>
对项目中的所有视图执行此操作。
本地化身份(Identity)视图
需要重写与身份相关的(如登录、注册和配置文件)才能进行修改。
右键单击项目名称,选择添加 -> 新建脚手架项目 ...
选择身份,然后单击添加:
选择“覆盖所有文件 ”,然后选择“ApplicationDbContext”:
单击添加时,将创建一个新的Areas文件夹,其中包括所有与身份相关的视图:
标识区域具有三个_ViewImports文件夹:
- Areas/Identity/Pages/_ViewImports.cshtml
- Areas/Identity/Pages/Account/_ViewImports.cshtml
- Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml
将以下代码添加到所有这些代码中,就像之前对Pages/_ViewImports.cshtml所做的那样:
@using System.Globalization
@addTagHelper *, LazZiya.TagHelpers
@addTagHelper *, LazZiya.ExpressLocalization
遍历视图并像以前一样使用本地化步骤来本地化视图并添加culture路由参数。以下是Register.cshtml页面:
@page
@model RegisterModel
@{
ViewData["Title"] = "Register";
var culture = CultureInfo.CurrentCulture.Name;
}
<h1 localize-content>Register</h1>
<div class="row">
<div class="col-md-4">
<form asp-route-returnUrl="@Model.ReturnUrl"
method="post" asp-route-culture="@culture">
<h4 localize-content>Create a new account.</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.Password"></label>
<input asp-for="Input.Password" class="form-control" />
<span asp-validation-for="Input.Password"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.ConfirmPassword"></label>
<input asp-for="Input.ConfirmPassword" class="form-control" />
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary" localize-content>Register</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}
如果你运行页面,并做一些非法输入,你会发现,验证消息都是英文的,所以我们需要本地化的数据注解的消息,例如Required,StringLength等等。
打开Areas/Identity/Pages/Account/Register.cshtml.cs文件,并在页面顶部添加对“LazZiya.ExpressLocalization.DataAnnotations”的引用;它提供了已经产生本地化错误消息的明确属性:
@using LazZiya.ExpressLocalization.DataAnnotations;
然后修改输入模型,如下所示:
public class InputModel
{
[ExRequired]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[ExRequired]
[ExStringLength(100, MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[ExCompare("Password"]
public string ConfirmPassword { get; set; }
}
编译并运行项目,您将看到本地化的数据注释错误消息:
本地化自定义后端消息
任何来自后端的自定义消息都可以使用LazZiya.ExpressLocalization中的ISharedCultureLocalizer进行本地化。要本地化任何自定义的后端消息,请按如下所示注入ISharedCultureLocalizer和调用本地化方法(MVC和RazorPages中的过程相同):
public IndexModel : PageModel
{
private readonly ISharedCultureLocalizer _loc;
public IndexModel(ISharedCultureLocalizer loc)
{
_loc = loc;
}
public void OnGet()
{
//...
var msg = _loc.GetLocalizedString("This is a custom backend message.");
// ...
}
}
后端消息的本地化文本必须在包含视图的本地化文本的同一资源文件中定义。
ISharedCultureLocalizer还有其他变体,可让您指定目标区域性和目标资源文件。浏览并测试该功能以查看其他可能性。
客户端验证
服务器端验证运行良好,但是我们仍然需要添加客户端验证,因此在提交表单之前,将在客户端验证输入字段。
客户端验证的一个主要问题是验证数字,日期等本地化输入。例如,如果您使用的是十进制输入,则将看到本地化数字的验证错误,例如1.3在英语中有效,但在土耳其语中无效因为它应该是1,3(逗号而不是句点)。
在这里,我们将使用LazZiya.TagHelpers自带的另一种有用的标记辅助LocalizationValidatonScripts。
在启动时注册taghelper:
services.AddTransient<ITagHelperComponent, LocalizationValidationScriptsTagHelperComponent>();
打开Register.cshtml页面,然后在默认的验证脚本局部下添加标签帮助器:
@section Scripts {
<partial name="_ValidationScriptsPartial" />
<localization-validation-scripts></localization-validation-scripts>
}
就是这样,现在将参考当前区域性(具有逗号或句点的十进制输入验证)来验证具有本地化输入的字段。
示例项目
您可以从GitHub下载包含19种以上区域性的示例项目:
- https://github.com/LazZiya/ExpressLocalizationSampleCore3(Core 3.x Razor页面和Mvc)
参考文献
在此处阅读有关使用的nuget软件包的更多详细信息: