Meertalige ondersteuning voor standaardberichten die worden weergegeven tijdens invoervalidatie

Aanmaakdatum van pagina :

milieu

ASP.NET kern
  • 5,0 MVC

Eerst

De meertalige ondersteuning van de standaardinvoervalidatieberichten met deze tips is mogelijk niet volledig. Ga alsjeblieft om met de ontbrekende.

Ook kan het wijzigen van de taal afhankelijk van de sessiestatus of cachestatus nog steeds worden weergegeven in tekst uit de vorige taal.

Er zijn verschillende manieren om het standaardinvoervalidatiebericht te wijzigen, die elk kunnen worden gecombineerd. Het is er niet een van, dus als je het betrouwbaar wilt veranderen, moet je met alle patronen corresponderen.

premisse

Deze tips zijn geschreven als begrip van de volgende tips:

Als u een nieuw project maakt, moet u ook de volgende bestanden en code hebben toegevoegd op basis van bovenstaande tips.

  • Maak een SharedResource.resx-bestand (+en, es). (Aangezien alleen het bericht van deze tips wordt vertaald, kan de inhoud leeg zijn.)
  • Een SharedResource.cs bestand maken
  • Lokalisatiecode toevoegen aan Startup.ConfigureServices
  • Lokalisatiecode toevoegen aan Startup.Configure
  • UserViewModel toegevoegd (deze keer toetsen we de standaardberichten niet expliciet in meerdere talen)
  • Door de gebruiker gemaakte schermacties en weergaven toegevoegd (Create.cshtml)

Startcode

Op basis van de bovenstaande aannames ziet elke code er als volgt uit:

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

Modelbindingsberichten

Als u het type modeleigenschap instelt int op of en het aan de weergave bindt en probeert het niet DateTime getypt te registreren, ziet u een bericht als '' De waarde '' is ongeldig'. Deze berichten worden weergegeven wanneer een lege tekenreeks int niet kan worden gekoppeld aan bijvoorbeeld het model. Omdat het een timing is die niet kan worden gebonden, vindt deze alleen plaats vóór andere waardevalidatie en server-side verwerking.

Deze berichten worden DefaultModelBindingMessageProvider gedefinieerd als .

Startup.cs Voeg toe aan de argumenten van de methode die sinds het begin zijn gedefinieerd services.AddControllersWithViews in , en voeg vervolgens toe Action . U kunt meertalig zijn door het geslaagde in het argument in te options ModelBindingMessageProvider stellen.

Er zijn 11 soorten berichten die u kunt instellen, dus u moet het bericht eerst als volgt definiëren: SharedResource.resx De naam van de sleutel is optioneel. Het standaard Engelse bericht in de kolom Opmerkingen is (opmerkingen hoeven niet te worden opgenomen). U hoeft niet elke taal te vertalen, dus u moet uw eigen vertaling maken (de voorbeeldcode bevat ook de vertaalde SharedResource.resx).

Opmerking over naamwaarde
ModelBinding_AttemptedValueIsInvalid '{0}' is een ongeldige waarde in de {1}. De waarde '{0}' is niet geldig voor {1}.
ModelBinding_MissingBindRequiredValue De waarde voor de {0} is niet opgegeven. Er is geen waarde voor de parameter of eigenschap '{0}' opgegeven.
ModelBinding_MissingKeyOrValue Vereist. Er is een waarde vereist.
ModelBinding_MissingRequestBodyRequiredValue Het verzoek moet een lichaam hebben. Een niet-lege aanvraaginstantie is vereist.
ModelBinding_NonPropertyAttemptedValueIsInvalid '{0}' is niet geldig. De waarde '{0}' is niet geldig.
ModelBinding_NonPropertyUnknownValueIsInvalid De waarde is niet geldig. De opgegeven waarde is ongeldig.
ModelBinding_NonPropertyValueMustBeANumber Het nummer moet worden opgegeven. Het veld moet een getal zijn.
ModelBinding_UnknownValueIsInvalid De waarde van de {0} is niet geldig. De opgegeven waarde is ongeldig voor {0}.
ModelBinding_ValueIsInvalid '{0}' is niet geldig. De waarde '{0}' is ongeldig.
ModelBinding_ValueMustBeANumber {0} moet een nummer zijn. Het veld {0} moet een getal zijn.
ModelBinding_ValueMustNotBeNull Vereiste invoer. De waarde '{0}' is ongeldig.

Los het opstarten.cs als volgt op:

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

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

