Mehrsprachige Unterstützung für Standardmeldungen, die während der Eingabevalidierung angezeigt werden

Erstellungsdatum der Seite :

Umwelt

ASP.NET Kern
  • 5,0 MVC

Zuerst

Die mehrsprachige Unterstützung der Standard-Eingabevalidierungsmeldungen mit diesen Tipps ist möglicherweise nicht vollständig. Bitte kümmern Sie sich um die fehlenden.

Außerdem kann das Ändern der Sprache in Abhängigkeit vom Sitzungsstatus oder Cachestatus weiterhin im Text der vorherigen Sprache angezeigt werden.

Es gibt mehrere Möglichkeiten, die Standardeingabevalidierungsnachricht zu ändern, die jeweils kombiniert werden können. Es ist nicht einer von ihnen, also wenn Sie es zuverlässig ändern wollen, müssen Sie mit allen Mustern korrespondieren.

Prämisse

Diese Tipps sind so geschrieben, dass sie die folgenden Tipps verstehen:

Wenn Sie ein neues Projekt erstellen, müssen Sie außerdem die folgenden Dateien und den folgenden Code basierend auf den obigen Tipps hinzugefügt haben.

  • Erstellen Sie eine Datei SharedResource.resx (+en, es). (Da nur die Nachricht dieser Tipps übersetzt wird, kann der Inhalt leer sein.)
  • Erstellen einer SharedResource.cs Datei
  • Hinzufügen von Lokalisierungscode zu Startup.ConfigureServices
  • Hinzufügen von Lokalisierungscode zu Startup.Configure
  • UserViewModel hinzugefügt (dieses Mal geben wir die Standardmeldungen nicht explizit in mehreren Sprachen ein)
  • Vom Benutzer erstellte Bildschirmaktionen und -ansichten hinzugefügt (Create.cshtml)

Startcode

Auf der Grundlage der oben genannten Annahmen hat jeder Code folgenden Wert:

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

Modellbindungsmeldungen

Wenn Sie den Modelleigenschaftstyp auf oder festlegen int und an die Ansicht binden und DateTime versuchen, ihn nicht typisiert zu registrieren, wird eine Meldung wie "Der Wert '' ist ungültig." angezeigt. Diese Meldungen treten auf, wenn eine leere Zeichenfolge int nicht gebunden werden kann, z. B. im Modell. Da es sich um ein Timing handelt, das nicht gebunden werden kann, tritt es nur vor einer anderen Wertüberprüfung und serverseitigen Verarbeitung auf.

Diese Meldungen sind DefaultModelBindingMessageProvider definiert als .

Startup.cs Fügen Sie zu den Argumenten der Methode hinzu, die seit dem Anfang in definiert services.AddControllersWithViews wurden, und fügen Sie dann Action hinzu. Sie können mehrsprachig sein, indem Sie das Übergebene im options ModelBindingMessageProvider Argument festlegen.

Es gibt 11 Arten von Nachrichten, die Sie festlegen können, daher müssen Sie die Nachricht zuerst wie folgt definieren: SharedResource.resx Der Name des Schlüssels ist optional. Die englische Standardnachricht in der Kommentarspalte lautet (Kommentare müssen nicht enthalten sein). Sie müssen nicht jede Sprache übersetzen, daher sollten Sie Ihre eigene Übersetzung durchführen (der Beispielcode enthält auch die übersetzte SharedResource.resx).

Name-Wert-Kommentar
ModelBinding_AttemptedValueIsInvalid '{0}' ist ein ungültiger Wert im {1}. Der Wert '{0}' ist für {1} nicht gültig.
ModelBinding_MissingBindRequiredValue Der Wert für die {0} wird nicht angegeben. Ein Wert für den Parameter oder die Eigenschaft '{0}' wurde nicht angegeben.
ModelBinding_MissingKeyOrValue Erforderlich. Ein Wert ist erforderlich.
ModelBinding_MissingRequestBodyRequiredValue Der Antrag muss eine Stelle haben. Ein nicht leerer Anforderungstext ist erforderlich.
ModelBinding_NonPropertyAttemptedValueIsInvalid "{0}" ist ungültig. Der Wert '{0}' ist ungültig.
ModelBinding_NonPropertyUnknownValueIsInvalid Der Wert ist ungültig. Der angegebene Wert ist ungültig.
ModelBinding_NonPropertyValueMustBeANumber Die Anzahl muss angegeben werden. Das Feld muss eine Zahl sein.
ModelBinding_UnknownValueIsInvalid Der Wert der {0} ist ungültig. Der angegebene Wert ist für {0} ungültig.
ModelBinding_ValueIsInvalid "{0}" ist ungültig. Der Wert '{0}' ist ungültig.
ModelBinding_ValueMustBeANumber {0} muss eine Zahl sein. Das Feld {0} muss eine Zahl sein.
ModelBinding_ValueMustNotBeNull Erforderliche Eingabe. Der Wert '{0}' ist ungültig.

Reparieren Sie den Start.cs wie folgt:

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

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

