Vícejazyčná podpora výchozích zpráv zobrazených během ověřování vstupu

Datum vytvoření stránky :

životní prostředí

jádro ASP.NET
  • 5,0 MVC

Nejprve

Vícejazyčná podpora výchozích zpráv o ověření vstupu s těmito tipy nemusí být úplná. Prosím, vyřešte ty chybějící.

Změna jazyka v závislosti na stavu relace nebo stavu mezipaměti se také může stále zobrazovat v textu z předchozího jazyka.

Existuje několik způsobů, jak změnit výchozí zprávu o ověření vstupu, z nichž každý lze kombinovat. Není to jeden z nich, takže pokud jej chcete spolehlivě změnit, musíte odpovídat všem vzorům.

předpoklad

Tento Tips je napsán jako pochopení následujících tipů:

Pokud vytváříte nový projekt, musíte také přidat následující soubory a kód na základě výše uvedených tipů.

  • Vytvořte soubor SharedResource.resx (+en, es). (Vzhledem k tomu, že je přeloženo pouze poselství těchto tipů, může být obsah prázdný.)
  • Vytvoření souboru SharedResource.cs
  • Přidání lokalizačního kódu do Startup.ConfigureServices
  • Přidání lokalizačního kódu do Startup.Configure
  • Přidán UserViewModel (tentokrát explicitně nezadáváme výchozí zprávy ve více jazycích)
  • Přidány akce a zobrazení obrazovky vytvořené uživatelem (Create.cshtml)

Startovní kód

Na základě výše uvedených předpokladů musí být každý kód následující:

Spuštění.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,
  }
}
Sdílený zdroj.cs
namespace LocalizationDefaultValidation
{
  // クラス名は作成した .resx のファイル名と同じにする必要がある
  public class SharedResource { }
}

Zprávy o vazbě modelu

Pokud nastavíte typ vlastnosti modelu int na nebo a svážete jej se DateTime zobrazením a pokusíte se jej zaregistrovat bez typu, zobrazí se zpráva jako "Hodnota '' je neplatná.". Tyto zprávy se zobrazí, když int nelze vázat prázdný řetězec, například v modelu. Vzhledem k tomu, že se jedná o časování, které nelze svázat, dochází k němu pouze před jiným ověřením hodnoty a zpracováním na straně serveru.

Tyto zprávy jsou DefaultModelBindingMessageProvider definovány jako .

Startup.cs Přidejte k argumentům metody, které byly definovány od začátku services.AddControllersWithViews v , a pak přidejte Action . Vícejazyčnost můžete projít nastavením předaná v options ModelBindingMessageProvider argumentu.

Existuje 11 typů zpráv, které můžete nastavit, takže je nutné nejprve definovat zprávu následujícím způsobem: SharedResource.resx Název klíče je volitelný. Výchozí anglická zpráva ve sloupci komentářů je (komentáře nemusí být zahrnuty). Nebudete muset překládat každý jazyk, takže byste měli udělat svůj vlastní překlad (ukázkový kód také obsahuje přeložený SharedResource.resx).

Komentář k hodnotě názvu
ModelBinding_AttemptedValueIsInvalid "{0}" je neplatná hodnota v {1}. Hodnota "{0}" není pro {1} platná.
ModelBinding_MissingBindRequiredValue Hodnota {0} není zadána. Hodnota parametru nebo vlastnosti "{0}" nebyla zadána.
ModelBinding_MissingKeyOrValue Požadovaný. Je vyžadována hodnota.
ModelBinding_MissingRequestBodyRequiredValue Žádost musí mít tělo. Je vyžadován neprázdný text požadavku.
ModelBinding_NonPropertyAttemptedValueIsInvalid "{0}" není platný. Hodnota "{0}" není platná.
ModelBinding_NonPropertyUnknownValueIsInvalid Hodnota není platná. Zadaná hodnota je neplatná.
ModelBinding_NonPropertyValueMustBeANumber Musí být zadáno číslo. Pole musí být číslo.
ModelBinding_UnknownValueIsInvalid Hodnota {0} není platná. Zadaná hodnota je pro {0} neplatná.
ModelBinding_ValueIsInvalid "{0}" není platný. Hodnota "{0}" je neplatná.
ModelBinding_ValueMustBeANumber {0} musí být číslo. Pole {0} musí být číslo.
ModelBinding_ValueMustNotBeNull Požadovaný vstup. Hodnota "{0}" je neplatná.