Het belangrijkste punt is services.AddControllersWithViews om een toe te voegen aan de methode die de option Action , options.ModelBindingMessageProvider Ik stel gelokaliseerde tekst in voor elke methode Set in .

De vertaalde tekst is IStringLocalizer opgehaald uit . Er is geen manier om directe code te IStringLocalizer ontvangen, wat een beetje een ronde code is. SharedResource.resx Als u code genereert, krijgt u er rechtstreeks gelokaliseerde waarden van.

IStringLocalizer De sleutel die u opgeeft, SharedResource.resx geeft de sleutel op die u hebt toegevoegd aan . Stel deze in op de inhoud van elke setmethode.

Ook omdat elk bericht een tekenreeksargument heeft, zoals , gebruik ik {0}{1} Eenvoudig met zodat u de ontvangen waarden kunt string.Format Func vervangen.

Standaardvalidatieberichten lokaliseren

Het standaardfoutbericht wanneer de kenmerken van de eigenschappen van Required een model zijn ingesteld op of zo, wordt bepaald door het framework en is in principe in het StringLength Engels.

Deze kunnen worden IValidationAttributeAdapterProvider gelokaliseerd door een klasse te definiëren die is afgeleid van de interface.

Registreer SharedResource.resx allereerst de tekst die meertalig is. De naam van de sleutel is willekeurig, maar om het programma te vereenvoudigen, registreert u het in de vorm van "Validator_< validatie attribuutnaam>".

Opmerking over naamwaarde
Validator_CompareAttribute De {0} en {1} komen niet overeen. '{0}' en '{1}' komen niet overeen.
Validator_CreditCardAttribute {0} is geen geldig kaartnummer. Het veld {0} is geen geldig creditcardnummer.
Validator_DataTypeAttribute_Date Voer een geldige datum in. Voer een geldige datum in.
Validator_EmailAddressAttribute {0} is geen geldig e-mailadres. Het veld {0} is geen geldig e-mailadres.
Validator_FileExtensionsAttribute {0} accepteert alleen bestanden met de volgende extensies: : {1} Het veld {0} accepteert alleen bestanden met de volgende extensies: {1}
Validator_MaxLengthAttribute {0} moet een tekenreeks of arraytype zijn met een maximale lengte van '{1}'. Het veld {0} moet een tekenreeks of arraytype zijn met een maximale lengte van '{1}'.
Validator_MinLengthAttribute {0} moet een tekenreeks of arraytype zijn met een minimale lengte van '{1}'. Het veld {0} moet een tekenreeks of arraytype zijn met een minimale lengte van '{1}'.
Validator_PhoneAttribute {0} is geen geldig telefoonnummer. Het veld {0} is geen geldig telefoonnummer.
Validator_RangeAttribute {0} moet variëren van {1} tot {2}. Het veld {0} moet tussen {1} en {2} liggen.
Validator_RegularExpressionAttribute {0} moet overeenkomen met de reguliere expressie '{1}'. Het veld {0} moet overeenkomen met de reguliere expressie '{1}'.
Validator_RequiredAttribute {0} is vereist. Het {0} veld is verplicht.
Validator_StringLengthAttribute {0} moet binnen {1} cijfers zijn. Het veld {0} moet een tekenreeks zijn met een maximale lengte van {1}.
Validator_UrlAttribute {0} is geen geldige URL. Het veld {0} is geen geldige volledig gekwalificeerde http-, https- of ftp-URL.
Validator_StringLengthAttributeWithMin {0} moeten ten minste {2} {1} cijfers zijn. Het veld {0} moet een tekenreeks zijn met een maximale lengte van {1} en een minimale lengte van {2}.

Maak vervolgens de volgende klasse AdapterProvider:

De naam van de klasse is willekeurig, maar deze keer CustomValidationAttributeAdapterProvider schrijf ik een klasse met de naam en schrijf ik de code als volgt: De locatie van het codebestand is willekeurig, maar het voorbeeld wordt in een map met de naam Adapter geplaatst.

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

Als u de eigenschappen van het model instelt Required op of , wordt de methode voor elke validatie aangeroepen. StringLength IValidationAttributeAdapterProvider.GetAttributeAdapter

ErrorMessageResourceName Als de eigenschap een waarde bevat, heeft de code aan de modelzijde al een bericht- of lokalisatiesleutel, dus deze wordt geretourneerd zoals deze is.

Als deze leeg is, komt u overeen met de klassenamen van het voorvoegsel en het validatiekenmerk om er de lokalisatiesleutel van te maken en haalt u vervolgens de gelokaliseerde tekst op uit de sleutel en stelt u deze in ErrorMessage op . Hierdoor kunnen de meeste standaardberichten worden gelokaliseerd.

