65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 LazZiya.ExpressLocalization 开发多文化 ASP.NET Core 3, 2, 1 项目

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (25投票s)

2019 年 6 月 3 日

CPOL

10分钟阅读

viewsIcon

70338

downloadIcon

1078

如何用简单的步骤创建多文化 ASP.NET Core 网站应用程序

背景

大多数网站应用程序都使用基于 URL 的本地化,因此我们可以在 URL 中看到选定的区域性,例如 http://www.example.com/en/Contact。默认情况下,ASP.NET Core 提供以下请求区域性提供程序:

  • QueryStringRequestCultureProvider
  • CookieRequestCultureProvider
  • AcceptLanguageHeaderRequestCultureProvider

为了实现路由值本地化,我们将构建自定义的本地化提供程序并定义一个全局路由模板,所以基本上,我们需要完成以下本地化步骤才能拥有完全本地化的网站应用程序:

  • 构建路由值请求区域性提供程序

    根据 {culture} 路由值进行请求本地化

  • 为 culture 参数定义全局路由模板

    将 {culture} 参数添加到 URL,例如 www.example.com/en-US/

  • 设置 DataAnnotations 本地化

    数据批注的本地化,如显示名称、必需、字符串长度等。
    DataAnnotations 定义在 System.ComponentModel.DataAnnotations 命名空间中。
    除了本地化默认的 DataAnnotation 外,自定义属性也必须使用相同的逻辑进行本地化。

    有关更多详细信息,请参阅 DataAnnotations。

  • 设置 ModelBinding 错误消息本地化

    服务器端提交后输入类型的验证,例如 ValueMustBeANumberAccessorAttemptedValueIsInvalidAccessor 等。

    有关更多详细信息,请参阅 DefaultModelBindingMessageProvider

  • 设置 IdentityDescriber 错误消息

    所有用户和角色相关消息的本地化,例如“用户已在角色中”、“用户名已存在”等。

    有关更多详细信息,请参阅 IdentityErrorDescriber

  • 设置 OnRedirectTo... 事件

    当用户被自动重定向到登录、注销或访问拒绝页面时,会触发这些事件。自动重定向不处理区域性值,因此必须手动配置。

    有关更多详细信息,请参阅 https://github.com/LazZiya/ExpressLocalization/issues/6

  • 设置视图本地化

    本地化 Razor 页面中的文本/HTML。

    有关更多详细信息,请参阅 ViewLocalization

  • 设置客户端验证脚本

    本地化客户端验证消息至关重要,表单将在提交前进行验证。必须本地化客户端验证脚本,以便用户看到本地化的消息,例如:字段是必需的,密码必须匹配等。

    还有一点很重要,那就是验证本地化的十进制数字,一些区域性使用句点,另一些区域性使用逗号作为十进制数字,例如(1,2)和(1.2),这种情况应在客户端验证中仔细处理。

    更重要的是:一些区域性可能使用完全不同的数字系统;例如,阿拉伯区域性使用数字,如“٠١٢٣٤٥٦٧٨٩”,而拉丁区域性使用 "0123456789"。如果数字系统设置不正确,将出现验证错误。(请参阅 本文 了解如何更改客户端验证的数字系统。)

    有关更多详细信息,请参阅 Client side validation 和本地化 GitHub 相关问题

  • 为每种区域性创建本地化的资源文件

    视图、数据批注、模型绑定和身份错误都要求有本地化的资源。这一步需要花费大量时间和精力!

所有这些步骤都需要大量工作并花费过多时间。因此,LazZiya.ExpressLocalization nuget 包的优势就在于此,它只需几行简单的代码即可消除本地化设置的时间和精力。

创建项目

让我们开始创建一个基本的 ASP.NET Core 网站应用程序(我使用的是 VS2019)

  1. 通过选择 ASP.NET Core Web Application 创建新项目

  2. 点击 Next,给项目一个友好的名称,然后点击 Create

  3. 选择 Web Application,将目标框架设置为任何 ASP.NET Core 版本(3、2 或 1),并确保将身份验证更改为 Individual User Accounts

  4. 点击 Create,等待解决方案创建基本模板,完成后,您可以通过在解决方案资源管理器中选择项目名称,然后按(Ctrl + Shift + B)构建项目,然后按(Ctrl + Shift + W)在浏览器中运行而不调试来测试。

