使用XLocalizer进行ASP.NET Core本地化

目录

介绍

XLocalizer提供什么?

安装

设置XLocalizer

为翻译服务添加API密钥

完整的XLocalizer启动代码

本地化视图

本地化验证属性错误、模型绑定错误和身份错误消息

自定义错误消息

JSON设置

添加语言导航

运行应用程序

支持的.NET Core版本

支持的项目类型

参考文献


介绍

开发一个多元文化的Web应用程序需要构建本地化基础结构,该基础结构将处理请求本地化并本地化视图、错误消息等。另一方面每个本地化区域性都至少需要一个资源文件,其中包含所有本地化的键值对。

构建本地化基础结构和填充资源文件可能会花费大量时间和精力。XLocalizer是从零开始开发的,可以解决这两个问题并使开发人员摆脱不必要的工作量。

XLocalizer提供什么?

简单的本地化设置:首先,它旨在帮助开发人员轻松创建本地化的Web应用程序,而不会浪费时间开发本地化基础结构。

自动本地化XLocalizer最吸引人的两个功能是自动资源创建在线翻译。因此,任何丢失的键都将被翻译并自动添加到相关的资源文件中。

支持多种资源类型:默认情况下,ASP.NET Core使用.resx资源文件存储本地化的字符串。XLocalizer通过提供内置的本地化存储(XMLRESXDB)打破了障碍。此外,可以实现任何其他文件或数据库格式的自定义资源类型。

灵活性XLocalizer使用标准的本地化接口IStringLocalizerIHtmlLocalizer,因此很容易从默认的.NET Core本地化系统切换到XLocalizer,反之亦然。借助内置资源导出,所有本地化资源都可以从任何文件/数据库类型导出为.resx文件类型。

定制XLocalizer可以对每个细节进行定制

  • 使用自定义资源类型(例如mysqljsoncsv等),
  • 使用自定义资源导出器将资源从任何来源导出到任何来源,
  • 使用定制翻译服务来翻译资源。

集中化:一个地方,以一种简单的方式轻松自定义所有验证错误,模型绑定错误和身份错误。

在本教程中,我将展示如何让带有XML资源文件的XLocalizer和在线翻译一起使用。要了解其他资源类型(如resxdb或自定义源)的设置,请访问 https://DOCS.Ziyad.info

安装

  • 要充分利用XLocalizer,需要一些细节,首先我将提及所有软件包,然后在下一步中我们将了解每个软件包的作用。
// The main package
PM > Install-Package XLocalizer

// Online translation support
PM > Install-Package XLocalizer.Translate

// Translation service
PM > Install-Package XLocalizer.Translate.MyMemoryTranslate

// Use html tags to localize views
PM > Install-Package XLocalizer.TagHelpers

// Additional taghelper package for language dropdown
PM > Install-Package LazZiya.TagHelpers
 
  • 资源文件夹:在项目根目录下,创建一个名为LocalizationResources的新文件夹,然后在其中创建一个名为LocSource的新空类。此类将用于从代码访问相关的资源文件。
// Dummy class for grouping and accessing resource files
public class LocSource { }

无需创建区域性特定的资源文件,它们将由XLocalizer创建并自动填充。

设置XLocalizer

加速编码的小技巧;VS2019可以自动插入缺少的命名空间(使用)。或者,您可以按(Crtl +.)查看上下文菜单,该菜单将添加缺少的命名空间。

  • 打开启动文件并像往常一样配置请求本地化选项:
services.Configure<RequestLocalizationOptions>(ops => 
{
    var cultures = new CultureInfo[] { 
       new CultureInfo("en"),
       new CultureInfo("tr"),
       ...
    };
    ops.SupportedCultres = cultures;
    ops.SupportedUICultures = cultures;
    ops.DefaultRequestCulture = new RequestCulture("en");

    // Optional: add custom provider to support localization 
    // based on route value
    ops.RequestCultureProviders.Insert
        (0, new RouteSegmentRequestCultureProvider(cultures));
});

XLocalizer支持多种资源类型,例如XMLRESXDB等。在此示例中,我将使用XML文件存储本地化的值,因此我们需要注册内置的XmlResourceProvider,该提供程序将帮助我们将XML文件用作存储本地化的键值对的资源文件。

services.AddSingleton<IXResourceProvider, XmlResourceProvider>();
  • XLocalizer在线翻译支持是它的主要好处之一,因此我们需要在启动文件中注册至少一个翻译服务。
services.AddHttpClient<ITranslator, MyMemoryTranslateService>();

