Hỗ trợ đa ngôn ngữ cho thư mặc định được hiển thị khi nhập xác minh

Ngày tạo trang :

Môi trường

ASP.NET Core
  • 5.0 MVC

Bắt đầu

Hỗ trợ đa ngôn ngữ cho thư xác minh đầu vào mặc định cho lời nhắc này có thể không đầy đủ. Vui lòng giải quyết mọi vấn đề bị mất.

Ngoài ra, khi bạn thay đổi ngôn ngữ dựa trên phiên hoặc trạng thái bộ nhớ cache, nó cũng có thể được hiển thị bằng văn bản của ngôn ngữ trước đó.

Có một số cách để thay đổi thông báo xác thực đầu vào mặc định, mỗi phương pháp có thể được đáp ứng bằng cách kết hợp. Bất kỳ một, vì vậy nếu bạn muốn thay đổi nó chắc chắn, bạn phải đối phó với tất cả các mô hình.

Tiền đề

Lời nhắc này có nghĩa là bạn hiểu các mẹo sau:

Ngoài ra, nếu bạn muốn tạo một dự án mới, hãy thêm các tệp và mã sau theo lời nhắc ở trên:

  • tạo SharedResource.resx (+ en, es) 文件。 (Vì chỉ có thông báo được dịch cho lời nhắc này, nội dung trống.)
  • Tạo tệp .cs tài nguyên được chia sẻ
  • Thêm mã bản địa hóa vào dịch vụ khởi động. Cấu hình
  • Thêm mã bản địa hóa vào Bắt đầu. Cấu hình
  • Thêm mô hình chế độ xem người dùng (lần này, vì thông báo mặc định là đa ngôn ngữ, không có điều khiển phím rõ ràng)
  • Thêm hành động và chế độ xem tạo màn hình do người dùng tạo (Create.cshtml)

Bắt đầu mã

Dựa trên các giả định trên, mỗi mã nên trông giống như sau:

Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Globalization;

namespace LocalizationDefaultValidation
{
  public class Startup
  {
    // 省略

