پشتیبانی چند زبانه از پیام های پیش فرض نمایش داده شده در هنگام اعتبارسنجی ورودی

تاریخ ایجاد صفحه :

محیط

ASP.NET هسته
  • 5.0 MVC

در ابتدا

پشتیبانی چند زبانه از پیام های اعتبارسنجی ورودی پیش فرض با این نکات ممکن است کامل نیست. لطفا با گمشده ها کنار بيا

همچنین تغییر زبان بسته به وضعیت جلسه یا حالت کش ممکن است هنوز هم در متن از زبان قبلی ظاهر شود.

راه های متعددی برای تغییر پیام اعتبارسنجی ورودی پیش فرض وجود دارد که هر کدام می توانند با هم ترکیب شوند. این یکی از آنها نیست، بنابراین اگر شما می خواهید آن را به طور قابل اعتماد تغییر دهید، شما نیاز به مکاتبه با تمام الگوها.

فرض

این نکات به عنوان درک نکات زیر نوشته شده است:

همچنین اگر در حال ایجاد یک پروژه جدید هستید، باید فایل ها و کدهای زیر را بر اساس راهنمایی های بالا اضافه کرده باشد.

  • ایجاد یک فایل SharedResource.resx (+en, es) . (از آنجا که فقط پیام این نکته ترجمه شده است، ممکن است محتویات خالی باشد.)
  • ایجاد یک فایل مشترکResource.cs است
  • افزودن کد محلی سازی به Startup.ConfigureServices
  • افزودن کد محلی سازی به Startup.Configure
  • اضافه شده UserViewModel (این بار، ما به صراحت کلید پیام های پیش فرض در زبان های متعدد)
  • اعمال و نماهای صفحه نمایش ایجاد شده توسط کاربر اضافه شده (Create.cshtml)

شروع کد

بر اساس فرضیات فوق هر کد به صورت زیر خواهد بود:

راه اندازی.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 { }
}

پیام های اتصال مدل

اگر نوع ملک مدل را به آن تنظیم کنید یا آن را به نمایش متصل کنید، و سعی کنید آن را بدون تایپ ثبت کنید، پیامی مانند «ارزش '' نامعتبر است» را خواهید int DateTime دید. این پیام ها زمانی ظاهر می شوند که یک رشته خالی نمی تواند به int عنوان مثال در مدل محدود شود. از آنجا که زمان بندی است که نمی تواند محدود شود، آن را تنها قبل از اعتبار سنجی ارزش دیگر و پردازش سمت سرور رخ می دهد.

این پیام ها به DefaultModelBindingMessageProvider این عنوان تعریف شده اند.

Startup.cs به آر استدلال های روشی که از ابتدا در تعریف شده اند اضافه کنید و سپس services.AddControllersWithViews اضافه Action کنید. شما می توانید چند زبانه با تنظیم عبور در options ModelBindingMessageProvider استدلال.

۱۱ نوع پیام وجود دارد که می توانید تنظیم کنید، بنابراین ابتدا باید پیام را به صورت زیر تعریف کنید: SharedResource.resx نام کلید اختیاری است. پیام پیش فرض انگلیسی در ستون نظرات است (نظرات لازم نیست گنجانده شود). شما مجبور به ترجمه هر زبان نیست، بنابراین شما باید ترجمه خود را انجام دهید (کد نمونه نیز شامل SharedResource.resx ترجمه شده).

نظر
ارزش نام
ModelBinding_AttemptedValueIsInvalid '{0}' یک مقدار نامعتبر در {1}. ارزش '{0}' برای آن معتبر {1}.
ModelBinding_MissingBindRequiredValue مقدار برای {0} مشخص نشده است. مقداری برای پارامتر یا {0} '' ارائه نشد.
ModelBinding_MissingKeyOrValue مورد نیاز. یک مقدار مورد نیاز است.
ModelBinding_MissingRequestBodyRequiredValue درخواست بايد يک جسد داشته باشه . یک بدن درخواست غیر خالی مورد نیاز است.
ModelBinding_NonPropertyAttemptedValueIsInvalid '{0}' معتبر نیست. مقدار '{0}' معتبر نیست.
ModelBinding_NonPropertyUnknownValueIsInvalid مقدار معتبر نیست. مقدار عرضه شده نامعتبر است.
ModelBinding_NonPropertyValueMustBeANumber شماره باید مشخص شود. فیلد باید یک عدد باشد.
ModelBinding_UnknownValueIsInvalid ارزش ارزش {0} معتبر نیست. مقدار عرضه شده برای یک {0}.
ModelBinding_ValueIsInvalid '{0}' معتبر نیست. مقدار '{0}' نامعتبر است.
ModelBinding_ValueMustBeANumber {0} باید یک عدد باشد. فیلد {0} باید یک عدد باشد.
ModelBinding_ValueMustNotBeNull ورودی مورد نیاز. مقدار '{0}' نامعتبر است.

رفع راه اندازی.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 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
      });

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