Der entscheidende Punkt besteht services.AddControllersWithViews darin, der Methode, die die option Action , options.ModelBindingMessageProvider Ich lege lokalisierten Text für jede Set-Methode in fest.

Der übersetzte Text wird IStringLocalizer aus abgerufen. Es gibt keine Möglichkeit, direkten Code zu IStringLocalizer erhalten, was ein bisschen ein runder Code ist. SharedResource.resx Wenn Sie Code generieren, erhalten Sie lokalisierte Werte direkt daraus.

IStringLocalizer Der Schlüssel, den Sie in SharedResource.resx angeben, gibt den Schlüssel an, den Sie zu hinzugefügt haben. Legen Sie es so fest, dass es dem Inhalt jeder Set-Methode entspricht.

Da jede Nachricht über ein Formatzeichenfolgenargument verfügt, z. B. , verwende ich {0}{1} Simple mit, damit Sie die empfangenen Werte ersetzen string.Format Func können.

Lokalisieren von Standardvalidierungsmeldungen

Die Standardfehlermeldung, wenn die Attribute der Required Eigenschaften eines Modells auf etwa so festgelegt sind, wird vom Framework bestimmt und ist StringLength im Wesentlichen in Englisch.

Diese können IValidationAttributeAdapterProvider lokalisiert werden, indem eine von der Schnittstelle abgeleitete Klasse definiert wird.

Registrieren Sie zunächst SharedResource.resx den Text, der mehrsprachig ist. Der Name des Schlüssels ist willkürlich, aber um das Programm zu vereinfachen, registrieren Sie ihn in Form von "Validator_< validation attribute name>".

Name-Wert-Kommentar
Validator_CompareAttribute Die {0} und {1} stimmen nicht überein. "{0}" und "{1}" stimmen nicht überein.
Validator_CreditCardAttribute {0} ist keine gültige Kartennummer. Das {0} Feld ist keine gültige Kreditkartennummer.
Validator_DataTypeAttribute_Date Geben Sie ein gültiges Datum ein. Bitte geben Sie ein gültiges Datum ein.
Validator_EmailAddressAttribute {0} ist keine gültige E-Mail-Adresse. Das {0} Feld ist keine gültige E-Mail-Adresse.
Validator_FileExtensionsAttribute {0} akzeptiert nur Dateien mit den folgenden Erweiterungen: : {1} Das Feld {0} akzeptiert nur Dateien mit den folgenden Erweiterungen: {1}
Validator_MaxLengthAttribute {0} muss ein String- oder Array-Typ mit einer maximalen Länge von '{1}' sein. Das Feld {0} muss ein String- oder Array-Typ mit einer maximalen Länge von '{1}' sein.
Validator_MinLengthAttribute {0} muss ein String- oder Array-Typ mit einer Mindestlänge von '{1}' sein. Das Feld {0} muss ein String- oder Array-Typ mit einer Mindestlänge von '{1}' sein.
Validator_PhoneAttribute {0} ist keine gültige Telefonnummer. Das feld {0} ist keine gültige Telefonnummer.
Validator_RangeAttribute {0} muss von {1} bis {2} reichen. Das Feld {0} muss sich zwischen {1} und {2} befinden.
Validator_RegularExpressionAttribute {0} muss mit dem regulären Ausdruck '{1}' übereinstimmen. Das Feld {0} muss mit dem regulären Ausdruck '{1}' übereinstimmen.
Validator_RequiredAttribute {0} ist erforderlich. Das feld {0} ist erforderlich.
Validator_StringLengthAttribute {0} muss innerhalb {1} Ziffern liegen. Das Feld {0} muss eine Zeichenfolge mit einer maximalen Länge von {1} sein.
Validator_UrlAttribute {0} ist keine gültige URL. Das feld {0} ist keine gültige vollqualifizierte http-, https- oder ftp-URL.
Validator_StringLengthAttributeWithMin {0} müssen mindestens {2} {1} Ziffern sein. Das Feld {0} muss eine Zeichenfolge mit einer maximalen Länge von {1} und einer Mindestlänge von {2} sein.

Erstellen Sie als Nächstes die folgende AdapterProvider-Klasse:

Der Klassenname ist willkürlich, aber dieses Mal CustomValidationAttributeAdapterProvider schreibe ich eine Aufgerufene Klasse und schreibe den Code wie folgt: Der Speicherort der Codedatei ist beliebig, aber das Beispiel wird in einem Ordner namens Adapter abgelegt.

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

Wenn Sie die Eigenschaften des Modells auf Required oder festlegen, wird die Methode für jede StringLength IValidationAttributeAdapterProvider.GetAttributeAdapter Validierung aufgerufen.

ErrorMessageResourceName Wenn die Eigenschaft einen Wert enthält, verfügt der Code auf der Modellseite bereits über eine Nachricht oder einen Lokalisierungsschlüssel, sodass er unverändert zurückgegeben wird.

Wenn leer, vergleichen Sie die Klassennamen des Präfixes und des Validierungsattributs, um es zum Lokalisierungsschlüssel zu machen, und rufen Sie dann den lokalisierten Text aus dem Schlüssel ab, und legen Sie ihn ErrorMessage auf fest. Auf diese Weise können die meisten Standardnachrichten lokalisiert werden.

