Viacjazyčná podpora predvolených správ zobrazených počas overovania vstupu

Dátum vytvorenia strany :

životné prostredie

ASP.NET jadro
  • 5,0 MVC

Najskôr

Viacjazyčná podpora predvolených správ na overenie vstupu s týmtoi tipmi nemusí byť úplná. Prosím, vysporiadajte sa s chýbajúcimi.

Zmena jazyka v závislosti od stavu relácie alebo stavu vyrovnávacej pamäte sa môže stále zobrazovať v texte z predchádzajúceho jazyka.

Existuje niekoľko spôsobov, ako zmeniť predvolenú správu o overení vstupu, z ktorých každý je možné kombinovať. Nie je to jeden z nich, takže ak ho chcete spoľahlivo zmeniť, musíte zodpovedať všetkým vzorom.

predpoklad

Tieto tipy sú napísané ako pochopenie nasledujúcich tipov:

Ak tiež vytvárate nový projekt, musíte pridať nasledujúce súbory a kód na základe vyššie uvedených tipov.

  • Vytvorte súbor SharedResource.resx (+en, es). (Keďže sa prekladá iba správa týchto tipov, obsah môže byť prázdny.)
  • Vytvorenie súboru SharedResource.cs
  • Pridanie lokalizačného kódu do služby Startup.ConfigureServices
  • Pridanie lokalizačného kódu do programu Startup.Configure
  • Pridaný UserViewModel (tentoraz explicitne neodsunievame predvolené správy vo viacerých jazykoch)
  • Pridané akcie a zobrazenia obrazovky vytvorené používateľom (Create.cshtml)

Počiatočný kód

Na základe uvedených predpokladov je každý kód takýto:

Štart.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 { }
}

Vzorové väzbové správy

Ak nastavíte typ vlastnosti modelu int alebo ho naviažete na DateTime zobrazenie a pokúsite sa ho zaregistrovať netypovo, zobrazí sa hlásenie s nápisom "Hodnota "" je neplatná. Tieto správy sa zobrazia, keď prázdny reťazec int nie je viazaný napríklad v modeli. Keďže ide o načasovanie, ktoré nie je možné viazať, vyskytuje sa len pred iným overovaním hodnoty a spracovaním na strane servera.

Tieto správy sú DefaultModelBindingMessageProvider definované ako .

Startup.cs Pridajte k argumentom metódy , ktorá bola definovaná od začiatku services.AddControllersWithViews v , a potom pridajte Action . Nastavením odovzdaného argumentu môžete byť options ModelBindingMessageProvider viacjazyční.

Existuje 11 typov správ, ktoré môžete nastaviť, takže musíte najprv definovať správu takto: SharedResource.resx Názov kľúča je voliteľný. Predvolená anglická správa v stĺpci komentárov je (komentáre nemusia byť zahrnuté). Nebudete musieť prekladať každý jazyk, takže by ste mali urobiť svoj vlastný preklad (vzorový kód obsahuje aj preklad SharedResource.resx).

Komentár k hodnote názvu
ModelBinding_AttemptedValueIsInvalid "{0}" je neplatná hodnota v {1}. Hodnota "{0}" nie je platná pre {1}.
ModelBinding_MissingBindRequiredValue Hodnota pre {0} nie je zadaná. Hodnota parametra alebo vlastnosti "{0}" nebola poskytnutá.
ModelBinding_MissingKeyOrValue Požadovaný. Vyžaduje sa hodnota.
ModelBinding_MissingRequestBodyRequiredValue Žiadosť musí mať telo. Vyžaduje sa ne vyprázdnenie tela žiadosti.
ModelBinding_NonPropertyAttemptedValueIsInvalid "{0}" nie je platné. Hodnota "{0}" nie je platná.
ModelBinding_NonPropertyUnknownValueIsInvalid Hodnota nie je platná. Zadaná hodnota je neplatná.
ModelBinding_NonPropertyValueMustBeANumber Číslo musí byť špecifikované. Pole musí byť číslo.
ModelBinding_UnknownValueIsInvalid Hodnota {0} nie je platná. Zadaná hodnota je pre {0} neplatná.
ModelBinding_ValueIsInvalid "{0}" nie je platné. Hodnota "{0}" je neplatná.
ModelBinding_ValueMustBeANumber {0} musí byť číslo. Pole {0} musí byť číslo.
ModelBinding_ValueMustNotBeNull Požadovaný vstup. Hodnota "{0}" je neplatná.

Opravte spustenie.cs takto:

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

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

Kľúčovým bodom je services.AddControllersWithViews pridať a do metódy, ktorá prijíma option Action , options.ModelBindingMessageProvider Nastavujem lokalizovaný text pre každú metódu množiny v .

Preložený text IStringLocalizer sa načíta z . Neexistuje žiadny spôsob, ako získať priamy IStringLocalizer kód, čo je trochu okrúhly kód. SharedResource.resx Ak generujete kód z neho, získate lokalizované hodnoty priamo z neho.

IStringLocalizer Kľúč, ktorý zadáte, SharedResource.resx určuje kľúč, ku ktorému ste pridali . Nastavte ho tak, aby zodpovedal obsahu každej metódy množiny.

Tiež preto, že každá správa má argument reťazca formátu, napríklad , {0}{1} používam jednoduché s, aby ste mohli nahradiť prijaté string.Format Func hodnoty.

Lokalizácia predvolených overovacích správ

Predvolené chybové hlásenie, keď sú atribúty Required vlastností modelu nastavené alebo sú nastavené tak, je určené rámcom a je v podstate v StringLength angličtine.

Tieto môžu byť IValidationAttributeAdapterProvider lokalizované definovaním triedy odvodenej z rozhrania.

V prvom rade SharedResource.resx zaregistrujte text, ktorý je viacjazyčný. Názov kľúča je ľubovoľný, ale na zjednodušenie programu ho zaregistrujte vo forme "Validator_< názov atribútu overenia>".

Komentár k hodnote názvu
Validator_CompareAttribute {0} a {1} sa nezhodujú. "{0}" a "{1}" sa nezhodujú.
Validator_CreditCardAttribute {0} nie je platné číslo karty. Pole {0} nie je platné číslo kreditnej karty.
Validator_DataTypeAttribute_Date Zadajte platný dátum. Zadajte platný dátum.
Validator_EmailAddressAttribute {0} nie je platná e-mailová adresa. Pole {0} nie je platnou e-mailovou adresou.
Validator_FileExtensionsAttribute {0} prijíma iba súbory s nasledujúcimi príponami: : {1} Pole {0} prijíma iba súbory s nasledujúcimi príponami: {1}
Validator_MaxLengthAttribute {0} musí byť typ reťazca alebo poľa s maximálnou dĺžkou "{1}". Pole {0} musí byť reťazec alebo typ poľa s maximálnou dĺžkou "{1}".
Validator_MinLengthAttribute {0} musí byť typ reťazca alebo poľa s minimálnou dĺžkou "{1}". Pole {0} musí byť reťazec alebo typ poľa s minimálnou dĺžkou "{1}".
Validator_PhoneAttribute {0} nie je platné telefónne číslo. Pole {0} nie je platné telefónne číslo.
Validator_RangeAttribute {0} sa musia pohybovať od {1} do {2}. Pole {0} musí byť medzi {1} a {2}.
Validator_RegularExpressionAttribute {0} sa musí zhodovať s regulárnym výrazom "{1}". Pole {0} musí zodpovedať regulárnemu výrazu "{1}".
Validator_RequiredAttribute {0} je potrebná. Vyžaduje sa pole {0}.
Validator_StringLengthAttribute {0} musí byť v {1} čísliciach. Pole {0} musí byť reťazec s maximálnou dĺžkou {1}.
Validator_UrlAttribute {0} nie je platná adresa URL. Pole {0} nie je platnou plne kvalifikovanou adresou HTTP, https alebo FTP.
Validator_StringLengthAttributeWithMin {0} musí byť aspoň {2} {1} číslic. Pole {0} musí byť reťazec s maximálnou dĺžkou {1} a minimálnou dĺžkou {2}.

Ďalej vytvorte nasledujúcu triedu AdapterProvider:

Názov triedy je ľubovoľný, ale tentoraz CustomValidationAttributeAdapterProvider napíšem triedu s názvom a napíšem kód takto: Umiestnenie súboru kódu je ľubovoľné, ale vzorka sa umiestni do priečinka s názvom Adaptér.

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

Ak nastavíte vlastnosti modelu na Required alebo , metóda sa požaduje pre každé StringLength IValidationAttributeAdapterProvider.GetAttributeAdapter overenie.

ErrorMessageResourceName Ak vlastnosť obsahuje hodnotu, kód na strane modelu už obsahuje správu alebo lokalizačný kľúč, takže sa vráti tak, ako je.