    public void ConfigureServices(IServiceCollection services)
    {
      services.AddControllersWithViews();

      services.AddMvc()
        // ローカライズに必要。Resx ファイルのフォルダパスを指定
        .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix, opts => { opts.ResourcesPath = "Resources"; })
        // DataAnnotations のローカライズに必要
        .AddDataAnnotationsLocalization(options =>
        {
          // DataAnnotation を使ったときのメッセージは SharedResource に集約する
          options.DataAnnotationLocalizerProvider = (type, factory) => factory.Create(typeof(SharedResource));
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment())
      {
        app.UseDeveloperExceptionPage();
      }
      else
      {
        app.UseExceptionHandler("/Home/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
      }

      // 標準の機能で切り替えたい言語を定義します。
      var supportedCultures = new[]
      {
        new CultureInfo("ja"),
        new CultureInfo("en"),
        new CultureInfo("es"),
      };

      // 標準の言語切り替え機能を有効にします。対応しているのは「クエリ文字列」「Cookie」「Accept-Language HTTP ヘッダー」です。
      app.UseRequestLocalization(new RequestLocalizationOptions
      {
        DefaultRequestCulture = new RequestCulture("ja"),
        SupportedCultures = supportedCultures,
        SupportedUICultures = supportedCultures
      });

      app.UseHttpsRedirection();
      app.UseStaticFiles();

      // 省略
    }
  }
}
HomeController.cs
using LocalizationDefaultValidation.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace LocalizationDefaultValidation.Controllers
{
  public class HomeController : Controller
  {
    // 省略

    public IActionResult Create()
    {
      SetDayOfWeeksViewData();
      return View();
    }

    [HttpPost]
    public IActionResult Create(UserViewModel model)
    {
      SetDayOfWeeksViewData();

      // エラーなら差し戻し
      if (ModelState.IsValid == false) return View(model);

      // 正常に登録できた場合は Index に戻る
      return RedirectToAction(nameof(Index));
    }

    /// <summary>曜日の一覧を ViewData に設定します。</summary>
    private void SetDayOfWeeksViewData()
      => ViewData["DayOfWeeks"] = ((DayOfWeek[])Enum.GetValues(typeof(DayOfWeek))).Select(x => new SelectListItem(x.ToString(), ((int)x).ToString()));
  }
}
Index.cshtml
@{
  ViewData["Title"] = "Home Page";
}

<div class="text-center">
  <h1 class="display-4">Welcome</h1>
  <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

<p>ユーザー作成画面に遷移します。リンクごとに選択した言語で表示できます。</p>
<ul>
  <li><a asp-action="Create">Create</a></li>
  <li><a asp-action="Create" asp-route-culture="ja">Create (ja)</a></li>
  <li><a asp-action="Create" asp-route-culture="en">Create (en)</a></li>
  <li><a asp-action="Create" asp-route-culture="es">Create (es)</a></li>
</ul>
Create.cshtml
@model LocalizationDefaultValidation.Models.UserViewModel
@using System.Globalization;
@{
  ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>UserViewModel</h4>
<hr />
<div class="row">
  <div class="col-md-4">
    <form asp-action="Create" enctype="multipart/form-data" >
      <div asp-validation-summary="ModelOnly" class="text-danger"></div>
      <div class="form-group">
        <label asp-for="ID" class="control-label"></label>
        <input asp-for="ID" class="form-control" />
        <span asp-validation-for="ID" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Name" class="control-label"></label>
        <input asp-for="Name" class="form-control" />
        <span asp-validation-for="Name" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Password" class="control-label"></label>
        <input asp-for="Password" class="form-control" />
        <span asp-validation-for="Password" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="ConfirmPassword" class="control-label"></label>
        <input asp-for="ConfirmPassword" class="form-control" />
        <span asp-validation-for="ConfirmPassword" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Email" class="control-label"></label>
        <input asp-for="Email" class="form-control" />
        <span asp-validation-for="Email" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Age" class="control-label"></label>
        <input asp-for="Age" class="form-control" />
        <span asp-validation-for="Age" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Gender"></label>
        <div>
          <label><input type="radio" asp-for="Gender" value="@(GenderType.None)" />@(GenderType.None)</label>
          <label><input type="radio" asp-for="Gender" value="@(GenderType.Man)" />@(GenderType.Man)</label>
          <label><input type="radio" asp-for="Gender" value="@(GenderType.Woman)" />@(GenderType.Woman)</label>
        </div>
        <span asp-validation-for="Gender" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Birthday" class="control-label"></label>
        <input asp-for="Birthday" class="form-control" />
        <span asp-validation-for="Birthday" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Phone" class="control-label"></label>
        <input asp-for="Phone" class="form-control" />
        <span asp-validation-for="Phone" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="PostalCode" class="control-label"></label>
        <input asp-for="PostalCode" class="form-control" />
        <span asp-validation-for="PostalCode" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="CreditCard" class="control-label"></label>
        <input asp-for="CreditCard" class="form-control" />
        <span asp-validation-for="CreditCard" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Money" class="control-label"></label>
        <input asp-for="Money" class="form-control" />
        <span asp-validation-for="Money" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="StartDateTime" class="control-label"></label>
        <input asp-for="StartDateTime" class="form-control" />
        <span asp-validation-for="StartDateTime" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="WakeUpTime" class="control-label"></label>
        <input asp-for="WakeUpTime" class="form-control" />
        <span asp-validation-for="WakeUpTime" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Homepage" class="control-label"></label>
        <input asp-for="Homepage" class="form-control" />
        <span asp-validation-for="Homepage" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="MyImage" class="control-label"></label>
        <input asp-for="MyImage" class="form-control" />
        <span asp-validation-for="MyImage" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="MyColor" class="control-label"></label>
        <input asp-for="MyColor" class="form-control" type="color" />
        <span asp-validation-for="MyColor" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="WorkingDays" class="control-label"></label>
        <select asp-for="WorkingDays" class="form-control" asp-items="@((IEnumerable<SelectListItem>)ViewData["DayOfWeeks"])" multiple></select>
        <span asp-validation-for="WorkingDays" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="VacationDay" class="control-label"></label>
        <select asp-for="VacationDay" class="form-control" asp-items="@((IEnumerable<SelectListItem>)ViewData["DayOfWeeks"])" multiple></select>
        <span asp-validation-for="VacationDay" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Comment" class="control-label"></label>
        <textarea asp-for="Comment" class="form-control"></textarea>
        <span asp-validation-for="Comment" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="FileName" class="control-label"></label>
        <input asp-for="FileName" class="form-control" />
        <span asp-validation-for="FileName" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="UploadFile" class="control-label"></label>
        <input asp-for="UploadFile" type="file" multiple />
        <span asp-validation-for="UploadFile" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Month" class="control-label"></label>
        <input asp-for="Month" class="form-control" type="month" />
        <span asp-validation-for="Month" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Search" class="control-label"></label>
        <input asp-for="Search" class="form-control" type="search" />
        <span asp-validation-for="Search" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Range" class="control-label"></label>
        <input asp-for="Range" class="form-control" type="range" min="10" max="100" />
        <span asp-validation-for="Range" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Week" class="control-label"></label>
        <input asp-for="Week" class="form-control" type="week" />
        <span asp-validation-for="Week" class="text-danger"></span>
      </div>
      <div class="form-group form-check">
        <label class="form-check-label">
          <input class="form-check-input" asp-for="IsAccepted" /> @Html.DisplayNameFor(model => model.IsAccepted)
        </label>
      </div>
      <div class="form-group">
        <input type="submit" value="Create" class="btn btn-primary" asp-route-culture="@CultureInfo.CurrentCulture.Name" />
      </div>
    </form>
  </div>
</div>

<div>
  <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
  @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
UserViewModel.cs
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace LocalizationDefaultValidation.Models
{
  public class UserViewModel
  {
    [Required]
    [Display(Name = "ID (半角英数字)")]
    [StringLength(20)]
    [RegularExpression(@"^[0-9a-zA-Z]*$")]
    public string ID { get; set; }

    [StringLength(50)]
    public string Name { get; set; }

    [StringLength(50, MinimumLength = 8)]
    [DataType(DataType.Password)]
    [RegularExpression(@"^[0-9a-zA-Z]*$")]
    public string Password { get; set; }

    [StringLength(50, MinimumLength = 8)]
    [DataType(DataType.Password)]
    [Compare(nameof(Password))]
    public string ConfirmPassword { get; set; }

    [EmailAddress]
    [DataType(DataType.EmailAddress)] // スキャフォールディングするときはコメントアウトする
    public string Email { get; set; }

    //[Int]
    [Range(0, 150)]
    public int Age { get; set; }

    [Required]
    [EnumDataType(typeof(GenderType))]
    public GenderType Gender { get; set; }

    [DataType(DataType.Date)]
    public DateTime Birthday { get; set; }

    [Phone()]
    [DataType(DataType.PhoneNumber)] // スキャフォールディングするときはコメントアウトする
    public string Phone { get; set; }

    [DataType(DataType.PostalCode)]
    public string PostalCode { get; set; }

    [CreditCard()]
    [DataType(DataType.CreditCard)] // スキャフォールディングするときはコメントアウトする
    public string CreditCard { get; set; }

    [DataType(DataType.Currency)]
    public decimal Money { get; set; }

    [DataType(DataType.DateTime)]
    public DateTime StartDateTime { get; set; }

    [DataType(DataType.Time)]
    public TimeSpan WakeUpTime { get; set; }

    [Url]
    [DataType(DataType.Url)] // スキャフォールディングするときはコメントアウトする
    public string Homepage { get; set; }

    [Url]
    [DataType(DataType.ImageUrl)] // スキャフォールディングするときはコメントアウトする
    public string MyImage { get; set; }

    public string MyColor { get; set; }

    [MaxLength(5)]
    public DayOfWeek[] WorkingDays { get; set; }

    [MinLength(3)]
    public DayOfWeek[] VacationDay { get; set; }

    [StringLength(200)]
    [DataType(DataType.MultilineText)]
    public string Comment { get; set; }

    [Display(Name = "ファイル名 (.png)")]
    [FileExtensions(Extensions = "png")]
    public string FileName { get; set; }

    [DataType(DataType.Upload)]
    public List<IFormFile> UploadFile { get; set; }

    public DateTime Month { get; set; }

    public string Search { get; set; }

    [Range(10, 100)]
    public int Range { get; set; }

    public string Week { get; set; }

    [Required]
    public bool IsAccepted { get; set; }
  }

  public enum GenderType
  {
    None,
    Man,
    Woman,
    Other,
  }
}
SharedResource.cs
namespace LocalizationDefaultValidation
{
  // クラス名は作成した .resx のファイル名と同じにする必要がある
  public class SharedResource { }
}

Mô hình ràng buộc thư

Đặt loại thuộc tính mô hình int thành hoặc bị ràng DateTime buộc với chế độ xem và thử đăng ký khi không điền, bạn sẽ thấy thông báo tương tự như Giá trị. Các thông báo này được hiển thị khi chuỗi trống không thể int bị ràng buộc với mô hình, v.v. Bởi vì nó không thể bị ràng buộc, nó chỉ xảy ra trước khi xác minh giá trị khác và trong xử lý phía máy chủ.

Những thông báo này DefaultModelBindingMessageProvider được định nghĩa là .

Startup.cs thêm các tham số cho phương pháp được xác định từ đầu services.AddControllersWithViews và sau đó nhấp Action vào Cài đặt có thể được truyền bằng cách thiết lập các tham số cho options ModelBindingMessageProvider đa ngôn ngữ.

Có 11 loại thư có thể được thiết lập, vì vậy trước tiên hãy SharedResource.resx định nghĩa thư là: Tên của khóa là tùy chọn. Thông báo tiếng Anh mặc định trong cột nhận xét (không cần phải là nhận xét). Chúng tôi không liệt kê bản dịch cho từng ngôn ngữ, vì vậy hãy tự dịch (mã mẫu bao gồm SharedResource.resx đã dịch).

tên
Giá trịGhi chú
ModelBinding_AttemptedValueIsInvalid Trong {1}, "{0}" không hợp lệ. The value '{0}' is not valid for {1}.
ModelBinding_MissingBindRequiredValue Giá trị không được chỉ định cho {0}. A value for the '{0}' parameter or property was not provided.
ModelBinding_MissingKeyOrValue Bắt buộc. A value is required.
ModelBinding_MissingRequestBodyRequiredValue Yêu cầu phải có nội dung. A non-empty request body is required.
ModelBinding_NonPropertyAttemptedValueIsInvalid "{0}" không hợp lệ. The value '{0}' is not valid.
ModelBinding_NonPropertyUnknownValueIsInvalid Giá trị này không hợp lệ. The supplied value is invalid.
ModelBinding_NonPropertyValueMustBeANumber Vui lòng chỉ định một số. The field must be a number.
ModelBinding_UnknownValueIsInvalid Giá trị {0} không hợp lệ. The supplied value is invalid for {0}.
ModelBinding_ValueIsInvalid "{0}" không hợp lệ. The value '{0}' is invalid.
ModelBinding_ValueMustBeANumber {0} phải chỉ định một số. The field {0} must be a number.
ModelBinding_ValueMustNotBeNull Đây là trường bắt buộc. The value '{0}' is invalid.

Sửa đổi .cs khởi động thành:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
using System;
using System.Globalization;
using System.Reflection;

namespace LocalizationDefaultValidation
{
  public class Startup
  {
    // 省略 (初期コード)

    /// <summary>
    /// 検証メッセージローカライズで使用。
    /// </summary>
    private IServiceProvider ServiceProvider { get; set; }

    private IStringLocalizer _localizer = null;
    /// <summary>
    /// 検証メッセージローカライズで使用。
    /// </summary>
    private IStringLocalizer Localizer
      => _localizer ?? (_localizer = ServiceProvider.GetService<IStringLocalizerFactory>()
               .Create(nameof(SharedResource), new AssemblyName(typeof(SharedResource).Assembly.FullName).Name));

    // このメソッドはランタイムによって呼び出されます。 このメソッドを使用して、コンテナーにサービスを追加します。
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddControllersWithViews(options =>
      {
        // 検証メッセージのローカライズで使用
        // モデルバインディング失敗時のエラーメッセージをカスタマイズ
        // サーバ側でモデルに値を格納する際に発生する可能性がある
        
        // メッセージの {0} や {1} を置換するための関数を定義
        static string f1(string f, string a1) => string.Format(f, a1);
        static string f2(string f, string a1, string a2) => string.Format(f, a1, a2);

        // 各メソッドを読んでメッセージを置き換えます
        var mp = options.ModelBindingMessageProvider;
        mp.SetAttemptedValueIsInvalidAccessor((x, y) => f2(Localizer["ModelBinding_AttemptedValueIsInvalid"], x, y));
        mp.SetMissingBindRequiredValueAccessor((x) => f1(Localizer["ModelBinding_MissingBindRequiredValue"], x));
        mp.SetMissingKeyOrValueAccessor(() => Localizer["ModelBinding_MissingKeyOrValue"]);
        mp.SetMissingRequestBodyRequiredValueAccessor(() => Localizer["ModelBinding_MissingRequestBodyRequiredValue"]);
        mp.SetNonPropertyAttemptedValueIsInvalidAccessor((x) => f1(Localizer["ModelBinding_NonPropertyAttemptedValueIsInvalid"], x));
        mp.SetNonPropertyUnknownValueIsInvalidAccessor(() => Localizer["ModelBinding_NonPropertyUnknownValueIsInvalid"]);
        mp.SetNonPropertyValueMustBeANumberAccessor(() => Localizer["ModelBinding_NonPropertyValueMustBeANumber"]);
        mp.SetUnknownValueIsInvalidAccessor((x) => f1(Localizer["ModelBinding_UnknownValueIsInvalid"], x));
        mp.SetValueIsInvalidAccessor((x) => f1(Localizer["ModelBinding_ValueIsInvalid"], x));
        mp.SetValueMustBeANumberAccessor((x) => f1(Localizer["ModelBinding_ValueMustBeANumber"], x));
        mp.SetValueMustNotBeNullAccessor((x) => f1(Localizer["ModelBinding_ValueMustNotBeNull"], x));
      });

      services.AddMvc()
        // ローカライズに必要。Resx ファイルのフォルダパスを指定
        .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix, opts => { opts.ResourcesPath = "Resources"; })
        // DataAnnotations のローカライズに必要
        .AddDataAnnotationsLocalization(options =>
        {
          // DataAnnotation を使ったときのメッセージは SharedResource に集約する
          options.DataAnnotationLocalizerProvider = (type, factory) => factory.Create(typeof(SharedResource));
        });
    }

    // このメソッドはランタイムによって呼び出されます。 このメソッドを使用して、HTTP要求パイプラインを構成します。
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      // ローカライズで使用するため IServiceProvider をプロパティに保持しておきます。
      ServiceProvider = app.ApplicationServices;

      // 標準の機能で切り替えたい言語を定義します。
      var supportedCultures = new[]
      {
        new CultureInfo("ja"),
        new CultureInfo("en"),
        new CultureInfo("es"),
      };

      // 標準の言語切り替え機能を有効にします。対応しているのは「クエリ文字列」「Cookie」「Accept-Language HTTP ヘッダー」です。
      app.UseRequestLocalization(new RequestLocalizationOptions
      {
        DefaultRequestCulture = new RequestCulture("ja"),
        SupportedCultures = supportedCultures,
        SupportedUICultures = supportedCultures
      });

      // 省略 (初期コード)
    }
  }
}

