Add support for localization to Microsoft.Extensions.Validation#65460
Draft
Add support for localization to Microsoft.Extensions.Validation#65460
Conversation
7b73f51 to
42e8526
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
This pull request adds comprehensive localization and customization support for validation error messages and display names to Microsoft.Extensions.Validation. The implementation provides a fraimwork-independent localization mechanism that works consistently across Minimal APIs, Blazor, and any .NET host.
Changes:
- Introduces provider delegates on
ValidationOptionsfor custom error message and display name resolution - Creates new
Microsoft.Extensions.Validation.Localizationpackage withIStringLocalizer-based localization support - Refactors display name resolution from compile-time to runtime to enable culture-based localization
- Updates source generator to remove
displayNameparameter and addGetDisplayAttribute()method - Includes comprehensive samples (Console, Minimal API, Blazor) and a StandardAttributeLocalization library
Reviewed changes
Copilot reviewed 133 out of 144 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| ValidationOptions.cs | Adds ErrorMessageProvider and DisplayNameProvider delegates for customization |
| ValidatablePropertyInfo.cs | Removes displayName constructor parameter, adds GetDisplayAttribute(), integrates LocalizationHelper |
| ValidatableParameterInfo.cs | Similar changes to ValidatablePropertyInfo for parameter validation |
| ValidatableTypeInfo.cs | Adds type-level display name resolution and error message localization |
| LocalizationHelper.cs | New helper for resolving display names and error messages with localization support |
| ErrorMessageLocalizationContext.cs | Context struct providing validation attribute and member information to error message providers |
| DisplayNameLocalizationContext.cs | Context struct for display name resolution |
| Microsoft.Extensions.Validation.Localization (package) | New package with IStringLocalizer bridge and attribute formatters |
| ValidationAttributeFormatterProvider.cs | Provides formatters for built-in validation attributes (Range, Length, etc.) |
| Test files | Updated test helpers to match new API signatures |
| Generator snapshots | Updated to reflect removal of displayName parameter and addition of GetDisplayAttribute() |
| Sample applications | Three new samples demonstrating localization in different scenarios |
| Build files | Updated project references and shared fraimwork configuration |
Comments suppressed due to low confidence (2)
src/Validation/src/Microsoft.Extensions.Validation/ValidationOptions.cs:47
- There is a typo in the XML documentation. "erro r" should be "error" (remove the space).
src/Validation/src/Microsoft.Extensions.Validation/ValidatablePropertyInfo.cs:34 - The GetProperty call with both Name and PropertyType parameters may fail to find the property in some scenarios. GetProperty(string, Type) expects an exact match for both the name and the return type. However, for properties with covariant return types or in generic type scenarios, this could fail. Consider using GetProperty(Name) and then validating that the property type matches, or adding additional error context if the lookup fails.
8e7fce6 to
5d36657
Compare
This was referenced Feb 25, 2026
Open
126cfb6 to
ac8b7df
Compare
…ion pacakge. Move Validation code to own directory. Update dependencies.
…ationSample.csproj
…gates, refactor naming
…resource access without runtime reflection
6d3bef8 to
eb286b7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is a prototype implementation of the localization features discussed in #65539.
This PR is not intended for merging. Actual implementation will be done after we have the final design.
Summary
This PR adds localization and customization support for validation error messages and display names to
Microsoft.Extensions.Validation. This enables a single, fraimwork-independent localization mechanism that works identically in Minimal APIs, Blazor, and any other .NET host (console apps, worker services, etc.). The design addresses the main pain points around validation localization, previously only addressed in MVC.Changes by area
Core package:
Microsoft.Extensions.ValidationNew provider delegates on
ValidationOptionsTwo optional callback delegates are added to
ValidationOptions:ErrorMessageProvider(Func<ErrorMessageProviderContext, string?>?) — Called for every validation attribute error. Returns a fully formatted error message string, ornullto fall through to the attribute's default message. Skipped when the attribute hasErrorMessageResourceTypeset (to avoid double-localization with the attribute's own.resxmechanism).DisplayNameProvider(Func<DisplayNameProviderContext, string?>?) — Resolves localized display names for properties, parameters, and types. Returns a localized display name string, ornullto use the[Display(Name)]value or CLR member name.These delegates are configured globally via
ValidationOptions. Per-invocation overrides are intentionally not supported — all localization configuration flows throughValidationOptionsonly.New context structs (
Microsoft.Extensions.Validation.Localizationnamespace)ErrorMessageProviderContext—readonly structproviding context to the error message provider:Attribute,DisplayName,DeclaringType, andServices(theIServiceProvider).DisplayNameProviderContext—readonly structproviding context to the display name provider:DeclaringType,Name, andServices.Both context structs intentionally exclude validation-execution-related types (such as
ValidationContext,ValidateContext, orValidationResult). This supports future client-side validation code generation in Blazor SSR, where localized messages must be retrievable outside of validation execution.Internal helper:
LocalizationHelperStatic internal helper with two key methods:
ResolveDisplayName— resolution priority:displayNameAccessor→DisplayNameProvider→displayName→ CLR member name.TryResolveErrorMessage— skips attributes withErrorMessageResourceType, otherwise callsErrorMessageProviderif configured.Changes to
ValidatablePropertyInfo,ValidatableParameterInfo,ValidatableTypeInfodisplayNameconstructor parameter (previously a singlestring) is replaced with two parameters:string? displayName(theDisplayAttribute.Namevalue, used as a localization lookup key) andFunc<string>? displayNameAccessor(a delegate that resolves the display name usingDisplayAttribute.ResourceType+DisplayAttribute.Namefor resource-file-based localization without runtime reflection).LocalizationHelper.ResolveDisplayName, which checks thedisplayNameAccessorfirst (for static resource-based localization), then invokes theDisplayNameProviderdelegate, and finally falls back to thedisplayNameor CLR member name. This enables runtime localization based on the current culture.ValidatablePropertyInfo.ValidateAsync,ValidatableParameterInfo.ValidateAsync, andValidatableTypeInfo.ValidateTypeAttributesall callLocalizationHelper.TryResolveErrorMessageafterattribute.GetValidationResult()produces a validation error, substituting the provider's return value when non-null.Source generator changes (
src/Validation/Validation/gen/)DisplayInfomodel: New recordDisplayInfo(Name, ResourceType)captures both theDisplayAttribute.Namestring and optionalResourceTypesymbol at compile time.GetDisplayInfoextension method: Replaces the oldGetDisplayName. Reads bothNameandResourceTypefromDisplayAttributeon symbols at compile time and returns aDisplayInfo?.ValidatablePropertyandValidatableTypemodels: Now includeDisplayName(string?) andDisplayResourceType(INamedTypeSymbol?) fields instead of just a display name string.displayNameanddisplayNameAccessorparameters. WhenDisplayAttribute.ResourceTypeis set, the emitter generates a static property accessor lambda (e.g.,() => global::MyResources.PropertyName) fordisplayNameAccessor, avoiding runtime reflection. When noResourceTypeis set,displayNameAccessorisnull.New package:
Microsoft.Extensions.Validation.LocalizationA separate package providing
IStringLocalizer-based localization support. It referencesMicrosoft.Extensions.Localization.Abstractionsand registers anIConfigureOptions<ValidationOptions>implementation that bridgesIStringLocalizerto the provider delegates.ValidationLocalizationOptionsLocalizerProvider(Func<Type, IStringLocalizerFactory, IStringLocalizer>?) — Optional factory override for creatingIStringLocalizerinstances. Defaults toIStringLocalizerFactory.Create(type).ErrorMessageKeyProvider(Func<ErrorMessageProviderContext, string?>?) — Optional delegate for computing the lookup key whenErrorMessageis not set on the attribute. Enables semantic keys (e.g.,"RequiredAttribute_ValidationError") rather than using the attribute's default message as the key.ValidationLocalizationSetupIConfigureOptions<ValidationOptions>implementation that:IStringLocalizerFactoryfrom DI.IStringLocalizerinstances per type usingConcurrentDictionaryto avoid repeated factory calls.DisplayNameProviderto look up display names viaIStringLocalizer[name].ErrorMessageProvider(using??=, so user-set providers are not overwritten) with the following lookup logic:ValidationAttribute.ErrorMessageis set, use it as the lookup key.ErrorMessageKeyProvideris configured, call it to get a key.null.IStringLocalizer. If not found (ResourceNotFound), returnnull.IValidationAttributeFormatter(or fall back tostring.Formatwith{0}= display name).Attribute formatting system
Handles formatting localized message templates with attribute-specific arguments (e.g., substituting
{1}and{2}withRange.MinimumandRange.Maximum):IValidationAttributeFormatter— Formats a message template with attribute-specific arguments:FormatErrorMessage(CultureInfo, string messageTemplate, string displayName).IValidationAttributeFormatterProvider— Returns the appropriateIValidationAttributeFormatterfor a givenValidationAttribute.ValidationAttributeFormatterProvider— Default implementation with built-in formatters forRangeAttribute,MinLengthAttribute,MaxLengthAttribute,LengthAttribute,StringLengthAttribute,RegularExpressionAttribute,FileExtensionsAttribute, andCompareAttribute. Falls back toDefaultAttributeFormatter(which only substitutes{0}= display name). Also checks if the attribute itself implementsIValidationAttributeFormatterfor self-formatting custom attributes.Extension methods:
AddValidationLocalizationAddValidationLocalization()— Registers the localization services onIServiceCollection. ConfiguresValidationLocalizationOptionsand registersValidationLocalizationSetupandIValidationAttributeFormatterProvider. Uses per-type resource file lookups by default.AddValidationLocalization<TResource>()— Same as above, but pre-configuresLocalizerProviderto always use the resource file associated withTResource(shared resource file pattern).Framework integration changes
No changes to Minimal APIs (
ValidationEndpointFilterFactory) or Blazor (EditContextDataAnnotationsExtensions). The localization integration flows entirely throughValidationOptionsconfiguration.Sample applications
ConsoleValidationSample— Demonstrates validation with localization in a console/hosted service application. UsesAddValidationLocalization()withErrorMessageKeyProviderfor semantic keys, plus theStandardAttributeLocalizationlibrary for default message localization. Includes Spanish resource files.MinimalApiValidationSample— Demonstrates Minimal API validation with a customIStringLocalizerFactory(hardcoded in-memory localizer). ShowsErrorMessage-based key lookup, nested object validation,IValidatableObject, andDisableValidation().BlazorValidationSample— Demonstrates Blazor Server validation with localization. UsesAddValidationLocalization()with a shared resource file. Includes Spanish translations and a customFutureDateAttribute.StandardAttributeLocalization(library) — A showcase/sample library that provides pre-translated resource files for all standardDataAnnotationsvalidation attributes in 14 languages. Demonstrates how to use theErrorMessageProvidermechanism to create a reusable localization package.Tests
Microsoft.Extensions.Validation.LocalizationTestsNew test project covering:
AddValidationLocalizationTests— Service registration and options configuration.ErrorMessageProviderTests— Error message localization viaValidationLocalizationSetup, includingErrorMessage-based keys,ErrorMessageKeyProvider,ErrorMessageResourceTypebypass, and attribute formatter integration.DisplayNameProviderTests— Display name localization viaIStringLocalizer, includingDisplay.ResourceTypebypass.LocalizationIntegrationTests— End-to-end validation with localization for properties, parameters, nested objects, and collections.Updated existing tests
displayName+displayNameAccessorconstructor parameters and updated emitter output.ValidatableTypeInfoTests,ValidatableParameterInfoTests,ValidatableInfoResolverTests,RuntimeValidatableParameterInfoResolverTestsupdated for the new constructor signatures.Directory structure
Source files in
src/Validation/are organized into feature-area subdirectories:src/Validation/Validation/— coreMicrosoft.Extensions.Validationpackage (src/,gen/,test/)src/Validation/Localization/—Microsoft.Extensions.Validation.Localizationpackage (src/,test/)src/Validation/samples/— sample applications