Suport multilingv pentru mesajele implicite afișate în timpul validării intrărilor

Data creării paginii :

mediu

ASP.NET Core
  • 5.0 MVC

La început

Este posibil ca suportul multilingv al mesajelor implicite de validare a intrărilor cu aceste sfaturi să nu fie complet. Vă rugăm să se ocupe de cele lipsă.

De asemenea, modificarea limbii în funcție de starea sesiunii sau de starea cache-ului poate apărea în continuare în text din limba anterioară.

Există mai multe modalități de a modifica mesajul implicit de validare a intrărilor, fiecare dintre acestea putând fi combinată. Nu este una dintre ele, deci dacă doriți să o schimbați în mod fiabil, trebuie să corespondați cu toate modelele.

premisă

Acest sfat este scris ca înțelegerea următoarele sfaturi:

De asemenea, dacă creați un proiect nou, trebuie să fi adăugat următoarele fișiere și cod pe baza sfaturilor de mai sus.

  • Creați un fișier SharedResource.resx (+en, es). (Deoarece numai mesajul acestui Sfat este tradus, conținutul poate fi gol.)
  • Crearea unui fișier SharedResource.cs
  • Adăugați codul de localizare la Startup.ConfigureServices
  • Adăugați codul de localizare la Startup.Configure
  • Adăugat UserViewModel (de data aceasta, nu cheie în mod explicit mesajele implicite în mai multe limbi)
  • Adăugat acțiuni și vizualizări de ecran create de utilizator (Create.cshtml)

Codul de pornire

Pe baza ipotezelor de mai sus, fiecare cod este următorul:

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

Mesaje de legare a modelelor

Dacă setați tipul de proprietate model int la sau și îl legați la vizualizare și încercați să îl DateTime înregistrați netipănit, veți vedea un mesaj de genul "Valoarea", nu este validă". Aceste mesaje apar atunci când un șir gol int nu poate fi legat, de exemplu, în model. Deoarece este o sincronizare care nu poate fi legată, se produce numai înainte de alte validare valoare și server-side procesare.

Aceste mesaje sunt DefaultModelBindingMessageProvider definite ca .

Startup.cs Adăugați la argumentele metodei care au fost definite de la început services.AddControllersWithViews în , apoi adăugați Action . Puteți fi multilingv prin stabilirea a trecut în options ModelBindingMessageProvider argumentul.

Există 11 tipuri de mesaje pe care le puteți seta, deci trebuie mai întâi să definiți mesajul după cum urmează: SharedResource.resx Numele cheii este opțional. Mesajul implicit în limba engleză din coloana de comentarii este (comentariile nu trebuie să fie incluse). Nu va trebui să traduceți fiecare limbă, așa că ar trebui să faceți propria traducere (codul eșantion include, de asemenea, sharedResource.resx tradus).

Nume Valoare Comentariu
ModelBinding_AttemptedValueIsInvalid "{0}" este o valoare nevalidă în {1}. Valoarea "{0}" nu este valabilă pentru {1}.
ModelBinding_MissingBindRequiredValue Valoarea pentru {0} nu este specificată. Nu a fost furnizată o valoare pentru parametrul sau proprietatea "{0}".
ModelBinding_MissingKeyOrValue Necesar. Este necesară o valoare.
ModelBinding_MissingRequestBodyRequiredValue Cererea trebuie să aibă un organism. Este necesar un organism de solicitare care nu este gol.
ModelBinding_NonPropertyAttemptedValueIsInvalid "{0}" nu este valabil. Valoarea "{0}" nu este validă.
ModelBinding_NonPropertyUnknownValueIsInvalid Valoarea nu este validă. Valoarea furnizată nu este validă.
ModelBinding_NonPropertyValueMustBeANumber Trebuie specificat numărul. Câmpul trebuie să fie un număr.
ModelBinding_UnknownValueIsInvalid Valoarea {0} nu este validă. Valoarea furnizată nu este validă pentru {0}.
ModelBinding_ValueIsInvalid "{0}" nu este valabil. Valoarea "{0}" nu este validă.
ModelBinding_ValueMustBeANumber {0} trebuie să fie un număr. Câmpul {0} trebuie să fie un număr.
ModelBinding_ValueMustNotBeNull Intrare necesară. Valoarea "{0}" nu este validă.

