สนับสนุนข้อความเริ่มต้นที่แสดงเมื่อตรวจสอบการป้อนข้อมูล

วันที่สร้างเพจ :

สภาพแวดล้อม

ASP.NET Core
  • 5.0 MVC

เริ่มต้นใช้งาน

การสนับสนุนหลายภาษาสําหรับข้อความการตรวจสอบการป้อนข้อมูลเริ่มต้นสําหรับเคล็ดลับนี้อาจไม่สมบูรณ์ โปรดแก้ไขปัญหาการสูญเสียแต่ละครั้ง

นอกจากนี้ เมื่อคุณเปลี่ยนภาษาตามเซสชันหรือสถานะแคช อาจแสดงเป็นข้อความในภาษาก่อนหน้า

มีหลายวิธีในการเปลี่ยนข้อความการตรวจสอบการป้อนข้อมูลเริ่มต้นซึ่งแต่ละวิธีสามารถตอบสนองได้โดยการรวมเข้าด้วยกัน ใด ๆ ดังนั้นถ้าคุณต้องการที่จะเปลี่ยนมันแน่นอนคุณต้องจัดการกับรูปแบบทั้งหมด

หลักฐาน

เคล็ดลับนี้หมายความว่าคุณเข้าใจเคล็ดลับต่อไปนี้:

นอกจากนี้หากคุณต้องการสร้างโครงการใหม่ให้เพิ่มไฟล์และรหัสต่อไปนี้ตามเคล็ดลับข้างต้น:

  • 创建 SharedResource.resx (+ en, es) 文件。 (เนื้อหาว่างเปล่าเนื่องจากข้อความพร้อมท์นี้ถูกแปลเท่านั้น)
  • สร้างแฟ้ม.csทรัพยากรที่ใช้ร่วมกัน
  • เพิ่มรหัสการแปลเป็นภาษาท้องถิ่นไปยังบริการเริ่มต้น.กําหนดค่า
  • เพิ่มรหัสการแปลเป็นภาษาท้องถิ่นลงใน Start.Connco
  • เพิ่มรูปแบบมุมมองผู้ใช้ (เวลานี้ไม่มีคีย์ที่ชัดเจนเนื่องจากข้อความเริ่มต้นเป็นหลายภาษา)
  • เพิ่มผู้ใช้สร้างการดําเนินการหน้าจอและมุมมอง (Create.cshtml)

รหัสเริ่มต้น

จากสมมติฐานข้างต้นแต่ละรหัสควรมีลักษณะดังนี้:

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 { }
}

ข้อความการผูกแบบจําลอง

ตั้งค่าชนิดของคุณสมบัติแบบจําลอง int เป็น หรือ DateTime ผูกกับมุมมอง และพยายามลงทะเบียนเมื่อไม่ถูกเติม คุณจะเห็นข้อความคล้ายกับค่า int ข้อความเหล่านี้จะแสดงขึ้นเมื่อไม่สามารถผูกสตริงว่างกับแบบจําลอง ฯลฯ ได้ เนื่องจากไม่สามารถผูกข้อมูลได้ จึงเกิดขึ้นเฉพาะก่อนการตรวจสอบค่าอื่น ๆ และในการประมวลผลฝั่งเซิร์ฟเวอร์เท่านั้น

ข้อความเหล่านี้ DefaultModelBindingMessageProvider ถูกกําหนดเป็น

Startup.cs พารามิเตอร์จะถูกเพิ่มลงในวิธีการที่กําหนดไว้จากจุดเริ่มต้น services.AddControllersWithViews Action แล้วคลิก options ModelBindingMessageProvider สามารถตั้งค่าเป็นหลายภาษาโดยการตั้งค่าพารามิเตอร์

มี 11 ข้อความที่สามารถตั้งค่าได้ดังนั้นโปรด SharedResource.resx กําหนดข้อความเป็น: ชื่อของคีย์เป็นตัวเลือก ข้อความภาษาอังกฤษเริ่มต้นในคอลัมน์ข้อคิดเห็น (ไม่จําเป็นต้องเป็นข้อคิดเห็น) เราจะไม่แสดงรายการการแปลสําหรับแต่ละภาษาดังนั้นโปรดแปลด้วยตัวคุณเอง (รหัสตัวอย่างรวมถึง SharedResource.resx ที่แปลแล้ว)