安装 LazZiya.ExpressLocalization

  1. 在解决方案资源管理器中,在项目名称下,右键单击 Dependencies,然后选择 "Manage Nuget Packages"。

  2. 转到 Browse 选项卡,搜索 "LazZiya",选择 "LazZiya.ExpressLocalization" 并点击 "Install",选择最新版本并安装它。

创建本地化资源

我已经为项目准备好了本地化资源,因此您不必花费时间创建本地化资源。

在项目根目录下,创建一个新文件夹并命名为 "LocalizationResources"

LocalizationResources 文件夹下,创建一个新的 public 类并命名为 "ViewLocalizationResource",这个类将用于对视图本地化资源文件进行分组。

namespace ExpressLocalizationSample.LocalizationResources
{
    public class ViewLocalizationResource
    {
    }
}

LocalizationResources 文件夹下,创建一个新的 public 类并命名为 "ExpressLocalizationResource",这个类将用于对身份、模型绑定和数据批注的资源文件进行分组。

namespace ExpressLocalizationSample.LocalizationResources
{
    public class ExpressLocalizationResource
    {
    }
}

我们将使用这两个类将资源类型传递给 Express Localization 方法。

注意:在本示例中,我将使用两个资源文件来存储本地化字符串,但您可以根据需要使用一个、两个或多个本地化资源文件来存储所有本地化字符串。在 AddExpressLocalization 的不同变体之间切换,以查看可用的选项。 在 GitHub 上阅读更多内容

最后,请从这个 仓库文件夹 下载相关的区域性资源。请注意,您需要为每种区域性下载两个文件,例如(ExpressLocalizationResource.tr.resxViewLocalizationResource.tr.resx)。将下载的文件复制到 "LocalizationResources" 文件夹。

Using the Code

最后,我们准备好进行本地化设置了。 :)

打开 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(...);

如果您在 startup 中定义了路由,则需要在路由表中添加区域性参数。

app.UseRequestLocalization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{culture=en}/{controller=Home}/{action=Index}/{id?}");
});

或者,如果您在控制器上使用路由属性,请将区域性路由参数添加到路由属性中。

["{culture}/Home"]
public HomeController : Controller
{
    ...
}

添加语言导航

此步骤需要另一个 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 按以下顺序使用所有区域性提供程序:

  1. RouteSegmentCultureProvider
  2. QueryStringRequestCultureProvider
  3. CookieRequestCultureProvider
  4. AcceptedLanguageHeaderRequestCultureProvider
  5. 使用 startup 设置中的默认请求区域性

要设置 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}" 将被语言标签助手替换,并引用每个列出的区域性。

添加 LanguageNav 标签助手后,我们就可以进行首次运行了。

很好,到目前为止,我们有了支持区域性的导航,但我们仍然需要本地化视图文本才能看到本地化版本。

访问 LanguageTagHelper wiki 以获取更多详细信息。

本地化视图

在下载的文件中的 "ViewLocalizationResource.xx.resx" 中已提供了默认项目的本地化文本。如果您需要为视图添加更多自定义文本,请将其添加到 "ViewLocalizationResource.xx.resx" 文件中。

选项 1

在这里,我们将使用 LazZiya.ExpressLocalization 随附的 LocalizeTagHelper,它提供了一个简单清晰的 HTML 标签来本地化视图。

打开 Pages/_ViewImports.cshtml 文件并添加本地化标签助手。

@addTagHelper *, LazZiya.ExpressLocalization

使用 LocalizeTagHelper 非常简单,只需向任何 HTML 标签添加 "localize-contents" 属性,或用 "<localize>" 标签将任何 HTML 内容括起来。

然后打开 Pages/Index.cshtml 并使用 Localize 标签助手来本地化文本/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 文件并注入 ISharedCultureLocalizer,该文件随 ExpressLocalization 一起提供。

@using LazZiya.ExpressLocalization
@inject ISharedCultureLocalizer _loc