Điểm mấu chốt là thêm nhận vào services.AddControllersWithViews phương option Action pháp, và sau đó options.ModelBindingMessageProvider Bạn vừa thiết lập văn bản được bản địa hóa cho mỗi phương pháp Set.

Văn bản sau khi dịch IStringLocalizer đến từ IStringLocalizer Vì nó không thể được nhận trực tiếp, mã là một chút vòng tròn. SharedResource.resx Nếu bạn tạo mã, bạn nhận được giá trị bản địa hóa trực tiếp từ nó.

IStringLocalizer Các phím được chỉ định trong là các phím được thêm SharedResource.resx vào. Đặt nó để sử dụng với nội dung của mỗi phương pháp Set.

Ngoài ra, vì mỗi tin nhắn có {0}{1} tham số chuỗi định dạng, chúng tôi sử dụng đơn giản để thay thế các giá trị nhận string.Format Func được.

Bản địa hóa thông báo xác minh mặc định

Khi các thuộc tính của thuộc tính mô hình được đặt thành Required bằng StringLength không, thông báo lỗi mặc định được xác định bởi khung, về cơ bản bằng tiếng Anh.

IValidationAttributeAdapterProviderBạn có thể bản địa hóa chúng bằng cách xác định các lớp học của giao diện phái sinh.

