دعم متعدد اللغات للرسائل الافتراضية المعروضة أثناء التحقق من صحة الإدخال

تاريخ إنشاء الصفحة :

وسط

ASP.NET كور
  • 5.0 MVC

في البداية

قد لا يكون الدعم متعدد اللغات لرسائل التحقق من صحة الإدخال الافتراضية مع هذه التلميحات كاملا. رجاء تعامل مع المفقودين

أيضا، تغيير اللغة استنادا إلى حالة جلسة العمل أو حالة ذاكرة التخزين المؤقت قد لا تزال تظهر في نص من اللغة السابقة.

هناك عدة طرق لتغيير رسالة التحقق من صحة الإدخال الافتراضية، يمكن دمج كل منها. انها ليست واحدة منهم، لذلك إذا كنت ترغب في تغييره بشكل موثوق، تحتاج إلى تتوافق مع جميع الأنماط.

فرضيه

تتم كتابة هذه النصائح كفهم للنصائح التالية:

أيضا، إذا كنت تقوم بإنشاء مشروع جديد، يجب أن يكون لديك إضافة الملفات والتعليمات البرمجية التالية استنادا إلى تلميحات أعلاه.

  • إنشاء ملف SharedResource.resx (+en, es). (بما أن رسالة هذه التلميحات فقط مترجمة، فقد تكون المحتويات فارغة.)
  • إنشاء ملف مشتركالمصدر.cs
  • إضافة رمز التعريب إلى Startup.ConfigureServices
  • إضافة رمز التعريب إلى بدء التشغيل.تكوين
  • تمت إضافة 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()));
  }
}
مؤشر.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>
إنشاء.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");}
}
المستخدمفيوموديل.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 اسم المفتاح اختياري. الرسالة الافتراضية باللغة الإنجليزية في عمود التعليقات هي (لا يلزم تضمين التعليقات). لن تضطر إلى ترجمة كل لغة، لذا يجب عليك القيام بالترجمة الخاصة بك (تتضمن نموذج التعليمات البرمجية أيضا المشتركة.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} غير محدد موقع معلومات صحيح. حقل {0} غير صالح مؤهل بالكامل http أو https أو FTP URL.
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 وتستبدل .

إذا قمت بتشغيله، تحقق وتغيرت الرسالة، فلا بأس.

رسائل عامة أخرى

لا يتم ترجمة بعض الرسائل بعد القيام بذلك حتى الآن. على سبيل المثال، الرسائل التي يتم تحديدها فقط في جافا سكريبت.

الهدف هو 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>

هناك رسائل أخرى غير مترجمة، ولكن يمكنك ترجمة لهم في الأساليب المذكورة أعلاه. أضف التعليمات البرمجية عندما تحتاج إليها.