ชื่อ ค่า ข้อคิดเห็น
ModelBinding_AttemptedValueIsInvalid ใน{1} "{0}" ไม่ถูกต้อง The value '{0}' is not valid for {1}.
ModelBinding_MissingBindRequiredValue ไม่ได้ระบุค่าสําหรับ{0} A value for the '{0}' parameter or property was not provided.
ModelBinding_MissingKeyOrValue จําเป็น A value is required.
ModelBinding_MissingRequestBodyRequiredValue คําขอต้องมีเนื้อความ A non-empty request body is required.
ModelBinding_NonPropertyAttemptedValueIsInvalid {0}ไม่ถูกต้อง The value '{0}' is not valid.
ModelBinding_NonPropertyUnknownValueIsInvalid ค่าไม่ถูกต้อง The supplied value is invalid.
ModelBinding_NonPropertyValueMustBeANumber โปรดระบุตัวเลข The field must be a number.
ModelBinding_UnknownValueIsInvalid ค่า{0}ไม่ถูกต้อง The supplied value is invalid for {0}.
ModelBinding_ValueIsInvalid {0}ไม่ถูกต้อง The value '{0}' is invalid.
ModelBinding_ValueMustBeANumber {0}ต้องระบุตัวเลข The field {0} must be a number.
ModelBinding_ValueMustNotBeNull นี่คือฟิลด์บังคับ The value '{0}' is invalid.

แก้ไข .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 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
      });

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

จุดสําคัญคือการเพิ่มการรับไปยัง services.AddControllersWithViews วิธีการ option Action แล้ว options.ModelBindingMessageProvider คุณเพิ่งตั้งค่าข้อความที่แปลเป็นภาษาท้องถิ่นสําหรับแต่ละวิธีการ Set

ข้อความที่แปลแล้ว IStringLocalizer มาจาก IStringLocalizer รหัสจะขดลวดเล็กน้อยเนื่องจากไม่สามารถรับได้โดยตรง SharedResource.resx ถ้าคุณสร้างรหัส คุณจะได้รับค่าที่แปลเป็นภาษาท้องถิ่นโดยตรงจาก

IStringLocalizer คีย์ที่ระบุใน คือ SharedResource.resx คีย์ที่เพิ่มเข้ามา ตั้งค่าให้ใช้กับเนื้อหาของแต่ละวิธี Set

นอกจากนี้เนื่องจากแต่ละข้อความมี {0}{1} พารามิเตอร์สตริงรูปแบบ string.Format เราใช้ง่ายเพื่อแทนที่ Func ค่าที่ได้รับ

แปลข้อความการตรวจสอบความถูกต้องเริ่มต้น

เมื่อคุณตั้งค่าคุณสมบัติของคุณสมบัติแบบจําลองเป็น Required ฯลฯ StringLength ข้อความแสดงข้อผิดพลาดเริ่มต้นจะถูกกําหนดโดยกรอบงาน และโดยทั่วไปจะเป็นภาษาอังกฤษ

IValidationAttributeAdapterProviderคุณสามารถแปลพวกเขาโดยการกําหนดคลาสของอินเตอร์เฟซที่ได้รับ

SharedResource.resxขั้นแรก คุณได้ลงทะเบียนข้อความที่เข้ากันได้กับหลายภาษาแล้ว ชื่อของคีย์เป็นตัวเลือก แต่เพื่อลดความซับซ้อนของโปรแกรม ให้>ลงทะเบียนในรูปแบบของ "ชื่อคุณสมบัติการตรวจสอบความถูกต้องValidator_<"

ชื่อ ค่า ข้อคิดเห็น
Validator_CompareAttribute {0}ไม่ตรงกับ{1} '{0}' and '{1}' do not match.
Validator_CreditCardAttribute {0}ไม่ใช่หมายเลขบัตรที่ถูกต้อง The {0} field is not a valid credit card number.
Validator_DataTypeAttribute_Date โปรดป้อนวันที่ที่ถูกต้อง Please enter a valid date.
Validator_EmailAddressAttribute {0}ไม่ใช่ที่อยู่อีเมลที่ถูกต้อง The {0} field is not a valid e-mail address.
Validator_FileExtensionsAttribute {0} ยอมรับเฉพาะไฟล์ที่มีส่วนขยายต่อไปนี้: : {1} The {0} field only accepts files with the following extensions: {1}
Validator_MaxLengthAttribute {0} ต้องเป็นสตริงหรือชนิดอาร์เรย์ที่มีความยาวสูงสุด "{1}" The field {0} must be a string or array type with a maximum length of '{1}'.
Validator_MinLengthAttribute {0} ต้องเป็นสตริงหรือชนิดอาร์เรย์ที่มีความยาวต่ําสุดคือ "{1}" The field {0} must be a string or array type with a minimum length of '{1}'.
Validator_PhoneAttribute {0}ไม่ใช่หมายเลขโทรศัพท์ที่ถูกต้อง The {0} field is not a valid phone number.
Validator_RangeAttribute {0}ควรอยู่ระหว่าง{1}และ{2} The field {0} must be between {1} and {2}.
Validator_RegularExpressionAttribute ระบุว่า{0}ตรงกับนิพจน์ "{1}" The field {0} must match the regular expression '{1}'.
Validator_RequiredAttribute {0}เป็นสิ่งจําเป็น The {0} field is required.
Validator_StringLengthAttribute {0}ต้อง{1} The field {0} must be a string with a maximum length of {1}.
Validator_UrlAttribute {0}ไม่ใช่ URL ที่ถูกต้อง The {0} field is not a valid fully-qualified http, https, or ftp URL.
Validator_StringLengthAttributeWithMin {0}ต้องมี{1}อย่างน้อย {2} บิต The field {0} must be a string with a maximum length of {1} and a minimum length of {2}.