Trước SharedResource.resx tiên, bạn đã đăng ký văn bản tương thích với đa ngôn ngữ. Tên của khóa là tùy chọn, nhưng để đơn giản hóa chương trình, > đăng ký dưới dạng "Validator_< xác minh tên thuộc tính".

tên
Giá trịGhi chú
Validator_CompareAttribute {0} không khớp với {1}. '{0}' and '{1}' do not match.
Validator_CreditCardAttribute {0} không phải là số thẻ hợp lệ. The {0} field is not a valid credit card number.
Validator_DataTypeAttribute_Date Vui lòng nhập ngày hợp lệ. Please enter a valid date.
Validator_EmailAddressAttribute {0} không phải là một địa chỉ email hợp lệ. The {0} field is not a valid e-mail address.
Validator_FileExtensionsAttribute {0} chỉ chấp nhận các tệp có phần mở rộng sau: : {1} The {0} field only accepts files with the following extensions: {1}
Validator_MaxLengthAttribute {0} phải là loại chuỗi hoặc mảng có độ dài tối đa là "{1}". The field {0} must be a string or array type with a maximum length of '{1}'.
Validator_MinLengthAttribute {0} phải là loại chuỗi hoặc mảng có độ dài tối thiểu là "{1}". The field {0} must be a string or array type with a minimum length of '{1}'.
Validator_PhoneAttribute {0} không phải là số điện thoại hợp lệ. The {0} field is not a valid phone number.
Validator_RangeAttribute {0} nên nằm giữa {1} và {2}. The field {0} must be between {1} and {2}.
Validator_RegularExpressionAttribute Chỉ định rằng {0} khớp với biểu thức chính quy "{1}". The field {0} must match the regular expression '{1}'.
Validator_RequiredAttribute {0} là bắt buộc. The {0} field is required.
Validator_StringLengthAttribute {0} phải {1} bit. The field {0} must be a string with a maximum length of {1}.
Validator_UrlAttribute {0} không phải là URL hợp lệ. The {0} field is not a valid fully-qualified http, https, or ftp URL.
Validator_StringLengthAttributeWithMin {0} phải có ít nhất {2} bit {1} bit. The field {0} must be a string with a maximum length of {1} and a minimum length of {2}.

