使用数据库本地化 ASP.NET Core MVC 应用程序





5.00/5 (1投票)
在 SQL Server 中维护您的本地化数据
引言
传统上,.NET 应用程序使用(编译的)资源文件进行本地化。开发人员工具用于编写这些文件,本地化字符串的更新通常需要应用程序的更新。
将本地化数据存储在数据库中,您可以更灵活地控制翻译的输入和更新者以及时间。您可以在所有翻译输入之前部署应用程序,并且在部署后仍然可以修复翻译。这可以通过基于数据库的任何用户界面远程完成。
但是,将本地化数据存储在数据库中也有一个缺点:数据库访问速度慢。特别是与加载到内存中并从内存中操作的资源文件相比。
我在这里向您介绍的组件 – MvcDasboardLocalize
– 包含一个数据库驱动的本地化解决方案,该解决方案使用内存加载的缓存以提高性能,以及一个用于输入和维护本地化数据的仪表板。它还提供了一些基于资源文件的本地化中没有的独特功能。
但在查看这些功能之前,让我们先创建一个 ASP.NET Core 应用程序并为其添加本地化功能。
构建应用程序
使用 Visual Studio 2022,我们选择在 C# 中创建一个类型为“ASP.NET Core Web App (Model-View-Controller)”的新项目。我们选择 .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; }
}
}
在 HomeController
中,我们添加一个动作 – UpdatePerson
– 它接收一个 PersonModel
并返回该模型的视图。结果是一个非常简单的动作方法,如清单 2 所示
// UpdatePerson action on HomeController:
public IActionResult UpdatePerson(PersonModel model)
{
return View(model);
}
视图有点复杂,渲染为清单 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>
您可能还想更新 Shared 文件夹中的 _Layout.cshtml 视图,以添加一个指向 UpdatePerson
动作的链接,这样您就可以通过点击链接而不是每次输入 URL 来访问页面。
应用程序就到此为止了!它并没有做任何有用的事情,但它已经包含了一些本地化挑战。当我们运行应用程序并导航到 /Home/UpdatePerson URL 时,我们会看到除了 EmailAddress
之外的所有字段都有验证错误消息。请参见图 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)。
Areas 文件夹包含一个 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();
添加了一个“Localization
”区域,其中包含大部分本地化服务配置。
区域外需要注意的一点是:对链式调用 AddControllersWithViews()
方法的 AddDataAnnotationsLocalizationFromSource()
方法的调用。请注意调用 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),您会看到几条警告,表明本地化键已被探测。
我们可以在本地化仪表板中定义这些键:导航到 /MvcDashboardLocalize。同样,为了您的方便,您可以选择在 _Layout.cshtml 中添加一个指向此 URL 的链接。您现在应该会看到仪表板主页:图 3
在页面顶部,您有指向此数据库本地化组件的三个概念的链接:域、键和查询。在本文中,我们将介绍域和键。
本地化域
本地化域是本地化键和查询的集合。多个域可以组合以本地化应用程序。域可以在应用程序之间共享。例如,您可以创建一个“Base
”域,其中包含“Yes”、“No”、“OK”、“Cancel”等通用键,并拥有与特定应用程序紧密相关的更具体域。
回顾清单 4,您会注意到我们已将 Domains 选项值定义为一个字符串数组,其中包含单个字符串“DemoApp
”。所以这将是我们在本应用程序中使用的域。
当列出多个域时,后续域可以覆盖前一个域的键,并且最后一个域决定了支持的文化。默认文化是最后一个域的第一个文化。
请注意,请求本地化选项也定义了支持的文化和默认文化。这些是关于在执行 Web 请求时设置为线程当前文化的设置。
域上的文化是域提供翻译的文化或语言。
要定义我们的“DemoApp
”域,请单击本地化仪表板导航栏中的 Domains
,然后单击“新建”链接。输入域的名称以及支持的文化列表(逗号分隔),如图 4 所示。然后按保存
域也可以导出和导入为 JSON 文件,从而轻松地在系统之间共享本地化数据。
本地化键
现在我们已经创建了域,我们可以开始向其中添加键。单击键,然后按新建按钮(或键盘上的+键)。
我们将必须为“en
”和“fr
”文化输入名称和值。我们还可以为“fr-CA
”文化输入值,但当未定义值时,本地化组件将始终回退到父文化,因此我们只有在“fr-CA
”文化的值与“fr
”文化的值不同时才必须提供值。
暂时将路径、参数名称等字段留空。
从图 2 来看,“First Name
”(带空格)和“LastName
”(不带空格)的键似乎已被查询。让我们首先根据下表中的值创建这些键
键名 | “en”值 | “fr”值 |
|
|
|
|
|
|
创建两个键后,我们返回仪表板主页(通过单击导航栏中的 MvcDashboardLocalize
)并按发布按钮。
我们现在可以测试结果:单击导航栏右侧的退出图标返回根 Web 应用程序,然后导航到 UpdatePerson
页面。
要以特定文化(例如法语
)查看页面,请在 URL 中添加“?culture=fr
”查询字符串。
您现在会看到所有 FirstName
和 LastName
的出现都已本地化,除了一个,即 LastName
字段的标签。这是因为 PersonModel
类上的 LastName
属性没有定义可本地化名称的 [Display]
属性。我们稍后会解决这个问题。
您还会注意到语言混淆导致错误消息,例如“The Prénom field is required
”(“Prénom 字段是必需的”)。如果您回看图 2(或查看当前的控制台输出),您会看到“The {0} field is required.
”(“{0} 字段是必需的。”)也是可以提供翻译的键。
您可以返回本地化仪表板并为所有尝试的消息定义带值的键。确保键的名称与消息完全匹配,包括句子末尾的句号。测试前不要忘记发布。如果您确实创建了所有缺失的键,您可能会在控制台上注意到尝试翻译“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
值来实现此目的。
最后一个选项是从翻译中完全删除引用字段名称的位置参数。那么“The {0}
field...”的翻译将变为“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 帮助器以及预览功能。
本地化值字段允许输入多行文本。如果需要,它们会自动扩展高度。
您还注意到每个翻译值下方的“已审核”复选框。这使您可以跟踪哪些翻译已审核,哪些可能仍需要审核。在我们的应用程序中,此设置目前无关紧要,因为我们在应用程序设置中将 UseOnlyReviewedLocalizationValues
选项设置为 false
(清单 4),但如果保留为 true
(其默认值),则未审核的值不会发布。
空白本地化值也被视为不存在。如果本地化值仅表示空白,则必须将其标记为已审核才能被考虑。
本地化视图
本地化错误消息实际上只是视图本地化工作的一部分。大部分工作将是本地化每个文本、标题和消息。本地化数据是来自资源文件还是来自数据库对本地化代码没有区别:我们都使用 ViewLocalizer
。
为了本地化 UpdatePerson.cshtml 视图,我们在文件顶部添加代码以注入 ViewLocalizer
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
从那时起,我们将每个静态文本替换为对 Localizer
的索引器属性的调用。例如
<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 视图中,我们将“Welcome
”一词替换为对 ViewBag
的调用
<code><h1 class="display-4">@ViewBag.Message</h1></code>
无需注入和使用 IViewLocalizer
,因为消息已由控制器本地化。
命名参数与位置参数
在 .NET Core 中,本地化器索引器可以接受额外的参数,这些参数将替换本地化消息中的位置参数。例如
ViewBag.Message = _localizer["Welcome", "Jane"];
如果“Welcome
”键翻译成字符串“Welcome {0}!
”,那么位置参数 {0}
将被值“Jane
”替换。
在本地化仪表板中,我们可以创建包含位置参数的值,例如
Welcome {0}!
但我们也可以选择使用命名参数
Welcome {{username}}!
(请注意,命名参数用双花括号括起来。)
这样做时,我们需要在键的命名参数字段中声明命名参数列表,按照它们对应的位置参数的顺序。因此,我们必须将“username
”声明为第一个(也是唯一一个)命名参数。
命名参数可以简化翻译人员的工作(特别是当一个值包含多个参数时)。命名参数是仪表板和数据库的东西,它们不暴露给本地化应用程序,也不能用于本地化键的名称。
访问文化、模型、ViewData...
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}}
”嵌入人物的名字。
视图和模型表达式也可以包含格式字符串,即“支付”按钮上的标签
Pay {{view:Price:#,##0.00}}
ViewData
和模型数据只能在视图中使用 ViewLocalizer
。
翻译值中可以包含以下引用
参考 | 描述 |
| 指当前的请求文化名称,即“ |
| 指当前的请求 UI 文化名称,即“ |
| 指当前请求路由的某个段(例如,“ |
| 指当前登录身份的名称。 |
| 递归引用另一个本地化键。 |
| 指视图模型属性。 ( |
| 指 |
(*) 仅在使用 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
”复选框都已选中。我们将保存并重新发布。然后您会看到,字面上的“PageTitle
”已经消失,因为它渲染的是空字符串。
从现在开始,我们可以创建特定于某些 URL 的名为“PageTitle
”的新本地化键。例如,创建一个名为“PageTitle
”的键,其 ForPath
字段值为“/Home/UpdatePerson”,类型为 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
”格式字符串:它告诉本地化器也本地化 ViewData["Title"]
的结果,返回“Privacy Policy
”字符串的本地化版本。
有了这个,我们就可以从本地化仪表板管理整个隐私政策页面。
每当存在具有 ForPath
值的相同键时,系统将选择与当前请求 URL 开头匹配的最长路径的键。匹配不区分大小写。
如果路由包含“culture
”或“uiculture
”段,就像使用 RouteDataRequestCultureProvider
时的情况一样,则该路由段将被忽略。
缓存文件
到现在,您已经运行了多次 Web 应用程序,您可能注意到应用程序并非每次需要本地化字符串时都访问数据库。事实上,在下一次运行时,应用程序可能根本不会访问数据库。
这得益于缓存文件,我们在清单 4 中通过 CacheFileName
选项配置了其名称。
应用程序启动时,它首先尝试从 LocalizationCache.json 缓存文件读取本地化数据。如果找到该文件,数据将加载到内存中,并在应用程序的其余生命周期中从那里使用。
只有在找不到缓存文件时,应用程序才会将所有本地化数据(用于当前应用程序的域)加载到内存中。然后它还会为下次启动写入缓存文件。
当我们点击本地化仪表板中的发布按钮时,类似的事情发生了:本地化数据从内存中清除,数据从数据库重新加载到内存中,并写入缓存文件。
这意味着我们可以以多种模式部署本地化
- 本地化应用程序也托管本地化仪表板。这是我们在此示例中使用的“完整”模式。
- 应用程序托管仪表板,但本身可能未本地化。仪表板用于维护其他应用程序的本地化数据。“仅仪表板”模式。
- 应用程序已本地化,但不托管仪表板:“无仪表板本地化”模式。
要刷新本地化数据,请删除本地化缓存文件并回收应用程序(池)。 - 应用程序已本地化,不托管仪表板且无法访问数据库:“无数据库访问本地化”模式。
要刷新本地化数据,请放置新缓存文件并回收应用程序(池)。
如果我们不提供 CacheFileName
,本地化数据将在每次启动时从数据库中检索。让应用程序(池)每天重启一次,您就可以确保应用程序每天都会刷新其本地化数据……
翻译服务
最后但同样重要的是,MvcDashboardLocalize
应用程序还提供了与 Bing (Microsoft)、Google 和 DeepL 自动翻译 API 的集成。
所有三个 API 都需要您创建一个帐户并获取 API 密钥。所有三个 API 都提供免费的翻译量。例如,在撰写本文时,Bing 提供每月免费翻译多达 200 万个字符。
在 Azure 上创建帐户并为 Translator 服务创建身份验证密钥后,您可以按如下方式集成翻译服务
在应用程序配置代码(清单 4)中,添加以下附加服务注册
builder.Services.AddTransient<ITranslationService,
BingTranslationService>();
然后右键单击项目文件并选择“管理用户机密”。如果您的此应用程序还没有用户机密,这将打开一个只有花括号的 JSON 文件。在这些花括号之间输入以下配置
"BingApi": {
"SubscriptionKey": "0a123bc45678d90efa12345bc6789...",
"Region": "eastus"
}
用您从微软获得的身份验证密钥覆盖订阅密钥,并设置您的翻译服务运行的区域代码(不是名称)。
下次在仪表板中编辑本地化键时,您会注意到一个面板,该面板提供从您选择的语言生成所有空闲且未审核值的翻译。
位于 Area\MvcDashboardLocalize 文件夹中的 ReadMe-MvcDashboardLocalize.html 文件也提供了如何与 Google 和 DeepL API 集成的详细信息。
运行示例
通过上述链接,您可以下载示例。
下载并解压后,您可以运行应用程序。在页面右上角,您会找到在英语和法语之间切换的链接。编辑链接会将您带到 MvcDashboardLocalize
仪表板。
您应该知道,应用程序无需数据库连接即可工作:它使用本地缓存文件 LocalizationCache.json。
为了使仪表板正常工作并使本地化数据可更新,您首先需要
- 运行应用程序之前,打开包管理器控制台并执行
PM> Update-Database -Context LocalizeDbContext
这将创建数据库并应用数据库迁移来设置数据库结构(表)。 - 完成后,运行应用程序,点击页面右上角的编辑按钮,然后进入域。点击导入并导入与解决方案文件一起存在的“DemoApp domain to import.json”文件。
您现在可以查看和编辑键。不要忘记在 MvcDasboardLocalize
主页上按发布按钮以应用更改并覆盖缓存文件。
下载的示例未设置 Bing 翻译服务。
视频
在 MvcDashboardLocalize
组件的 Nuget 主页上,您还会找到指向两个教程视频的链接,其中演示了仪表板组件的安装和使用,用于本地化 ASP.NET Core MVC 应用程序,包括翻译服务
摘要
MvcDashboardLocalize
组件为本地化 ASP.NET Core (MVC) 应用程序提供了一个非常完整的解决方案。本地化数据存储在数据库中,使其易于访问,同时通过缓存文件保证了高性能。
此外,整个解决方案都是开源的。仪表板代码嵌入到您的项目中,可以轻松根据您的需求进行定制,而所引用的 NuGet 组件的源代码可在 GitHub 上获取。
更多
要管理 ASP.NET Core 项目的本地身份存储,您可能还会对使用 MvcDashboardIdentity
感兴趣
历史
- 2022 年 11 月 29 日:初始版本