Skip to content

[Blazor] Data annotations localization support #12158

Description

@KT315AM

In my case, I have custom localization service (like IStringLocalizer), that retrieve localization from database for user-specific culture. User culture configures in user settings, that may changes in application runtime, i.e. without page reload.

ILocalizationService
{
	string this[string key] { get; }
	string GetString(string key);
	void ChangeLanguage(CultureInfo culture);
}

Service registers as scoped, i.e. each user have own ILocalizationService with own specific culture settings.

services.AddScoped<ILocalizationService, LocalizationService>();

In components this works great, just inject service.

@page "/some-page"
@inject ILocalizationService T
<h1>@T["SomeText"]</h1>

Of course, this does not work in standard case of using data annotation attributes. For example:

User
{
	[Required]
	[Display(Name = "Name")]
	string Name { get; set; }
	
	[Display(Name = "Email")]
	string Email { get; set; }
}

Data annotation attributes can be provided with custom type with localized strings. Something like this:

User
{
	[Required(ErrorMessageResourceName = "Validation_Required", ErrorMessageResourceType = typeof(LocalizationResources))]
	[Display(Name = "Name", ResourceType = typeof(LocalizationResources))]
	string Name { get; set; }
	
	[Display(Name = "Email", ResourceType = typeof(LocalizationResources))]
	string Email { get; set; }
}

LocalizationResources
{
	public static string Name => GetString();
	public static string Email => GetString();
	public static string Validation_Required => GetString();
	
	public static Func<ILocalizationService> LocalizationServiceProvider { get; set; }

	public static string GetString([CallerMemberName] string key = null)
	{
		return LocalizationServiceProvider().GetString(key);
	}
}

Main problem of this approach is that localized strings in LocalizationResources must be as static properties. So we cant directly inject user-specific ILocalizationService. But we can try get user-specific ILocalizationService via Func<ILocalizationService> LocalizationServiceProvider that can be configured, for example, in Configure() method:

public void Configure(IServiceProvider serviceProvider)
{
	LocalizationResources.LocalizationServiceProvider = () => serviceProvider.GetRequiredService<ILocalizationService>();
}

Of course, this does not work, because of scope of serviceProvider in Configure() method differs from user-specific scopes. In order for this approach to work it is necessary in LocalizationResources.LocalizationServiceProvider uses some kind of context that will provide user-scoped IServiceProvider to resolve user-specific ILocalizationService.

So, at this moment, in server-side Blazor is exists any method to localize the data annotation attributes in non-resx scenarios? Have a plan to implement its in future?