ถัดไปคุณจะสร้างคลาสผู้ให้บริการอะแด็ปเตอร์ต่อไปนี้:

ชื่อคลาสเป็นตัวเลือก แต่คราวนี้เราจะสร้าง CustomValidationAttributeAdapterProvider คลาสที่มีชื่อและเขียนโค้ดที่แสดงด้านล่าง ตําแหน่งของแฟ้มรหัสเป็นตัวเลือก แต่ตัวอย่างจะถูกวางไว้ในโฟลเดอร์ที่ชื่ออะแด็ปเตอร์

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);
    }
  }
}

ถ้าคุณสมบัติแบบจําลอง Required ถูกตั้งค่า StringLength เป็น IValidationAttributeAdapterProvider.GetAttributeAdapter วิธีการจะถูกเรียกสําหรับการตรวจสอบความถูกต้องแต่ละครั้ง

ErrorMessageResourceName ถ้าคุณสมบัติมีค่า ให้ส่งคืนเนื่องจากรหัสแบบจําลองได้ตั้งค่าข้อความหรือคีย์การแปลแล้ว

ถ้าชื่อคลาสของคํานําหน้าและการตรวจสอบคุณสมบัติว่างเปล่า ให้ตั้งค่าเป็นคีย์การแปล และดึงข้อมูลข้อความที่แปลเป็นภาษาท้องถิ่นจากคีย์ ErrorMessage และตั้งค่าเป็น ซึ่งช่วยให้ข้อความเริ่มต้นส่วนใหญ่ถูกแปลเป็นภาษาท้องถิ่น

จากนั้นลงทะเบียนคลาสนี้ใน .cs เริ่มต้น" โดยทั่วไปคุณสามารถเพิ่มสิ่งต่อไปนี้:

// 省略

using Microsoft.AspNetCore.Mvc.DataAnnotations;

namespace LocalizationDefaultValidation
{
  public class Startup
  {
    // 省略

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

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

เรียกใช้เพื่อให้แน่ใจว่าทํางานอย่างถูกต้อง

เปลี่ยนข้อความที่มีอยู่ตามพารามิเตอร์

ตัวอย่างเช่น StringLength ข้อความคุณสมบัติถูกแปลเป็นภาษาท้องถิ่นว่า "{0}ต้องอยู่ภายใน{1}บิต" MinimumLength ถ้าคุณตั้งค่าคุณสมบัติ คุณอาจต้องเปลี่ยนบิต{0}{1}{2}บิตหรือมากกว่า"

ในกรณีนี้ CustomValidationAttributeAdapterProvider คลาสถูกขยายเป็น:

// 省略

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);
        }
      }

      // 省略
    }
  }
}

เนื่องจากแต่ละการตรวจสอบความถูกต้อง GetAttributeAdapter เรียกวิธีการ ดังนั้นถ้าตัวแปรที่ attribute ส่งผ่าน StringLengthAttribute เป็นคุณสมบัติ MinimumLength ให้ตรวจสอบคุณสมบัติ ถ้ามีการตั้งค่า คุณจะได้รับข้อความที่พิจารณาจํานวนบิตต่ําสุดและ ErrorMessage แทนที่ด้วย

เป็นเรื่องปกติถ้าคุณเรียกใช้และยืนยันว่าข้อความมีการเปลี่ยนแปลง

ข้อความทั่วไปอื่นๆ

