Kelių lingvistinis numatytųjų pranešimų, rodomų tikrinant įvestį, palaikymas

Puslapio sukūrimo data :

Aplinkos

ASP.NET branduolys
  • 5,0 MVC

Iš pradžių

Daugiakalbis numatytųjų įvesties tikrinimo pranešimų palaikymas naudojant šiuos patarimus gali būti nebaigtas. Prašome susidoroti su dingusiais.

Be to, kalbos keitimas atsižvelgiant į seanso būseną arba talpyklos būseną vis tiek gali būti rodomas ankstesnės kalbos tekste.

Yra keletas būdų, kaip pakeisti numatytąjį įvesties tikrinimo pranešimą, kurių kiekvieną galima sujungti. Tai nėra vienas iš jų, todėl, jei norite jį patikimai pakeisti, turite atitikti visus modelius.

Prielaida

Šie patarimai yra parašyta kaip suprasti šiuos patarimus:

Be to, jei kuriate naują projektą, turite pridėti šiuos failus ir kodą pagal anksčiau pateiktus patarimus.

  • Sukurkite SharedResource.resx (+en, es) failą. (Kadangi išverstas tik šių patarimų pranešimas, turinys gali būti tuščias.)
  • SharedResource.cs failo kūrimas
  • Įtraukti lokalizacijos kodą į Startup.ConfigureServices
  • Įtraukti lokalizacijos kodą į Startup.Configure
  • Pridėta UserViewModel (šį kartą mes nėra aiškiai raktas numatytuosius pranešimus keliomis kalbomis)
  • Pridėti vartotojo sukurti ekrano veiksmai ir rodiniai (Create.cshtml)

Pradinis kodas

Remiantis pirmiau pateiktomis prielaidomis, kiekvienas kodas yra toks:

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

Modelio susiejimo pranešimai

Jei nustatysite modelio ypatybės tipą int arba susiesite jį su DateTime rodiniu ir bandysite jį užregistruoti neįrašytą, pamatysite pranešimą "Reikšmė '' neleistina." Šie pranešimai rodomi, kai tuščios eilutės int negalima susieti, pvz., modelyje. Kadangi tai yra laikas, kurio negalima susieti, jis įvyksta tik prieš kitų reikšmių tikrinimą ir serverio apdorojimą.

Šie pranešimai DefaultModelBindingMessageProvider apibrėžiami kaip .

Startup.cs Įtraukite į metodo argumentus, kurie buvo apibrėžti nuo pradžios services.AddControllersWithViews , tada pridėkite Action . Galite būti daugiakalbiai nustatydami options ModelBindingMessageProvider argumente perduotas.

Yra 11 tipų pranešimų, kuriuos galite nustatyti, todėl pirmiausia turite apibrėžti pranešimą taip: SharedResource.resx Rakto pavadinimas yra pasirinktinis. Numatytasis angliškas pranešimas komentarų stulpelyje yra (komentarų įtraukti nereikia). Jums nereikės versti kiekvienos kalbos, todėl turėtumėte atlikti savo vertimą (kodo pavyzdyje taip pat yra išverstas SharedResource.resx).

Pavadinimo reikšmės komentaras
ModelBinding_AttemptedValueIsInvalid "{0}" yra neleistina {1} reikšmė. Reikšmė "{0}" netinkama {1}.
ModelBinding_MissingBindRequiredValue Nenurodyta {0} reikšmė. Nepateikta parametro "{0}" arba ypatybės reikšmė.
ModelBinding_MissingKeyOrValue Reikalingas. Būtina reikšmė.
ModelBinding_MissingRequestBodyRequiredValue Prašyme turi būti kūnas. Reikalingas neištuštėjantis užklausos tekstas.
ModelBinding_NonPropertyAttemptedValueIsInvalid "{0}" negalioja. Reikšmė "{0}" neleistina.
ModelBinding_NonPropertyUnknownValueIsInvalid Reikšmė neleistina. Pateikta reikšmė neleistina.
ModelBinding_NonPropertyValueMustBeANumber Turi būti nurodytas numeris. Laukas turi būti skaičius.
ModelBinding_UnknownValueIsInvalid {0} reikšmė neleistina. Pateikta {0} reikšmė neleistina.
ModelBinding_ValueIsInvalid "{0}" negalioja. Reikšmė "{0}" neleistina.
ModelBinding_ValueMustBeANumber {0} turi būti skaičius. Lauko {0} turi būti skaičius.
ModelBinding_ValueMustNotBeNull Būtina įvestis. Reikšmė "{0}" neleistina.

Pataisykite paleisties.cs taip:

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

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

