Wielojęzyczna obsługa komunikatów domyślnych wyświetlanych podczas sprawdzania poprawności danych wejściowych
środowisko
- Rdzeń ASP.NET
-
- 5,0 MVC
Na początku
Wielojęzyczna obsługa domyślnych komunikatów sprawdzania poprawności danych wejściowych za pomocą tych porad może nie być zakończona. Proszę zająć się brakującymi.
Ponadto zmiana języka w zależności od stanu sesji lub stanu pamięci podręcznej może nadal pojawiać się w tekście z poprzedniego języka.
Istnieje kilka sposobów zmiany domyślnego komunikatu sprawdzania poprawności danych wejściowych, z których każdy można połączyć. Nie jest to jeden z nich, więc jeśli chcesz go niezawodnie zmienić, musisz korespondować ze wszystkimi wzorcami.
przesłanka
Ta wskazówka jest napisana jako zrozumienie następujących wskazówek:
- ASP.NET Wbudowane funkcje Core MVC do przełączania wielojęzycznego
- Wielojęzyczna obsługa DataAnnotations używanych dla nazw parametrów, komunikatów sprawdzania poprawności danych wejściowych itp.
Ponadto, jeśli tworzysz nowy projekt, musisz dodać następujące pliki i kod na podstawie powyższych wskazówek.
- Utwórz plik SharedResource.resx (+en, es). (Ponieważ przetłumaczona jest tylko wiadomość z tych wskazówek, zawartość może być pusta).
- Tworzenie pliku SharedResource.cs
- Dodawanie kodu lokalizacji do startup.ConfigureServices
- Dodaj kod lokalizacji do Startup.Configure
- Dodano UserViewModel (tym razem nie kluczujemy jawnie domyślnych komunikatów w wielu językach)
- Dodano utworzone przez użytkownika akcje i widoki ekranu (Create.cshtml)
Kod startowy
W oparciu o powyższe założenia każdy kod jest następujący:
Uruchamianie.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Globalization;
namespace LocalizationDefaultValidation
{
public class Startup
{
// 省略
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddMvc()
// ローカライズに必要。Resx ファイルのフォルダパスを指定
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix, opts => { opts.ResourcesPath = "Resources"; })
// DataAnnotations のローカライズに必要
.AddDataAnnotationsLocalization(options =>
{
// DataAnnotation を使ったときのメッセージは SharedResource に集約する
options.DataAnnotationLocalizerProvider = (type, factory) => factory.Create(typeof(SharedResource));
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
// 標準の機能で切り替えたい言語を定義します。
var supportedCultures = new[]
{
new CultureInfo("ja"),
new CultureInfo("en"),
new CultureInfo("es"),
};
// 標準の言語切り替え機能を有効にします。対応しているのは「クエリ文字列」「Cookie」「Accept-Language HTTP ヘッダー」です。
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("ja"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
});
app.UseHttpsRedirection();
app.UseStaticFiles();
// 省略
}
}
}
HomeController.cs
using LocalizationDefaultValidation.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace LocalizationDefaultValidation.Controllers
{
public class HomeController : Controller
{
// 省略
public IActionResult Create()
{
SetDayOfWeeksViewData();
return View();
}
[HttpPost]
public IActionResult Create(UserViewModel model)
{
SetDayOfWeeksViewData();
// エラーなら差し戻し
if (ModelState.IsValid == false) return View(model);
// 正常に登録できた場合は Index に戻る
return RedirectToAction(nameof(Index));
}
<summary>曜日の一覧を ViewData に設定します。</summary>
private void SetDayOfWeeksViewData()
=> ViewData["DayOfWeeks"] = ((DayOfWeek[])Enum.GetValues(typeof(DayOfWeek))).Select(x => new SelectListItem(x.ToString(), ((int)x).ToString()));
}
}
Indeks.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>
Utwórz.cshtml
@model LocalizationDefaultValidation.Models.UserViewModel
@using System.Globalization;
@{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>UserViewModel</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data" >
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ID" class="control-label"></label>
<input asp-for="ID" class="form-control" />
<span asp-validation-for="ID" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="control-label"></label>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Age" class="control-label"></label>
<input asp-for="Age" class="form-control" />
<span asp-validation-for="Age" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Gender"></label>
<div>
<label><input type="radio" asp-for="Gender" value="@(GenderType.None)" />@(GenderType.None)</label>
<label><input type="radio" asp-for="Gender" value="@(GenderType.Man)" />@(GenderType.Man)</label>
<label><input type="radio" asp-for="Gender" value="@(GenderType.Woman)" />@(GenderType.Woman)</label>
</div>
<span asp-validation-for="Gender" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Birthday" class="control-label"></label>
<input asp-for="Birthday" class="form-control" />
<span asp-validation-for="Birthday" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Phone" class="control-label"></label>
<input asp-for="Phone" class="form-control" />
<span asp-validation-for="Phone" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PostalCode" class="control-label"></label>
<input asp-for="PostalCode" class="form-control" />
<span asp-validation-for="PostalCode" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreditCard" class="control-label"></label>
<input asp-for="CreditCard" class="form-control" />
<span asp-validation-for="CreditCard" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Money" class="control-label"></label>
<input asp-for="Money" class="form-control" />
<span asp-validation-for="Money" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="StartDateTime" class="control-label"></label>
<input asp-for="StartDateTime" class="form-control" />
<span asp-validation-for="StartDateTime" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="WakeUpTime" class="control-label"></label>
<input asp-for="WakeUpTime" class="form-control" />
<span asp-validation-for="WakeUpTime" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Homepage" class="control-label"></label>
<input asp-for="Homepage" class="form-control" />
<span asp-validation-for="Homepage" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MyImage" class="control-label"></label>
<input asp-for="MyImage" class="form-control" />
<span asp-validation-for="MyImage" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MyColor" class="control-label"></label>
<input asp-for="MyColor" class="form-control" type="color" />
<span asp-validation-for="MyColor" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="WorkingDays" class="control-label"></label>
<select asp-for="WorkingDays" class="form-control" asp-items="@((IEnumerable<SelectListItem>)ViewData["DayOfWeeks"])" multiple></select>
<span asp-validation-for="WorkingDays" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="VacationDay" class="control-label"></label>
<select asp-for="VacationDay" class="form-control" asp-items="@((IEnumerable<SelectListItem>)ViewData["DayOfWeeks"])" multiple></select>
<span asp-validation-for="VacationDay" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Comment" class="control-label"></label>
<textarea asp-for="Comment" class="form-control"></textarea>
<span asp-validation-for="Comment" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="FileName" class="control-label"></label>
<input asp-for="FileName" class="form-control" />
<span asp-validation-for="FileName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="UploadFile" class="control-label"></label>
<input asp-for="UploadFile" type="file" multiple />
<span asp-validation-for="UploadFile" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Month" class="control-label"></label>
<input asp-for="Month" class="form-control" type="month" />
<span asp-validation-for="Month" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Search" class="control-label"></label>
<input asp-for="Search" class="form-control" type="search" />
<span asp-validation-for="Search" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Range" class="control-label"></label>
<input asp-for="Range" class="form-control" type="range" min="10" max="100" />
<span asp-validation-for="Range" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Week" class="control-label"></label>
<input asp-for="Week" class="form-control" type="week" />
<span asp-validation-for="Week" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="IsAccepted" /> @Html.DisplayNameFor(model => model.IsAccepted)
</label>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" asp-route-culture="@CultureInfo.CurrentCulture.Name" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
UserViewModel.cs
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace LocalizationDefaultValidation.Models
{
public class UserViewModel
{
[Required]
[Display(Name = "ID (半角英数字)")]
[StringLength(20)]
[RegularExpression(@"^[0-9a-zA-Z]*$")]
public string ID { get; set; }
[StringLength(50)]
public string Name { get; set; }
[StringLength(50, MinimumLength = 8)]
[DataType(DataType.Password)]
[RegularExpression(@"^[0-9a-zA-Z]*$")]
public string Password { get; set; }
[StringLength(50, MinimumLength = 8)]
[DataType(DataType.Password)]
[Compare(nameof(Password))]
public string ConfirmPassword { get; set; }
[EmailAddress]
[DataType(DataType.EmailAddress)] // スキャフォールディングするときはコメントアウトする
public string Email { get; set; }
//[Int]
[Range(0, 150)]
public int Age { get; set; }
[Required]
[EnumDataType(typeof(GenderType))]
public GenderType Gender { get; set; }
[DataType(DataType.Date)]
public DateTime Birthday { get; set; }
[Phone()]
[DataType(DataType.PhoneNumber)] // スキャフォールディングするときはコメントアウトする
public string Phone { get; set; }
[DataType(DataType.PostalCode)]
public string PostalCode { get; set; }
[CreditCard()]
[DataType(DataType.CreditCard)] // スキャフォールディングするときはコメントアウトする
public string CreditCard { get; set; }
[DataType(DataType.Currency)]
public decimal Money { get; set; }
[DataType(DataType.DateTime)]
public DateTime StartDateTime { get; set; }
[DataType(DataType.Time)]
public TimeSpan WakeUpTime { get; set; }
[Url]
[DataType(DataType.Url)] // スキャフォールディングするときはコメントアウトする
public string Homepage { get; set; }
[Url]
[DataType(DataType.ImageUrl)] // スキャフォールディングするときはコメントアウトする
public string MyImage { get; set; }
public string MyColor { get; set; }
[MaxLength(5)]
public DayOfWeek[] WorkingDays { get; set; }
[MinLength(3)]
public DayOfWeek[] VacationDay { get; set; }
[StringLength(200)]
[DataType(DataType.MultilineText)]
public string Comment { get; set; }
[Display(Name = "ファイル名 (.png)")]
[FileExtensions(Extensions = "png")]
public string FileName { get; set; }
[DataType(DataType.Upload)]
public List<IFormFile> UploadFile { get; set; }
public DateTime Month { get; set; }
public string Search { get; set; }
[Range(10, 100)]
public int Range { get; set; }
public string Week { get; set; }
[Required]
public bool IsAccepted { get; set; }
}
public enum GenderType
{
None,
Man,
Woman,
Other,
}
}
SharedResource.cs
namespace LocalizationDefaultValidation
{
// クラス名は作成した .resx のファイル名と同じにする必要がある
public class SharedResource { }
}
Modelowanie komunikatów wiążących
Jeśli ustawisz typ właściwości modelu int
na lub i powiążesz go z DateTime
widokiem i spróbujesz zarejestrować go bez wpisu, zobaczysz komunikat taki jak "Wartość '' jest nieprawidłowa.".
Komunikaty te pojawiają się, gdy pusty ciąg int
nie może być powiązany, na przykład w modelu.
Ponieważ jest to czas, którego nie można wiązać, występuje tylko przed sprawdzaniem poprawności innych wartości i przetwarzaniem po stronie serwera.
Te komunikaty są DefaultModelBindingMessageProvider
zdefiniowane jako .
Startup.cs
Dodaj do argumentów metody, które były zdefiniowane od początku services.AddControllersWithViews
w programie , a następnie dodaj Action
.
Możesz być wielojęzyczny, ustawiając przekazany w options
ModelBindingMessageProvider
argumencie.
Istnieje 11 typów wiadomości, które można ustawić, więc najpierw należy zdefiniować wiadomość w następujący sposób: SharedResource.resx
Nazwa klucza jest opcjonalna. Domyślna wiadomość w języku angielskim w kolumnie komentarzy to (komentarze nie muszą być uwzględniane).
Nie będziesz musiał tłumaczyć każdego języka, więc powinieneś wykonać własne tłumaczenie (przykładowy kod zawiera również przetłumaczony SharedResource.resx).
do wartości | nazwy | |
---|---|---|
ModelBinding_AttemptedValueIsInvalid | "{0}" jest nieprawidłową wartością w {1}. | Wartość "{0}" nie jest prawidłowa dla {1}. |
ModelBinding_MissingBindRequiredValue | Wartość {0} nie jest określona. | Wartość parametru lub właściwości "{0}" nie została podana. |
ModelBinding_MissingKeyOrValue | Wymagane. | Wymagana jest wartość. |
ModelBinding_MissingRequestBodyRequiredValue | Wniosek musi mieć organ. | Wymagana jest niepusty korpus żądania. |
ModelBinding_NonPropertyAttemptedValueIsInvalid | "{0}" jest nieprawidłowe. | Wartość "{0}" jest nieprawidłowa. |
ModelBinding_NonPropertyUnknownValueIsInvalid | Wartość jest nieprawidłowa. | Podawana wartość jest nieprawidłowa. |
ModelBinding_NonPropertyValueMustBeANumber | Należy podano numer. | Pole musi być liczbą. |
ModelBinding_UnknownValueIsInvalid | Wartość {0} jest nieprawidłowa. | Podawana wartość jest nieprawidłowa dla {0}. |
ModelBinding_ValueIsInvalid | "{0}" jest nieprawidłowe. | Wartość "{0}" jest nieprawidłowa. |
ModelBinding_ValueMustBeANumber | {0} musi być liczba. | Pole {0} musi być liczbą. |
ModelBinding_ValueMustNotBeNull | Wymagane dane wejściowe. | Wartość "{0}" jest nieprawidłowa. |
Napraw uruchamianie.cs w następujący sposób:
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
});
// 省略 (初期コード)
}
}
}
Kluczowym punktem jest services.AddControllersWithViews
dodanie a do metody, która odbiera option
Action
,
options.ModelBindingMessageProvider
Ustawiam zlokalizowany tekst dla każdej metody Set w programie .
Przetłumaczony tekst jest IStringLocalizer
pobierany z pliku .
Nie ma możliwości otrzymania bezpośredniego IStringLocalizer
kodu, który jest trochę okrągłym kodem.
SharedResource.resx
Jeśli generujesz kod, otrzymasz zlokalizowane wartości bezpośrednio z niego.
IStringLocalizer
Klucz określony w SharedResource.resx
polu Określa klucz dodany do pliku .
Ustaw go tak, aby pasował do zawartości każdej metody Set.
Ponadto, ponieważ każda wiadomość ma argument ciągu formatu, taki jak , {0}{1}
używam Simple z, aby można było zastąpić string.Format
Func
odebrane wartości.
Lokalizuj domyślne komunikaty sprawdzania poprawności
Domyślny komunikat o błędzie, gdy atrybuty Required
właściwości modelu są ustawione na lub tak, jest określany przez strukturę i jest StringLength
zasadniczo w języku angielskim.
Można IValidationAttributeAdapterProvider
je zlokalizować, definiując klasę pochodzącą z interfejsu.
Przede SharedResource.resx
wszystkim zarejestruj tekst, który jest wielojęzyczny.
Nazwa klucza jest dowolna, ale aby uprościć program, zarejestruj go w postaci "Validator_< nazwa atrybutu sprawdzania poprawności>".
do wartości | nazwy | |
---|---|---|
Validator_CompareAttribute | {0} i {1} nie pasują do siebie. | "{0}" i "{1}" nie pasują do siebie. |
Validator_CreditCardAttribute | {0} nie jest prawidłowy numer karty. | Pole {0} nie jest prawidłowym numerem karty kredytowej. |
Validator_DataTypeAttribute_Date | Wprowadź prawidłową datę. | Podaj prawidłową datę. |
Validator_EmailAddressAttribute | {0} jest nieprawidłowy adres e-mail. | Pole {0} nie jest prawidłowym adresem e-mail. |
Validator_FileExtensionsAttribute | {0} akceptuje tylko pliki z następującymi rozszerzeniami: : {1} | Pole {0} akceptuje tylko pliki z następującymi rozszerzeniami: {1} |
Validator_MaxLengthAttribute | {0} musi być typu ciągu lub tablicy o maksymalnej długości "{1}". | Pole {0} musi być typem ciągu lub tablicy o maksymalnej długości "{1}". |
Validator_MinLengthAttribute | {0} musi być typu ciągu lub tablicy o minimalnej długości "{1}". | Pole {0} musi być typem ciągu lub tablicy o minimalnej długości "{1}". |
Validator_PhoneAttribute | {0} nie jest prawidłowym numerem telefonu. | Pole {0} nie jest prawidłowym numerem telefonu. |
Validator_RangeAttribute | {0} muszą wynosić od {1} do {2}. | {0} pola musi znajdować się w zakresie od {1} do {2}. |
Validator_RegularExpressionAttribute | {0} musi być zgodny z wyrażeniem regularnym "{1}". | Pole {0} musi być zgodne z wyrażeniem regularnym "{1}". |
Validator_RequiredAttribute | {0} jest wymagane. | Pole {0} jest wymagane. |
Validator_StringLengthAttribute | {0} musi mieścić się w {1} cyfrach. | Pole {0} musi być ciągiem o maksymalnej długości {1}. |
Validator_UrlAttribute | {0} nie jest prawidłowym adresem URL. | Pole {0} nie jest prawidłowym w pełni kwalifikowanym adresem URL http, https lub ftp. |
Validator_StringLengthAttributeWithMin | {0} musi być co najmniej {2} {1} cyfr. | Pole {0} musi być ciągiem o maksymalnej długości {1} i minimalnej długości {2}. |
Następnie utwórz następującą klasę AdapterProvider:
Nazwa klasy jest dowolna, ale tym razem CustomValidationAttributeAdapterProvider
napiszę klasę o nazwie i napiszę kod w następujący sposób:
Lokalizacja pliku kodu jest dowolna, ale próbka jest umieszczana w folderze o nazwie 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);
}
}
}
Jeśli ustawisz właściwości modelu na Required
lub , metoda jest wywoływana dla każdej StringLength
IValidationAttributeAdapterProvider.GetAttributeAdapter
walidacji.
ErrorMessageResourceName
Jeśli właściwość zawiera wartość, kod po stronie modelu ma już komunikat lub klucz lokalizacji, więc zwraca się tak, jak jest.
Jeśli jest pusty, dopasuj nazwy klas prefiksu i atrybutu sprawdzania poprawności, aby uczynić go kluczem lokalizacji, a następnie pobierz zlokalizowany tekst z klucza i ustaw go ErrorMessage
na .
Umożliwia to zlokalizowanie większości wiadomości domyślnych.
Następnie zarejestruj tę klasę w .cs startowym. Zasadniczo możesz dodać go w następujący sposób.
// 省略
using Microsoft.AspNetCore.Mvc.DataAnnotations;
namespace LocalizationDefaultValidation
{
public class Startup
{
// 省略
// このメソッドはランタイムによって呼び出されます。 このメソッドを使用して、コンテナーにサービスを追加します。
public void ConfigureServices(IServiceCollection services)
{
// 省略
// 作成した CustomValidationAttributeAdapterProvider をシングルトンとして登録します
services.AddSingleton<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
}
// 省略
}
}
Uruchom go, aby upewnić się, że działa poprawnie.
Zmienianie istniejącej wiadomości według parametrów
Na przykład StringLength
komunikat atrybutu jest zlokalizowany, na przykład "{0} musi znajdować się w {1} cyfrach".
MinimumLength
Jeśli właściwość jest ustawiona, możesz zmienić {0} jako "Określ {2} cyfr lub więcej {1} w obrębie kilku cyfr".
W CustomValidationAttributeAdapterProvider
takim przypadku rozszerz klasę w następujący sposób:
// 省略
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);
}
}
// 省略
}
}
}
Ponieważ metoda jest wywoływana dla każdej GetAttributeAdapter
walidacji, jeśli attribute
StringLengthAttribute
przekazana zmienna była MinimumLength
atrybutem, sprawdź właściwość,
Jeśli jest ustawiona, otrzymasz komunikat, który uwzględnia również minimalną liczbę cyfr i ErrorMessage
zastępuje .
Jeśli go uruchomisz, zaznaczysz, a komunikat umieje się, wszystko jest w porządku.
Inne wiadomości ogólne
Niektóre wiadomości nie są lokalizowane po zrobieniu tego do tej pory. Na przykład wiadomości, które są określane tylko w Javascript.
Obiektem docelowym jest jquery.validate.js
komunikat wymieniony w pliku.
Ponieważ ten plik nie powinien być edytowany bezpośrednio, aby zastąpić te wiadomości zlokalizowanym input
tekstem, należy dodać atrybut podobny do znacznika i ustawić data-val-XXXX
tam zlokalizowany tekst.
Część XXXX zawiera klucz na jquery.validate.js
powyższym rysunku. (np. data-val-number
itp.)
data-val-XXXX
Aby ustawić zlokalizowany tekst na atrybuty w
Zlokalizujmy wiadomość, gdy znaki nieliczbowe są zawarte w polu Wejścia Wiek jako przykład.
UserViewModel.Age
ma Range
atrybut, a wyjściowy kod HTML data-val-range
dołącza atrybut input.
<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>
Jednak data-val-number
numeryczny komunikat kontrolny nie jest zlokalizowany, ponieważ nie ma atrybutu.
Dodasz kolejny atrybut po stronie modelu, aby wydrukować również zlokalizowany atrybut dodany do data-val-number
wiadomości.
Pierwszym krokiem jest utworzenie klasy atrybutów, która sprawdza, czy jest to IntAttribute
liczba: Jest on ograniczony do tutaj, ale możesz int
go zmienić w zależności od sytuacji.
Plik kodu może znajdować się w dowolnym miejscu, ale tym razem znajduje się w folderze Adapter.
using System.ComponentModel.DataAnnotations;
namespace LocalizationDefaultValidation
{
public class IntAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
// 渡された値が int であれば有効な値とする
return value is int;
}
}
}
Jeśli ustawiona wartość jest int
typem, jest to atrybut, który jest tylko normalnym werdyktem, ale w praktyce jest w 100% normalny, jeśli jest ustawiony na właściwość typu int, więc przetwarzanie tej klasy samo w sobie nie ma sensu.
Celem tego czasu jest wyświetlenie zlokalizowanych komunikatów o błędach w kliencie.
Następnie utwórz następującą IntAttributeAdapter
klasę adaptera: Znajduje się on również w folderze Adapter.
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;
}
}
Chodzi tutaj AddValidation
o metodę opisaną w MergeAttribute
metodzie.
Scalanie atrybutów jako atrybutów znacznika wejściowego w celu data-val-number
powrotu do klienta.
Wartość ustawiona w atrybcie ustawia zlokalizowany komunikat o błędzie.
Zwróć tę kartę w utworzonej CustomValidationAttributeAdapterProvider
wcześniej klasie.
// 省略
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);
}
// 省略 (前に追加したコード)
}
}
}
Jeśli wartość do sprawdzenia poprawności IntAttribute
to , zwróć utworzoną wcześniej IntAttributeAdapter
wartość.
Właściwość z atrybutem Int jest teraz dodawana, gdy jest wyświetlana data-val-number
po stronie klienta.
Na koniec UserViewModel.Age
IntAttribute
dodaj do .
Po naprawieniu kodu wypróbuj go, aby sprawdzić, czy chcesz go wyewidencjonować.
<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>
Istnieją inne niezlokalizowane wiadomości, ale można je zlokalizować w metodach opisanych powyżej. Dodawaj kod, kiedy go potrzebujesz.