我在XLocalizer开发时曾经用过MyMemoryTranslateService,但是您可以自由选择任何可用的翻译服务,甚至可以自己的翻译服务

  • (可选)将Razor页面配置为使用基于路由的本地化提供程序,因此我们可以使用以下网址:http://localhost:111/en/Index。然后XLocalizer在同一步骤中进行配置XLocalizer
services.AddRazorPages()
    .AddRazorPagesOptions(ops => 
    {
        ops.Conventions.Insert(0, new RouteTemplateModelConventionRazorPages());
    })
    .AddXLocalizer<LocSource, MyMemoryTranslateService>(ops => 
    {
        ops.ResourcesPath = "LocalizationResources";
        ops.AutoAddKeys = true;
        ops.AutoTranslate = true;
        ops.TranslateFromCulture = "en";
    });
 
  • 配置应用程序以使用本地化中间件:
app.UseRequestLocalization();

为翻译服务添加API密钥

MyMemory翻译API提供免费的匿名用法,直到1000/天(撰写本文时为止)。因此,基本上,您无需添加任何密钥即可对其进行测试。无论如何,您只需提供电子邮件和免费生成的密钥,就可以将免费使用量增加到每天30.000个单词。有关更多详细信息,请参见MyMemory API使用限制

使用MyMemory API Keygen获取密钥,然后将具有有效电子邮件地址的密钥添加到用户机密文件中,如下所示:

{   
  "XLocalizer.Translate": {
    "MyMemory": {
       "Email": "...",
       "Key": "..."
    }
  }
}

不同的翻译服务可能需要不同的设置。有关设置不同翻译服务的详细信息,请参见翻译服务文档

完整的XLocalizer启动代码

示例启动文件,为简化起见省略了不必要的代码。

public class Startup
{    
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }
    
    // ...

    public void ConfigureServices(IServiceCollection services)
    {
        // Configure request localization
        services.Configure<RequestLocalizationOptions>(ops =>
        {
            var cultures = new CultureInfo[] { new CultureInfo("en"), 
            new CultureInfo("tr"), new CultureInfo("ar") };
            ops.SupportedCultures = cultures;
            ops.SupportedUICultures = cultures;
            ops.DefaultRequestCulture = 
                new Microsoft.AspNetCore.Localization.RequestCulture("en");
            ops.RequestCultureProviders.Insert
                (0, new RouteSegmentRequestCultureProvider(cultures));
        });

        // Register translation service
        services.AddHttpClient<ITranslator, MyMemoryTranslateService>();

        // Register XmlResourceProvider
        services.AddSingleton<IXResourceProvider, XmlResourceProvider>();

        services.AddRazorPages()
            .AddRazorPagesOptions(ops => 
            { ops.Conventions.Insert(0, new RouteTemplateModelConventionRazorPages()); })
            // Add XLocalizer
            .AddXLocalizer<LocSource, MyMemoryTranslateService>(ops =>
            {
                ops.ResourcesPath = "LocalizationResources";
                ops.AutoAddKeys = true;
                ops.AutoTranslate = true;
                
                // Optional: Just in case you need to change the source translation culture.
                // if not provided, the default culture will be used
                ops.TranslateFromCulture = "en";
                
                // Recommended: turn on caching during production for faster localization
                ops.UseExpressMemoryCache = true; 
            });
    }

    // This method gets called by the runtime. 
    // Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // ...
        // Use request localization middleware
        app.UseRequestLocalization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }
}

这就是启动文件中所需的所有设置。接下来,我们将配置视图和后端本地化。

本地化视图

我们已经安装了用于本地化视图的便捷nuget  XLocalizer.TagHelpers,此软件包使您可以轻松使用html标签和html属性来本地化视图,从而使html代码保持整洁并易于阅读和维护。

  • _ViewImports.cshtml文件中添加taghelper
@addTagHelper *, XLocalizer.TagHelpers
  • html标记内使用localize-content属性来本地化内部文本/html
<h1 localize-content>Welcome</h1>
  • 使用localize html标记本地化text/htm段落:
<localize>
    <h1>Welcome</h1>
    <p>My contents...</p>
</localize>
 
  • 使用参数本地化html字符串:
@{
    var args = new object[] { "http://DOCS.Ziyad.info" }
}

<p localize-args="args">
    Visit <a href="{0}">DOCS</a> for more details.
</p>
 
  • 本地化html属性(例如title):
<img src="../picture.jpg" localize-att-title="Nature picture" />

下面是Register.cshtml页面的完全本地化的示例,请注意,我们只需要在相关标签中添加localize-content属性,即可使页面代码保持整洁并易于阅读和更新。

@page
@model RegisterModel
@{
    ViewData["Title"] = "Register";
}

<h1 localize-content>@ViewData["Title"]</h1>

<div class="row">
    <div class="col-md-4">
        <form asp-route-returnUrl="@Model.ReturnUrl" method="post">
            <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" />
}