然后打开 Pages/Index.cshtml 并为文本使用 localizer 函数。

@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

当页面处于非默认区域性时,如果您点击 PrivacyLoginRegister 链接,您会注意到我们丢失了选定的区域性,这是因为我们没有将区域性路由值添加到链接中。

打开 Pages/_ViewImports.cshtml 并添加对 System.Globalization 的引用。

@using System.Globalization

然后打开 Pages/_LoginPartial.cshtml 并在页面顶部添加一个 culture 参数,如下所示:

@{
    var culture = CultureInfo.CurrentCulture.Name;
}

使用此参数为所有链接提供 culture 路由值,如下所示:

<a class="nav-link text-dark"
   asp-area="Identity"
   asp-page="/Account/Register"
   asp-route-culture="@culture"
   localize-content>Register</a>

对项目中的所有视图执行此操作。

本地化身份视图

身份相关内容,如登录、注册和配置文件,需要被覆盖才能修改。

右键单击项目名称,选择 Add --> New Scaffolded Item...

选择 Identity 并点击 Add

选择 "Override all files" 并选择 "ApplicationDbContext"

当您点击 Add 时,将创建一个新的 Areas 文件夹,其中包含所有身份相关的视图。

Identity area 包含三个 _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" />
}

如果您运行页面并输入无效输入,您会注意到验证消息是英文的,因此我们需要本地化数据批注消息,例如 RequiredStringLength 等。

打开 Areas/Identity/Pages/Account/Register.cshtml.cs 文件并在页面顶部添加对 "LazZiya.ExpressLocalization.DataAnnotations" 的引用;它提供了 express 属性,这些属性已生成本地化的错误消息。

@using LazZiya.ExpressLocalization.DataAnnotations;

然后修改 Input 模型,使其如下所示:

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 还有其他变体,允许您指定目标区域性和目标资源文件。浏览和测试函数以查看其他可能性。

Client Side Validation

服务器端验证工作正常,但我们仍然需要添加客户端验证,以便在提交表单之前在客户端验证输入字段。

客户端验证的一个主要问题是验证本地化输入,如数字、日期等。例如,如果您使用十进制输入,您会看到本地化数字的验证错误,例如 1.3 在英语区域性中是有效的,但在土耳其区域性中是无效的,因为它应该是 1,3(逗号而不是句点)。

在这里,我们将使用另一个有用的标签助手 LocalizationValidatonScripts,它来自 LazZiya.TagHelpers

在 startup 中注册标签助手。

services.AddTransient<ITagHelperComponent, LocalizationValidationScriptsTagHelperComponent>();

打开 Register.cshtml 页面,并在默认验证脚本部分下方添加标签助手。

@section Scripts {

    <partial name="_ValidationScriptsPartial" />
    <localization-validation-scripts></localization-validation-scripts>
}

就完成了,现在具有本地化输入的字段将根据当前区域性进行验证,例如十进制输入使用逗号或句点进行验证。

示例项目

您可以从 GitHub 下载包含超过 19 种区域性的示例项目。

参考文献

在此处阅读有关使用的 nuget 包的更多详细信息:

历史

  • 2019 年 6 月 3 日
    • 初始版本
  • 2019 年 6 月 17 日
    • 使用 LazZiya.TagHelpers.Localization 更新的版本
  • 2019 年 6 月 22 日
    • 更新示例,对本地化 ModelBinding 和身份消息中的一些问题进行了热修复(更新 LazZiya.ExpressLocalization 至 v2.0.1)
  • 2019 年 10 月 2 日
    • DotNet Core 3.0 支持
    • MVC 和 Razor 页面设置
    • 配置身份验证 Cookie 重定向路径
    • 添加了两个下载示例(.NET Core 3.0 和 .NET Core 2.2)
    • 将 nuget 包更新到最新版本
  • 2019 年 10 月 3 日
    • LazZiya.TagHelpers ViewContext 更新通知
  • 2020 年 2 月 22 日
    • 更新到最新版本的 ExpressLocalization 和 TagHelpers
    • 用于区域性值的 Cookie 处理程序
  • 2020 年 2 月 22 日
    • 下载示例已更新
© . All rights reserved.