Πολυγλωσσική υποστήριξη για προεπιλεγμένα μηνύματα που εμφανίζονται κατά την επικύρωση εισόδου

Ημερομηνία δημιουργίας σελίδας :

περιβάλλον

πυρήνας ASP.NET
  • 5.0 MVC

Αρχικά

Η πολύγλωσση υποστήριξη των προεπιλεγμένων μηνυμάτων επικύρωσης εισόδου με αυτές τις Συμβουλές ενδέχεται να μην έχει ολοκληρωθεί. Σε παρακαλώ ασχολήσου με τους αγνοούμενους.

Επίσης, η αλλαγή της γλώσσας ανάλογα με την κατάσταση περιόδου λειτουργίας ή την κατάσταση προσωρινής μνήμης ενδέχεται να εξακολουθεί να εμφανίζεται σε κείμενο από την προηγούμενη γλώσσα.

Υπάρχουν διάφοροι τρόποι για να αλλάξετε το προεπιλεγμένο μήνυμα επικύρωσης εισόδου, καθένα από τα οποία μπορεί να συνδυαστεί. Δεν είναι ένα από αυτά, οπότε αν θέλετε να το αλλάξετε αξιόπιστα, πρέπει να αντιστοιχήσετε με όλα τα μοτίβα.

προϋπόθεση

Αυτές οι Συμβουλές γράφονται ως κατανόηση των ακόλουθων συμβουλών:

Επίσης, εάν δημιουργείτε ένα νέο έργο, πρέπει να έχετε προσθέσει τα ακόλουθα αρχεία και κώδικα με βάση τις παραπάνω συμβουλές.

  • Δημιουργήστε ένα αρχείο SharedResource.resx (+en, es). (Δεδομένου ότι μόνο το μήνυμα αυτών των Συμβουλών μεταφράζεται, τα περιεχόμενα ενδέχεται να είναι κενά.)
  • Δημιουργία αρχείου sharedResource.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 Το όνομα του κλειδιού είναι προαιρετικό. Το προεπιλεγμένο αγγλικό μήνυμα στη στήλη σχολίων είναι (τα σχόλια δεν χρειάζεται να συμπεριληφθούν). Δεν θα χρειαστεί να μεταφράσετε κάθε γλώσσα, επομένως θα πρέπει να κάνετε τη δική σας μετάφραση (το δείγμα κώδικα περιλαμβάνει επίσης το μεταφρασμένο 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} χρησιμοποιώ το Simple με το οποίο μπορείτε να αντικαταστήσετε τις τιμές που 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 θα γράψω μια τάξη που ονομάζεται και θα γράψω τον κωδικό ως εξής: Η θέση του αρχείου κώδικα είναι αυθαίρετη, αλλά το δείγμα τοποθετείται σε ένα φάκελο που ονομάζεται Adapter.

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 αντικαθιστά .

Αν το εκτελέσετε, ελέγξτε και το μήνυμα αλλάζει, δεν πειράζει.

Άλλα γενικά μηνύματα

Ορισμένα μηνύματα δεν είναι μεταφρασμένες μετά από εσάς μέχρι στιγμής. Για παράδειγμα, μηνύματα που καθορίζονται μόνο σε Javascript.

Ο προορισμός είναι jquery.validate.js το μήνυμα που παρατίθεται στο αρχείο.

Επειδή αυτό το αρχείο δεν πρέπει να επεξεργαστεί απευθείας, για να αντικαταστήσετε αυτά τα μηνύματα με μεταφρασμένο κείμενο, input πρέπει να προσθέσετε ένα χαρακτηριστικό όπως στο tag και να ορίσετε 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 μέθοδο.

Συγχώνευση χαρακτηριστικών ως χαρακτηριστικών του tag εισόδου για επιστροφή 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>

Υπάρχουν άλλα μη μεταφρασμένη μηνύματα, αλλά μπορείτε να τα εντοπίσετε με τις μεθόδους που περιγράφονται παραπάνω. Προσθέστε κώδικα όταν τον χρειάζεστε.