Localization in ASP.NET 5 & MVC 6

ASP.NET 5 is a pretty revolutionary version – it’s a complete rewrite, and an open source effort. They say that the reason for the ground-up redesign is the fact that the world has changed, and it has. We now have micro-services, on-premise servers that do not have to be IIS, and it can even run on Linux & Mac. In essence, Microsoft had to remove all dependencies to System.Web, and once they started doing that, well… I guess it was just easier to rewrite it from scratch :-).

And, because the entire framework is new, there are a lot of new concepts, and localization is just one of them.

I’ll talk about the insides of ASP.NET 5 and MVC 6 at Cancel Conference on the 29th and 30th September in Šempeter pri Gorici. Come listen!

Why a new localization approach?

At the moment, ASP.NET localization approach is quite old, and it is based on Resources. There is nothing inherently wrong with this, but a lot of projects I have seen so far actually use other sources for localized strings. For example, most products I’ve worked on recently use a database as the source. Also, the concepts in place right now work badly with Dependency Injection.

So, enter the new localization.

This is all pre-release code, based on Beta 7, so it might yet change drastically, making this blog post irrelevant.

The basic Concepts – Middleware & Feature

In the new ASP.NET framework, interaction with the hosting component (server), is done through abstractions called Features. The first major part is IRequestCultureFeature . The default implementation of that uses an implementation of IRequestCultureProvider. There are a couple implementations built-in:

  • CookieRequestCultureProvider
  • QueryStringRequestCultureProvider
  • AcceptLanguageHeaderRequestCultureProvider

All of those look at different things each, and provides the proper culture to the localization feature. The providers are listed in RequestLocalizationOptions , which provides an ordered list that can be modified. The first provider that returns a non-null result for a given request “wins” and that result is used. This process is performed by RequestLocalizationMiddleware . The middleware is activated by calling the UseRequestLocalization  extension method on top of an instance of IApplicationBuilder .

Additionally, the middleware also sets the thread’s culture (and UI Culture).

You can access the culture from mapped calbacks throughout the pipeline:

The Basic Concepts – The Factory and Provider

Of course, none of this really matters, if you can’t access the resources (translations). localization framework introduces IStringLocalizerFactory which is a way to create (hence the name – factory) actual IStringLocalizer  instances. This gives us a simple option to extend this.

The factory interface is really simple:

The actual localizer is also simple:

The factory then, only knows how to create an instance of the localizer. This is invoked by the framework itself (the DI framework underneath requires the factory to be registered, but not the localizer) and then passed onward to the components that require it.

What is much better now is the fact that we can override the localizer and access our translations from the database. Because the factory is instantiated through DI, we can even use our services and repositories for accessing the database – and pas this to the localizers when we instantiate it.

The Next Step – MVC

Now we need to use all of this on the actual UI. MVC introduces a couple more classes, most importantly  IHtmlLocalizer  and the corresponding factory IHtmlLocalizerFactory. This is registered with the DI and services container with the extension method AddViewLocalization() on top of IMvcBuilder. This actually does a couple other things that we’ll get to later.

The provided implementation of this interface has a dependency to the aforementioned IStringLocalizerFactory . The html variant actually provides a way to get HTML strings back. The intended usage is to be injected either into the controller, like this:

or it can be injected into a view (or all of them). For example, we can add the following into _ViewImports.cshtml.

The @inject  keyword is amazing. It basically lets us use DI services in the View. But in this case, it gives us access to LocString across all our views. By the way, the interface is simply a derivative of the IHtmlLocalizer. I’m assuming it’s a new one only to enable expanding it with additional info later down the line.

Anyway, inside our views, we can now do:

Update: I asked on Twitter what is the purpose of the IViewLocalizer, and this is the answer:

 The View Localization Problem

Sometimes, simply changing the message is not enough localization. You actually want to display a different “view”. This was really hard to achieve without a custom view engine in the past. With the introduction of View Location Expanders (more on this, hopefully in a later post), this is now much easier, and much more efficient.

When you call the AddViewLocalization()  extension method, this also happens:

This enables the view engine to look for views that are suffixed with the culture information, so for example, I can have Index.cshtml, and If the request localization middleware and feature set the culture to sl-SI, the view engine will try and find the view suffixed with that and load it – or fallback to the default view.

Tying it all together

By default, MVC, is implemented to use the default resource providers, so it will, as Damian answered above, look for resource files that are named like the view. So, for example, you can have Views.Home.Index.cshtml.en-GB.resx . This will be picked up by default. So, this is the easiest way to try it out.

Another way is to implement your own IStringLocalizerFactory  and IStringLocalizer . Just remember to register the factory in the ConfigureServices  method:

I will publish some demos and samples after the Cancel conference.

Anže Vodovnik is a proud father, enthusiastic guitarist and passionate software developer. He enjoys presenting at conferences sharing his experience of over 15 years of creating software. He was briefly a Microsoft MVP for Azure before forfeiting the title when he joined Microsoft UK, where he’s now working hand-in-hand with customers to help them develop and use solutions based on the Microsoft Azure platform.

View Comments
  • Great info, thanks!

    • Avodovnik

      Thanks Rick!

  • Pingback: My readings in 2015 week 39 | My path to become awesome dev()

  • Tom Archer [tarcher@MSFT]

    Fantastic write-up! You mentioned publishing a sample after the Cancel conference. Have you had a chance to do that yet?

    • Avodovnik

      Hey Tom, thanks! I’m planning on uploading the samples on GitHub either today or tomorrow! One of those samples is the localization one.

      • Tom Archer [tarcher@MSFT]

        Thanks, Anže. I’ll keep an eye out for it.

        • Avodovnik

          Hey Tom, the demos for Cancel conf are online here: The last one (08) is localization. Let me know what you think. Would love to get feedback!

          • Tom Archer [tarcher@MSFT]

            Thanks, Anže. Would you rather I post here or via your direct email? If you’d prefer email, please send me your email at tarcher[at]

        • Avodovnik

          Hey Tom, I’ve sent you an email. Looking forward to the feedback!

  • wainting good localization….

    I realy hope, that multilanguage resx files start to work in beta8 as worked before – because this model here is nothing without resx files

    • Avodovnik

      That genuinely depends. Personally, I never liked resx files. I think there are better approaches to localization in big projects, especially ones where there are translators involved, etc.

  • Into ASPNET 5

    Great info, thanks for the write up!

    Is there any idea as to how different will be localization from Beta7 to Beta8 as I hear the localization is going to part of beta8 by default?

    Also I downloaded the demo project from the Github and getting the error “The ‘webroot’ property value ‘wwwroot’ in the project.json file points to a folder that does not exists”, have you faced the error of this kind before? Any help would be fine.

    • Beta8 will be out very soon.

      • Into ASPNET 5

        Hey Rick,

        Thanks for the info. I could see that the release date is pushed to tomorrow in the github wiki

  • Aleksandr Poltarjonok

    Great post!
    Your post partly help me implement translation/localization from DB in my project:

    • Avodovnik

      That’s awesome to hear! Thanks!

  • Pingback: ASP.NET 5 MVC 6 Localization | Software Engineering()

  • Pingback: Using DataAnnotations and Localization in ASP.NET 5 MVC 6 | Software Engineering()

  • Pingback: What’s new in MVC 6: View Location Expanders | public void Photograph()()

  • Pingback: ASP.NET Core 1.0 using SQL Localization | Software Engineering()

  • Pingback: Friday links 201 – A Programmer with Microsoft tools()