Activity

  1. mkArtakMSFT commented on Jul 16, 2019

    @mkArtakMSFT
    Contributor

    Thanks for contacting us, @qw-34.
    We don't have any plans to support this in 3.0, but we'll look to bring support for the in the future.

  2. added this to the Backlog milestone on Jul 16, 2019
  3. sven5 commented on Sep 2, 2019

    @sven5

    Please also add support for DataAnnotationLocalizerProvider in Blazor - we already have this in MVC.

  4. agonzalezm commented on Oct 3, 2019

    @agonzalezm

    will this be supported in 3.1? we also need full multi-language support for server-side application texts, data annotations validations, etc

  5. marinasundstrom commented on Apr 20, 2020

    @marinasundstrom

    Localization is now supported in Blazor, usingMicrosoft.AspNetCore.Localization:

    builder.Services.AddLocalization();
    

    DataAnnotations are not really supported though.

    After some quick investigation, my conclusion is that the DataAnnotationsValidator is the class that should be responsible for resolving the string from the preferred .resx file.

    It could be implemented there by injecting StringLocalizer<T>(where T is the Component) into the ``DataAnnotationsValidatorand then pass it to EditContextDataAnnotationsExtensions .AddDataAnnotationsValidation```.

    https://github.com/dotnet/aspnetcore/blob/057ec9cfae1831f91f1a0e01397059b20431db7b/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs

    But you need to create adapters to handle the localization of specific data annotations, like in MVC:

    https://github.com/dotnet/aspnetcore/tree/c565386a3ed135560bc2e9017aa54a950b4e35dd/src/Mvc/Mvc.DataAnnotations/src

    It might be a bigger task than expected.

    There is also an experimental ObjectGraphDataAnnotationsValidator that validates not only top-level properties. Maybe that will replace the DataAnnotationsValidator in the future.

  6. fededim commented on Mar 30, 2021

    @fededim

    Is there any schedule to support Data Annotations localization in Blazor ? Without them Blazor apps are fully localized in a language, but the standard data annotation errors remain still in English....this "double language" is not great from the user perspective. Think about this feature, it's important for every application.

  7. changed the title [-][Blazor] Problems with localization of data annotation attributes in server-side Blazor[/-] [+][Blazor] Data annotations localization support[/+] on Oct 18, 2021
  8. 62 remaining items

  9. oroztocil commented on Feb 27, 2026

    @oroztocil
    Member

    I submitted a design proposal for localization support in Microsoft.Extensions.Validation: #65539

    This would cover all the localization features requested in this thread to Blazor form validation.

  10. mrpmorris commented on Mar 11, 2026

    @mrpmorris

    This is how I solved the problem.

    Set the Build Tool property of your resx file to PublicResXFileCodeGenerator

    Then add the code needed to localize a Blazor app...

    builder.Services.AddLocalization();
    
    // This is the line that already exists in your Program.cs file
    var app = builder.Build();
    
    // Localisation
    var supportedCultures = new[]
    {
        new CultureInfo("en"),
        new CultureInfo("fr"),
        // etc
    };
    
    var requestLocalizationOptions = new RequestLocalizationOptions
    {
        DefaultRequestCulture = new RequestCulture("en"),
        SupportedCultures = supportedCultures,
        SupportedUICultures = supportedCultures,
    };
    
    requestLocalizationOptions.RequestCultureProviders =
    [
        new AcceptLanguageHeaderRequestCultureProvider(),
    ];
    
    app.UseRequestLocalization(requestLocalizationOptions);

    Now when you access the static property MyResx.PropertyName it will use the relevant translation based on the user's browser request. No need for IStringLocalizer, just use the static properties.

    As for the ValidationAttribute descendants, if you are willing to use a Fody weaver then I created AutoLocalize which lets you do this

    [assembly:AutoLocalizeValidationAttributes(typeof(MyGeneratedResxClass))]

    It will automatically set the ErrorMessageResourceType on all validation attributes (that you've not already set it on) and set the ErrorMessageResourceName to AutoLocalize_{ShortAttributeName}

    For example

    • [Required] => AutoLocalize_Required
    • [StringLength(50)] => AutoLocalize_StringLength
    • etc

    Note, you can change the AutoLocalize_ prefix in the assembly-level attribute.

  11. mrpmorris commented on Mar 13, 2026

    @mrpmorris

    I really disliked the IStringLocalizer<T> approach. Being able to use magic strings was not a good experience. In my client's app they use multiple resx files (one per page) and I saw many examples of

    • Using the correct keys, but on the wrong T in IStringLocalizer<T>.
    • Using a non-existent key.
    • Attempting to use a strongly typed key by using nameof(SomeResource.SomeProperty) but using the wrong SomeResource class (which has a same or similar looking property name).

    Once I realised that literally putting @SomeGeneratedStaticClassForResx.SomeProperty in razor cs code would use the current user's locale, I was not only very surprised (magic) but also relieved that I could dump all those IStringLocalizer<T> injections in my app. But that only solved the text I wanted to show in the UI, not error messages from Microsoft.

    What about having a static ValidationAttribute.DefaultErrorMessageResourceType property that is used whenever the ErrorMessageResourceType instance property is null? You could use the default ValidatorAttribute_{ClassShortName} for the ErrorMessageResourceName too.

    Would that not solve the problem very easily? It would just need two static members added, and the getter for the two instance properties changed to return a default value when none is present. Perhaps have a protected bool UseDefaultErrorMessageResourceTypeAndName instance method on ValidatorAttribute so descendants have to set it to True to opt in. You could then enable it in MS code to opt-in without affecting other people's code.

    public class ValidationAttribute : Attribute
    {
      public static Type DefaultErrorMessageResourceType { get; set; }
      protected virtual bool UseDefaultErrorMessageResourceTypeAndName() => false;
    
      public Type ErrorMessageResourceType
      {
        set => field = value;
        get =>  field ??  UseDefaultErrorMessageResourceTypeAndName() ?  UseDefaultErrorMessageResourceTypeAndName : null;
      }
    
      public string ErrorMessageResourceName
      {
        set => field = value;
        get =>  field ??  ErrorMessageResourceType is null ? null : GetShortName();
      }
    }
    
    public class RequiredAttribute: ValidationAttribute
    {
      protected override bool UseDefaultErrorMessageResourceTypeAndName() => true;
    }

    Benefits

    1. Default behaviour is backwards compatible.
    2. Validator attributes have to opt-in.
    3. User of the attributes has to opt-in.
    4. Would work no matter what front-end you use, WPF, Blazor, et al.
  12. mrpmorris commented on May 18, 2026

    @mrpmorris

    @oroztocil I see you have closed this as completed, but there are no PR's associated with it for us to look at the source code.

  13. oroztocil commented on May 18, 2026

    @oroztocil
    Member

    @mrpmorris I apologize, that was a mistake I made in my PR description. See #66646

    Note that the PR description is not completely up to date after few changes we settled on during review (including offline review).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Pillar: Technical DebtPriority:2Work that is important, but not critical for the releaseaffected-fewThis issue impacts only small number of customersarea-blazorIncludes: Blazor, Razor ComponentsenhancementThis issue represents an ask for new feature or an enhancement to an existing onefeature-blazor-serverfeature-localizationfeature-validationIssues related to model validation in minimal and controller-based APIson-roadmapListed on the current release roadmapseverity-majorThis label is used by an internal tooltriaged

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions