Giriş doğrulaması sırasında görüntülenen varsayılan iletiler için çok dilli destek

Sayfa oluşturma tarihi :

çevre

ASP.NET Çekirdek
  • 5.0 MVC

İlk başta

Bu İpuçları içeren varsayılan giriş doğrulama iletilerinin çok dilli desteği tamamlanmış olmayabilir. Lütfen kayıplarla ilgilenin.

Ayrıca, oturum durumuna veya önbellek durumuna bağlı olarak dili değiştirmek önceki dildeki metinde görünmeye devam edebilir.

Her biri birleştirilebilen varsayılan giriş doğrulama iletisini değiştirmenin birkaç yolu vardır. Bunlardan biri değildir, bu yüzden güvenilir bir şekilde değiştirmek istiyorsanız, tüm desenlerle karşılık gelen yapmanız gerekir.

Öncül

Bu İpuçları aşağıdaki ipuçlarını anlamak olarak yazılmıştır:

Ayrıca, yeni bir proje oluşturuyorsanız, yukarıdaki ipuçlarına göre aşağıdaki dosyaları ve kodu eklemiş olmanız gerekir.

  • SharedResource.resx (+en, es) dosyası oluşturun. (Yalnızca bu İpuçlarının iletisi çevrildiği için, içerik boş olabilir.)
  • SharedResource.cs dosyası oluşturma
  • Startup.ConfigureServices'e yerelleştirme kodu ekleme
  • Startup.Configure'a yerelleştirme kodu ekleme
  • UserViewModel eklendi (bu sefer, birden çok dilde varsayılan iletileri açıkça anahtarlamıyoruz)
  • Kullanıcı tarafından oluşturulan ekran eylemleri ve görünümleri eklendi (Create.cshtml)

Başlangıç kodu

Yukarıdaki varsayımlara dayanarak, her kod aşağıdaki gibi olacaktır:

Başlangıç.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();

      // 省略
    }
  }
}
Ev Kontrol.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 { }
}

Model bağlama iletileri

Model özellik türünü ayarlar int veya görünüme bağlarsanız DateTime ve yazılmamış olarak kaydetmeye çalışırsanız, "'' değeri geçersiz" gibi bir ileti görürsünüz. Bu iletiler, örneğin modelde boş bir int dizeye bağlanamadığında görünür. Bağlanamayan bir zamanlama olduğundan, yalnızca diğer değer doğrulama ve sunucu tarafı işlemeden önce oluşur.

Bu iletiler DefaultModelBindingMessageProvider olarak tanımlanır.

Startup.cs 'de başından beri tanımlanan yöntemin bağımsız değişkenlerine services.AddControllersWithViews ekleyin ve sonra Action ekleyin. Bağımsız değişkende geçirileni ayarlayarak çok dilli options ModelBindingMessageProvider olabilirsiniz.

