目录
介绍
传统上,.NET应用程序是使用(编译的)资源文件进行本地化的。开发人员工具用于创作这些文件,本地化字符串的更新通常需要更新应用程序。
将本地化数据存储在数据库中,可以更灵活地选择由谁以及何时输入和更新翻译。您可以在输入所有翻译之前部署应用程序,并且仍然可以在部署后修复翻译。这可以通过数据库顶部的任何类型的用户界面远程完成。
但是,将本地化数据存储在数据库中也存在一个回退:数据库访问速度很慢。特别是如果您将其与加载到内存中并从那里操作的资源文件进行比较。
我在这里向您展示的MvcDasboardLocalize组件包括一个数据库驱动的本地化解决方案,该解决方案使用内存加载缓存来提高性能,以及一个仪表板来输入和维护本地化数据。它还提供了一些在基于资源文件的本地化中找不到的独特功能。
但在查看这些功能之前,让我们创建一个ASP.NET Core应用程序并为其添加本地化。
构建应用程序
在Visual Studio 2022中,我们选择在C#中创建类型为“ASP.NET Core Web 应用(模型-视图-控制器)”的新项目。我们为.NET 6.0和身份验证类型选择“无”,并保留默认值以使用顶级语句。我将我的应用程序命名为“LocalizationDemo”。这是Visual Studio也将用作根命名空间的名称。
如果一切顺利,我们现在有一个Web应用程序,其中包含一个控制器(HomeController)和两个视图,位于 Views\Home 文件夹(Index.cshtml 和 Privacy.cshtml)。
为了使本地化演示更具挑战性,我们将添加模型类型和Web表单,以便我们可以编辑模型并获取或不获取ModelState错误。
在 Models 文件夹中,我们添加一个PersonModel类,其中包含一些字段,如 FirstName、LastName、Age和EmailAddress。我们还添加了不同风格的数据注释属性。结果是清单1 中的类:
using System.ComponentModel.DataAnnotations;
namespace LocalizationDemo.Models
{
public class PersonModel
{
[Display(Name = "First Name")]
[Required]
public string? FirstName { get; set; }
[Required]
public string? LastName { get; set; }
[Range(6, 140, ErrorMessage = "Please enter your age.")]
public int Age { get; set; }
[EmailAddress]
public string? EmailAddress { get; set; }
}
}
清单1:我们应用程序的模型类
对于HomeController,我们添加一个操作——UpdatePerson,该操作接收PersonModel并返回该模型的视图。结果是一个非常简单的操作方法,如清单2 所示:
// UpdatePerson action on HomeController:
public IActionResult UpdatePerson(PersonModel model)
{
return View(model);
}
清单2:HomeController UpdatePerson操作
该视图更加详细,呈现为清单3:
@model PersonModel
<style>
.validation-summary-valid {
display: none;
}
</style>
<form method="post">
<div asp-validation-summary="All" class="alert alert-danger mb-3">
<strong>Following errors have occured:</strong>
</div>
<div class="mb-3">
<label asp-for="FirstName" class="form-label"></label>
<input asp-for="FirstName" type="text" class="form-control">
<span asp-validation-for="FirstName" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="LastName" class="form-label"></label>
<input asp-for="LastName" type="text" class="form-control">
<span asp-validation-for="LastName" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Age" class="form-label"></label>
<input asp-for="Age" type="text" class="form-control">
<span asp-validation-for="Age" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="EmailAddress" class="form-label"></label>
<input asp-for="EmailAddress" type="text" class="form-control">
<span asp-validation-for="EmailAddress" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
清单3:UpdatePerson视图
您可能还希望更新共享文件夹中的 _Layout.cshtml 视图以添加指向UpdatePerson操作的链接,以便可以通过单击链接来访问页面,而不是每次都键入URL。
应用程序就是这样!它没有做任何有用的事情,但它已经包含了一些本地化挑战。当我们运行应用程序并导航到/Home/UpdatePerson URL时,我们将看到除EmailAddress之外的每个字段都有验证错误消息。参见图1。现在,我们将看到几种本地化这些错误消息的方法。
|
图1:应用程序
添加本地化
现在,我们可以通过使用GitHub上的Arebis.MvcDashboardLocalize .NET Core模板包开始向应用程序添加本地化:
若要在Visual Studio中执行此操作,请转到“工具”菜单、NuGet 包管理器并打开包管理器控制台。
首先,我们必须在我们的机器上安装软件包。我们在包管理器控制台中使用以下命令执行此操作:
dotnet new --install Arebis.MvcDashboardLocalize
然后我们使用以下命令安装MvcDashboardLocalize模板:
dotnet new MvcDashboardLocalize -n LocalizationDemo
“LocalizationDemo”是我的ASP.NET Core项目的名称。如果您以不同的方式命名项目,则也必须在此处更改名称。
如果一切顺利,您会收到以下消息:
The template "ASP.NET MVC Dashboard Localize" was created successfully.
您还将注意到项目中的一些新增功能:添加了三个文件夹(Areas、Data和Localize)和一个附加文件(ModelStateLocalization.json)。
区域文件夹包含一个MvcDashboardLocalize文件夹,该文件夹包含一个 ReadMe-MvcDashboardLocalize.html 文件。此文件包含其余的设置说明,我们也将在此处遵循这些说明:
步骤1:添加包依赖项
对于我们的项目,我们必须添加以下NuGet包依赖项:
- Arebis.Core.AspNet.Mvc.Localization
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
我们通过右键单击项目中的“依赖项”节点来添加这些依赖项,选择“管理NuGet包”,然后从“浏览”选项卡中安装这三个包。
步骤2:创建数据库
由于我们将本地化数据存储在数据库中,因此我们需要创建一个SqlServer数据库。可以从Visual Studio中的SQL Server对象资源管理器或SQL Server Management Studio创建数据库,无论您喜欢什么。我将我的数据库命名为“LocalizationDemoDb”。
无需添加任何表。这些将通过运行迁移在步骤6 中添加。
步骤3:配置数据库连接字符串
在我们项目的 appsettings.json 中,我们将一个连接字符串添加到我们刚刚创建的数据库中:
"ConnectionStrings": {
"DefaultConnection": "Server=(local);Database=LocalizationDemoDb;
Trusted_Connection=True;MultipleActiveResultSets=true"
}
步骤4:配置应用程序和应用程序服务
现在我们已经设置了数据库,我们可以配置我们的应用程序服务了。这是通过将代码添加到 Program.cs 文件中来完成的(因为我们使用的是顶级语句)。完整的更新Program.cs 文件可在清单4 中找到:
using Arebis.Core.AspNet.Mvc.Localization;
using Arebis.Core.Localization;
using LocalizationDemo.Localize;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
#region Localization
builder.Services
.AddDbContext<LocalizationDemo.Data.Localize.LocalizeDbContext>(
optionsAction: options => options.UseSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services
.AddTransient<ILocalizationSource, DbContextLocalizationSource>();
builder.Services
.AddLocalizationFromSource(builder.Configuration, options => {
options.Domains = new string[] { "DemoApp" };
options.CacheFileName = "LocalizationCache.json";
options.UseOnlyReviewedLocalizationValues = false;
options.AllowLocalizeFormat = true;
});
builder.Services.AddModelBindingLocalizationFromSource();
builder.Services.AddControllers(config =>
{
config.Filters.Add<ModelStateLocalizationFilter>();
});
#endregion
// Add services to the container.
builder.Services.AddControllersWithViews()
.AddDataAnnotationsLocalizationFromSource();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days...
app.UseHsts();
}
#region Request Localization
var supportedCultures =
new[] { "en", "en-US", "en-GB", "fr", "fr-CA" };
var localizationOptions = new RequestLocalizationOptions()
.SetDefaultCulture(supportedCultures[0])
.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures);
app.UseRequestLocalization(localizationOptions);
#endregion
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "area",
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
清单4:Program.cs
添加了包含大部分本地化服务配置的“Localization”区域。
在区域之外需要注意的一点是:对AddDataAnnotationsLocalizationFromSource()方法的调用链接到AddControllersWithViews()方法调用。请注意调用该AddDataAnnotationsLocalizationFromSource()方法,而不是常规AddDataAnnotationsLocalization()方法。“FromSource”方法后缀表示我们正在使用来自某个可配置源的本地化,此处来自我们的数据库。
还有一个“Request Localization”区域,其中以通用方式配置请求本地化。支持的区域性是请求将支持的区域性。正如我们将看到的,我们的本地化数据库不需要完全匹配这些文化。
最后,还添加了一个名为“area”的控制器路由。这是访问作为MVC区域实现的本地化仪表板所必需的。
步骤5:取消保护本地化仪表板
谈到本地化仪表板,它开箱即用,具有基于角色的安全性,仅向具有Administrator或LocalizeAdministrator角色的用户授予访问权限。
由于我们尚未为此演示应用程序设置身份验证,因此我们不会有经过身份验证的用户,更不用说角色中的用户了。
目前最简单的方法是删除此安全性。在 Areas/MvcDashboardLocalize/Controllers 文件夹中编辑 BaseController.cs 文件,并注释包含该[Authorize]属性的代码行(即第11行)。
步骤6:构建数据库
作为最后一步,我们必须构建数据库:执行数据库迁移,以便创建用于保存本地化数据的表。
返回到程序包管理器控制台并执行以下命令:
Update-Database -Context LocalizeDbContext
输入本地化数据
完成所有这些步骤后,我们的应用程序现在尚未本地化,但已准备好进行本地化。
尽管如此,有了正确的数据,应用程序的某些部分已经本地化了。让我们运行应用程序并再次导航到/Home/UpdatePerson 页面。页面上没有任何实际更改,但如果您查看控制台输出(图2),您将看到几个警告,指示已探测本地化密钥。
图2:显示探测的本地化密钥的控制台输出
我们可以在本地化仪表板中定义这些键:导航到/MvcDashboardLocalize。同样,为方便起见,您可以选择在 _Layout.cshtml 中添加指向此URL的链接。现在,您应该看到仪表板主页:图3:
图3:MvcDashboard本地化主页
在页面顶部,您有指向此数据库本地化组件的三个概念的链接:域、键和查询。在本文中,我们将介绍域和键。
本地化域
本地化域是一组本地化键和查询。可以组合多个域来本地化应用程序。域可以通过应用程序共享。例如,您可以创建一个包含通用键的“Base”域,如“是”、“否”、“确定”、“取消”等。并具有与特定应用程序紧密相关的更具体的域。
回顾清单 4,您会注意到我们将一个Domains选项值定义为包含单个字符串“DemoApp”的字符串数组。这就是我们将在此应用程序中使用的域。
列出多个域时,后续域可以覆盖前面域的键,最后一个域确定支持的区域性。默认区域性是最后一个域的第一个区域性。
请注意,请求本地化选项还定义了支持的区域性和默认区域性。这些是关于在执行Web请求时在线程上设置为当前区域性。
域上的区域性是域为其提供翻译的区域性或语言。
要定义我们的“DemoApp”域,请单击本地化仪表板的导航栏中的“Domains”,然后单击“新建”链接。输入域的名称以及支持的区域性列表(逗号分隔),如图 4 所示。然后按保存:
图 4:在MvcDashboardLocalize中创建域
域也可以作为JSON文件导出和导入,从而可以轻松地在系统上共享本地化数据。
本地化键
现在我们已经创建了域,我们可以开始向其添加键了。单击“键”,然后按“新建”按钮(或键盘上的 + 键)。
我们必须为“en”和“fr”区域性输入名称和值。我们也可以为“fr-CA”区域性输入一个值,但是当未定义值时,本地化组件将始终回退到父区域性,因此,我们只能为“fr-CA”区域性提供一个值,如果它与“fr”区域性不同。
保留路径、参数名称等字段。暂时空的。
从图 2 可以看出,“First Name”(带空格)和“LastName”(不带空格)的键似乎已被查询。让我们根据下表中的值创建这些键:
键名称 | “en”值 | “fr”值 |
First Name | First name | Prénom |
LastName | Last name | Nom de famille |
创建这两个键后,我们返回到仪表板主页(通过单击导航栏中的MvcDashboardLocalize),然后按“发布”按钮。
现在,我们可以测试结果:单击导航栏右侧的退出图标以返回到根Web应用程序,然后导航到该UpdatePerson页面。
例如,若要在特定区域性中查看页面,例如French,请将“?culture=fr”查询字符串添加到URL。
现在,您将看到FirstName和LastName的所有匹配项都已本地化,但LastName字段的标签除外。这是因为PersonModel类上的LastName属性没有定义可本地化名称的[Display]属性。我们稍后会解决这个问题。
您还会注意到语言混淆,导致错误消息为“The Prénom field is required”。如果回顾图 2(或查看当前的控制台输出),您会发现“The {0} field is required.”也是可以提供翻译的键。
您可以返回到本地化仪表板,并使用所有尝试的消息的值定义键。确保密钥的名称与消息完全匹配,包括句子末尾的句点。不要忘记在测试前发布。如果您确实创建了所有缺少的键,您可能会在控制台上看到一条警告,提示您尝试翻译“Prénom”(法语为姓氏)。我会解释...
幕后故事
到目前为止,我们尚未更改视图以添加本地化,但现在已经本地化了多个标签和错误消息。现在查看FirstName字段标签。它在视图中描述为:
<label asp-for="FirstName" class="form-label"></label>
标记没有内容。内容由标记本身基于asp-for表达式生成,该表达式指向具有设置属性显示名称的[Display]属性的属性(FirstName)。这一点很重要,因为.NET永远不会尝试本地化属性名称,但会尝试本地化属性的显示名称。
这就是为什么First Name标签被本地化而其他标签没有。
对于错误消息,解释稍微复杂一些。我们必须首先了解,有三种错误消息来源可能会出现在ModelState(除了我们从代码中添加的那些)中:
- 在数据批注属性的ErrorMessage属性中定义的错误消息。
例如,在PersonModel类的Age属性的[Range]属性中定义的“Please enter your age.”消息。 - 源自未设置ErrorMessage属性的数据批注属性的错误消息。例如,PersonModel类上的[Required]和[EmailAddress]属性。
- 模型绑定器生成的错误消息,例如,如果将该Age字段留空或输入非数值。
来自第一个源的消息是最简单的消息:ErrorMessage属性值也是本地化键。
来自第三个源的消息源自模型绑定程序。这些是一组固定的、有限的(11)条可翻译消息。您可以在此链接中找到消息列表。
来自第二个源的消息是不同的。这些由数据注释属性生成的消息不可翻译。如果使用资源文件,则不会,如果使用数据库本地化,则不会。.NET根本不允许对其进行本地化。
所以需要一些诡计。您可能已经注意到,在应用程序设置中添加了一个ModelStateLocalizationFilter控制器过滤器(清单4)。此过滤器将搜索ModelState错误消息,并尝试从添加到项目根目录的ModelStateLocalization.json文件中查找匹配的模式。如果找到(正则表达式模式)匹配项,则使用关联的本地化键。
现在是复杂的部分。这些本地化键中的大多数都包含位置参数(如“{0}”或“{1}”)。一些位置参数代表文字用户输入,不应本地化。其他位置参数代表字段名称,可能已经本地化,也可能尚未本地化,我们不知道。如果与此字段相关的属性具有[Display]属性,则该属性已本地化。否则不会。
在默认设置中,假设如果数据批注不包含ErrorMessage属性值,则模型属性也将没有[Display]属性。因此,每当需要字段名称时,它仍然必须本地化。这解释了本地化“Prénom”的尝试。
因此,一种解决方案是删除FirstName属性上的[Display]属性。
另一种选择是在每个属性上都有一个[Display]属性,并告诉系统所有字段名称都已本地化。为此,可以更改 ModelStateLocalization.json 文件ArgLocalization属性中所有的true和false值来实现。
最后一个选项是从翻译中删除引用字段名称的位置参数。“{0}字段…”的翻译就会变成“This field...”。(然后您还应该将验证摘要切换到“ModelOnly”模式。
但我们还没有到那一步。[EmailAddress]数据注释属性不符合这些规则。尽管它没有ErrorMessage属性值,但其错误消息仍将本地化,就好像它有一个属性值一样。但由于该EmailAddress属性没有[Display]属性,因此本地化的错误消息包含未本地化的字段名称。
要解决此问题,我们最好简单地禁用DataAnnotationLocalizationFromSource。为此,我们从应用程序配置中删除对此方法的调用(清单4)。然后,将不会本地化数据注释错误消息。回退ModelStateLocalizationFilter将有机会本地化消息,包括字段名称的本地化版本。
然而,这导致了另一个问题:数据注释上的自定义错误消息将不再本地化。因此,不会本地化Age属性上[Range]注释的错误消息。
这真是个棘手的问题...
幸运的是,有一种解决方案适用于每种情况:
- 不要在模型属性上使用[Display]属性。
- 不要启用DataAnnotationLocalizationFromSource。
- 也不要调用AddModelBindingLocalizationFromSource()。
- 使用尚未本地化的错误消息(例如Age属性上[Range]注释中的错误消息)扩展 ModelStateLocalization.json 文件。
- 为元素提供显式内容(请参见本地化视图)。<label>
使用此解决方案,我们可以预先禁用错误消息的本地化,并等待它们在ModelState中出现。然后,我们使用ModelStateLocalizationFilter来本地化所有消息。
有关键的更多信息
输入键时,您可能注意到在纯文本和HTML之间切换的选项。这实际上只影响自动翻译API,这些API会被告知要翻译的文本格式。但是切换到HTML也会激活值编辑器上的一些HTML帮助程序以及预览功能。
本地化值字段允许多行文本。如果需要,它们会自动扩大高度。
您还注意到每个翻译值下方的“已审核” checkbox。这使您可以跟踪哪些翻译已经过审核,哪些可能仍需要审核。在我们的应用程序中,这个设置目前并不重要,因为我们在应用程序设置(清单 4)中设置了UseOnlyReviewedLocalizationValues选项为false,但如果保留为true(其默认值),则不会发布未检查的值。
此外,空白本地化值也被视为不存在。如果本地化值只是空白,则必须将其标记为已审核才能考虑在内。
本地化视图
本地化错误消息实际上只是视图本地化工作的一部分。主要工作将是本地化每个文本、标题和消息。无论本地化数据来自资源文件还是来自数据库,对本地化代码都没有影响:我们采用ViewLocalizer。
为了本地化 UpdatePerson.cshtml 视图,我们在文件顶部添加代码以注入ViewLocalizer:
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
从那时起,我们将每个静态文本替换为对Localizer的indexer属性的调用。例如:
<strong>Following errors have occured:</strong>
成为:
<strong>@Localizer["Following errors have occured:"]</strong>
对于代表没有[Display]属性且因此未本地化的模型属性的字段标签,我们可以提供明确的标签内容。如果我们利用这个机会为必填字段添加分号和星号,那么对于已经本地化的字段(例如FirstName)这样做甚至是一个很好的论据,而无需在我们的模型属性上同时具有[Display]属性。
我们的FirstName字段标签现在变为:
<label asp-for="FirstName" class="form-label">
@Localizer["First Name"]*:
</label>
(或者,如果删除[Display]属性并重命名数据库中的键定义,请使用本地化键“FirstName”(不带空格)。
不要忘记使用仪表板在数据库中定义本地化键。然后发布以使更改生效。
本地化控制器
您还可以从控制器访问本地化人员。同样,使用资源文件或数据库作为源进行本地化之间几乎没有区别:我们在控制器的构造函数方法中注入StringLocalizers和/或HtmlLocalizers,然后从操作方法中使用它们。
但是,有一个区别:从数据库本地化不支持按类型隔离:本地化键和值不能与.NET类相关,就像资源文件一样。
因此,也不需要使用IStringLocalizer和IHtmlLocalizer的泛型变体(虽然你可以,但它没有区别),也不需要注入多个本地化器(就像资源文件经常做的那样:一个本地化器用于共享资源,一个本地化器用于与类型相关的资源)。
为了在我们的应用程序的HomeController中注入一个IStringLocalizer,我们扩展了HomeController构造函数方法的参数列表,并定义了一个实例字段来保存注入的对象。现有_logger字段、添加的额外字段和更新的构造函数方法将如下所示:
private readonly ILogger<HomeController> _logger;
private readonly IStringLocalizer _localizer;
public HomeController(ILogger<HomeController> logger, IStringLocalizer localizer)
{
_logger = logger;
_localizer = localizer;
}
例如,我们现在可以在动作控制器Index方法中本地化Welcome消息:
public IActionResult Index()
{
ViewBag.Message = _localizer["Welcome"];
return View();
}
在 Index.cshtml 视图中,我们通过调用ViewBag来替换“Welcome”这个词:
<code><h1 class="display-4">@ViewBag.Message</h1></code>
无需注入和使用 ,因为控制器已对消息进行了本地化。IViewLocalizer
命名参数与位置参数
在.NET Core中,本地化程序索引器可以采用其他参数来替换本地化消息中的位置参数。举个例子:
ViewBag.Message = _localizer["Welcome", "Jane"];
如果“Welcome”键转换为string “Welcome {0}!”,则位置参数{0}将替换为值“Jane”。
在本地化仪表板中,我们可以创建包含位置参数的值,如下所示:
Welcome {0}!
但我们也可以选择使用命名参数:
Welcome {{username}}!
请注意,命名参数由双大括号括起来。
这样做时,我们需要在键的命名参数字段中按照其相应位置参数的顺序声明命名参数的列表。因此,我们必须将“username”声明为第一个(也是唯一)命名参数。
命名参数可以简化翻译人员的工作(尤其是当值包含多个参数时)。命名参数是仪表板和数据库的事情,它们不向本地化应用程序公开,也不能在本地化键的名称中使用。
访问区域性、模型、视图数据...
使用MvcDashboardLocalize组件进行本地化的另一个独特功能是本地化值能够引用当前区域性名称、路由段、模型属性、ViewData属性和当前登录用户的名称。
为了说明这一点,让我们将HomeController的Index操作方法重写为:
public IActionResult Index()
{
ViewBag.User = new PersonModel { FirstName = "Jane" };
return View();
}
接下来,在 Index.cshtml 视图中,我们将注入一个本地化器并使用它来检索Welcome消息:
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">@Localizer["Welcome"]</h1>
<p>@Localizer["Learn about ASP.NET Core"]</p>
</div>
现在我们将“Welcome”键的翻译值更新为(对于英语):
Welcome {{view:User.FirstName}}!
发布本地化数据中的更改后,主页现在将显示“Welcome Jane!”。
“User.FirstName”表达式是一个属性路径:由属性调用组成的表达式。本地化人员不会搜索ViewData["User.FirstName"]值,而是搜索ViewData["User"]值,然后调用该值的FirstName属性。仅支持属性,不支持方法。
同样,可以访问模型属性。例如,如果视图有一个PersonModel类型的模型,本地化值可以使用“{{model:FirstName}}”嵌入人的名字。
视图和模型表达式还可以包括格式化string,即支付按钮上的标签:
Pay {{view:Price:#,##0.00}}
ViewData和模型数据只能通过ViewLocalizer在视图中使用。
翻译值中可以包含以下引用:
参考 | 描述 |
{{culture:name}} | 引用当前请求区域性名称,即“en-US”。 |
{{uiculture:name}} | 引用当前请求UI区域性名称,即“en-US”。 |
{{route:<segment>}} | 引用当前请求路由的一段(即“{{route:controller}}”指控制器路由段。 |
{{user:name}} | 引用当前登录的标识的名称。 |
{{localizer:<localizationkey>}} | 递归引用另一个本地化键。 |
{{model:<propertypath>}} | 引用视图模型属性。(*) |
{{view:<propertypath>}} | 引用ViewData属性。(*) |
(*)仅在使用 ViewLocalizer 的视图中可用。
ForPath
按请求路径隔离本地化密钥的功能是该MvcDashboardLocalize组件的另一个强大功能。
想象一下,我们希望我们网站上的页面顶部有一个标题。当然,标题必须本地化。由于它是所有页面的共同点,因此我们可以在共享的_Layout.cshtml 页面中定义一个“PageTitle”本地化器部分。例如,就在@RenderBody()调用之上:
<div class="container">
<main role="main" class="pb-3">
@Localizer["PageTitle"]
@RenderBody()
</main>
</div>
我们也必须注入一个IViewLocalizer。
在运行我们的应用程序时,我们现在按预期在每个页面的顶部看到一个文字 “PageTitle”:当数据库中找不到匹配的键时,将呈现键文本本身。
因此,让我们转到本地化仪表板并创建一个键“PageTitle”,其中所有值为空,所有“Is reviewed” checkbox都已选中。我们将保存它并重新发布。看,文字“PageTitle”已经消失了,因为它呈现了一个空的string。
从现在开始,我们可以创建名称为“PageTitle”的新本地化密钥,特定于某些URL。例如,在ForPath字段中创建一个值为“/Home/UpdatePerson”的“PageTitle”键,类型为HTML,值为(英语):
<h1>Update Person</h1>
我们可以创建另一个名为“PageTitle”的键,其中包含ForPath “/Home/Privacy”和(英语)值:
<h1>{{view:Title:localize}}</h1>
<p>Use this page to detail your site's privacy policy.</p>
保存并发布。并从 Privacy.cshtml 文件中删除相应的代码,使其现在仅包含:
@{
ViewData["Title"] = "Privacy Policy";
}
请注意{{view:Title:localize}}表达式中特殊的“localize”格式化string:它告诉本地化器也本地化ViewData["Title"]的结果,返回“Privacy Policy” string的本地化版本。
有了这个,我们可以从本地化仪表板管理我们的整个隐私政策页面。
只要存在具有ForPath值的相同键,系统将选择具有与当前请求URL开头匹配的最长路径的键。匹配不区分大小写。
如果路由包含“culture”或“uiculture”段,则使用RouteDataRequestCultureProvider时的情况是,将忽略该路由段。
缓存文件
到目前为止,您已经多次运行Web应用程序,并且您可能注意到应用程序每次需要本地化string时都不会命中数据库。事实上,在下一次运行时,应用程序可能根本不会命中数据库。
这要归功于我们使用清单4 中的CacheFileName选项配置了其名称的缓存文件。
当应用程序启动时,它首先尝试从 LocalizationCache.json 缓存文件中读取本地化数据。如果找到该文件,数据将加载到内存中,并在应用程序的剩余生存期内从内存中使用。
仅当找不到缓存文件时,应用程序才会将所有本地化数据(针对当前应用程序的域)读入内存。然后,它还将为下一次启动写入缓存文件。
当我们点击本地化仪表板中的“发布”按钮时,也会发生类似的事情:本地化数据从内存中刷新,数据从数据库重新加载到内存中,并写出到缓存文件中。
这意味着我们可以在多种模式下部署本地化:
- 本地化的应用程序还托管本地化仪表板。这是我们在本示例中一直使用的“完整”模式。
- 应用程序承载仪表板,但本身可能未本地化。托管仪表板以允许维护其他应用程序的本地化数据。“仅仪表板”模式。
- 应用程序已本地化,但不托管仪表板:“无仪表板已本地化”模式。
若要刷新本地化数据,将删除本地化缓存文件并回收应用程序(池)。 - 应用程序已本地化,不托管仪表板,也没有数据库访问权限:“本地化而不访问数据库”模式。
要刷新本地化数据,将新的缓存文件放在原位,并回收应用程序(池)。
如果我们不提供CacheFileName,则在每次启动时从数据库中检索本地化数据。让应用程序(池)每天重新启动一次,您确定应用程序每天都会刷新其本地化数据......
翻译服务
最后但并非最不重要的一点是,该MvcDashboardLocalize应用程序还提供与Bing(Microsoft),Google和DeepL的自动翻译API的集成。
所有三个API都要求您创建一个帐户并获取API密钥。所有三个API都提供免费的翻译量。例如,在撰写本文时,必应每月免费翻译多达2万个字符。
在Azure上创建帐户并为翻译器服务创建身份验证密钥后,可以按如下所示集成翻译服务:
在应用程序配置代码(清单4)中,添加以下附加服务注册:
builder.Services.AddTransient<ITranslationService,
BingTranslationService>();
然后右键单击项目文件并选择“管理器用户机密”。如果尚无此应用程序的用户机密,则会打开一个仅包含大括号的JSON文件。在这些大括号之间输入以下配置:
"BingApi": {
"SubscriptionKey": "0a123bc45678d90efa12345bc6789...",
"Region": "eastus"
}
使用从Microsoft获取的身份验证密钥覆盖订阅密钥,并设置运行翻译服务的区域代码(而不是名称)。
下次在仪表板中编辑本地化键时,您会注意到一个面板,该面板提供从您选择的语言生成所有空的未审阅值的翻译。
位于Area\MvcDashboardLocalize文件夹中的ReadMe-MvcDashboardLocalize.html文件提供了有关如何与Google和DeepL API集成的详细信息。
运行示例
通过上面的链接,您可以下载示例。
下载并解压缩后,您可以运行该应用程序。在页面的右上角,您将找到在英语和法语之间切换的链接。“编辑”链接将带您进入MvcDashboardLocalize仪表板。
您应该知道,该应用程序在没有数据库连接的情况下工作:它使用本地缓存文件 LocalizationCache.json。
要使仪表板正常工作并更新本地化数据,您首先需要:
- 在运行应用程序之前,请打开包管理器控制台并执行:
PM>更新数据库——Context LocalizeDbContext
这将创建数据库并应用数据库迁移来设置数据库结构(表)。 - 完成此操作后,运行应用程序,按页面右上角的“编辑”按钮,然后转到“域”。按“导入”并导入位于解决方案文件旁边的“DemoApp域到Import.json”文件。
现在,您可以查看和编辑密钥。不要忘记按MvcDasboardLocalize主页上的“发布”按钮以应用更改并覆盖缓存文件。
下载示例未设置必应翻译服务。
视频
在MvcDashboardLocalize组件的Nuget主页上,还可以找到指向两个教程视频的链接,其中安装了仪表板组件并用于本地化ASP.NET Core MVC 应用程序,包括翻译服务:
总结
该MvcDashboardLocalize组件提供了一个非常完整的解决方案来本地化 ASP.NET Core(MVC)应用程序。本地化数据存储在数据库中,使其易于访问,而高性能则通过缓存文件得到保证。
此外,整个解决方案是开源的。仪表板代码嵌入在项目中,可以根据需求轻松定制,而引用的NuGet组件的源代码可在GitHub上找到。
更多
要管理ASP.NET Core项目的本地标识存储,您可能还有兴趣使用MvcDashboardIdentity:
https://www.codeproject.com/Articles/5348357/Localizing-ASP-NET-Core-MVC-Applications-from-Data