Localizing Data Annotation Attributes in ASP.NET Core

You are currently viewing Localizing Data Annotation Attributes in ASP.NET Core

In one of my previous posts, ASP.NET Core Localization from Database, I showed different techniques to fetch localized contents from the database and display these contents in ASP.NET Core MVC Controllers and Views. Many people asked me the question to implement a similar type of localization with Data Annotations so that they can decorate their model properties with some data annotations and bind those models with the localized Razor Views or Forms.

This post is based on my previous post ASP.NET Core Localization from Database so I won’t start it from scratch and won’t show you how to implement localization services to fetch localization contents from the database and display a dropdown on top to switch contents from one language to another language. You can read ASP.NET Core Localization from Database to get some basic knowledge of localization in ASP.NET Core.

Implementing a simple ASP.NET Core MVC form

Let’s first create a model class Customer in the Models folder. It is a simple model class with only two properties.

Customer.cs

public class Customer
{
    public int Id { get; set; }

    public string? FirstName { get; set; }

    public string? LastName { get; set; }
}

Next, bind the above model class with a Razor view and create a simple ASP.NET Core MVC form as shown below.

@model Customer;

@{
    ViewData["Title"] = Localize("customer.page.create.title");
}

<div class="myform">
    <h3>@Localize("customer.page.create.title")</h3>

    <form asp-action="form" method="post">

        <div class="form-group">
            <label asp-for="FirstName"></label>
            <input asp-for="FirstName" class="form-control" />
        </div>

        <div class="form-group">
            <label asp-for="LastName"></label>
            <input asp-for="LastName" class="form-control" />
        </div>

        <button type="submit" class="btn btn-primary">
            @Localize("general.button.create")
        </button>

    </form>

</div>

The above code snippet is using a Localize method that I implemented in my previous post, ASP.NET Core Localization from Database to display localized strings from the database. Run this project now and you should see the following form where all contents are rendered in the English language.

READ ALSO:  How to Consume Third-party Web APIs in ASP.NET Core
ASP.NET Core MVC Form without Localized Data Annotations
ASP.NET Core MVC Form without Localized Data Annotations

Try to change the language from English to French from the top dropdown and you will notice that the contents rendered using the Localize method are displaying French language strings but the form labels FirstName and LastName are still displaying in the English language. This is because we have not implemented our custom localized data annotation yet.

ASP.NET Core MVC Form without Localized Data Annotations in French
ASP.NET Core MVC Form without Localized Data Annotations in French

Implementing a custom localized DisplayNameAttribute

We want to implement a custom DisplayNameAttribute that can automatically detect the current language and fetch the localized contents from the database. Once this attribute is implemented, we will be able to use this attribute with FirstName and LastName properties as follows.  

[LocalizedDisplayName("customer.page.create.firstname")]
public string? FirstName { get; set; }

[LocalizedDisplayName("customer.page.create.lastname")]
public string? LastName { get; set; }

Let’s add a new class LocalizedDisplayNameAttribute to the project and inherit this class from DisplayNameAttribute class.

LocalizedDisplayNameAttribute.cs

public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    private string _resourceKey = string.Empty;
    public HttpContext _httpContext => new HttpContextAccessor().HttpContext;

    public LocalizedDisplayNameAttribute(string resourceKey) : base(resourceKey)
    {
        _resourceKey = resourceKey;
    }

    public override string DisplayName
    {
        get
        {
            // Resolve Services 
            ILanguageService _languageService = (ILanguageService)_httpContext.RequestServices.GetService(typeof(ILanguageService));
            ILocalizationService _localizationService = (ILocalizationService)_httpContext.RequestServices.GetService(typeof(ILocalizationService));

            // Get Language Information from Database based on Current Culture
            var currentCulture = Thread.CurrentThread.CurrentUICulture.Name;
            var language = _languageService.GetLanguageByCulture(currentCulture);

            if (language != null)
            {
                // Get String Resource Value from Database
                var stringResource = _localizationService.GetStringResource(_resourceKey, language.Id);
                if (stringResource != null && !string.IsNullOrEmpty(stringResource.Value))
                {
                    return stringResource?.Value ?? _resourceKey;
                }
            } 

            return _resourceKey;
        }
    } 
}

The constructor of the above class is accepting a resourceKey parameter that will be used to query the localized contents from the database.

public LocalizedDisplayNameAttribute(string resourceKey) : base(resourceKey)
{
    _resourceKey = resourceKey;
}

The main functionality is implemented in the overridden DisplayName property where we are first resolving our custom localization and language services we implemented in the previous post, ASP.NET Core Localization from Database.

ILanguageService _languageService = (ILanguageService)_httpContext.RequestServices.GetService(typeof(ILanguageService));
ILocalizationService _localizationService = (ILocalizationService)_httpContext.RequestServices.GetService(typeof(ILocalizationService));

You also have to add the following line in Startup.cs file in order to access to the current HttpContext in the custom attribute.

services.AddHttpContextAccessor();

Next, we are detecting the current UI culture and fetching the language information from the database.

var currentCulture = Thread.CurrentThread.CurrentUICulture.Name;
var language = _languageService.GetLanguageByCulture(currentCulture);

Finally, we are reading the resource string value from the database using the resource key and language.

var stringResource = _localizationService.GetStringResource(_resourceKey, language.Id);
if (stringResource != null && !string.IsNullOrEmpty(stringResource.Value))
{
    return stringResource?.Value ?? _resourceKey;
}

We can now update the Customer class code and LocalizedDisplayName with the properties we want to localize.

public class Customer
{
    public int Id { get; set; }

    [LocalizedDisplayName("customer.page.create.firstname")]
    public string? FirstName { get; set; }

    [LocalizedDisplayName("customer.page.create.lastname")]
    public string? LastName { get; set; }
}

Run the project once again and you will notice that now the form labels are showing the strings from the database.

READ ALSO:  Mediator Design Pattern in ASP.NET Core
Localized Display Name attribute in English Language
Localized Display Name attribute in English Language

If you will select the French language, the form labels will automatically change to display strings in the French language.

Localized Display Name attribute in French Language
Localized Display Name attribute in French Language

Selecting the German language will change the form labels once again to display German language strings from the database.

Localized Display Name attribute in German
Localized Display Name attribute in German

Summary

I hope you have found this post useful. If you have any comments or suggestions, please leave your comments below. Don’t forget to share this tutorial with your friends or community. You can also download the complete source code of this post using the Download Source Code button shown at the start of this post.

This Post Has 4 Comments

  1. Amer

    Hi Anwar,

    Is it possible to use this Data Annotations with database localization

  2. Muhammad Ali

    Hi Waqar, Do you have a sample project for ASP.NET Blazor Server Side application that’s using localization from database and with ability to use localized error messages in data annotation?

    1. Waqas Anwar

      Hi Ali, I don’t have such type of Blazor app.

Leave a Reply