Tiếp theo, bạn sẽ tạo các lớp nhà cung cấp bộ điều hợp sau:

Tên lớp là tùy chọn, nhưng lần này chúng ta sẽ tạo CustomValidationAttributeAdapterProvider một lớp được gọi là và viết mã như sau. Vị trí của tệp mã là tùy chọn, nhưng ví dụ đặt nó trong một thư mục được gọi là bộ điều hợp.

using Microsoft.AspNetCore.Mvc.DataAnnotations;
using Microsoft.Extensions.Localization;
using System;
using System.ComponentModel.DataAnnotations;

namespace LocalizationDefaultValidation
{
  public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
  {
    /// <summary>コード簡略化のためのリソースキー命名規則プリフィックス</summary>
    private const string RESOURCE_KEY_PREFIX = "Validator_";

    private readonly IValidationAttributeAdapterProvider _fallback = new ValidationAttributeAdapterProvider();

    /// <summary>
    /// 指定された ValidationAttribute の IAttributeAdapter を返します。
    /// </summary>
    /// <param name="attribute">IAttributeAdapter を作成するための ValidationAttribute。</param>
    /// <param name="stringLocalizer">メッセージの作成に使用される IStringLocalizer。</param>
    /// <returns>指定された属性の IAttributeAdapter。</returns>
    IAttributeAdapter IValidationAttributeAdapterProvider.GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
    {
      // すでにエラーメッセージが設定されている場合はそれを使用するのでここでは何も設定しない
      if (attribute.ErrorMessageResourceName != null) return _fallback.GetAttributeAdapter(attribute, stringLocalizer);

      // attribute には「Required」や「StringLength」などが設定されています
      Type attrType = attribute.GetType();
      
      // プリフィックスと属性のクラス名からローカライズ用のキーを生成します
      var key = RESOURCE_KEY_PREFIX + attrType.Name;

      // IStringLocalizer から指定したキーでローカライズされたテキストを取得します
      // ない場合は getString にそのまま key の値が入ります
      var getString = stringLocalizer[key];

      // 正しくローカライズされたテキストが取得できた場合は ErrorMessage に値をセットします。
      if (key != getString && attribute.ErrorMessage != getString)
      {
        attribute.ErrorMessage = getString;
      }

      // 設定した attribute を渡します
      return _fallback.GetAttributeAdapter(attribute, stringLocalizer);
    }
  }
}