Registreer deze klasse vervolgens in opstart- .cs. Kortom, u kunt het als volgt toevoegen.

// 省略

using Microsoft.AspNetCore.Mvc.DataAnnotations;

namespace LocalizationDefaultValidation
{
  public class Startup
  {
    // 省略

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

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

Voer het uit om er zeker van te zijn dat het goed werkt.

Een bestaand bericht wijzigen op parameters

Het kenmerkbericht is bijvoorbeeld StringLength gelokaliseerd, zoals '{0} moet zich binnen {1} cijfers bevindt'. MinimumLength Als de eigenschap is ingesteld, kunt u de {0} wijzigen in 'Geef {2} cijfers of meer {1} op binnen een aantal cijfers'.

Breid in dat CustomValidationAttributeAdapterProvider geval de klasse als volgt uit:

// 省略

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

      // 省略
    }
  }
}

Omdat de methode voor elke validatie wordt GetAttributeAdapter aangeroepen, controleert u de eigenschap als de doorgegeven variabele attribute een kenmerk StringLengthAttribute MinimumLength was, Indien ingesteld, krijgt u een bericht dat ook rekening houdt met het minimum aantal cijfers en ErrorMessage vervangt .

Als u het uitvoert, controleert en het bericht verandert, is het OK.

Andere algemene berichten

Sommige berichten worden niet gelokaliseerd nadat u dat tot nu toe hebt gedaan. Bijvoorbeeld berichten die alleen in Javascript worden bepaald.

Het doel is jquery.validate.js het bericht dat in het bestand wordt vermeld.

Omdat dit bestand niet rechtstreeks mag worden bewerkt, moet u een input kenmerk toevoegen zoals aan de tag en gelokaliseerde tekst instellen om deze berichten te vervangen door gelokaliseerde data-val-XXXX tekst. Het XXXX-gedeelte bevat de sleutel in de jquery.validate.js bovenstaande afbeelding. (bijv. data-val-number enz.)

data-val-XXXX Als u gelokaliseerde tekst wilt instellen op kenmerken in

Laten we het bericht lokaliseren wanneer niet-numerieke tekens als voorbeeld zijn opgenomen in het invoerveld Leeftijd. UserViewModel.Age heeft Range een attribuut en de uitvoer-HTML data-val-range voegt het invoerkenmerk toe.

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

Het data-val-number numerieke controlebericht is echter niet gelokaliseerd omdat er geen kenmerk is. U voegt nog een attribuut toe aan de modelzijde om ook het gelokaliseerde, aan bericht toegevoegde kenmerk af te data-val-number drukken.

De eerste stap is het maken van een kenmerkklasse die controleert of het IntAttribute een getal is: Het is beperkt tot hier, maar je bent int vrij om het te veranderen afhankelijk van de situatie. Het codebestand kan zich overal bevinden, maar dit keer bevindt het zich in de map Adapter.

using System.ComponentModel.DataAnnotations;

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

Als de ingestelde waarde int een type is, is het een attribuut dat slechts een normaal oordeel is, maar in de praktijk is het 100% normaal als het wordt ingesteld op een eigenschap van type int, dus de verwerking van deze klasse zelf heeft geen zin. Het doel van deze tijd is om gelokaliseerde foutmeldingen in de client weer te geven.

Maak vervolgens de volgende IntAttributeAdapter klasse Adapter: Dit bevindt zich ook in de map Adapter.

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

Het punt hier is AddValidation de methode die in de methode wordt MergeAttribute beschreven.

Kenmerken samenvoegen als kenmerken van de invoertag om terug te keren data-val-number naar de client. De waarde die in het kenmerk moet worden ingesteld, stelt een gelokaliseerd foutbericht in.

Retourneer deze adapter in de klasse die u eerder hebt CustomValidationAttributeAdapterProvider gemaakt.

// 省略

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

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

Als de te valideren waarde IntAttribute is, retourneer je de waarde die je eerder hebt IntAttributeAdapter gemaakt. De eigenschap met het kenmerk Int wordt nu toegevoegd wanneer deze aan de clientzijde wordt data-val-number weergegeven.

Voeg ten slotte UserViewModel.Age IntAttribute toe aan .

Zodra je je code hebt opgelost, probeer je het uit om te zien of je het wilt bekijken.

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

Er zijn andere niet-gelokaliseerde berichten, maar u kunt deze lokaliseren op de hierboven beschreven methoden. Voeg code toe wanneer u deze nodig hebt.