Багатомовна підтримка повідомлень за промовчанням, що відображаються під час перевірки вводу
середовище
- ASP.NET ядро
-
- 5.0 MVC
Спочатку
Багатомовна підтримка повідомлень перевірки вводу за промовчанням за допомогою цих порад може бути не завершена. Будь ласка, розберіться з зниклими безвісти.
Крім того, зміна мови залежно від стану сеансу або стану кеша все ще може відображатися в тексті з попередньої мови.
Існує кілька способів змінити повідомлення перевірки вводу за промовчанням, кожен з яких можна об'єднати. Він не один з них, тому якщо ви хочете його надійно змінити, потрібно відповідати всім візерункам.
Передумови
Ця порада написана як розуміння наступних порад:
- ASP.NET Вбудовані функції Core MVC для багатомовного перемикання
- Багатомовна підтримка dataAnnotations, що використовуються для імен параметрів, повідомлень перевірки вводу тощо.
Крім того, якщо ви створюєте новий проект, ви повинні додати наступні файли та код на основі наведених вище порад.
- Створіть файл SharedResource.resx (+en, es). (Оскільки перекладено лише повідомлення цієї поради, вміст може бути пустим.)
- Створення файлу спільного джерела.cs
- Додати код локалізації до запуску.ConfigureServices
- Додати код локалізації до запуску.Configure
- Додано UserViewModel (цього разу ми явно не бремимо повідомлення за замовчуванням на декількох мовах)
- Додано створені користувачем дії та подання екрана (Create.cshtml)
Початковий код
Виходячи з наведених вище припущень, кожен код повинен бути таким:
Запуск.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Globalization;
namespace LocalizationDefaultValidation
{
public class Startup
{
// 省略
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddMvc()
// ローカライズに必要。Resx ファイルのフォルダパスを指定
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix, opts => { opts.ResourcesPath = "Resources"; })
// DataAnnotations のローカライズに必要
.AddDataAnnotationsLocalization(options =>
{
// DataAnnotation を使ったときのメッセージは SharedResource に集約する
options.DataAnnotationLocalizerProvider = (type, factory) => factory.Create(typeof(SharedResource));
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
// 標準の機能で切り替えたい言語を定義します。
var supportedCultures = new[]
{
new CultureInfo("ja"),
new CultureInfo("en"),
new CultureInfo("es"),
};
// 標準の言語切り替え機能を有効にします。対応しているのは「クエリ文字列」「Cookie」「Accept-Language HTTP ヘッダー」です。
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("ja"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
});
app.UseHttpsRedirection();
app.UseStaticFiles();
// 省略
}
}
}
ГоловнаКонтролер.cs
using LocalizationDefaultValidation.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace LocalizationDefaultValidation.Controllers
{
public class HomeController : Controller
{
// 省略
public IActionResult Create()
{
SetDayOfWeeksViewData();
return View();
}
[HttpPost]
public IActionResult Create(UserViewModel model)
{
SetDayOfWeeksViewData();
// エラーなら差し戻し
if (ModelState.IsValid == false) return View(model);
// 正常に登録できた場合は Index に戻る
return RedirectToAction(nameof(Index));
}
<summary>曜日の一覧を ViewData に設定します。</summary>
private void SetDayOfWeeksViewData()
=> ViewData["DayOfWeeks"] = ((DayOfWeek[])Enum.GetValues(typeof(DayOfWeek))).Select(x => new SelectListItem(x.ToString(), ((int)x).ToString()));
}
}
Index.cshtml
@{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
<p>ユーザー作成画面に遷移します。リンクごとに選択した言語で表示できます。</p>
<ul>
<li><a asp-action="Create">Create</a></li>
<li><a asp-action="Create" asp-route-culture="ja">Create (ja)</a></li>
<li><a asp-action="Create" asp-route-culture="en">Create (en)</a></li>
<li><a asp-action="Create" asp-route-culture="es">Create (es)</a></li>
</ul>
Create.cshtml
@model LocalizationDefaultValidation.Models.UserViewModel
@using System.Globalization;
@{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>UserViewModel</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data" >
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ID" class="control-label"></label>
<input asp-for="ID" class="form-control" />
<span asp-validation-for="ID" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="control-label"></label>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Age" class="control-label"></label>
<input asp-for="Age" class="form-control" />
<span asp-validation-for="Age" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Gender"></label>
<div>
<label><input type="radio" asp-for="Gender" value="@(GenderType.None)" />@(GenderType.None)</label>
<label><input type="radio" asp-for="Gender" value="@(GenderType.Man)" />@(GenderType.Man)</label>
<label><input type="radio" asp-for="Gender" value="@(GenderType.Woman)" />@(GenderType.Woman)</label>
</div>
<span asp-validation-for="Gender" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Birthday" class="control-label"></label>
<input asp-for="Birthday" class="form-control" />
<span asp-validation-for="Birthday" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Phone" class="control-label"></label>
<input asp-for="Phone" class="form-control" />
<span asp-validation-for="Phone" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PostalCode" class="control-label"></label>
<input asp-for="PostalCode" class="form-control" />
<span asp-validation-for="PostalCode" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreditCard" class="control-label"></label>
<input asp-for="CreditCard" class="form-control" />
<span asp-validation-for="CreditCard" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Money" class="control-label"></label>
<input asp-for="Money" class="form-control" />
<span asp-validation-for="Money" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="StartDateTime" class="control-label"></label>
<input asp-for="StartDateTime" class="form-control" />
<span asp-validation-for="StartDateTime" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="WakeUpTime" class="control-label"></label>
<input asp-for="WakeUpTime" class="form-control" />
<span asp-validation-for="WakeUpTime" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Homepage" class="control-label"></label>
<input asp-for="Homepage" class="form-control" />
<span asp-validation-for="Homepage" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MyImage" class="control-label"></label>
<input asp-for="MyImage" class="form-control" />
<span asp-validation-for="MyImage" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MyColor" class="control-label"></label>
<input asp-for="MyColor" class="form-control" type="color" />
<span asp-validation-for="MyColor" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="WorkingDays" class="control-label"></label>
<select asp-for="WorkingDays" class="form-control" asp-items="@((IEnumerable<SelectListItem>)ViewData["DayOfWeeks"])" multiple></select>
<span asp-validation-for="WorkingDays" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="VacationDay" class="control-label"></label>
<select asp-for="VacationDay" class="form-control" asp-items="@((IEnumerable<SelectListItem>)ViewData["DayOfWeeks"])" multiple></select>
<span asp-validation-for="VacationDay" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Comment" class="control-label"></label>
<textarea asp-for="Comment" class="form-control"></textarea>
<span asp-validation-for="Comment" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="FileName" class="control-label"></label>
<input asp-for="FileName" class="form-control" />
<span asp-validation-for="FileName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="UploadFile" class="control-label"></label>
<input asp-for="UploadFile" type="file" multiple />
<span asp-validation-for="UploadFile" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Month" class="control-label"></label>
<input asp-for="Month" class="form-control" type="month" />
<span asp-validation-for="Month" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Search" class="control-label"></label>
<input asp-for="Search" class="form-control" type="search" />
<span asp-validation-for="Search" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Range" class="control-label"></label>
<input asp-for="Range" class="form-control" type="range" min="10" max="100" />
<span asp-validation-for="Range" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Week" class="control-label"></label>
<input asp-for="Week" class="form-control" type="week" />
<span asp-validation-for="Week" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="IsAccepted" /> @Html.DisplayNameFor(model => model.IsAccepted)
</label>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" asp-route-culture="@CultureInfo.CurrentCulture.Name" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
UserViewModel.cs
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace LocalizationDefaultValidation.Models
{
public class UserViewModel
{
[Required]
[Display(Name = "ID (半角英数字)")]
[StringLength(20)]
[RegularExpression(@"^[0-9a-zA-Z]*$")]
public string ID { get; set; }
[StringLength(50)]
public string Name { get; set; }
[StringLength(50, MinimumLength = 8)]
[DataType(DataType.Password)]
[RegularExpression(@"^[0-9a-zA-Z]*$")]
public string Password { get; set; }
[StringLength(50, MinimumLength = 8)]
[DataType(DataType.Password)]
[Compare(nameof(Password))]
public string ConfirmPassword { get; set; }
[EmailAddress]
[DataType(DataType.EmailAddress)] // スキャフォールディングするときはコメントアウトする
public string Email { get; set; }
//[Int]
[Range(0, 150)]
public int Age { get; set; }
[Required]
[EnumDataType(typeof(GenderType))]
public GenderType Gender { get; set; }
[DataType(DataType.Date)]
public DateTime Birthday { get; set; }
[Phone()]
[DataType(DataType.PhoneNumber)] // スキャフォールディングするときはコメントアウトする
public string Phone { get; set; }
[DataType(DataType.PostalCode)]
public string PostalCode { get; set; }
[CreditCard()]
[DataType(DataType.CreditCard)] // スキャフォールディングするときはコメントアウトする
public string CreditCard { get; set; }
[DataType(DataType.Currency)]
public decimal Money { get; set; }
[DataType(DataType.DateTime)]
public DateTime StartDateTime { get; set; }
[DataType(DataType.Time)]
public TimeSpan WakeUpTime { get; set; }
[Url]
[DataType(DataType.Url)] // スキャフォールディングするときはコメントアウトする
public string Homepage { get; set; }
[Url]
[DataType(DataType.ImageUrl)] // スキャフォールディングするときはコメントアウトする
public string MyImage { get; set; }
public string MyColor { get; set; }
[MaxLength(5)]
public DayOfWeek[] WorkingDays { get; set; }
[MinLength(3)]
public DayOfWeek[] VacationDay { get; set; }
[StringLength(200)]
[DataType(DataType.MultilineText)]
public string Comment { get; set; }
[Display(Name = "ファイル名 (.png)")]
[FileExtensions(Extensions = "png")]
public string FileName { get; set; }
[DataType(DataType.Upload)]
public List<IFormFile> UploadFile { get; set; }
public DateTime Month { get; set; }
public string Search { get; set; }
[Range(10, 100)]
public int Range { get; set; }
public string Week { get; set; }
[Required]
public bool IsAccepted { get; set; }
}
public enum GenderType
{
None,
Man,
Woman,
Other,
}
}
Загальне джерело.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
додавання a до методу, який отримує 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
текстом, потрібно додати такий атрибут до тегу та встановити data-val-XXXX
там локалізований текст.
Частина XXXX містить ключ на малюнку jquery.validate.js
вище. (наприклад, data-val-number
і т.д.)
data-val-XXXX
Щоб встановити локалізований текст атрибути в
Давайте локалізуємо повідомлення, коли нечислові символи включені в поле введення Age як приклад.
UserViewModel.Age
має Range
атрибут, а вихідний HTML data-val-range
додає вхідний атрибут.
<div class="form-group">
<label class="control-label" for="Age">Age</label>
<input class="form-control" type="number" data-val="true" data-val-range="Ageは0から150の範囲で指定してください。" data-val-range-max="150" data-val-range-min="0" data-val-required="Ageは必須です。" id="Age" name="Age" value="" />
<span class="text-danger field-validation-valid" data-valmsg-for="Age" data-valmsg-replace="true"></span>
</div>
Однак, data-val-number
числове повідомлення перевірки не локалізовано, оскільки атрибут відсутній.
Ви також додасте інший атрибут на стороні моделі, щоб надрукувати атрибут, доданий до data-val-number
локалізованого повідомлення.
Першим кроком є створення класу атрибутів, який перевіряє, чи є це IntAttribute
числом: Вона обмежена тут, але ви int
можете змінити його в залежності від ситуації.
Кодовий файл може бути в будь-якому місці, але на цей раз він є в папці Adapter.
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>
Є й інші нелокалізовані повідомлення, але їх можна локалізувати описаними вище способами. Додайте код, коли він вам потрібен.