Többnyelvű támogatás a beviteli ellenőrzés során megjelenő alapértelmezett üzenetekhez

Oldal létrehozásának dátuma :

környezet

ASP.NET mag
  • 5,0 MVC

Először

Előfordulhat, hogy az alapértelmezett bevitel-érvényesítési üzenetek többnyelvű támogatása ezzel a tippel nem teljes. Kérem, foglalkozzon a hiányzókkal.

A nyelv munkamenet állapotától vagy gyorsítótár állapotától függően is megjelenhet az előző nyelv szövegében.

Az alapértelmezett bevitel-érvényesítési üzenet többféleképpen módosítható, amelyek mindegyike kombinálható. Ez nem tartozik közéjük, így ha megbízhatóan szeretné megváltoztatni, meg kell felelnie az összes mintának.

előtétel

Ez a tipp a következő tippek megértéseként íródott:

Továbbá, ha új projektet hoz létre, a fenti tippek alapján hozzá kell adnia a következő fájlokat és kódot.

  • Hozzon létre egy SharedResource.resx (+en, es) fájlt. (Mivel csak ennek a tippnek az üzenete van lefordítva, a tartalom üres lehet.)
  • SharedResource.cs fájl létrehozása
  • Honosítási kód hozzáadása a Startup.ConfigureServices szolgáltatáshoz
  • Honosítási kód hozzáadása a Startup.Configure
  • Hozzáadva UserViewModel (ezúttal nem kulcsodjuk meg explicit módon az alapértelmezett üzeneteket több nyelven)
  • Felhasználó által létrehozott képernyőműveletek és nézetek hozzáadása (Create.cshtml)

Kezdő kód

A fenti feltételezések alapján minden kód a következő:

Indítás.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 { }
}

Kötési üzenetek modellezése

Ha a modelltulajdonság típusát a int nézethez állítja, vagy a DateTime nézethez köti, és nem beírva próbálja regisztrálni, egy olyan üzenet jelenik meg, mint "Az "Érték érvénytelen". Ezek az üzenetek akkor jelennek meg, ha egy üres karakterlánc int nem kötődhet például a modellhez. Mivel ez egy olyan időzítés, amelyet nem lehet kötni, csak más értékérvényesítés és kiszolgálóoldali feldolgozás előtt fordul elő.

Ezek az üzenetek a DefaultModelBindingMessageProvider következőképpen vannak definiálva: .

Startup.cs Adja hozzá a metódusnak a kezdet óta definiált argumentumait, services.AddControllersWithViews majd adja hozzá a Action . Többnyelvű lehet az argumentumban az átadott options ModelBindingMessageProvider beállítással.

11 típusú üzenetet állíthat be, ezért először az alábbiak szerint kell definiálnia az üzenetet: SharedResource.resx A kulcs neve nem kötelező. A megjegyzések oszlopban az alapértelmezett angol üzenet (a megjegyzéseket nem kell belefoglalni). Nem kell minden nyelvet lefordítania, ezért saját fordítást kell végeznie (a mintakód tartalmazza a lefordított SharedResource.resx-et is).

Névérték megjegyzés
ModelBinding_AttemptedValueIsInvalid A "{0}" érvénytelen érték a {1}. A "{0}" érték nem érvényes {1}.
ModelBinding_MissingBindRequiredValue A {0} értéke nincs megadva. A "{0}" paraméter vagy tulajdonság értékét nem adták meg.
ModelBinding_MissingKeyOrValue Szükséges. Értékre van szükség.
ModelBinding_MissingRequestBodyRequiredValue A kérésnek rendelkeznie kell egy testtel. Nem üres kérelem törzsére van szükség.
ModelBinding_NonPropertyAttemptedValueIsInvalid A "{0}" érvénytelen. A "{0}" érték érvénytelen.
ModelBinding_NonPropertyUnknownValueIsInvalid Az érték érvénytelen. A megadott érték érvénytelen.
ModelBinding_NonPropertyValueMustBeANumber Meg kell adni a számot. A mezőnek számnak kell lennie.
ModelBinding_UnknownValueIsInvalid A {0} értéke érvénytelen. A megadott érték érvénytelen {0}.
ModelBinding_ValueIsInvalid A "{0}" érvénytelen. A "{0}" érték érvénytelen.
ModelBinding_ValueMustBeANumber {0} kell lennie egy számnak. A mező {0} számnak kell lennie.
ModelBinding_ValueMustNotBeNull Szükséges bemenet. A "{0}" érték érvénytelen.

Az indítás javítása.cs az alábbiak szerint:

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

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

