Localization in ASP.NET Core Web API

Last Updated on October 9, 2022 by Aram

Localization is a very important topic when you are planning to build an app or a site targeting multiple cultures and languages. You have to prepare your app or site to be ready to display all the information in accordance to the user’s relevant culture, this is very critical to achieve a the wider range of audiences.

Imagine you are building an app, for Arabic speaking country, but the main language of the app is in English. This way your app won’t be useful for the larger proportion of your target market and you will significantly lose audience and thus your app will be forsaken with no returning users.

The more localized your content is , the better usability of your app or your site would be. You should always target full localization to guarantee frictionless interaction with your product.

In this tutorial, we will learn how to apply localization in ASP.NET Core Web API, by adding Arabic resource file. We will build the a RESTful API using the latest version of .NET 6 and Visual Studio 2022, therefore proceed to installing VS 2022 prior to continuing this tutorial. And finally, we will test the API using Postman.

Creating the Localization Project

Start Visual Studio 2022 and Create new project – ASP.NET Core Web API

Give it a name like ‘LocalizationInAspNetCoreWebApi’

Then choose .NET 6 and press create.

As always, make sure to remove the template WeatherForecast controller and entity.

3 Steps of Localization in ASP.NET Core Web API

Now to apply localization an ASP.NET Core Web API project, there are 3 major steps to do:

  1. Including the localization into the API project middleware
  2. Adding the needed localization resource file
  3. Using the IStringLocalizer to access the resource file

Now let’s explain every step in details while going through the tutorial:

1. Including the localization into the APIs middleware

We need to let the ASP.NET Core Web API know that we will be doing localization with specifying the options that Resources file path would be in the Resources folder that we will create later in this tutorial.

Therefore let’s add the below code just after the CreateBuilder(args) call:

Moreover, we have to inject the localization configurations into the APIs middleware to let it understand to which culture we are trying to localize to, of course you can specify multiple locales if you are targeting 2 or more cultures in your localization.

In your program.cs file, let’s add the below code right after the builder.build() method call:

Here we are saying that we will be supporting 2 cultures or locales which are “en-US” and “ar” , where the “en-US” will be the default if there is no instruction from the http request otherwise.

2. Adding the needed localization resource file

In this tutorial, we will learn how to create and use resource files in ASP.NET Core Web API using 2 ways:

  • Using the Controller Resource Structure
  • Using the Shared Resource Structure

Let’s create a new folder under the project with name ‘Resources’.

We will use this folder to host all the .resx files in it.

Using the Controller Resource Structure

By using the controller resource structure, you can assign a resource file per Controller, which will allow you to structure and partition your resources over multiple files.

Before that, let’s make sure that we have the Controller created so that we can create the resource file that matches its name.

In your Controllers Folder, add a new Controller with name PostsController, and make it an Empty Api Controller:

For now, let’s keep this controller, we will come back later to develop it so that it can read from the resource files.

In your Resources folder, right click and create a new folder with name ‘Controllers’, and inside this new ‘Controllers’ folder create a new Resource with name ‘PostsController.ar.resx’

This will serve as the Arabic localization file for the posts controller. In ASP.NET Core, there is no need to add a localization file for the default locale or culture, this is because in the localized file you will have the name as the default and the value will be the localized value, if the StringLocalizer was not able to find the entry for a given string, then the string itself will be returned.

Furthermore, you won’t require to access the Resource file through other ways than the StringLocalizer.

Now back to our new resource file, inside this file, let’s add some sample data to be able to test our work later on.

Let’s see how the resource folder looks like:

You can alternatively remove the Controllers folder from the Resources Folder and rely on the Dot naming structure, so that resource file name will include the Controllers as a prefix like the below:

Resources\Controllers.PostsController.ar.resx

However, I prefer to use the folders structuring since it looks more organized and more readable.

Using the Shared Resource Structure

In this way, we can rely on a single file to have all the localization entries in it, so this file can be used among multiple controllers or other classes.

