תמיכה רב-לשונית עבור הודעות ברירת מחדל המוצגות במהלך אימות קלט

תאריך יצירת דף :

סביבה

ליבה ASP.NET
  • 5.0 MVC

בהתחלה

ייתכן שהתמיכה הרב-לשונית של הודעות אימות הקלט המהוות ברירת מחדל עם עצות אלה לא תושלם. אנא טפלו בחסרים.

כמו כן, שינוי השפה בהתאם למצב ההפעלה או למצב המטמון עשוי עדיין להופיע בטקסט מהשפה הקודמת.

קיימות מספר דרכים לשינוי הודעת אימות הקלט המהווה ברירת מחדל, שכל אחת מהן ניתן לשלב. זה לא אחד מהם, אז אם אתה רוצה לשנות את זה באופן אמין, אתה צריך להתכתב עם כל הדפוסים.

הנחת יסוד

טיפים אלה נכתבים כהבנת העצות הבאות:

כמו כן, אם אתה יוצר פרוייקט חדש, עליך להוסיף את הקבצים והקוד הבאים בהתבסס על עצות לעיל.

  • צור קובץ SharedResource.resx (+en, es). (מאחר שרק ההודעה של עצות אלה מתורגמת, ייתכן שהתוכן ריק.)
  • יצירת קובץ מקור משותף.cs
  • הוסף קוד לוקליזציה לאתחול.קביעת תצורה שלServices
  • הוסף קוד לוקליזציה לאתחול.קבע תצורה
  • נוסף 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();

      // 省略
    }
  }
}
"הום קונטרולר.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");}
}
משתמשViewדוג.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,
  }
}
מקור משותף.cs
namespace LocalizationDefaultValidation
{
  // クラス名は作成した .resx のファイル名と同じにする必要がある
  public class SharedResource { }
}

הודעות איגוד מודל

אם תגדיר את סוג מאפיין המודל int לתצוגה או תאגד אותו DateTime ותנסה לרשום אותו לא מוקלד, תראה הודעה כגון "הערך '' אינו חוקי.". הודעות אלה מופיעות כאשר אין אפשרות לאגד מחרוזת int ריקה, לדוגמה, למודל. מאחר שזהו תזמון שלא ניתן לאגדו, הוא מתרחש רק לפני אימות ערך אחר ועיבוד בצד השרת.

הודעות אלה מוגדרות כ - . הודעות אלה DefaultModelBindingMessageProvider מוגדרות כ .

Startup.cs הוסף לארגומנטים של פעולת השירות שהוגדרו מאז services.AddControllersWithViews תחילתה ב ולאחר מכן הוסף Action . באפשרותך להיות רב-לשוני על-ידי הגדרת העברת options ModelBindingMessageProvider הארגומנט.

קיימים 11 סוגים של הודעות שבאפשרותך להגדיר, לכן עליך להגדיר תחילה את ההודעה באופן הבא: 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 אני מגדיר טקסט מקומי עבור כל פעולת שירות Set ב- .

הטקסט המתורגם IStringLocalizer מאוחזר מ- . אין דרך לקבל IStringLocalizer קוד ישיר, שהוא קצת קוד עגול. SharedResource.resx אם אתה יוצר קוד מ, תקבל ממנו ערכים המותאמים לשפות מקומיות ישירות ממנו.

IStringLocalizer המפתח בו ציינת SharedResource.resx מציין את המפתח שהוספת ל- . הגדר אותו כך שיתאים לתוכן של כל פעולת שירות Set.

כמו כן, מכיוון שלכל הודעה יש ארגומנט מחרוזת עיצוב, כגון , אני {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} אינו כתובת URL חוקית של http, https או ftp.
Validator_StringLengthAttributeWithMin {0} חייבות להיות ספרות {2} {1} לפחות. השדה {0} חייב להיות מחרוזת עם אורך מרבי של {1} ואורך מינימלי של {2}.

לאחר מכן, צור את מחלקת AdapterProvider הבאה:

שם המחלקה שרירותי, אבל הפעם 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>();
    }
    
    // 省略
  }
}

הפעל אותו כדי לוודא שזה עובד כמו שצריך.

שינוי הודעה קיימת לפי פרמטרים

לדוגמה, StringLength הודעת התכונה היא המותאמת לשפות מקומיות, כגון "{0} חייב להיות בתוך {1} הספרות". MinimumLength אם המאפיין מוגדר, ייתכן שתרצה לשנות את {0} כ"ציין ספרות {2} או {1} יותר במספר ספרות".

במקרה 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 .

אם אתה מפעיל אותו, בדוק וההודעה משתנה, זה בסדר.

הודעות כלליות אחרות

הודעות מסוימות אינן מקומיות לאחר שתעשה זאת עד כה. לדוגמה, הודעות שנקבעו רק בכתב Java.

היעד הוא jquery.validate.js ההודעה המפורטת בקובץ.

מאחר שאין לערוך קובץ זה ישירות, כדי להחליף הודעות אלה בטקסט המותאמת לשפות מקומיות, input עליך להוסיף תכונה כגון לתג ולהגדיר שם טקסט data-val-XXXX המותאמת לשפות מקומיות. חלק XXXX מכיל את המפתח באיור jquery.validate.js לעיל. (למשל data-val-number וכו')

data-val-XXXX כדי להגדיר טקסט המותאמת לשפות מקומיות לתכונות ב-

בוא נמקם את ההודעה באופן לשפות מקומי כאשר תווים שאינם מספריים נכללים בשדה קלט גיל כדוגמה. 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 סוג, זוהי תכונה שהיא רק פסק דין רגיל, אך בפועל היא 100% רגילה אם מוגדרת למאפיין מסוג 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>

קיימות הודעות אחרות שאינן מקומיות, אך באפשרותך להתאים אותן לשפות אחרות בשיטות שתוארו לעיל. הוסף קוד כאשר אתה צריך את זה.