Opravte spuštění.cs následujícím způsobem:

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

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

Klíčovým bodem je services.AddControllersWithViews přidání a k metodě, která přijímá option Action , options.ModelBindingMessageProvider Nastavuji lokalizovaný text pro každou metodu Set v .

Přeložený text je IStringLocalizer načten z . Neexistuje žádný způsob, jak získat přímý IStringLocalizer kód, což je trochu kulatý kód. SharedResource.resx Pokud generujete kód z, získáte lokalizované hodnoty přímo z něj.

IStringLocalizer Klíč, který zadáte v SharedResource.resx části, určuje klíč, který jste přidali do rozhraní . Nastavte ji tak, aby odpovídala obsahu každé metody Set.

Také proto, že každá zpráva má argument formátovacího řetězce, například , {0}{1} používám Simple s, abyste mohli nahradit přijaté string.Format Func hodnoty.

Lokalizace výchozích ověřovacích zpráv

Výchozí chybová zpráva, když jsou atributy Required vlastností modelu nastaveny na nebo tak, je určena rozhraním a je StringLength v podstatě v angličtině.

Ty mohou být IValidationAttributeAdapterProvider lokalizovány definováním třídy odvozené z rozhraní.

Nejprve SharedResource.resx zaregistrujte text, který je vícejazyčný. Název klíče je libovolný, ale pro zjednodušení programu jej zaregistrujte ve formě "Validator_< název ověřovacího atributu>".

Komentář k hodnotě názvu
Validator_CompareAttribute {0} a {1} se neshodují. "{0}" a "{1}" se neshodují.
Validator_CreditCardAttribute {0} není platné číslo karty. Pole {0} není platné číslo platební karty.
Validator_DataTypeAttribute_Date Zadejte platné datum. Zadejte platné datum.
Validator_EmailAddressAttribute {0} není platná e-mailová adresa. Pole {0} není platná e-mailová adresa.
Validator_FileExtensionsAttribute {0} přijímá pouze soubory s následujícími příponami: : {1} Pole {0} přijímá pouze soubory s následujícími příponami: {1}
Validator_MaxLengthAttribute {0} musí být řetězec nebo typ pole s maximální délkou "{1}". Pole {0} musí být řetězec nebo typ pole s maximální délkou "{1}".
Validator_MinLengthAttribute {0} musí být řetězec nebo typ pole s minimální délkou "{1}". Pole {0} musí být řetězec nebo typ pole s minimální délkou "{1}".
Validator_PhoneAttribute {0} není platné telefonní číslo. Pole {0} není platné telefonní číslo.
Validator_RangeAttribute {0} se musí pohybovat od {1} do {2}. Pole {0} musí být mezi {1} a {2}.
Validator_RegularExpressionAttribute {0} se musí shodovat s regulárním výrazem "{1}". Pole {0} se musí shodovat s regulárním výrazem "{1}".
Validator_RequiredAttribute {0} je vyžadováno. Pole {0} je povinné.
Validator_StringLengthAttribute {0} musí být v {1} číslicích. Pole {0} musí být řetězec s maximální délkou {1}.
Validator_UrlAttribute {0} není platná adresa URL. Pole {0} není platná úplná adresa URL protokolu HTTP, https nebo ftp.
Validator_StringLengthAttributeWithMin {0} musí být alespoň {2} {1} číslic. Pole {0} musí být řetězec s maximální délkou {1} a minimální délkou {2}.

Dále vytvořte následující třídu AdapterProvider:

Název třídy je libovolný, ale tentokrát CustomValidationAttributeAdapterProvider napíšu třídu nazvanou a napíšu kód následujícím způsobem: Umístění souboru kódu je libovolné, ale ukázka je umístěna do složky s názvem 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);
    }
  }
}

Pokud nastavíte vlastnosti modelu na Required nebo , metoda je volána pro každé StringLength IValidationAttributeAdapterProvider.GetAttributeAdapter ověření.

ErrorMessageResourceName Pokud vlastnost obsahuje hodnotu, kód na straně modelu již má zprávu nebo lokalizační klíč, takže se vrátí tak, jak je.