Ayarlayabileceğiniz 11 ileti türü vardır, bu nedenle önce iletiyi aşağıdaki gibi tanımlamanız gerekir: SharedResource.resx Anahtarın adı isteğe bağlıdır. Yorumlar sütunundaki varsayılan İngilizce iletidir (yorumların dahil edilmesi gerekmemektedir). Her dili çevirmeniz gerekmeyecek, bu nedenle kendi çevirinizi yapmalısınız (örnek kod çevrilmiş SharedResource.resx'i de içerir).

Ad Değeri Açıklaması
ModelBinding_AttemptedValueIsInvalid '{0}' {1} geçersiz bir değerdir. '{0}' değeri {1} için geçerli değil.
ModelBinding_MissingBindRequiredValue {0} değeri belirtilmemiş. '{0}' parametresi veya özelliği için bir değer sağlanmadı.
ModelBinding_MissingKeyOrValue Gerekli. Bir değer gereklidir.
ModelBinding_MissingRequestBodyRequiredValue İsteğin bir gövdesi olmalı. Boş olmayan bir istek gövdesi gereklidir.
ModelBinding_NonPropertyAttemptedValueIsInvalid '{0}' geçerli değil. '{0}' değeri geçerli değil.
ModelBinding_NonPropertyUnknownValueIsInvalid Değer geçerli değil. Sağlanan değer geçersiz.
ModelBinding_NonPropertyValueMustBeANumber Numara belirtilmelidir. Alan bir sayı olmalıdır.
ModelBinding_UnknownValueIsInvalid {0} değeri geçerli değil. Sağlanan değer {0} için geçersiz.
ModelBinding_ValueIsInvalid '{0}' geçerli değil. '{0}' değeri geçersiz.
ModelBinding_ValueMustBeANumber {0} bir numara olmalı. {0} alan bir sayı olmalıdır.
ModelBinding_ValueMustNotBeNull Gerekli giriş. '{0}' değeri geçersiz.

Başlatmayı .cs aşağıdaki gibi düzeltin:

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

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

Anahtar nokta, öğesini services.AddControllersWithViews alan yönteme bir option Action eklemektir. options.ModelBindingMessageProvider 'deki her Set yöntemi için yerelleştirilmiş metin ayarlıyorum.

Çevrilen metin ' IStringLocalizer den alınır. Biraz yuvarlak kod olan doğrudan kod almanın bir yolu IStringLocalizer yoktur. SharedResource.resx Kod oluşturuyorsanız, yerelleştirilmiş değerleri doğrudan ondan alırsınız.

IStringLocalizer 'de belirttiğiniz SharedResource.resx anahtar, 'ye eklediğiniz anahtarı belirtir. Her Set yönteminin içeriğiyle eşleşecek şekilde ayarlayın.

Ayrıca, her iletinin bir biçim dizesi bağımsız değişkeni olduğundan, {0}{1} örneğin, alınan değerleri değiştirebilmeniz için Basit'i kullanıyorum. string.Format Func

Varsayılan doğrulama iletilerini yerelleştirme

Bir modelin özelliklerinin öznitelikleri çerçeve tarafından belirlendiğinde varsayılan hata iletisi Required temel StringLength olarak İngilizce'dir.

Bunlar IValidationAttributeAdapterProvider arabirimden türetilen bir sınıf tanımlayarak yerelleştirilebilir.

Her şeyden SharedResource.resx önce, çok dilli metni kaydedin. Anahtarın adı rasgeledir, ancak programı basitleştirmek için "Validator_< doğrulama özniteliği adı>" biçiminde kaydedin.

Ad Değeri Açıklaması
Validator_CompareAttribute {0} ve {1} uyuşmuyor. '{0}' ve '{1}' eşleşmiyor.
Validator_CreditCardAttribute {0} geçerli bir kart numarası değil. {0} alanı geçerli bir kredi kartı numarası değil.
Validator_DataTypeAttribute_Date Geçerli bir tarih girin. Lütfen geçerli bir tarih girin.
Validator_EmailAddressAttribute {0} geçerli bir e-posta adresi değildir. {0} alanı geçerli bir e-posta adresi değil.
Validator_FileExtensionsAttribute {0} yalnızca aşağıdaki uzantılara sahip dosyaları kabul eder: : {1} {0} alanı yalnızca aşağıdaki uzantılara sahip dosyaları kabul eder: {1}
Validator_MaxLengthAttribute {0} en fazla '{1}' uzunluğunda bir dize veya dizi türü olmalıdır. Alan {0} en fazla '{1}' uzunluğunda bir dize veya dizi türü olmalıdır.
Validator_MinLengthAttribute {0} en az '{1}' uzunluğunda bir dize veya dizi türü olmalıdır. Alan {0} en az '{1}' uzunluğunda bir dize veya dizi türü olmalıdır.
Validator_PhoneAttribute {0} geçerli bir telefon numarası değil. {0} alanı geçerli bir telefon numarası değil.
Validator_RangeAttribute {0} {1} ile {2} arasında değişmelidir. Alan {0} {1} ile {2} arasında olmalıdır.
Validator_RegularExpressionAttribute {0} '{1}' normal ifadesiyle eşleşmelidir. Alan {0} normal '{1}' ifadesiyle eşleşmelidir.
Validator_RequiredAttribute {0} gereklidir. {0} alanı gereklidir.
Validator_StringLengthAttribute {0} {1} basamak içinde olmalıdır. Alan {0} en fazla {1} uzunluğa sahip bir dize olmalıdır.
Validator_UrlAttribute {0} geçerli bir URL değil. {0} alanı geçerli bir tam http, https veya ftp URL'si değildir.
Validator_StringLengthAttributeWithMin {0} en az {2} {1} basamak olmalıdır. Alan {0}, en fazla {1} uzunluğuna ve en az {2} uzunluğa sahip bir dize olmalıdır.

Ardından, aşağıdaki AdapterProvider sınıfını oluşturun:

Sınıf adı rasgele, ancak bu sefer CustomValidationAttributeAdapterProvider şöyle bir sınıf yazacağım ve kodu şöyle yazacağım: Kod dosyasının konumu rasgeledir, ancak örnek Bağdaştırıcı adlı bir klasöre yerleştirilir.

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

Modelin özelliklerini or olarak Required ayarlarsanız, yöntem her doğrulama için çağrılır. StringLength IValidationAttributeAdapterProvider.GetAttributeAdapter

ErrorMessageResourceName Özellik bir değer içeriyorsa, model tarafındaki kodun zaten bir ileti veya yerelleştirme anahtarı vardır, bu nedenle olduğu gibi döner.

Boşsa, yerelleştirme anahtarı yapmak için önek ve doğrulama özniteliğinin sınıf adlarını eşleştirin ve sonra yerelleştirilmiş metni anahtardan alın ve olarak ErrorMessage ayarlayın. Bu, varsayılan iletilerin çoğunun yerelleştirilmesine izin verir.

Ardından, bu sınıfı başlangıç .cs kaydedin. Temel olarak, aşağıdaki gibi ekleyebilirsiniz.

// 省略

using Microsoft.AspNetCore.Mvc.DataAnnotations;

namespace LocalizationDefaultValidation
{
  public class Startup
  {
    // 省略

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

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

Düzgün çalıştığından emin olmak için çalıştır.

Varolan iletiyi parametrelere göre değiştirme

Örneğin, StringLength öznitelik iletisi "{0} {1} basamaklar içinde olmalıdır" gibi yerelleştirilir. MinimumLength Özellik ayarlanmışsa, {0} "Bir dizi basamak içinde {2} basamak veya daha fazla {1} belirtin" olarak değiştirmek isteyebilirsiniz.

Bu CustomValidationAttributeAdapterProvider durumda, sınıfı aşağıdaki gibi genişletin:

// 省略

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

      // 省略
    }
  }
}

