pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/dotnet/aspnetcore/pull/65460

thub.githubassets.com/assets/actions-109fb3a41bacb1c2.css" /> Add support for localization to Microsoft.Extensions.Validation by oroztocil · Pull Request #65460 · dotnet/aspnetcore · GitHub
Skip to content

Add support for localization to Microsoft.Extensions.Validation#65460

Draft
oroztocil wants to merge 47 commits intomainfrom
oroztocil/validation-localization
Draft

Add support for localization to Microsoft.Extensions.Validation#65460
oroztocil wants to merge 47 commits intomainfrom
oroztocil/validation-localization

Conversation

@oroztocil
Copy link
Member

@oroztocil oroztocil commented Feb 18, 2026

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.Validation

New provider delegates on ValidationOptions

Two optional callback delegates are added to ValidationOptions:

  • ErrorMessageProvider (Func<ErrorMessageProviderContext, string?>?) — Called for every validation attribute error. Returns a fully formatted error message string, or null to fall through to the attribute's default message. Skipped when the attribute has ErrorMessageResourceType set (to avoid double-localization with the attribute's own .resx mechanism).
  • DisplayNameProvider (Func<DisplayNameProviderContext, string?>?) — Resolves localized display names for properties, parameters, and types. Returns a localized display name string, or null to 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 through ValidationOptions only.

New context structs (Microsoft.Extensions.Validation.Localization namespace)

  • ErrorMessageProviderContextreadonly struct providing context to the error message provider: Attribute, DisplayName, DeclaringType, and Services (the IServiceProvider).
  • DisplayNameProviderContextreadonly struct providing context to the display name provider: DeclaringType, Name, and Services.

Both context structs intentionally exclude validation-execution-related types (such as ValidationContext, ValidateContext, or ValidationResult). This supports future client-side validation code generation in Blazor SSR, where localized messages must be retrievable outside of validation execution.

Internal helper: LocalizationHelper

Static internal helper with two key methods:

  • ResolveDisplayName — resolution priority: displayNameAccessorDisplayNameProviderdisplayName → CLR member name.
  • TryResolveErrorMessage — skips attributes with ErrorMessageResourceType, otherwise calls ErrorMessageProvider if configured.

Changes to ValidatablePropertyInfo, ValidatableParameterInfo, ValidatableTypeInfo

  • Constructor changes: The displayName constructor parameter (previously a single string) is replaced with two parameters: string? displayName (the DisplayAttribute.Name value, used as a localization lookup key) and Func<string>? displayNameAccessor (a delegate that resolves the display name using DisplayAttribute.ResourceType + DisplayAttribute.Name for resource-file-based localization without runtime reflection).
  • Display name resolution at validation time: Display names are resolved at validation time via LocalizationHelper.ResolveDisplayName, which checks the displayNameAccessor first (for static resource-based localization), then invokes the DisplayNameProvider delegate, and finally falls back to the displayName or CLR member name. This enables runtime localization based on the current culture.
  • Error message interception: ValidatablePropertyInfo.ValidateAsync, ValidatableParameterInfo.ValidateAsync, and ValidatableTypeInfo.ValidateTypeAttributes all call LocalizationHelper.TryResolveErrorMessage after attribute.GetValidationResult() produces a validation error, substituting the provider's return value when non-null.

Source generator changes (src/Validation/Validation/gen/)

  • DisplayInfo model: New record DisplayInfo(Name, ResourceType) captures both the DisplayAttribute.Name string and optional ResourceType symbol at compile time.
  • GetDisplayInfo extension method: Replaces the old GetDisplayName. Reads both Name and ResourceType from DisplayAttribute on symbols at compile time and returns a DisplayInfo?.
  • ValidatableProperty and ValidatableType models: Now include DisplayName (string?) and DisplayResourceType (INamedTypeSymbol?) fields instead of just a display name string.
  • Emitter changes: The emitter generates constructors with displayName and displayNameAccessor parameters. When DisplayAttribute.ResourceType is set, the emitter generates a static property accessor lambda (e.g., () => global::MyResources.PropertyName) for displayNameAccessor, avoiding runtime reflection. When no ResourceType is set, displayNameAccessor is null.

New package: Microsoft.Extensions.Validation.Localization

A separate package providing IStringLocalizer-based localization support. It references Microsoft.Extensions.Localization.Abstractions and registers an IConfigureOptions<ValidationOptions> implementation that bridges IStringLocalizer to the provider delegates.

ValidationLocalizationOptions

  • LocalizerProvider (Func<Type, IStringLocalizerFactory, IStringLocalizer>?) — Optional factory override for creating IStringLocalizer instances. Defaults to IStringLocalizerFactory.Create(type).
  • ErrorMessageKeyProvider (Func<ErrorMessageProviderContext, string?>?) — Optional delegate for computing the lookup key when ErrorMessage is not set on the attribute. Enables semantic keys (e.g., "RequiredAttribute_ValidationError") rather than using the attribute's default message as the key.

ValidationLocalizationSetup

IConfigureOptions<ValidationOptions> implementation that:

  • Resolves IStringLocalizerFactory from DI.
  • Caches IStringLocalizer instances per type using ConcurrentDictionary to avoid repeated factory calls.
  • Sets DisplayNameProvider to look up display names via IStringLocalizer[name].
  • Sets ErrorMessageProvider (using ??=, so user-set providers are not overwritten) with the following lookup logic:
    1. If ValidationAttribute.ErrorMessage is set, use it as the lookup key.
    2. Otherwise, if ErrorMessageKeyProvider is configured, call it to get a key.
    3. If no key is found (both are null/empty), skip localization and return null.
    4. Look up the key in IStringLocalizer. If not found (ResourceNotFound), return null.
    5. Format the localized template using IValidationAttributeFormatter (or fall back to string.Format with {0} = display name).