Pokud je prázdná, porovnejte názvy tříd prefixu a ověřovacího atributu, aby se z nich stalo lokalizační klíč, a pak načtěte lokalizovaný text z klíče a nastavte jej ErrorMessage na . To umožňuje lokalizaci většiny výchozích zpráv.

Dále zaregistrujte tuto třídu v .cs spouštění. V podstatě jej můžete přidat následujícím způsobem.

// 省略

using Microsoft.AspNetCore.Mvc.DataAnnotations;

namespace LocalizationDefaultValidation
{
  public class Startup
  {
    // 省略

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

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

Spusťte jej, abyste se ujistili, že funguje správně.

Změna existující zprávy podle parametrů

Například StringLength zpráva atributu je lokalizována, například "{0} musí být v {1} číslic". MinimumLength Pokud je vlastnost nastavena, můžete změnit {0} jako "Zadejte {2} číslice nebo více {1} v rámci počtu číslic".

V takovém CustomValidationAttributeAdapterProvider případě rozšiřte třídu 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);
        }
      }

      // 省略
    }
  }
}

Vzhledem k tomu, že metoda je volána pro každé GetAttributeAdapter ověření, pokud byla předaná attribute StringLengthAttribute proměnná MinimumLength atributem, zkontrolujte vlastnost, Pokud je nastaveno, získejte zprávu, která také bere v úvahu minimální počet číslic a ErrorMessage nahradí .

Pokud ji spustíte, zkontrolujete a zpráva se změní, je to v pořádku.

Další obecné zprávy

Některé zprávy nejsou lokalizovány poté, co jste tak učinili. Například zprávy, které jsou určeny pouze v jazyce Javascript.

Cílem je jquery.validate.js zpráva uvedená v souboru.

Vzhledem k tomu, že tento soubor by neměl být upravován přímo, chcete-li tyto zprávy nahradit lokalizovaným input textem, musíte do značky přidat atribut like a nastavit data-val-XXXX lokalizovaný text. Část XXXX obsahuje klíč na obrázku jquery.validate.js výše. (např. data-val-number atd.)

data-val-XXXX Chcete-li nastavit lokalizovaný text na atributy v

Pojďme lokalizovat zprávu, když jsou nečíselné znaky zahrnuty do vstupního pole Věk jako příklad. UserViewModel.AgeRange atribut a výstupní HTML připojí data-val-range vstupní atribut.

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

Zpráva o číselné kontrole však data-val-number není lokalizována, protože neexistuje žádný atribut. Na straně modelu přidáte další atribut, který vytiskne také lokalizovaný atribut přidaný data-val-number ke zprávě.

Prvním krokem je vytvoření třídy atributu, která kontroluje, zda IntAttribute se jedná o číslo: Je omezena na zde, ale můžete int ji změnit v závislosti na situaci. Soubor kódu může být kdekoli, ale tentokrát je ve složce Adaptér.

using System.ComponentModel.DataAnnotations;

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

Pokud je nastavená hodnota int typ, jedná se o atribut, který je pouze normálním verdiktem, ale v praxi je 100% normální, pokud je nastaven na vlastnost typu int, takže zpracování této třídy samo o sobě nedává žádný smysl. Účelem tohoto času je zobrazení lokalizovaných chybových zpráv v klientovi.

Dále vytvořte následující IntAttributeAdapter třídu adaptéru: To je také ve složce 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;
  }
}

Jde o AddValidation metodu popsanou v MergeAttribute metodě.

Sloučení atributů jako atributů vstupní značky pro návrat data-val-number do klienta. Hodnota, která se má nastavit v atributu, nastaví lokalizovanou chybovou zprávu.

Vraťte tento adaptér ve třídě, kterou jste CustomValidationAttributeAdapterProvider vytvořili dříve.

// 省略

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

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

Pokud je hodnota, IntAttribute kterou chcete ověřit, , vraťte hodnotu, kterou jste IntAttributeAdapter vytvořili dříve. Vlastnost s atributem Int je nyní přidána při zobrazení na data-val-number straně klienta.

Nakonec UserViewModel.Age IntAttribute přidejte do .

Jakmile kód opravíte, vyzkoušejte ho, abyste zjistili, zda ho chcete zkontrolovat.

<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í i jiné nelokalizované zprávy, ale můžete je lokalizovat výše popsanými metodami. Přidejte kód, když ho potřebujete.