نکته کلیدی این است services.AddControllersWithViews که اضافه کردن یک به روشی که دریافت می کند option Action ، من options.ModelBindingMessageProvider تنظیم متن محلی برای هر روش تنظیم در .

متن ترجمه شده از IStringLocalizer بازیابی شده است. هیچ راهی برای دریافت کد مستقیم IStringLocalizer که کمی کد گرد است وجود ندارد. اگر SharedResource.resx شما در حال تولید کد از, شما ارزش های محلی به طور مستقیم از آن دریافت کنید.

IStringLocalizer کلیدی که شما در آن مشخص می SharedResource.resx کنید کلید را که به آن اضافه کرده بودید مشخص می کند. آن را تنظیم کنید تا با محتویات هر روش Set مطابقت داشته باشد.

همچنین چون هر پیام یک آر استدلال رشته فرمت دارد، مانند ، من از Simple با آن استفاده می کنم تا شما می توانید مقادیر {0}{1} دریافتی را جایگزین string.Format Func کنید.

محلی سازی پیام های اعتبارسنجی پیش فرض

پیام خطای پیش فرض هنگامی که ویژگی های ویژگی های یک مدل به یا بیشتر تنظیم می شوند توسط چارچوب تعیین می شود و اساساً به Required StringLength زبان انگلیسی است.

این ها را می IValidationAttributeAdapterProvider توان با تعریف یک کلاس مشتق شده از رابط محلی کرد.

اول از SharedResource.resx همه، متنی که چند زبانه است را ثبت کنید. نام کلید دلخواه است، اما برای ساده سازی برنامه، آن را در قالب «نام ویژگی اعتبارسنجی Validator_<» ثبت >.

نظر
ارزش نام
Validator_CompareAttribute تیم {0} و {1} با هم مطابقت نمی کنند. "{0}" و "{1}" با هم مطابقت نداره.
Validator_CreditCardAttribute {0} شماره کارت معتبر نیست. رشته {0} شماره کارت اعتباری معتبر نیست.
Validator_DataTypeAttribute_Date تاریخ معتبری را وارد کنید. لطفاً تاریخ معتبری را وارد کنید.
Validator_EmailAddressAttribute {0} یک آدرس ایمیل معتبر نیست. فیلد {0} یک آدرس ایمیل معتبر نیست.
Validator_FileExtensionsAttribute {0} فایل ها را با پسوندهای زیر می پذیرد: : {1} فیلد {0} فایل ها را با پسوندهای زیر می پذیرد: {1}
Validator_MaxLengthAttribute {0} باید یک رشته یا نوع آرایه با حداکثر طول '{1}' باشد. فیلد {0} باید یک نوع رشته یا آرایه با حداکثر طول '{1}' باشد.
Validator_MinLengthAttribute {0} باید یک نوع رشته یا آرایه با حداقل طول '{1}' باشد. فیلد {0} باید یک نوع رشته یا آرایه با حداقل طول '{1}' باشد.
Validator_PhoneAttribute {0} یک شماره تلفن معتبر نیست. فیلد {0} شماره تلفن معتبر نیست.
Validator_RangeAttribute {0} باید از {1} تا {2}. میدانی که {0} باید بین {1} و {2}.
Validator_RegularExpressionAttribute {0} باید با بیان منظم '{1}' مطابقت داشته باشد. میدانی که {0} باید با عبارت منظم '{1}' مطابقت داشته باشد.
Validator_RequiredAttribute {0} مورد نیاز است. رشته {0} مورد نیاز است.
Validator_StringLengthAttribute {0} باید درون {1} باشد. فیلد {0} باید رشته ای با حداکثر طول {1}.
Validator_UrlAttribute {0} یک URL معتبر نیست. زمینه {0} معتبر به طور کامل واجد شرایط http، https، و یا FTP URL نیست.
Validator_StringLengthAttributeWithMin {0} باید حداقل {2} {1} باشد. فیلد {0} باید رشته ای با حداکثر طول {1} و حداقل طول {2}.

بعد، ایجاد کلاس آداپتورپرویدر زیر:

نام کلاس دلخواه است، اما این بار کلاسی به نام می نویسم و کد CustomValidationAttributeAdapterProvider را به این قرار می نویسم: محل فایل کد دلخواه است، اما نمونه در پوشه ای به نام آداپتور قرار می گیرد.

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

اگر شما خواص مدل را به یا ، روش برای Required هر اعتبار سنجی نامیده می StringLength IValidationAttributeAdapterProvider.GetAttributeAdapter شود.

ErrorMessageResourceName اگر ویژگی حاوی مقدار باشد، کد سمت مدل در حال حاضر یک پیام یا کلید محلی سازی دارد، بنابراین همان طور که هست برمی گردد.

اگر خالی باشد، با نام های کلاسی پیش درآمد و ویژگی اعتبارسنجی مطابقت داشته باشد تا آن را کلید محلی سازی کند، و سپس متن محلی شده را از کلید بازیابی کنید و آن را به ErrorMessage . این اجازه می دهد تا بسیاری از پیام های پیش فرض به محلی.