จนถึงขณะนี้บางข้อความยังไม่ได้รับการแปลเป็นภาษาท้องถิ่น ตัวอย่างเช่น เฉพาะข้อความที่กําหนดโดยสคริปต์ Java เท่านั้น

ซึ่งใช้กับ jquery.validate.js ข้อความที่แสดงอยู่ในแฟ้ม

เนื่องจากคุณไม่ควรแก้ไขแฟ้มนี้โดยตรง คุณต้อง input เพิ่มคุณสมบัติลงในแท็ก data-val-XXXX และตั้งค่าข้อความที่แปลเป็นภาษาท้องถิ่นเพื่อแทนที่ข้อความเหล่านี้ ส่วน XXXX มีคีย์ในภาพด้านบน jquery.validate.js ( data-val-number ตัวอย่างเช่น ตัวอย่างเช่น)

data-val-XXXX เมื่อต้องการตั้งค่าข้อความที่แปลเป็นภาษาท้องถิ่นในคุณสมบัติ คุณสามารถตั้งค่าได้เมื่อฝั่งเซิร์ฟเวอร์ส่งกลับ HTML

ตัวอย่างเช่น การแปลข้อความเมื่อมีอักขระที่ไม่ใช่ตัวเลขอยู่ในฟิลด์อินพุต Age UserViewModel.Age Range มีคุณสมบัติและ HTML ที่ส่งออก data-val-range จะแนบคุณสมบัติกับการป้อนข้อมูล

<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>

data-val-numberอย่างไรก็ตาม ข้อความการตรวจสอบตัวเลขไม่ได้ถูกแปลเป็นภาษาท้องถิ่นเนื่องจากไม่มีคุณสมบัติ ดังนั้น เพิ่มคุณสมบัติอื่นลงในแบบจําลอง และเอา data-val-number ท์พุทด้วยคุณสมบัติที่มีข้อความที่แปลเป็นภาษาท้องถิ่น

ขั้นแรก ให้สร้างคลาสคุณสมบัติที่ตรวจสอบว่าเป็นตัวเลข IntAttribute หรือไม่ดังนี้: int ที่นี่คุณสามารถเปลี่ยนแปลงได้ตามสถานการณ์ได้อย่างอิสระ ตําแหน่งของไฟล์รหัสไม่สําคัญ แต่เวลานี้ฉันใส่ไว้ในโฟลเดอร์อะแดปเตอร์

using System.ComponentModel.DataAnnotations;

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

ถ้าค่าที่ตั้งค่า int เป็นชนิด คุณสมบัตินี้เป็นเพียงการตัดสินใจปกติ แต่คลาสเองไม่มีความหมายพิเศษ เนื่องจากคุณสมบัติที่ตั้งค่าเป็นชนิด int จริงเป็นเรื่องปกติ 100% เป้าหมายคือการแสดงข้อความแสดงข้อผิดพลาดที่แปลเป็นภาษาท้องถิ่นบนไคลเอนต์

ถัดไป IntAttributeAdapter คุณจะสร้างคลาสอะแด็ปเตอร์ต่อไปนี้: ฉันยังใส่ไว้ในโฟลเดอร์อะแดปเตอร์

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;
  }
}

จุดสําคัญคือ AddValidation วิธีการที่ฉันเขียนไว้ใน MergeAttribute วิธีการ

ผสานคุณสมบัติเป็นคุณสมบัติที่จะส่งกลับไปยังแท็กอินพุตไปยังไคลเอนต์ data-val-number ตั้งค่าข้อความแสดงข้อผิดพลาดที่แปลเป็นภาษาท้องถิ่นสําหรับค่าที่ตั้งค่าไว้ในคุณสมบัติ

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);
      }

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

ถ้าคุณต้องการตรวจสอบ ค่า IntAttribute ใช่ ให้ส่งคืนค่าที่คุณเพิ่งสร้างขึ้น IntAttributeAdapter ตอนนี้คุณสมบัติที่มีคุณสมบัติ Int จะถูกเพิ่มเมื่อไคลเอนต์แสดง data-val-number

สุดท้าย UserViewModel.Age IntAttribute จะถูกเพิ่ม

หลังจากแก้ไขรหัสแล้ว ให้เรียกใช้เพื่อดูรหัสนั้น

<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>

มีข้อความอื่น ๆ ที่ไม่ได้แปลเป็นภาษาท้องถิ่น แต่สามารถแปลเป็นภาษาท้องถิ่นโดยวิธีการข้างต้น เพิ่มรหัสถ้าจําเป็น