Remediați pornirea.cs după cum urmează:

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

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

Punctul cheie este services.AddControllersWithViews de a adăuga o la metoda care primește option Action , options.ModelBindingMessageProvider Sunt setarea de text localizat pentru fiecare metodă Set în .

Textul tradus este IStringLocalizer preluat din . Nu există nici o modalitate de a primi IStringLocalizer cod direct, care este un pic de un cod rotund. SharedResource.resx Dacă generați cod din acesta, veți obține valori localizate direct de la acesta.

IStringLocalizer Cheia pe care o specificați în SharedResource.resx specifică cheia la care ați adăugat . Setați-l să se potrivească cu conținutul fiecărei metode Set.

De asemenea, deoarece fiecare mesaj are un argument șir de format, cum ar fi , {0}{1} folosesc Simplu cu, astfel încât să puteți înlocui string.Format Func valorile primite.

Localizarea mesajelor de validare implicite

Mesajul de eroare implicit atunci când atributele Required proprietăților unui model sunt setate la sau cam asa ceva este determinată de cadru și este StringLength practic în limba engleză.

Acestea pot fi IValidationAttributeAdapterProvider localizate prin definirea unei clase derivate din interfață.

În primul SharedResource.resx rând, înregistrați textul multilingv. Numele cheii este arbitrar, dar pentru a simplifica programul, înregistrați-l sub formă de "Validator_< nume atribut validare>".

Nume Valoare Comentariu
Validator_CompareAttribute {0} și {1} nu se potrivesc. "{0}" și "{1}" nu se potrivesc.
Validator_CreditCardAttribute {0} nu este un număr valid de card. Câmpul {0} nu este un număr valid al cardului de credit.
Validator_DataTypeAttribute_Date Introduceți o dată validă. Vă rugăm să introduceți o dată validă.
Validator_EmailAddressAttribute {0} nu este o adresă de e-mail validă. Câmpul {0} nu este o adresă de e-mail validă.
Validator_FileExtensionsAttribute {0} acceptă numai fișiere cu următoarele extensii: : {1} Câmpul {0} acceptă numai fișiere cu următoarele extensii: {1}
Validator_MaxLengthAttribute {0} trebuie să fie un șir sau un tip de matrice cu o lungime maximă de "{1}". Câmpul {0} trebuie să fie un șir sau un tip de matrice cu o lungime maximă de "{1}".
Validator_MinLengthAttribute {0} trebuie să fie un tip de șir sau matrice cu o lungime minimă de "{1}". Câmpul {0} trebuie să fie un șir sau un tip de matrice cu o lungime minimă de "{1}".
Validator_PhoneAttribute {0} nu este un număr de telefon valid. Câmpul {0} nu este un număr de telefon valid.
Validator_RangeAttribute {0} trebuie să variați de la {1} la {2}. câmpul {0} trebuie să fie între {1} și {2}.
Validator_RegularExpressionAttribute {0} trebuie să corespundă expresiei regulate "{1}". Câmpul {0} trebuie să corespundă expresiei regulate "{1}".
Validator_RequiredAttribute {0} este necesară. Câmpul {0} este necesar.
Validator_StringLengthAttribute {0} trebuie să se încadre în {1} cifre. Câmpul {0} trebuie să fie un șir cu o lungime maximă de {1}.
Validator_UrlAttribute {0} nu este o adresă URL validă. Câmpul {0} nu este un URL valid complet calificat http, https sau ftp.
Validator_StringLengthAttributeWithMin {0} trebuie să fie de cel puțin {2} {1} cifre. Câmpul {0} trebuie să fie un șir cu o lungime maximă de {1} și o lungime minimă de {2}.

Apoi, creați următoarea clasă AdapterProvider:

Numele clasei este arbitrar, dar de data aceasta CustomValidationAttributeAdapterProvider voi scrie o clasă numită și voi scrie codul după cum urmează: Locația fișierului de cod este arbitrară, dar eșantionul este plasat într-un folder numit Adaptor.

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

Dacă setați proprietățile modelului la Required sau , metoda este apelată pentru fiecare StringLength IValidationAttributeAdapterProvider.GetAttributeAdapter validare.

ErrorMessageResourceName Dacă proprietatea conține o valoare, codul din partea modelului are deja un mesaj sau o cheie de localizare, deci se întoarce așa cum este.

Dacă este gol, potriviți numele clasei prefixului și atributului de validare pentru a-l face cheia de localizare, apoi regăsiți textul localizat din cheie și setați-l ErrorMessage la . Acest lucru permite localizarea majorității mesajelor implicite.

Apoi, înregistrați această clasă în .cs de pornire. Practic, îl puteți adăuga după cum urmează.

// 省略

using Microsoft.AspNetCore.Mvc.DataAnnotations;

namespace LocalizationDefaultValidation
{
  public class Startup
  {
    // 省略

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

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

Rulați-l pentru a vă asigura că funcționează corect.

Modificarea unui mesaj existent după parametri

De exemplu, StringLength mesajul atribut este localizat, cum ar fi "{0} trebuie să fie în termen de {1} cifre." MinimumLength Dacă proprietatea este setată, poate doriți să modificați {0} ca "Specificați {2} cifre sau mai multe {1} într-un număr de cifre".

În CustomValidationAttributeAdapterProvider acest caz, extindeți clasa după cum urmează:

// 省略

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

      // 省略
    }
  }
}

Deoarece metoda este apelată pentru fiecare GetAttributeAdapter validare, dacă variabila attribute transmisă a fost StringLengthAttribute un MinimumLength atribut, verificați proprietatea, Dacă este setat, primiți un mesaj care ia în considerare și numărul minim de cifre și ErrorMessage înlocuiește .

Dacă îl rulați, verificați și mesajul se schimbă, este în regulă.

Alte mesaje generice

Unele mesaje nu sunt localizate după ce faceți acest lucru până acum. De exemplu, mesajele care sunt determinate numai în Javascript.

Ținta este jquery.validate.js mesajul listat în fișier.

Deoarece acest fișier nu trebuie editat direct, pentru a înlocui aceste mesaje cu text localizat, input trebuie să adăugați un atribut ca la etichetă și să setați data-val-XXXX textul localizat acolo. Partea XXXX conține cheia din figura de jquery.validate.js mai sus. (de data-val-number exemplu, etc.)

data-val-XXXX Pentru a seta textul localizat la atributele din

Să localizăm mesajul atunci când caracterele non-numerice sunt incluse în câmpul de intrare Vârstă ca exemplu. UserViewModel.Age are Range un atribut, iar HTML-ul de ieșire data-val-range adaugă atributul de intrare.

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

Cu toate acestea, data-val-number mesajul de verificare numerică nu este localizat, deoarece nu există nici un atribut. Veți adăuga un alt atribut pe partea de model pentru a imprima și atributul localizat adăugat la data-val-number mesaj.

Primul pas este să creați o clasă de atribute care verifică dacă este IntAttribute un număr: Este limitat la aici, dar sunteți int liberi să-l schimbați în funcție de situație. Fișierul de cod poate fi oriunde, dar de data aceasta este în folderul Adaptor.

using System.ComponentModel.DataAnnotations;

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

Dacă valoarea setată este int un tip, este un atribut care este doar un verdict normal, dar în practică este 100% normal dacă este setat la o proprietate de tip int, astfel încât prelucrarea acestei clase în sine nu are niciun sens. Scopul acestui timp este de a afișa mesaje de eroare localizate în client.

Apoi, creați următoarea IntAttributeAdapter clasă adaptor: Acest lucru este, de asemenea, în folderul Adaptor.

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

Punctul de aici este AddValidation metoda descrisă în MergeAttribute metodă.

Îmbinarea atributelor ca atribute ale etichetei de intrare pentru a reveni data-val-number la client. Valoarea de setat în atribut setează un mesaj de eroare localizat.

Returnați acest adaptor în clasa pe care ați creat-o CustomValidationAttributeAdapterProvider anterior.

// 省略

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

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

Dacă valoarea de validat IntAttribute este , returnați valoarea pe care ați creat-o IntAttributeAdapter mai devreme. Proprietatea cu atributul Int este acum adăugat atunci când este afișat pe data-val-number partea client.

În cele din urmă, UserViewModel.Age IntAttribute adăugați la .

După ce ați remediat codul, încercați-l pentru a vedea dacă doriți să îl verificați.

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

Există și alte mesaje nelocalizate, dar le puteți localiza în metodele descrise mai sus. Adăugați cod atunci când aveți nevoie.