Ak je prázdny, priraďte názvy tried atribútu predpony a overenia, aby sa z neho urobil lokalizačný kľúč, a potom načítať lokalizovaný text z kľúča a nastaviť ho ErrorMessage na . To umožňuje lokalizáciu väčšiny predvolených správ.

Ďalej zaregistrujte túto triedu v startupovej .cs. V podstate ho môžete pridať nasledovne.

// 省略

using Microsoft.AspNetCore.Mvc.DataAnnotations;

namespace LocalizationDefaultValidation
{
  public class Startup
  {
    // 省略

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

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

Spustite ho, aby ste sa uistili, že funguje správne.

Zmena existujúcej správy podľa parametrov

Správa atribútu je napríklad StringLength lokalizovaná, napríklad "{0} musí byť v {1} číslicach". MinimumLength Ak je vlastnosť nastavená, možno budete chcieť zmeniť {0} ako "Zadajte {2} číslic alebo viac {1} v rámci viacerých číslic".

V takom CustomValidationAttributeAdapterProvider prípade rozšírte triedu takto:

// 省略

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

      // 省略
    }
  }
}

Keďže metóda je privolaná pre každé GetAttributeAdapter overenie, ak odovzdaná premenná attribute bola StringLengthAttribute MinimumLength atribútom, skontrolujte vlastnosť, Ak je nastavené, zobrazí sa hlásenie, ktoré zohľadňuje aj minimálny počet číslic a ErrorMessage nahrádza .

Ak ho spustíte, skontrolujte a správa sa zmení, je to v poriadku.

Ďalšie všeobecné správy

Niektoré správy nie sú lokalizované po tom, čo tak urobíte. Napríklad správy, ktoré sú určené iba v jazyku JavaScript.

Cieľom je jquery.validate.js správa uvedená v súbore.

Keďže tento súbor by sa nemal upravovať priamo, nahradiť tieto správy lokalizovaným input textom, musíte pridať atribút, ako je značka a nastaviť data-val-XXXX lokalizovaný text tam. Časť XXXX obsahuje kľúč na obrázku jquery.validate.js vyššie. (napr. data-val-number atď.)

data-val-XXXX Ak chcete nastaviť lokalizovaný text na atribúty v

Lokalizovajme správu, keď sú napríklad do vstupného poľa Vek zahrnuté nečíselné znaky. UserViewModel.AgeRange atribút a výstupný HTML data-val-range prikladá vstupný atribút.

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

data-val-numberNumerická kontrolná správa však nie je lokalizovaná, pretože neexistuje žiadny atribút. Na strane modelu pridáte ďalší atribút a vytlačíte aj lokalizovaný atribút pridaný data-val-number správou.

Prvým krokom je vytvorenie triedy atribútov, ktorá skontroluje, či ide IntAttribute o číslo: Je to obmedzené na tu, ale môžete int ho zmeniť v závislosti od situácie. Súbor kódu môže byť kdekoľvek, ale tentoraz je v priečinku Adaptér.

using System.ComponentModel.DataAnnotations;

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

Ak je nastavená hodnota int typom, je to atribút, ktorý je len normálnym verdiktom, ale v praxi je 100% normálny, ak je nastavený na vlastnosť typu int, takže samotné spracovanie tejto triedy nedáva zmysel. Účelom tejto doby je zobraziť lokalizované chybové hlásenia v klientovi.

Ďalej vytvorte nasledujúcu IntAttributeAdapter triedu adaptéra: Nachádza sa aj v priečinku Adaptér.

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

Tu je AddValidation metóda opísaná v MergeAttribute metóde.

Zlúčenie atribútov ako atribútov vstupnej značky na návrat data-val-number k klientovi. Hodnota, ktorú sa má nastaviť v atribúte, nastavuje lokalizované chybové hlásenie.

Vráťte tento adaptér do triedy, ktorú ste vytvorili CustomValidationAttributeAdapterProvider predtým.

// 省略

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

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

Ak je hodnota, ktorú chcete IntAttribute overiť, vrátená hodnota, ktorú ste vytvorili IntAttributeAdapter predtým. Vlastnosť s atribútom Int sa teraz pridá, keď sa zobrazí na data-val-number strane klienta.

Nakoniec UserViewModel.Age IntAttribute pridajte k .

Akonáhle ste opravili kód, vyskúšajte ho, aby ste zistili, či ho chcete skontrolovať.

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

Existujú aj iné neau lokalizované správy, ale môžete ich lokalizovať v metódach popísaných vyššie. Pridajte kód, keď ho potrebujete.