Globalization and localization in ASP.NET Core – Part 2

If you just landed on this page, this page is of series of Post for localization / globalization

Part 1 – Introduction

Part 2 – ASP.NET Core ways of Localization / Globalization

Part 3 – Old trustworthy ResourseManager

In this post, I would be hinting on the ASP.NET Core way of  Localization / Globalization. There is already a good post on this from the Microsoft official Docs for ASP.NET Core at and I am not going to repeat what’s in that post but would be sharing few highlight’s of that article.

In order to work with IStringLocalizer, important, you need to add following in the Startup.cs

// Add Localization services to the system
services.AddLocalization(options => options.ResourcesPath = "Resources")

// Configure supported cultures and localization options
services.Configure<RequestLocalizationOptions>(options =>
       var supportedCultures = new[]
                              new CultureInfo("en-US"),
                              new CultureInfo("de-DE")

// State what the default culture for your application is. 
// This will be used if no specific culture can be determined for 
// a given request.

options.DefaultRequestCulture = new RequestCulture("en-US", "en-US");

// You must explicitly state which cultures your application supports.
// These are the cultures the app supports for formatting 
// numbers, dates, etc.

options.SupportedCultures = supportedCultures;

// These are the cultures the app supports for UI strings, 
// i.e. we have localized resources for.

options.SupportedUICultures = supportedCultures;

In Configure in Startup.cs:

// Configure localization.
var locOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();

This code (UseRequestLocalization) would set current culture info and current ui culture on the request execution thread.

By default, It determines culture from QueryString, Cookie and Request Header. Read first post to get some insight.

In order to understand better, I have create a sample application and have put the it on the GitHub, where you can get the execution in action.

In HelloController.cs, local method on api1, you could see this code

string cul = Thread.CurrentThread.CurrentCulture.Name;
string culUI = Thread.CurrentThread.CurrentUICulture.Name;

If you debug this application, if your startup.cs initialization code is correct, it would see the set the thread culture.

In the same method, it set the localize string from IStringLocalizer _localizer.

msg = _localizer["Hello"];

This _localizer in injected in the controller by ASP.NET Core dependency injection, and would read from HelloController resources


Placement and name of this resources file are important, otherwise, it won’t be able to read it. Great extent of naming and placement is written at That’s all you need to work with IStringLocalizer.

If you are curious just as me, to find out how this IStringLocalizer works? Then, you need to checkout it official GitHub Repository,

Dotnet Core is opensource, and you can read it code. When Dependency Inject (DI) is called to inject IStringLocalizer, it calls the ResourceManagerStringLocalizerFactory, which in turn create an instance of ResourceManagerStringLocalizer.

When ResourceManagerStringLocalizerFactory creates the instance of ResourceManagerStringLocalizer, it add new instance of the ResourceMananger

protected virtual ResourceManagerStringLocalizer 
                                                 Assembly assembly,
                                                 string baseName)
    return new ResourceManagerStringLocalizer(
                        new ResourceManager(baseName, assembly),

ResourceManager then in turn work with the resource files to get the translated text for the key

msg = _localizer["Hello"];

Here, “Hello” is the key and whatever value in the resource file, it will get that. Resource + culture file to be specific.

IStringLocalizer –> DI –> ResourceManagerStringLocalizerFactory –> ResourceManagerStringLocalizer –> ResourceManger

That’s the flow, how IStringLocalizer translated the messages.

Few interesting points here,

Type and Resource File Naming

IStringLocalizer takes the type. i.e.

IStringLocalizer<HelloController> _localizer

HelloController is the type. You resource needs to have HelloController name without default language resource file. What does in mean?

In ResourceManager era, default language resource file which won’t have culture suffix, example, abc resource would be named

"abc.FR-fr.resx" //French
"abc.ES-es.resx" //Spanish

“abc.resx” is default, if for any other cultures is pass apart from “FR-fr”, “ES-es”, this file would be used.

But, when working with IStringLocalizer, your default language set in Startup.cs. (see startup.cs code at top) Hence, you resource file should be named



Location of resource files

In the startup.cs code, you mention resource path, where to find all the resources. It doesn’t need to “Resources”, it could be anything. When assembly is build, resource file would go as emended resources into the dll. ASP.NET Core would build satellite assemblies for different culture which you have mention.


By definition, satellite assemblies do not contain code, except for that which is auto generated. Therefore, they cannot be executed as they are not in the main assembly. However, note that satellite assemblies are associated with a main assembly that usually contains default neutral resources (for example, en-US). A big benefit is that you can add support for a new culture or replace/update satellite assemblies without recompiling or replacing the application’s main assembly.

Dotnet Peek view of assembly


ResourceManager would find the resources embedded in assembly / satellite assembly base on the resource path and type name.



It all good, when you are going translation in the same assembly, where resource files are located. But, in my case, resource file where in one assembly, and code to read from resource file was in another assembly. This where ASP.NET Core IStringLocalizer faded and was limited to for me. I need to resorted to good old ResourceManager which I would describe in the other post.








2 thoughts on “Globalization and localization in ASP.NET Core – Part 2

  1. Pingback: Globalization and localization in ASP.NET Core – Part 1 | Technical Insanity

  2. Pingback: Globalization and localization in ASP.NET Core – Part 3 | Technical Insanity

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s