使用XLocalizer.TagHelpers查看本地化示例

本地化验证属性错误、模型绑定错误和身份错误消息

本地化所有框架错误消息不需要使用XLocalizer进行任何其他设置,并且没有必要在attribute标签内提供任何错误消息!所有错误消息都将通过XLocalizer的默认设置进行分配和本地化。

以下是一些验证属性的示例用法:

[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }

此外,对于模型绑定错误身份错误,我们无需进行任何其他设置,默认情况下XLocalizer将负责本地化所有错误消息。

以下是Register.cshtml.cs文件的后端本地化示例:

public class InputModel
{
    [Required]
    [EmailAddress]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [Required]
    [StringLength(100, MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password")]
    public string ConfirmPassword { get; set; }
}

验证属性使用情况,但未定义错误消息

自定义错误消息

在某些情况下,您可能需要针对验证属性,模型绑定或身份自定义错误消息。或者,您可能希望在除en之外的区域性中提供默认错误消息,因此XLocalizer可以从正确的区域性进行翻译。

第一种解决方案是通过提供以下相关错误消息,在启动文件中使用内联选项设置:

services.AddRazorPages()
        .AddXLocalizer<...>(ops =>
        {
            // ...
            ops.ValidationErrors = new ValidationErrors 
            {
                RequiredAttribute_ValidationError = "The {0} field is required.",
                CompareAttribute_MustMatch = 
                "'{0}' and '{1}' do not match.",
                StringLengthAttribute_ValidationError = 
                "The field {0} must be a string with a maximum length of {1}.",
                // ...
            };
            ops.ModelBindingErrors = new ModelBindingErrors 
            {
                AttemptedValueIsInvalidAccessor = "The value '{0}' is not valid for {1}.",
                MissingBindRequiredValueAccessor = 
                "A value for the '{0}' parameter or property was not provided.",
                MissingKeyOrValueAccessor = "A value is required.",
                // ...
            };
            ops.IdentityErrors = new IdentityErrors 
            {
                DuplicateEmail = "Email '{0}' is already taken.",
                DuplicateUserName = "User name '{0}' is already taken.",
                InvalidEmail = "Email '{0}' is invalid.",
                // ...
            };
        });

在启动文件中自定义错误消息。

另一个选项是在json文件中配置所有XLocalizer设置。

JSON设置

如果您是偏爱保持启动文件整洁的开发人员,像我一样,您将很高兴知道可以在json文件中进行所有这些自定义,并且仅需一行即可读取启动时的配置。

{
    "XLocalizerOptions" : {
        "AutoAddKeys" : true,
        "AutoTranslate" : true,
        // ...
    }
}
 
  • 进行设置XLocalizer以阅读相关的配置部分:
services.AddRaqzorPages()
        .AddXLocalizer<...>
        (ops => Configuration.GetSection("XLocalizerOptions").Bind(ops));
 
  • 以下是XLocalizer带有可自定义错误消息的选项的示例json设置:
{
  "XLocalizerOptions": {
    "ResourcesPath": "LocalizationResources",
    "AutoAddKeys": true,
    "AutoTranslate": true,
    "UseExpressMemoryCache": true,
    "TranslateFromCulture": "en",
    "ValidationErrors": {
      "CompareAttribute_MustMatch": "'{0}' 
            and '{1}' do not match. They should not be different!",
      "CreditCardAttribute_Invalid": 
            "The {0} field is not a valid credit card number.",
      "CustomValidationAttribute_ValidationError": "{0} is not valid.",
      "DataTypeAttribute_EmptyDataTypeString": 
            "The custom DataType string cannot be null or empty.",
      "EmailAddressAttribute_Invalid": 
            "The {0} field is not a valid e-mail address.",
      "FileExtensionsAttribute_Invalid": 
            "The {0} field only accepts files with the following extensions: {1}",
      "MaxLengthAttribute_ValidationError": 
            "The field {0} must be a string or array type 
             with a maximum length of '{1}'.",
      "MinLengthAttribute_ValidationError": 
            "The field {0} must be a string or array type 
             with a minimum length of '{1}'.",
      "PhoneAttribute_Invalid": "The {0} field is not a valid phone number.",
      "RangeAttribute_ValidationError": 
            "The field {0} must be between {1} and {2}.",
      "RegexAttribute_ValidationError": 
            "The field {0} must match the regular expression '{1}'.",
      "RequiredAttribute_ValidationError": 
            "The {0} field is required. Don't bypass this field!",
      "StringLengthAttribute_ValidationError": 
            "The field {0} must be a string with a maximum length of {1}.",
      "StringLengthAttribute_ValidationErrorIncludingMinimum": 
            "The field {0} must be a string with a minimum length of {2}
             and a maximum length of {1}.",
      "UrlAttribute_Invalid": "The {0} field is not a valid fully-qualified http,
                               https, or ftp URL.",
      "ValidationAttribute_ValidationError": "The field {0} is invalid."
    },
    "IdentityErrors": {
      "DuplicateEmail": "Email '{0}' is already taken.",
      "DuplicateUserName": "User name '{0}' is already taken. 
                            Please try another one.",
      "InvalidEmail": "Email '{0}' is invalid.",
      "DuplicateRoleName": "Role name '{0}' is already taken.",
      "InvalidRoleName": "Role name '{0}' is invalid.",
      "InvalidToken": "Invalid token.",
      "InvalidUserName": 
              "User name '{0}' is invalid, can only contain letters or digits.",
      "LoginAlreadyAssociated": "A user with this login already exists.",
      "PasswordMismatch": "Incorrect password.",
      "PasswordRequiresDigit": "Passwords must have at least one digit ('0'-'9').",
      "PasswordRequiresLower": 
               "Passwords must have at least one lowercase ('a'-'z').",
      "PasswordRequiresNonAlphanumeric": 
               "Passwords must have at least one non alphanumeric character.",
      "PasswordRequiresUniqueChars": 
               "Passwords must use at least {0} different characters.",
      "PasswordRequiresUpper": 
               "Passwords must have at least one uppercase ('A'-'Z').",
      "PasswordTooShort": "Passwords must be at least {0} characters.",
      "UserAlreadyHasPassword": "User already has a password set.",
      "UserAlreadyInRole": "User already in role '{0}'.",
      "UserNotInRole": "User is not in role '{0}'.",
      "UserLockoutNotEnabled": "Lockout is not enabled for this user.",
      "RecoveryCodeRedemptionFailed": "Recovery code redemption failed.",
      "ConcurrencyFailure": "Optimistic concurrency failure, 
                             object has been modified.",
      "DefaultError": "An unknown failure has occurred."
    },
    "ModelBindingErrors": {
      "AttemptedValueIsInvalidAccessor": "The value '{0}' is not valid for {1}.",
      "MissingBindRequiredValueAccessor": 
            "A value for the '{0}' parameter or property was not provided.",
      "MissingKeyOrValueAccessor": "A value is required.",
      "MissingRequestBodyRequiredValueAccessor": 
                     "A non-empty request body is required.",
      "NonPropertyAttemptedValueIsInvalidAccessor": "The value '{0}' is not valid.",
      "NonPropertyUnknownValueIsInvalidAccessor": "The supplied value is invalid.",
      "NonPropertyValueMustBeANumberAccessor": "The field must be a number.",
      "UnknownValueIsInvalidAccessor": "The supplied value is invalid for {0}.",
      "ValueIsInvalidAccessor": 
              "The value '{0}' is invalid. You entered something weird!",
      "ValueMustBeANumberAccessor": 
            "The field {0} must be a number. 
             Don't use letters or special characters.",
      "ValueMustNotBeNullAccessor": 
             "The value '{0}' is invalid. This can't be null."
    }
  }
}

自定义json文件中的所有XLocalizer选项。

因此,这是自定义所有错误消息的简单方法。XLocalizer将根据请求区域性将这些消息翻译成其他区域性。

添加语言导航

每个多文化Web应用程序都必须提供一种在不同语言之间进行切换的方法。您可能有自己的语言导航实现,但以防万一您需要轻松添加一个(我们之前已安装的LazZiya.TagHelpers):

  • 添加taghelpers_ViewImports文件中:
@addTagHelper *, LazZiya.TagHelpers
  • 打开_layout.cshtml然后在需要显示的位置添加语言导航:
<language-nav> </ language-nav >

我强烈建议按照此处的文档页面中的说明设置语言导航以配置区域性cookie 。因此,区域性选择可以存储在cookie中以备后用。

运行应用程序

如果正确完成了所有步骤,然后启动应用程序, 请查看VS中的输出窗口以查看日志,您将看到该窗口XLocalizer已开始翻译视图并自动插入值。此外,所有验证属性,模型绑定和身份错误也已本地化。

注意:本地化身份页面需要将身份脚手架放入项目中。

您需要添加新区域性的是:将区域性添加到启动文件中受支持的区域性,其余所有工作由XLocalizer来完成。:)

支持的.NET Core版本

  • 2.x
  • 3.x
  • 5.0

支持的项目类型

  • Razor Pages
  • MVC
  • Blazor Server

参考文献

https://www.codeproject.com/Articles/5275604/ASP-NET-Core-Localization-with-XLocalizer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值