Nếu thuộc tính mô hình Required được đặt thành StringLength hoặc, phương pháp được gọi mỗi khi bạn xác IValidationAttributeAdapterProvider.GetAttributeAdapter minh.

ErrorMessageResourceName Nếu thuộc tính chứa giá trị, nó được trả về vì mã mô hình đã thiết lập một tin nhắn hoặc phím bản địa hóa.

Nếu tiền tố và thuộc tính xác thực có tên lớp trống, hãy đặt nó thành phím bản địa hóa và lấy văn bản được bản địa hóa từ khóa ErrorMessage và đặt nó thành. Điều này cho phép hầu hết các thư mặc định được bản địa hóa.

Sau đó, đăng ký lớp này trong .cs Bắt đầu. Về cơ bản, bạn có thể thêm những điều sau đây:

// 省略

using Microsoft.AspNetCore.Mvc.DataAnnotations;

namespace LocalizationDefaultValidation
{
  public class Startup
  {
    // 省略

    // このメソッドはランタイムによって呼び出されます。 このメソッドを使用して、コンテナーにサービスを追加します。
    public void ConfigureServices(IServiceCollection services)
    {
      // 省略

      // 作成した CustomValidationAttributeAdapterProvider をシングルトンとして登録します
      services.AddSingleton<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
    }
    