Registrieren Sie als Nächstes diese Klasse in der Startup-.cs. Grundsätzlich können Sie es wie folgt hinzufügen.

// 省略

using Microsoft.AspNetCore.Mvc.DataAnnotations;

namespace LocalizationDefaultValidation
{
  public class Startup
  {
    // 省略

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

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

Führen Sie es aus, um sicherzustellen, dass es ordnungsgemäß funktioniert.

Ändern einer vorhandenen Nachricht nach Parametern

Beispielsweise StringLength ist die Attributnachricht lokalisiert, z. B. "{0} muss innerhalb {1} Ziffern liegen". MinimumLength Wenn die Eigenschaft festgelegt ist, können Sie die {0} wie folgt ändern: "Geben Sie {2} Ziffern oder mehr {1} innerhalb einer Anzahl von Ziffern an".

Erweitern CustomValidationAttributeAdapterProvider Sie in diesem Fall die Klasse wie folgt:

// 省略

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

      // 省略
    }
  }
}

Da die Methode für jede Validierung aufgerufen GetAttributeAdapter wird, überprüfen Sie die Eigenschaft, wenn die übergebene Variable attribute ein Attribut StringLengthAttribute MinimumLength war. Wenn festgelegt, erhalten Sie eine Nachricht, die auch die Mindestanzahl von Ziffern berücksichtigt und ErrorMessage ersetzt .

Wenn Sie es ausführen, überprüfen und sich die Nachricht ändert, ist es in Ordnung.

Andere generische Nachrichten

Einige Nachrichten werden nicht lokalisiert, nachdem Sie dies bisher getan haben. Zum Beispiel Nachrichten, die nur in Javascript ermittelt werden.

Das Ziel ist jquery.validate.js die in der Datei aufgeführte Nachricht.

Da diese Datei nicht direkt bearbeitet werden sollte, müssen Sie, um diese Nachrichten durch lokalisierten Text zu ersetzen, input ein Attribut wie das Tag hinzufügen und dort data-val-XXXX lokalisierten Text festlegen. Der XXXX-Teil enthält den Schlüssel in der jquery.validate.js obigen Abbildung. (z.B. data-val-number etc.)

data-val-XXXX Um lokalisierten Text auf Attribute in

Lassen Sie uns die Nachricht lokalisieren, wenn beispielsweise nicht numerische Zeichen im Eingabefeld Alter enthalten sind. UserViewModel.Age über Range ein Attribut verfügt, und der Ausgabe-HTML-Code data-val-range hängt das Inputattribut an.

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

Die numerische Prüfmeldung ist jedoch data-val-number nicht lokalisiert, da kein Attribut vorhanden ist. Sie fügen ein weiteres Attribut auf der Modellseite hinzu, um auch das lokalisierte Attribut mit hinzugefügter Nachricht zu data-val-number drucken.

Der erste Schritt besteht darin, eine Attributklasse zu erstellen, die überprüft, ob es sich um IntAttribute eine Zahl handelt: Es ist auf hier beschränkt, aber Sie können int es je nach Situation ändern. Die Codedatei kann sich überall befinden, aber dieses Mal befindet sie sich im Ordner Adapter.

using System.ComponentModel.DataAnnotations;

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

Wenn der Mengenwert int ein Typ ist, ist es ein Attribut, das nur ein normales Urteil ist, aber in der Praxis ist es 100% normal, wenn es auf eine Eigenschaft vom Typ int gesetzt wird, so dass die Verarbeitung dieser Klasse selbst keinen Sinn ergibt. Der Zweck dieser Zeit besteht darin, lokalisierte Fehlermeldungen im Client anzuzeigen.

Erstellen Sie als Nächstes die folgende IntAttributeAdapter Adapterklasse: Dies befindet sich ebenfalls im Ordner 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;
  }
}

Der Punkt hier ist AddValidation die in der Methode beschriebene MergeAttribute Methode.

Zusammenführen von Attributen als Attribute des Input-Tags, um data-val-number zum Client zurückzukehren. Der im Attribut festzulegende Wert legt eine lokalisierte Fehlermeldung fest.

Geben Sie diesen Adapter in der Zuvor erstellten Klasse CustomValidationAttributeAdapterProvider zurück.

// 省略

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

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

Wenn der zu validierende Wert IntAttribute ist, geben Sie den zuvor erstellten Wert IntAttributeAdapter zurück. Die Eigenschaft mit dem Int-Attribut wird nun hinzugefügt, wenn sie auf der Clientseite angezeigt data-val-number wird.

Fügen Sie schließlich UserViewModel.Age IntAttribute zu hinzu.

Sobald Sie Ihren Code repariert haben, probieren Sie ihn aus, um zu sehen, ob Sie ihn auschecken möchten.

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

Es gibt andere nicht lokalisierte Nachrichten, aber Sie können sie mit den oben beschriebenen Methoden lokalisieren. Fügen Sie Code hinzu, wenn Sie ihn benötigen.