Yöntem her doğrulama için GetAttributeAdapter çağrıldığından, geçirilen değişken attribute bir StringLengthAttribute MinimumLength öznitelikse, özelliği denetleyin, Ayarlanırsa, minimum basamak sayısını da dikkate alan ve değiştiren bir ileti ErrorMessage alın.

Çalıştırırsanız, kontrol edin ve mesaj değişirse sorun yok.

Diğer genel iletiler

Şimdiye kadar siz bunu yaptığınızda bazı iletiler yerelleştirilmez. Örneğin, yalnızca Javascript'te belirlenen iletiler.

Hedef, jquery.validate.js dosyada listelenen iletidir.

Bu dosya doğrudan düzenlenmediğinden, bu iletileri yerelleştirilmiş metinle değiştirmek için input etikete bir öznitelik eklemeniz ve yerelleştirilmiş metni orada ayarlamanız data-val-XXXX gerekir. XXXX bölümü yukarıdaki şekilde anahtarı jquery.validate.js içerir. (örn. data-val-number vb.)

data-val-XXXX Yerelleştirilmiş metni

Örnek olarak Yaş giriş alanına sayısal olmayan karakterler dahil edildiğinde iletiyi yerelleştirelim. UserViewModel.Age Range bir özniteliği vardır ve çıktı HTML data-val-range giriş özniteliğini ekler.

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

Ancak, data-val-number öznitelik olmadığından sayısal denetim iletisi yerelleştirilmez. Yerelleştirilmiş ileti eklenen özniteliği de yazdırmak için model tarafına başka bir data-val-number öznitelik ekleyeceksiniz.

İlk adım, bir sayı olup olmadığını denetleyen bir öznitelik sınıfı IntAttribute oluşturmaktır: Burada sınırlıdır, ancak int duruma bağlı olarak değiştirmekte özgürdür. Kod dosyası her yerde olabilir, ancak bu sefer Bağdaştırıcı klasöründedir.

using System.ComponentModel.DataAnnotations;

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

Ayarlanan değer int bir türse, yalnızca normal bir karar olan bir özniteliktir, ancak pratikte int türünde bir özelliğe ayarlanırsa% 100 normaldir, bu nedenle bu sınıfın kendisinin işlenmesi hiçbir anlam ifade etmez. Bu sürenin amacı istemcide yerelleştirilmiş hata iletileri görüntülemektir.

Ardından, aşağıdaki Adapter IntAttributeAdapter sınıfını oluşturun: Bu da Bağdaştırıcı klasöründedir.

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

Buradaki nokta, AddValidation yöntemde açıklanan MergeAttribute yöntemdir.

İstemciye dönmek için öznitelikleri giriş etiketinin öznitelikleri olarak data-val-number birleştirme. Öznitelikte ayarlanan değer yerelleştirilmiş bir hata iletisi ayarlar.

Bu bağdaştırıcıyı daha önce oluşturduğunuz sınıfta CustomValidationAttributeAdapterProvider döndürün.

// 省略

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

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

Doğrulanacak değer IntAttribute ise, daha önce oluşturduğunuz değeri IntAttributeAdapter döndürün. Int özniteliğine sahip özellik artık istemci tarafında görüntülendiğinde data-val-number eklenir.

Son olarak, UserViewModel.Age IntAttribute 'ye ekleyin.

Kodunuzu düzeltdikten sonra, kontrol etmek isteyip istemediğinizi görmek için deneyin.

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

Yerelleştirilmemiş başka iletiler de vardır, ancak bunları yukarıda açıklanan yöntemlerde yerelleştirebilirsiniz. İhtiyacınız olduğunda kod ekleyin.