    // 省略
  }
}

Chạy nó để đảm bảo rằng nó hoạt động đúng.

Thay đổi thông báo hiện có theo tham số

Ví dụ: StringLength thông báo thuộc tính đã được bản địa hóa thành "{0} phải {1} bit." MinimumLength Nếu thuộc tính được thiết lập, bạn có thể cần phải thay đổi bit {0}{1}{2} hoặc nhiều hơn."

Trong trường hợp này, CustomValidationAttributeAdapterProvider mở rộng lớp để:

// 省略

namespace LocalizationDefaultValidation
{
  public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
  {
    // 省略

    IAttributeAdapter IValidationAttributeAdapterProvider.GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
    {
      if (attribute is StringLengthAttribute slAttribute)
      {
        // attribute が StringLengthAttribute で MinimumLength が設定されている場合はメッセージを変える
        if (slAttribute.MinimumLength >= 1)
        {
          attribute.ErrorMessage = stringLocalizer["Validator_StringLengthAttributeWithMin"];
          return _fallback.GetAttributeAdapter(slAttribute, stringLocalizer);
        }
      }

      // 省略
    }
  }
}

Bởi vì mỗi xác nhận GetAttributeAdapter gọi một phương pháp, nếu biến được truyền attribute là một thuộc StringLengthAttribute MinimumLength tính, kiểm tra các thuộc tính, Nếu bạn đã thiết lập, nhận được một thông báo cũng xem xét số bit tối thiểu và thay thế nó ErrorMessage bằng.

Nếu bạn chạy và xác nhận rằng thư thay đổi, nó sẽ là ok.

Thông điệp chung khác

Cho đến nay, một số thư chưa được bản địa hóa. Ví dụ: thư chỉ được xác định bởi tập lệnh Java.

Điều này áp dụng cho các jquery.validate.js thư được liệt kê trong tệp.