بعد، این کلاس را در استارتاپ .cs. اصولاً می توانید آن را به صورت زیر اضافه کنید.

// 省略

using Microsoft.AspNetCore.Mvc.DataAnnotations;

namespace LocalizationDefaultValidation
{
  public class Startup
  {
    // 省略

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

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

اجرا کنید تا مطمئن شوید که درست کار می کند.

تغییر پیام موجود بر اساس پارامترها

به عنوان مثال، پیام ویژگی محلی شده است، مانند "{0} باید درون StringLength {1} باشد." اگر این ملک تنظیم شده باشد، ممکن است بخواهید {0} را به عنوان "{2} یا تعداد بیشتری از {1} در تعدادی از MinimumLength ارقام" تغییر دهید.

در آن CustomValidationAttributeAdapterProvider صورت، گسترش کلاس به صورت زیر است:

// 省略

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

      // 省略
    }
  }
}

چون روش برای هر اعتبارسنجی فراخوانی می شود، اگر متغیر عبور کرده یک ویژگی بود، ویژگی GetAttributeAdapter attribute را بررسی StringLengthAttribute MinimumLength کنید، در صورت تنظیم، پیامی دریافت کنید که حداقل تعداد ارقام را نیز در نظر بگیرد و ErrorMessage جایگزین آن شود.

اگر آن را اجرا کنید، بررسی کنید و پیام تغییر کند، اشکالی ندارد.

سایر پیام های عمومی

برخی از پیام ها پس از انجام این کار تا کنون محلی نمی شوند. برای مثال پیام هایی که فقط در جاوا اسکریپت تعیین می شوند.

هدف پیامی jquery.validate.js است که در پرونده ذکر شده است.

از آنجا که این فایل نباید به طور مستقیم ویرایش شود، برای جایگزینی این پیام ها با متن محلی شده، باید یک ویژگی مانند برچسب اضافه کنید و متن محلی شده را در آنجا input data-val-XXXX تنظیم کنید. قسمت XXXX شامل کلید در شکل بالا jquery.validate.js است. (مانند و data-val-number غیره)

data-val-XXXX برای تنظیم متن محلی شده به ویژگی ها در

هنگامی که کاراکترهای غیر عددی به عنوان مثال در زمینه ورودی Age گنجانده می شوند، پیام را محلی سازی کنیم. یک UserViewModel.Age ویژگی Range دارد، و HTML خروجی ویژگی ورودی را data-val-range آپند می کند.

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

با این حال، پیام بررسی عددی محلی نمی data-val-number شود زیرا هیچ ویژگی وجود ندارد. شما یک ویژگی دیگر در سمت مدل برای چاپ ویژگی محلی پیام اضافه شده data-val-number نیز اضافه کنید.

مرحله اول ایجاد یک کلاس ویژگی است که چک می کند اگر آن را IntAttribute یک عدد است: این محدود به اینجا است، اما شما آزاد int هستید که بسته به وضعیت آن را تغییر دهید. فایل کد می تواند در هر جایی باشد، اما این بار در پوشه آداپتور است.

using System.ComponentModel.DataAnnotations;

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

اگر مقدار مجموعه یک نوع باشد، یک ویژگی است که فقط یک حکم عادی است، اما در عمل اگر به یک ویژگی از نوع int تنظیم شود، صد در صد طبیعی است، بنابراین پردازش خود این کلاس هیچ معنایی int ندارد. هدف از این زمان نمایش پیام های خطای محلی شده در سرویس گیرنده است.

بعد، کلاس آداپتور زیر را ایجاد IntAttributeAdapter کنید: این هم در پوشه آداپتور.

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

نکته در اینجا روش AddValidation توصیف شده در روش MergeAttribute است.

ادغام ویژگی ها به عنوان ویژگی های تگ ورودی برای بازگشت data-val-number به مشتری است. مقداری که در ویژگی تنظیم می شود، یک پیام خطای محلی شده را تعیین می کند.

این آداپتور را در کلاسی که قبلاً ایجاد کرده CustomValidationAttributeAdapterProvider اید برگرداند.

// 省略

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

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

اگر مقدار اعتبار است IntAttribute ، بازگشت که شما زودتر ایجاد شده IntAttributeAdapter است. ویژگی با ویژگی Int در حال حاضر اضافه شده است زمانی که آن را در سمت مشتری نمایش داده می data-val-number شود.

در نهایت ، UserViewModel.Age IntAttribute اضافه کردن به .

هنگامی که شما کد خود را ثابت, سعی کنید آن را ببینید که آیا شما می خواهید آن را بررسی کنید.

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

پیام های غیر محلی شده دیگری نیز وجود دارد، اما شما می توانید آنها را در روش های شرح داده شده در بالا بومی سازی کنید. اضافه کردن کد زمانی که شما به آن نیاز دارید.