A legfontosabb pont az, services.AddControllersWithViews hogy adjunk hozzá egy a metódushoz, amely megkapja a option Action , options.ModelBindingMessageProvider Lokalizált szöveget állítok be a .

A lefordított szöveg IStringLocalizer a következőből lesz beolvasva: . Nincs mód közvetlen IStringLocalizer kód fogadására, ami egy kicsit kerek kód. SharedResource.resx Ha kód generál, akkor közvetlenül ebből kap lokalizált értékeket.

IStringLocalizer A megadott kulcs SharedResource.resx a kulcshoz hozzáadott kulcsot adja meg. Állítsa be úgy, hogy megfeleljen az egyes Set metódusok tartalmának.

Továbbá, mivel minden üzenetnek van egy formátum karakterlánc argumentuma, például a {0}{1} Simple használatával, amellyel lecserélheti a fogadott string.Format Func értékeket.

Az alapértelmezett érvényesítési üzenetek lokalizálása

Az alapértelmezett hibaüzenetet, amikor a modell tulajdonságainak attribútumait Required úgy állítja be, a keretrendszer határozza meg, és StringLength alapvetően angol nyelven van.

Ezek IValidationAttributeAdapterProvider lokalizálhatók az interfészből származtatott osztály meghatározásával.

Először is SharedResource.resx regisztrálja a többnyelvű szöveget. A kulcs neve tetszőleges, de a program egyszerűsítése érdekében regisztrálja azt "Validator_< érvényesítési attribútum neve>" formájában.

Névérték megjegyzés
Validator_CompareAttribute A {0} és a {1} nem egyeznek. A "{0}" és a "{1}" nem egyezik.
Validator_CreditCardAttribute {0} nem érvényes kártyaszám. A {0} mező nem érvényes hitelkártyaszám.
Validator_DataTypeAttribute_Date Adjon meg egy érvényes dátumot. Adjon meg érvényes dátumot.
Validator_EmailAddressAttribute {0} nem érvényes e-mail cím. A {0} mező nem érvényes e-mail cím.
Validator_FileExtensionsAttribute {0} csak a következő kiterjesztésű fájlokat fogadja el: : {1} A {0} mező csak a következő kiterjesztésű fájlokat fogadja el: {1}
Validator_MaxLengthAttribute {0} karakterláncnak vagy tömbtípusnak kell lennie, amelynek maximális hossza "{1}". A mező {0} karakterláncnak vagy tömbtípusnak kell lennie, amelynek maximális hossza "{1}".
Validator_MinLengthAttribute {0} karakterláncnak vagy tömbtípusnak kell lennie, amelynek minimális hossza "{1}". A mező {0} karakterláncnak vagy tömbtípusnak kell lennie, amelynek minimális hossza "{1}".
Validator_PhoneAttribute {0} nem érvényes telefonszám. A {0} mező nem érvényes telefonszám.
Validator_RangeAttribute {0} {1}-től {2} terjednie kell. A mező {0} {1} és {2} között kell lennie.
Validator_RegularExpressionAttribute {0} meg kell egyeznie a "{1}" reguláris kifejezéssel. A mezőnek {0} meg kell egyeznie a "{1}" reguláris kifejezéssel.
Validator_RequiredAttribute {0} van szükség. A {0} mező szükséges.
Validator_StringLengthAttribute {0} {1} számjegyen belül kell lennie. A mező {0} legfeljebb {1} hosszúságú karakterláncnak kell lennie.
Validator_UrlAttribute {0} nem érvényes URL-cím. A {0} mező nem érvényes, teljesen minősített http,https vagy ftp URL- cím.
Validator_StringLengthAttributeWithMin {0} legalább {2} {1} számjegynek kell lennie. A mező {0} legfeljebb {1} hosszúságú és {2} hosszúságú karakterláncnak kell lennie.

Ezután hozza létre a következő AdapterProvider osztályt:

Az osztály neve tetszőleges, de ezúttal CustomValidationAttributeAdapterProvider írok egy osztályt hívott, és írja a kódot a következőképpen: A kódfájl helye tetszőleges, de a minta egy Adapter nevű mappába kerül.

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

Ha a modell tulajdonságait Required vagy , a metódust minden StringLength IValidationAttributeAdapterProvider.GetAttributeAdapter érvényesítéshez meg kell hívni.

ErrorMessageResourceName Ha a tulajdonság értéket tartalmaz, a modelloldalon lévő kód már rendelkezik üzenettel vagy lokalizációs kulccsal, így úgy ad vissza, ahogy van.

Ha üres, illené meg az előtag és az érvényesítési attribútum osztályneveit, hogy lokalizációs kulcs legyen, majd olvassa be a honosított szöveget a kulcsból, és állítsa be ErrorMessage a . Ez lehetővé teszi az alapértelmezett üzenetek nagy részének lokalizációját.

Ezután regisztrálja ezt az osztályt az indítási .cs. Alapvetően a következőképpen adhatod hozzá.

// 省略

using Microsoft.AspNetCore.Mvc.DataAnnotations;

namespace LocalizationDefaultValidation
{
  public class Startup
  {
    // 省略

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

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

Futtasd le, hogy megbizonyosodj róla, hogy megfelelően működik.

Meglévő üzenet módosítása paraméterek szerint

Például StringLength az attribútumüzenet lokalizált, például "{0} {1} számjegyen belül kell lennie". MinimumLength Ha a tulajdonság be van állítva, érdemes lehet módosítani a {0} "Adjon meg {2} számjegyeket vagy több {1} több számjegyen belül".

Ebben CustomValidationAttributeAdapterProvider az esetben az osztályt a következőképpen kell kiterjeszteni:

// 省略

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

      // 省略
    }
  }
}

Mivel a metódus minden egyes érvényesítéshez meg van GetAttributeAdapter hívva, ha az átadott változó attribútum attribute StringLengthAttribute MinimumLength volt, ellenőrizze a tulajdonságot, Ha be van állítva, kap egy üzenetet, amely figyelembe veszi a számjegyek minimális számát is, és ErrorMessage lecseréli a .

Ha futtatja, ellenőrizze, és az üzenet megváltozik, rendben van.

Egyéb általános üzenetek

Egyes üzenetek nem lesznek lokalizálva, miután eddig megtette. Például olyan üzenetek, amelyek csak Javascriptben vannak meghatározva.

A cél jquery.validate.js a fájlban felsorolt üzenet.

Mivel ezt a fájlt nem szabad közvetlenül szerkeszteni, az üzenetek lokalizált szövegre való cseréjéhez input hozzá kell adnia egy attribútumot, mint a címkéhez, és ott kell beállítania a data-val-XXXX honosított szöveget. Az XXXX rész a fenti ábrán szereplő kulcsot jquery.validate.js tartalmazza. (pl. data-val-number stb.)

data-val-XXXX Ha a honosított szöveget

Lokalizáljuk az üzenetet, ha például nem numerikus karakterek szerepelnek az Age beviteli mezőben. UserViewModel.Age Range attribútummal rendelkezik, és a kimeneti HTML data-val-range hozzáfűzi a bemeneti attribútumot.

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

A numerikus ellenőrző üzenet azonban data-val-number nincs honosítva, mert nincs attribútum. A honosított üzenet hozzáadásához egy másik attribútumot is hozzáadhat a data-val-number modelloldalhoz.

Az első lépés egy attribútumosztály létrehozása, amely ellenőrzi, hogy IntAttribute szám-e: Ez itt csak korlátozott, de int a helyzettől függően szabadon megváltoztathatja. A kódfájl bárhol lehet, de ezúttal az Adapter mappában van.

using System.ComponentModel.DataAnnotations;

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

Ha a beállított érték int egy típus, akkor ez egy olyan attribútum, amely csak egy normál ítélet, de a gyakorlatban 100% -ban normális, ha egy int típusú tulajdonságra van állítva, így ennek az osztálynak a feldolgozása önmagában nincs értelme. Ennek az időnek az a célja, hogy lokalizált hibaüzeneteket jelenítsen meg az ügyfélben.

Ezután hozza létre a következő Adapter IntAttributeAdapter osztályt: Ez az Adapter mappában is található.

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

A lényeg itt AddValidation a módszerben leírt MergeAttribute módszer.

Az attribútumok összevonása a bemeneti címke attribútumaként, hogy visszatérjen data-val-number az ügyfélhez. Az attribútumban beállítandó érték honosított hibaüzenetet állít be.

Adja vissza ezt az adaptert a korábban létrehozott CustomValidationAttributeAdapterProvider osztályba.

// 省略

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

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

Ha az érvényesíteni kívánt érték IntAttribute a , adja vissza a korábban létrehozott IntAttributeAdapter értéket. Az Int attribútummal rendelkező tulajdonság most hozzáadódik, amikor megjelenik az data-val-number ügyféloldalon.

Végül UserViewModel.Age IntAttribute adja hozzá a .

Miután megjavította a kódot, próbálja ki, hogy megnézze, szeretné-e megnézni.

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

Vannak más nem lokalizált üzenetek is, de a fent leírt módszerekkel lokalizálhatja őket. Adjon hozzá kódot, amikor szüksége van rá.