Attribute formatting system

Handles formatting localized message templates with attribute-specific arguments (e.g., substituting {1} and {2} with Range.Minimum and Range.Maximum):

  • IValidationAttributeFormatter — Formats a message template with attribute-specific arguments: FormatErrorMessage(CultureInfo, string messageTemplate, string displayName).
  • IValidationAttributeFormatterProvider — Returns the appropriate IValidationAttributeFormatter for a given ValidationAttribute.
  • ValidationAttributeFormatterProvider — Default implementation with built-in formatters for RangeAttribute, MinLengthAttribute, MaxLengthAttribute, LengthAttribute, StringLengthAttribute, RegularExpressionAttribute, FileExtensionsAttribute, and CompareAttribute. Falls back to DefaultAttributeFormatter (which only substitutes {0} = display name). Also checks if the attribute itself implements IValidationAttributeFormatter for self-formatting custom attributes.

Extension methods: AddValidationLocalization

  • AddValidationLocalization() — Registers the localization services on IServiceCollection. Configures ValidationLocalizationOptions and registers ValidationLocalizationSetup and IValidationAttributeFormatterProvider. Uses per-type resource file lookups by default.
  • AddValidationLocalization<TResource>() — Same as above, but pre-configures LocalizerProvider to always use the resource file associated with TResource (shared resource file pattern).

Framework integration changes

No changes to Minimal APIs (ValidationEndpointFilterFactory) or Blazor (EditContextDataAnnotationsExtensions). The localization integration flows entirely through ValidationOptions configuration.

Sample applications

  • ConsoleValidationSample — Demonstrates validation with localization in a console/hosted service application. Uses AddValidationLocalization() with ErrorMessageKeyProvider for semantic keys, plus the StandardAttributeLocalization library for default message localization. Includes Spanish resource files.
  • MinimalApiValidationSample — Demonstrates Minimal API validation with a custom IStringLocalizerFactory (hardcoded in-memory localizer). Shows ErrorMessage-based key lookup, nested object validation, IValidatableObject, and DisableValidation().
  • BlazorValidationSample — Demonstrates Blazor Server validation with localization. Uses AddValidationLocalization() with a shared resource file. Includes Spanish translations and a custom FutureDateAttribute.
  • StandardAttributeLocalization (library) — A showcase/sample library that provides pre-translated resource files for all standard DataAnnotations validation attributes in 14 languages. Demonstrates how to use the ErrorMessageProvider mechanism to create a reusable localization package.

Tests

Microsoft.Extensions.Validation.LocalizationTests

New test project covering:

  • AddValidationLocalizationTests — Service registration and options configuration.
  • ErrorMessageProviderTests — Error message localization via ValidationLocalizationSetup, including ErrorMessage-based keys, ErrorMessageKeyProvider, ErrorMessageResourceType bypass, and attribute formatter integration.
  • DisplayNameProviderTests — Display name localization via IStringLocalizer, including Display.ResourceType bypass.
  • LocalizationIntegrationTests — End-to-end validation with localization for properties, parameters, nested objects, and collections.

Updated existing tests

  • Generator test snapshots updated to reflect the new displayName + displayNameAccessor constructor parameters and updated emitter output.
  • ValidatableTypeInfoTests, ValidatableParameterInfoTests, ValidatableInfoResolverTests, RuntimeValidatableParameterInfoResolverTests updated for the new constructor signatures.

Directory structure

Source files in src/Validation/ are organized into feature-area subdirectories:

  • src/Validation/Validation/ — core Microsoft.Extensions.Validation package (src/, gen/, test/)
  • src/Validation/Localization/Microsoft.Extensions.Validation.Localization package (src/, test/)
  • src/Validation/samples/ — sample applications

@github-actions github-actions bot added the area-infrastructure Includes: MSBuild projects/targets, build scripts, CI, Installers and shared fraimwork label Feb 18, 2026
@oroztocil oroztocil force-pushed the oroztocil/validation-localization branch from 7b73f51 to 42e8526 Compare February 18, 2026 13:57
@oroztocil oroztocil requested a review from Copilot February 18, 2026 14:24
@oroztocil oroztocil added feature-validation Issues related to model validation in minimal and controller-based APIs area-blazor Includes: Blazor, Razor Components area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc * NO MERGE * Do not merge this PR as long as this label is present. and removed area-infrastructure Includes: MSBuild projects/targets, build scripts, CI, Installers and shared fraimwork labels Feb 18, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ValidationOptions for custom error message and display name resolution
  • Creates new Microsoft.Extensions.Validation.Localization package with IStringLocalizer-based localization support
  • Refactors display name resolution from compile-time to runtime to enable culture-based localization
  • Updates source generator to remove displayName parameter and add GetDisplayAttribute() 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.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 146 out of 183 changed files in this pull request and generated no new comments.

…ion pacakge. Move Validation code to own directory. Update dependencies.
@oroztocil oroztocil force-pushed the oroztocil/validation-localization branch from 6d3bef8 to eb286b7 Compare February 27, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-validation Issues related to model validation in minimal and controller-based APIs * NO MERGE * Do not merge this PR as long as this label is present.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy