source code github
I’m working on a ASP.NET Core project that requires localization of the UI. Damien Bod does a very good job of explaining how to do this using good old resx files. However, I’m writing the entire front-end with Visual Studio Code and resx somehow didn’t seem like a good match. It’s a clunky XML format that requires a header containing an XML schema definition and although .NET Core supports the format, Visual Studio Code lacks any support (at least none that I could find).
Table of Contents
So I thought, why not store resources in JSON files? My initial requirements are text resources only so a simple key-value format should do the trick. JSON seems an obvious match. For comparison, I have a (Dutch) JSON resource file first:
And the corresponding resx file for expressing ‘the same’ information. This comparison is of course not entirely fair but when you need just a simple key-value mapping, the resx format is a bit bloated to say the least…
Did you find my key and value entirely at the bottom of the file? The rest of the file is metadata.
So, what does a localization middleware component look like that reads its resources from JSON files? By the way, all the code for this post can be found in this GitHub repository (still very much in beta at the moment of writing). And here’s a good explanation of doing something similar but then from a database.
Configuration
First, the configuration. This happens in the ConfigureServices method:
The call to AddJsonLocalization installs the required localization services, which we will discuss next. At the moment, it has one configuration parameter: ResourcesPath to specify where to look for the JSON resource files.
The framework-supported AddViewLocalization installs an html-safe wrapper around our localization services and an IViewLocationExpander that selects views based on current culture: LanguageViewLocationExpander. For example, it can generate the view name Views/Home/nl/Action when you’re in Holland, pretty cool.
Middleware
Middleware configuration begins with the AddJsonLocalization call which is an extension method for IServicesCollection.
The AddJsonLocalization method basically adds two additional singleton services: JsonStringLocalizerFactory and JsonStringLocalizer. JsonStringLocalizerFactory is an implementation of IStringLocalizerFactory and this interface provides two factory methods:
These correspond to the two usage patterns for localizers. The first is for injection into classes, a controller class for example:
The second is called when a localizer is injected directly into a view:
Suppose the view is Views/Home/Index.cshtml and your application is located in a folder called My.Application then the second IStringLocalizerFactory method is called with parameters (baseName: "Views.Home.Index.cshtml", location: "My.Application").
Resource location algorithm
I’m not going into details on the JsonStringLocalizerFactory and JsonStringLocalizer classes themselves because the code is on GitHub so you can check it out there. What’s more interesting is the algorithm that looks for resource files. If you want to actually use this middleware that may be more useful, I think.
Suppose we inject a IHtmlLocalizer<HomeController> into a My.Application.HomeController class. Suppose furthermore we are in Holland so the culture is nl-NL and we have set the JsonLocalizationOptions.ResourcesPath to "Resources". The algorithm will look for a JSON resource file with the following paths in order:
So the algorithm starts with the most specific culture and falls back to less specific cultures. This ‘looking for resource files’ operation is relatively expensive so the result is cached for later use and guaranteed to execute just once.
System.Text.Json to save the file system
Useful links
example
https://github.com/blogifierdotnet/Blogifier/blob/master/src/Blogifier/Resources/localization.json