LocalizationModule¶
The LocalizationModule provides basic features to work with localized
resources for the cultures supported by the application under development.
The LocalizationModule sits on top of the features provided by .NET, this
means that all the standard localization features can still be used to build up
your applications.
Note
By now the LocalizationModule manages the localization resources related
to backend tier, but in the future either backend or frontend resources
will be managed by this module.
Localization concepts and components¶
The LocalizationModule is organized in Bundles, each bundle contains a set of
resources related to a feature or module, for all the supported cultures. You
can think of a bundle as a key-value dictionary of related resources required
by your module.
Each localization Bundle is represented by an instance of
ILocalizationResource, which in turns can be assigned a set of
ILocalizationResourceContributor to contribute filling out the dictionary.
Each Bundle has attributes to define its characteristics, content and
behavior:
- 
An identifier: it is a Typeused to uniquely identify the resource bundle. You will need to use this type identifier to refer to the resource bundle whenever you need to localize a resource out of it. TheBundletype identifier is accessible through theResourceTypeattribute .
- 
A contributor list: contributors fills out the resource bundle from a variety of sources, such as text files, databases, remote calls, etc. The contributor list is accessible through Contributorsattribute of theILocalizationResourceinstance.
- 
A default culture: default culture will be used when no other culture matches the search criteria to localize a resource. The culture is set through the DefaultCultureNameattribute of theILocalizationResourceinstance.
- 
Base resource collection: base resources act as parent localization source from which the resource bundle being defined is extending and / or overwriting preexistent resources. The base resource type collection is accessible through the BaseResourceTypesattribute of theILocalizationResourceinstance.
Important
Every single message, text, label, etc., needed to be shown, sent, or
displayed to the end user in your application must be done aware of the
current culture. Thus, it must be obtained out of the proper
IStringLocalizer instance.
Registering localization bundles¶
Each module must register its own bundle in the Resources collection of the
LocalizationModuleOptions object provided by the Suite Framework, to be able
to localize resources later on.
The following example shows the way to add and configure a new resource bundle:
Some things to note from the example shown earlier:
- 
The configuration is made by providing an action with the desired code block in SetupModulemethod of yourISuiteModuleimplementation.
- 
The Addmethod available inLocalizationModuleOptionsinstance is requires a type parameter that acts as the module identifier.
- 
In the example the AddJsonEmbeddedDirectory,AddJsonDirectory,AddJsonFile, andAddJsonEmbeddedFilemethods add aJsonFileLocalizationContributorinstance to fill out the localization dictionary. See here for further information on usingJsonfiles for localization.
- 
Several contributors were added to the bundle being configured, each of one providing a culture specific localization resource set. 
Note
Typically there will be one Bundle for each Suite Module, more instances
are seldom needed, we will assume the case where only one bundle exists for
each module for the rest of the document.
Localizing resources¶
Any time you want to provide a localized message you will need to obtain an
IStringLocalizer instance and get the LocalizedString containing the
localized resource for a given culture out of it.
IStringLocalizer instances are created by the IStringLocalizerFactory
component. The default .Net Core Framework factory is replaced during
bootstrapping with the one provided by the `Suite Framework to provide an
enhanced version with the needed behavior to support our use cases.
The next example illustrates how to get an instance of IStringLocalizer
injecting it in the constructor of an IAppService. This mechanism can be used
to inject the desired IStringLocalizer instance out of DI in any other
component or feature, such as controllers and services, for instance.
Some things to note from previous example:
- 
The IStringLocalizerinstance is injected by DI and referring to theJobsAppModuleResourcesresource bundle using its type parameter.
- 
The IStringLocalizer<T>instance can be assigned to a more genericTypeattribute, such asIStringLocalizer, for clarity and simplicity of code.
- 
The IStringLocalizerinstance is used to retrieve the culture aware resource identified byMSG_GREETINGandMSG_GREETING_WITH_TIMEnames.
- 
A given resource can be used to show plain texts or as templates whose placeholders are filled in with provided params at runtime (one or more parameters can be provided at once). 
The localization will be performed using the CurrentUICulture of the current
Thread. Please continue reading next sections for further information about
this topic.
Note
An intent to localize a resource not contained in a Bundle will produce a
non empty result always. Every localization operation ends up with an instance
of LocalizedString, even if no resource can be found with the given key. A
default response will be provided in such cases, and if you have to take care
about the successful or not, you will then need to inspect the
ResourceNotFound property of the result.
A more complex scenario might be supported by injecting multiple instances of
IStringLocalizer pointing to a different Bundle each, or even injection
IStringLocalizationFactory, which allows us to get instances of the desired
IStringLocalizer for specific bundles. Let's see in the following examples.
Using multiple typed localizer instances:
Using the localizer factory:
Determining the CurrentUICulture¶
The Localization module depends on the Localization middleware features
provided by the ASP.Net Core Framework.
The Suite Framework relays in the request culture providers of the
ASP.Net Core Localization Middleware. The way the framework assign defines
which is the target culture is explained
here
DefaultCulture and SupportedCultures assigned to the
Suite Framework's LocalizationModule attributes at bootstrap time are used
later to configure the underlying ASP.Net Core Localization Middleware. The
request culture providers provided by the framework, out of the box, are then
configured and used as well to define the current culture. The order in which
the provides performs to determine the CurrentUICulture is as follows:
- 
QueryStringRequestCultureProvider: uses the QueryStringparameters to determine the request culture.
- 
CookieRequestCultureProvider gets the current culture out of a cookie. 
- 
AcceptLanguageHeaderRequestCultureProvider uses a well-known header value to determine the request culture. 
LocalizationModule options¶
The LocalizationModule provides the following options to configure the feature and its behavior:
- Resources: the collection of resource bundles for the application.
- DefaultCulture: the default culture which is used by the application when a supported culture could not be determined by one of the configured- Microsoft.AspNetCore.Localization.IRequestCultureProviders. By default the Suite Framework sets the- en(english neutral) culture name.
- SupportedCultures: the set of the supported cultures by the application.
The SupportedCultures and DefaultCulture play an important role when the
current culture must be determined for a given incoming request, in conjunction
with IRequestCultureProviders.
DefaultCulture and SupportedCultures values are used to configure the said
and underlying ASP.Net Core Localization Middleware through
RequestLocalizationOptions. For more information please refer to the
Official .NET Documentation
Culture determination procedure (fallback)¶
Any time a Localized resource is requested for a Bundle, the following
procedure is followed (the first not null outcome is returned):
- Contributors for the Bundleare invoked in the defined order, using the current culture.
- If not results were found with current culture, and if a parent culture exists for the current culture, the contributors will be invoked again for the parent culture.
- If we haven't succeeded yet finding the resource, if a DefaultCultureNamewas defined for theBundle, the contributors will be invoked again for theBundle's default culture.
- Finally base resource localizer instances, if any, will be invoked in its defined order, using the current culture initially, and culture fallback procedure mentioned here previously, if needed, afterwards.
Seamlessly integration with Resource (.resx) files¶
The LocalizationModule integrates seamlessly with .resx Resource files, such
files can still be used for translation.
The recommended way to define Bundles when working with the Suite Framework is
using LocalizationResourceDictionary, because this way the you will be able to
extend from an existent Bundle, overriding existing, or adding new resources
to it.
Any time a localized resource is requested, and the Bundle is not known by the
LocalizationModule, the localization operation will be delegated to the
underlying ResourceManagerStringLocalizerFactory, which will try to handle the
localization procedure using Resource (.resx) files.
The ResourceManagerStringLocalizerFactory factory receives all the
localization requests that cannot be handled using SuiteLocalizerFactory.
Note
When using resource files (.resx), the base is set by default to Resources.
Take this into account because it makes the difference at the moment of deciding
where to place the resource file (and its  locale specific satellite assembly
siblings for each supported culture). See here
for more information about naming resource (.resx) files.
Using base types¶
Each Bundle can be assigned one or more Base types in order to inherit and
extends, or even overwrite, the resources defined by base Bundles. In order to
point to a base Bundle its identifier type must be used on the Bundle being
extending it. Lets see an example:
In the above example the Type identifying SomeBaseResource Bundle was used
to indicate that MyModuleResources Bundle will use, extend or overwrite
resources coming from SomeBaseResource Bundle. Think of this inheritance as a
class hierarchy where ancestors provide resources than can be used by
descendants. The inheritance is followed all along the hierarchy, thus if
SomeBaseResource in turn has some inheritance definition, those localization
resources will be available also for the MyModuleResources Bundle .
The next code snippet continues extends the previous example showing how the
Bundle registration is done in a more complete code snippet.
Note
Take care of ancestors being registered as Bundles or it will produce a race
condition when trying to configure and use the LocalizationModule, because of
the reference made to the missing base Bundle.
Remember that you can overwrite partially or none at all the inherited localization resources defined by base resources. Therefore, the following scenarios are valid, and its combination also:
- Extend base resources with new ones, if you wan to extend previous definitions only.
- Replace whole specific culture, if you want to replace the definitions only for an idiom or locale for example.
- Replace some specific resource, if you need to do some sort of branding.
Using Json files for localization¶
When defining localized resources, a Json file can be used. Each Json file
must provide resources only for one culture.
Note
By now, the only available contributor is JsonFileLocalizationContributor,
which allows us to feed the Bundle instance from Json files. We expect to
add more contributors in the feature.
Keep in mind that Json files will be read at runtime, therefore any kind of
race condition will not be seen until the application is running.
Warning
Avoid to provide more than one file for the same culture, because that will produce an exception at runtime.
Json file structure¶
The structure of the Json file must follow a set of directives to be
considered Well Formed and its content can be read by the framework. Below you
can see an example of a valid Json localization resource file.
| JSON | |
|---|---|
In the file there must be defined a culture attribute, and texts attribute
to hold the collection of key/value pairs holding the localization resource
name and value respectively.
Loading resources from assembly embedded files¶
To use json files embedded as assembly resources, you need to point to the
correct embedded resource name. As embedded resources don't have a "directory"
concept, AddJsonEmbeddedDirectory() will actually add all resources that start
with the specified directory path, so you need to be careful to not add
subdirectories by mistake. Also, as embedded resources don't allow directory
separator characters in their names, the "/" and "\" characters will be mapped
to the "." character, which is the way they are converted when embedded.
Note
Using embedded resources is preferred over using filesystem files, as this last option has caused conflicts in the past when different modules tried to add files to the "\resources" directory.
You need to modify your project's .csproj file to embed files in the assembly manifest. For example, to add all json files in the Resources directory to the assembly manifest, you should add this to your project's .csproj file:
| XML | |
|---|---|
Warning
Please be careful configuring the build action correctly so that the embedded resources are added to the assembly.
Note
The culture name suffix present in the file name of the previous example is
merely for illustration and clarity purpose, those suffixes will be ignored
because the target culture is defined by the culture attribute
inside each Json file.
Warning
Avoid using embedded resources that don't contain Json localization
files only, because it can produce unwanted exceptions at runtime. Use this
feature with caution.
Loading resources from files or directories¶
Json file contributors can be added through suitable extensions methods
provided by the Suite framework. To use it you need to point to a valid Json
path or directory. If the given path is not fully qualified, the path will be
considered relative to the current executing assembly, and the resulting path
will be the combination of both.
Warning
Please be careful configuring the build action correctly for all the Json
files you want to use, because they must be present a runtime in the path you
pointed at when adding to the Bundle.
| C# | |
|---|---|
Note
The culture name suffix present in the file name of the previous example is
merely for illustration and clarity purpose, those suffixes will be ignored
because the target culture is defined by the culture attribute
inside each Json file.
In the previous example the resources will be loaded at runtime from the provided path relative to the executing assembly location:
| Text Only | |
|---|---|
and
| Text Only | |
|---|---|
respectively.
Warning
Avoid pointing to a directory whose content doesn't contain Json localization
files only, because it can produce unwanted exceptions at runtime. Use this
feature with caution.
Localization for UI applications¶
The AspNetLocalizationModule along with LocalizationModule provide the
features needed to localize UI applications leveraging the Suite's localization
features.
To do so, have to create a module that depends on LocalizationModule and
AspNetLocalizationModule altogether. Said module holds all the localizable
resources required by the UI application to be exposed to the UI, which will be
exposed through an http api, using a well known path.
The steps to follow are these:
- Create the UI localization module, you can leverage the
   suiteuilocalizationmoduletemplate.
- Make your module depend on the LocalizationModule, adding your resource bundle to its options.
- Make your module depend on the AspNetLocalizationModule, exposing the bundle added in the previous step.
- Add your UI resources to the module, for all supported languages.
- Make your ApplicationorBFFdepend on the UI localization module. Add other UI localization modules, if needed.
Note
Most of this burden is done by the template, we encourage you to use it for bootstrapping the development process.
Step 1 to 3 are shown in the example here below.
Naming convention¶
Given the fact that it's very likely to have to tweak the localization resources after build, perhaps because of branding, minor adjustments, or even to fix misspelled phrases or terms, we must be clear when naming the localization resources, to be able to distinguish clearly and decide correctly which one to change among all the ones contained in the resource bundle.
We encourage you to use this convention, it provides guidelines to name localization resource assets, giving the reader a decent hint, or context, on what the purpose of the resource is, and where is it supposed to be used, just by reading its key. After all, we don't have anything else but key to provide context for the resource being described.
The following are proposed rules for naming resources:
- LBL_: use this prefix when the resource is going to be used to label an item which is might be bounded in size, such as menu items, tab titles, etc. LBL_ADMIN_ASSETS_HEADER
- BTN_: same meaning as LBL_ but specifically used to denote an
    action rather than a noun, typically used to label buttons or links. For
    example: BTN_SAVE, orBTN_SAVE_CURRENT_ASSET.
- MSG_: use this when the resource is used as text without sizing
    boundaries, such as text blocks or text paragraph in applications, being
    harmless if its size may vary when localized. For example:
    MSG_INVALID_USER_MESSAGE
Some things to note about this convention:
- The names are in UpperSnakeCase
- The suffix identify the purpose or context where the resource is used
- The localized text can be either plain texts or templates
Keep in mind that in the future these definitions can vary to hold more scenarios or to fit some new features.
Note
There is no need to provide application id suffix or prefix, because the framework will do it for you, isolating resources located in each bundle. Keep the resource names simple, but yet meaningful.
Localized resources are limited by now to texts, no multimedia nor binary resources are supported today. If you wan to provide localization for those scenarios, you can use identifiers pointing to external assets (files, videos, blob storage, etc/) valid for the target culture.
Using localized templates¶
Localized resources can contain placeholders, which will be later filled in with provided parameters at runtime. The valid format and placeholders are those defined by String.Format
Info
The way we provide formatting for can be changed in the future, when adding
localization support for client applications that can be written in other
languages and technologies other than C#.
Warning
When using templates avoid composing phrases combining several terms in your application, the reason is that for some languages the term position can vary, and the phrase can completely change its sense (or even make no sense at all) depending on the target language.