Let’s add a new resource file with name SharedResource.ar.resx

Just to keep it simple, inside we will add the same entries we added previously in the PostsController resource file:

Now for this to work as an actually shared resource we need to create a empty or dummy class with the same name ‘SharedResource’ , and we should place it somewhere other than inside the Resources Folder, we can create a new Folder under the project with name Entities and place it there:

We have prepared the need localization files, in both ways, time now has come to see how can we use and access these resources from the controller.

3. Using the IStringLocalizer to access the resource file

The last step will be to access these resource files, this can happen in ASP.NET Core through the IStringLocalizer injected into the Controller through its constructor.

Let’s see the below code for the PostsController:

First, in the constructor we are injecting 2 instances for the IStringLocalizer: One will be used to access the PostsController Resource file and the other will instance will access the SharedResource file, notice the difference in the Type used for each instance.

Next, we have defined 2 endpoints to be able to showcase the access difference between the PostsController Resource versus the Shared Resource.

Furthermore, we are able to access the resource file entries through either using the key name of the dictionary or using the method GetString. Both are valid and return the same results.

In case the searched entry name does not exist in the resource dictionary, the stringLocalizer will return a flag of ResourceNotFound true.

Run the project and make sure the browser is showing Swagger documentation of your endpoints.

Testing on Postman

Open Postman and create a new request, assign it to the url you have after your run the app along with the api/method route that you have and in the headers add the Accept-Language header with the value as ‘ar’

PostsController Resource – ar

PostsController Resource – en

Shared Resource – ar

Shared Resource – en

Adding the Content-Language in the response headers

One last thing to test, is to add the Content-Language to the response headers, this is mainly used to describe the content language of the response for the users.

Open program file, add the below line just before app.UseRequestLocalization(localizationOptions) :

Run your API again, and toggle back to Postman.

Try to call one of the API requests:

Notice once the endpoint returns the result, in the Headers tab of the Response part, you will see a new header with name Content-Language with value ‘ar’ , this means that the content returned is in ar locale.

Summary

In this tutorial we learned how to localize in ASP.NET Core Web API 6, we implemented 3 steps to achieve the localization: Applying the localization configurations to the middleware, creating the needed resource files and lastly using the IStringLocalizer to access the entries of the resource files.

Also we learned that there are 2 ways to add the resource files for Controllers: Controller structure Resource and Shared Resource, based on the fact that in the tutorial we implement both strategies and created 2 endpoint to access the resource for each resource file.

Eventually, we managed to test all our use cases, using Postman, and tested as well applying the Content-Language header for the responses so that we can broadcast or tell the users that the response is returned in the requested locale.

Localization should be done on multiple layers; On UI you should make sure to localize all the labels, placeholders, titles, front-end validation messages…etc. Then, on the API side you have to make sure that you return the proper exceptions with code and message so that the UI can translate the code into a localized message on the UI. Also, your API should define resource files with the intended localization for any needed translation strings and finally, you should keep separate tables for localized fixed or less frequently changing content such as countries, cities, categories, types …etc.

And if you are working on a huge product you might need to consult with professional copywriters or marketing specialists to advise and provide you with the best localization strategies for your target cultures, and prepare culturally accurate localized texts and wordings that would appear native and seamlessly understood by your targeted users.

References

You can find the code in my GitHub account.

For further information about localization in ASP.NET Core, you can check Microsoft’s Official Documentation.

Also for further reading about localization in general, you can check this article.

Bonus

Enjoy the poetic tunes of the piano genius “Chopin” – Waltz Op.69 No.2, played by Vladimir Ashkenazy

2 Comments on “Localization in ASP.NET Core Web API”

    1. Double-check and revise your changes, aare you sure you’ve added the needed configurations in the startup.cs file ? Also If you try to output or log the ‘CultureInfo.CurrentCulture.TwoLetterISOLanguageName’ , do you still get ‘EN’ ? And are you sure you are passing the Accept-Language Header with value ar? Double check the spelling for any mistakes as well.

Leave a Reply