Svarbiausia yra services.AddControllersWithViews įtraukti a į metodą, kuris gauna option Action , options.ModelBindingMessageProvider Kiekvienam nustatymo metodui nustatau lokalizuotą tekstą programoje .

Išverstas tekstas IStringLocalizer nuskaitomas iš . Nėra jokio būdo gauti tiesioginį IStringLocalizer kodą, kuris yra šiek tiek apvalaus kodo. SharedResource.resx Jei generuojate kodą, lokalizuotas reikšmes gausite tiesiai iš jo.

IStringLocalizer Raktas, kurį nurodote SharedResource.resx dalyje, nurodo raktą, kurį įtraukėte į . Nustatykite, kad jis atitiktų kiekvieno nustatymo metodo turinį.

Be to, kadangi kiekvienas pranešimas turi formato eilutės argumentą, pvz., , aš {0}{1} naudoju paprasta, kad galėtumėte pakeisti gautas string.Format Func reikšmes.

Lokalizuoti numatytuosius tikrinimo pranešimus

Numatytasis klaidos pranešimas, kai modelio ypatybių atributai Required yra nustatyti arba taip, nustatomas pagal sistemą ir iš esmės yra StringLength anglų kalba.

Juos galima IValidationAttributeAdapterProvider lokalizuoti apibrėžiant klasę, gautą iš sąsajos.

Visų pirma SharedResource.resx užregistruokite daugiakalbį tekstą. Rakto pavadinimas yra savavališkas, tačiau, siekiant supaprastinti programą, užregistruokite jį "Validator_< patvirtinimo atributo pavadinimą>" forma.

Pavadinimo reikšmės komentaras
Validator_CompareAttribute {0} ir {1} nesutampa. "{0}" ir "{1}" nesutampa.
Validator_CreditCardAttribute {0} nėra galiojantis kortelės numeris. Laukas {0} nėra tinkamas kredito kortelės numeris.
Validator_DataTypeAttribute_Date Įveskite tinkamą datą. Įveskite tinkamą datą.
Validator_EmailAddressAttribute {0} nėra galiojantis el. pašto adresas. Laukas {0} nėra leistinas el. pašto adresas.
Validator_FileExtensionsAttribute {0} priima tik failus su šiais plėtiniais: : {1} Lauke {0} priimami tik failai su šiais plėtiniais: {1}
Validator_MaxLengthAttribute {0} turi būti eilutės arba masyvo tipas, kurio maksimalus ilgis yra "{1}". Lauko {0} turi būti eilutės arba masyvo tipas, kurio maksimalus ilgis yra "{1}".
Validator_MinLengthAttribute {0} turi būti eilutės arba masyvo tipas, kurio mažiausias ilgis yra "{1}". Lauko {0} turi būti eilutės arba masyvo tipas, kurio mažiausias ilgis yra "{1}".
Validator_PhoneAttribute {0} nėra galiojantis telefono numeris. Laukas {0} nėra tinkamas telefono numeris.
Validator_RangeAttribute {0} turi svyruoti nuo {1} iki {2}. Lauko {0} turi būti nuo {1} iki {2}.
Validator_RegularExpressionAttribute {0} turi atitikti įprastą posakį "{1}". Lauko {0} turi atitikti įprastą išraišką "{1}".
Validator_RequiredAttribute {0} reikia. Laukas {0} yra būtinas.
Validator_StringLengthAttribute {0} turi būti {1} skaitmenų ribose. Lauko {0} turi būti eilutė, kurios maksimalus ilgis turi būti {1}.
Validator_UrlAttribute {0} nėra leistinas URL. Laukas {0} nėra tinkamas visiškai kvalifikuotas http, https arba ftp URL.
Validator_StringLengthAttributeWithMin {0} turi būti bent {2} {1} skaitmenų. Lauko {0} turi būti eilutė, kurios maksimalus ilgis yra {1} ir mažiausias ilgis {2}.

Tada sukurkite šią AdapterProvider klasę:

Klasės pavadinimas yra savavališkas, bet šį kartą CustomValidationAttributeAdapterProvider aš parašyti klasės vadinamas ir parašyti kodą taip: Kodo failo vieta yra savavališka, tačiau pavyzdys dedamas į aplanką, pavadintą Adapteris.

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

Jei modelio ypatybes nustatysite Required kaip arba , metodas bus iškviestas kiekvienam StringLength IValidationAttributeAdapterProvider.GetAttributeAdapter tikinimui.

ErrorMessageResourceName Jei ypatybėje yra reikšmė, modelio pusėje yra pranešimas arba lokalizavimo raktas, todėl jis grįžta toks, koks yra.

Jei tuščia, sutapkite su priešdėlio ir tikrinimo atributo klasių pavadinimais, kad jis taptų lokalizavimo raktu, tada nuskaitykite lokalizuotą tekstą iš rakto ir nustatykite jį ErrorMessage kaip . Tai leidžia lokalizuoti daugumą numatytųjų pranešimų.

Tada užregistruokite šią klasę paleisties .cs. Iš esmės galite jį pridėti taip.

// 省略

using Microsoft.AspNetCore.Mvc.DataAnnotations;

namespace LocalizationDefaultValidation
{
  public class Startup
  {
    // 省略

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

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

Paleiskite jį, kad įsitikintumėte, jog jis veikia tinkamai.

Esamo pranešimo keitimas pagal parametrus

Pavyzdžiui, StringLength atributo pranešimas yra lokalizuotas, pvz., "{0} turi būti {1} skaitmenyse". MinimumLength Jei ypatybė nustatyta, galbūt norėsite pakeisti {0} kaip "Nurodyti {2} skaitmenis ar daugiau {1} skaitmenų skaičiuje".

Tokiu CustomValidationAttributeAdapterProvider atveju išplėskite klasę taip:

// 省略

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

      // 省略
    }
  }
}

Kadangi metodas iškviečiamas kiekvienam GetAttributeAdapter patvirtinimui, jei perduotas kintamasis attribute buvo StringLengthAttribute MinimumLength atributas, patikrinkite ypatybę, Jei nustatyta, gaukite pranešimą, kuriame taip pat atsižvelgiama į minimalų skaitmenų skaičių ir ErrorMessage pakeičiamas .

Jei jį paleisite, patikrinkite ir pranešimas pasikeis, viskas gerai.

Kiti bendrieji pranešimai

Kai kurie pranešimai nėra lokalizuoti po to, kai tai darote iki šiol. Pavyzdžiui, pranešimai, kurie nustatomi tik Javascript.

Tikslas yra jquery.validate.js faile pateiktas pranešimas.

Kadangi šio failo negalima redaguoti tiesiogiai, norėdami pakeisti šiuos pranešimus lokalizuotu tekstu, input turite pridėti atributą, pvz., žymę, ir ten nustatyti data-val-XXXX lokalizuotą tekstą. XXXX dalyje yra aukščiau pateikto paveikslo jquery.validate.js raktas. (pvz., data-val-number ir t. t.)

data-val-XXXX Norint nustatyti lokalizuotą tekstą į atributus

Lokalizuokime pranešimą, kai ne skaitiniai simboliai įtraukiami į lauką Amžiaus įvestis kaip pavyzdys. UserViewModel.Age turi Range atributą, o data-val-range išvesties HTML pridedamas įvesties atributas.

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

Tačiau data-val-number skaitinio tikrinimo pranešimas nėra lokalizuotas, nes nėra atributo. Modelio pusėje įtrauksite kitą atributą, kad išspausdintumėte lokalizuotą prie pranešimo pridėtą data-val-number atributą.

Pirmas žingsnis yra sukurti atributų klasę, kuri tikrina, ar tai yra IntAttribute skaičius: Jis apsiriboja čia, bet jūs galite int jį pakeisti priklausomai nuo situacijos. Kodo failas gali būti bet kur, bet šį kartą jis yra aplanke Adapteris.

using System.ComponentModel.DataAnnotations;

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

Jei nustatyta reikšmė yra int tipas, tai yra atributas, kuris yra tik normalus verdiktas, tačiau praktiškai jis yra 100% normalus, jei nustatytas į int tipo savybę, todėl pačios šios klasės apdorojimas neturi jokios prasmės. Šio laiko tikslas yra rodyti lokalizuotus klaidų pranešimus kliento.

Tada sukurkite šią adapterio IntAttributeAdapter klasę: Tai taip pat yra aplanke Adapteris.

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

Čia yra AddValidation metodas, aprašytas MergeAttribute metode.

Suliekite atributus kaip įvesties žymės atributus, kad data-val-number grįžtumėte į klientą. Atribute nustatyta reikšmė nustato lokalizuotą klaidos pranešimą.

Grąžinkite šį adapterį klasėje, kurią sukūrėte CustomValidationAttributeAdapterProvider anksčiau.

// 省略

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

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

Jei tikrintina reikšmė IntAttribute yra , grąžinkite anksčiau sukurtą IntAttributeAdapter reikšmę. Ypatybė su atributu Int dabar įtraukiama, kai ji rodoma kliento data-val-number pusėje.

Galiausiai UserViewModel.Age IntAttribute pridėkite prie .

Kai nustatote kodą, pabandykite jį patikrinti, ar norite jį patikrinti.

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

Yra ir kitų ne lokalizuotų pranešimų, tačiau galite juos lokalizuoti pirmiau aprašytais metodais. Pridėkite kodą, kai jo reikia.