Vì tệp này không nên được chỉnh sửa trực tiếp, bạn phải input thêm thuộc tính vào thẻ và đặt văn bản bản địa hóa thành thay thế các thư data-val-XXXX này. Phần XXXX có các phím trong hình jquery.validate.js trên. (ví data-val-number dụ:

data-val-XXXX Để thiết lập văn bản được bản địa hóa trong thuộc tính, bạn có thể đặt văn bản đó khi HTML được trả về ở phía máy chủ.

Ví dụ: bản địa hóa thư khi các ký tự không phải số được bao gồm trong trường đầu vào Age. UserViewModel.Age HTML có Range thuộc tính và đầu ra data-val-range đính kèm thuộc tính vào đầu vào.

<div class="form-group">
  <label class="control-label" for="Age">Age</label>
  <input class="form-control" type="number" data-val="true" data-val-range="Ageは0から150の範囲で指定してください。" data-val-range-max="150" data-val-range-min="0" data-val-required="Ageは必須です。" id="Age" name="Age" value="" />
  <span class="text-danger field-validation-valid" data-valmsg-for="Age" data-valmsg-replace="true"></span>
</div>

Tuy data-val-number nhiên, vì không có thuộc tính, thư kiểm tra kỹ thuật số không được bản địa hóa. Vì vậy, thêm một thuộc tính khác vào mô hình và đầu ra các thuộc tính có chứa thư được bản địa data-val-number hóa.

Trước tiên, tạo một lớp thuộc tính kiểm tra xem nó có phải là số hay IntAttribute không, như sau: Ở đây int bạn có thể tự do thay đổi nó tùy thuộc vào hoàn cảnh. Vị trí của tệp mã không quan trọng, nhưng lần này tôi đặt nó trong thư mục bộ điều hợp.

using System.ComponentModel.DataAnnotations;

namespace LocalizationDefaultValidation
{
  public class IntAttribute : ValidationAttribute
  {
    public override bool IsValid(object value)
    {
      // 渡された値が int であれば有効な値とする
      return value is int;
    }
  }
}

Nếu giá trị được đặt int là loại, thuộc tính này chỉ là một quyết định bình thường, nhưng bản thân lớp này không có ý nghĩa đặc biệt vì thuộc tính thực sự được đặt thành loại int là 100% bình thường. Mục đích là để hiển thị thông báo lỗi bản địa hóa trên máy khách.

Tiếp theo, IntAttributeAdapter bạn sẽ tạo các lớp bộ điều hợp sau: Tôi cũng đặt nó trong thư mục bộ điều hợp.

using Microsoft.AspNetCore.Mvc.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.Localization;

namespace LocalizationDefaultValidation
{
  /// <summary>Int 属性のアダプター。</summary>
  public class IntAttributeAdapter : AttributeAdapterBase<IntAttribute>
  {
    /// <summary>受け取った IStringLocalizer を保持しておく</summary>
    IStringLocalizer _stringLocalizer;

    public IntAttributeAdapter(IntAttribute attribute, IStringLocalizer stringLocalizer)
        : base(attribute, stringLocalizer)
    {
      _stringLocalizer = stringLocalizer;
    }

    /// <summary>バリデーションの追加処理として呼ばれる。</summary>
    public override void AddValidation(ClientModelValidationContext context)
    {
      // 新たに data-val-number 属性をマージして追加します。値はローカライズしたテキストをセットします。
      MergeAttribute(context.Attributes, "data-val-number", _stringLocalizer["ModelBinding_NonPropertyValueMustBeANumber"]);
    }

    /// <summary>サーバーのエラーメッセージはそのまま返します。</summary>
    public override string GetErrorMessage(ModelValidationContextBase validationContext) => Attribute.ErrorMessage;
  }
}

Điểm chính ở đây là AddValidation phương pháp tôi đã viết trong phương pháp của MergeAttribute tôi.

Hợp nhất thuộc tính thành thuộc tính mà bạn muốn trả về thẻ đầu vào cho khách data-val-number hàng. Đặt thông báo lỗi được bản địa hóa cho các giá trị được đặt cho thuộc tính.

Trả về bộ điều hợp này trong lớp được tạo trước CustomValidationAttributeAdapterProvider đó.

// 省略

namespace LocalizationDefaultValidation
{
  public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
  {
    // 省略
    
    IAttributeAdapter IValidationAttributeAdapterProvider.GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
    {
      // IntAttribute の場合は IntAttributeAdapter 経由で返す
      if (attribute is IntAttribute intAttribute)
      {
        return new IntAttributeAdapter(intAttribute, stringLocalizer);
      }

      // 省略 (前に追加したコード)
    }
  }
}

Nếu giá trị bạn muốn xác minh IntAttribute là, trả về những gì bạn vừa IntAttributeAdapter tạo. Bây giờ các thuộc tính có thuộc tính Int được thêm vào khi máy khách được hiển data-val-number thị.

Cuối cùng, UserViewModel.Age IntAttribute thêm vào .

Sau khi bạn sửa chữa mã, chạy nó để xem nó.

<div class="form-group">
  <label class="control-label" for="Age">Age</label>
  <input class="form-control" type="number" data-val="true" data-val-number="数字を指定してください。" data-val-range="Ageは0から150の範囲で指定してください。" data-val-range-max="150" data-val-range-min="0" data-val-required="Ageは必須です。" id="Age" name="Age" value="" />
  <span class="text-danger field-validation-valid" data-valmsg-for="Age" data-valmsg-replace="true"></span>
</div>

Có những tin nhắn khác không được bản địa hóa, nhưng có thể được bản địa hóa bằng cách sử dụng các phương pháp trên. Nếu cần, hãy thêm mã.