Table of Contents

Using Tlumach via Dependency Injection

Dependency Injection lets you instantiate libraries at one location of your code and then use them in a more generic way than with direct references to classes and objects.

Tlumach supports this model since version 1.2. At the same time, the way Microsoft implemented the interface (IStringLocalizer) - by using a string for a key/name of the translation unit - makes it error-prone. The approach Tlumach offers via auto-generated TranslationUnit instances is more robust because with it, any typo in the key/ name will be detected by the compiler. Due to this, we recommend using DI only when you are already using it in your project and need to replace your current translation code with Tlumach.

An alternative approach is to use Generator to generate TranslationUnit instances and/or string constants and then use those instances and constants as keys. As you will see below, Tlumach supports this model that provides grounds for syntax checking and avoiding typos.

What to Reference

Tlumach includes the "Tlumach.Extensions.Localization" project if you use the source code and the assembly with the same name if you use NuGet.

Setting Up DI

Here's the sample C# Code:

TranslationManager manager = new TranslationManager(new TranslationConfiguration(Assembly.GetExecutingAssembly(), "sample.cfg", null, TextFormat.Arb));
...
var host = Host.CreateDefaultBuilder(args)
            .ConfigureServices(services =>
                services.AddTlumachLocalization(
                    // These are the default settings. They are used when you request IStringLocalizer without context.
                    options => 
                        { 
                            options.TranslationManager = manager; 
                        },

                    // These are the options for the countext-bound IStringLocalizer<Tlumach.Sample.Strings> (the class name is an example)
                    provider => provider.AddContext("Tlumach.Sample.Strings", new TlumachLocalizationOptions()
                        {
                            TranslationManager = Strings.TranslationManager,
                        })
                )
            )
            .Build();

What this code does is register the necessary classes and configures the options. "Global" options are used for instances of IStringLocalizer created without a context. When we want to use context and options specific to that context, we use the "provider" parameter to create and register these individual options using the name of the context.

Using Tlumach via DI

The use of Tlumach via DI and IStringLocalizer interfaces is no different from any translation engine:

var genericLocalizer = host.Services.GetRequiredService<IStringLocalizer>();
Console.WriteLine(genericLocalizer["HelloName", "John Doe"]);
...
var localizer = host.Services.GetRequiredService<IStringLocalizer<Strings>>();
Console.WriteLine(localizer["Welcome"]);
// or use this form which enables syntax checking.
// WelcomeKey is a string constant, generated by Generator
Console.WriteLine(localizer[Strings.WelcomeKey]);

Do not pass a translation unit such as Strings.Welcome to a localizer. The unit has an implicit conversion to string, but the conversion yields the translated text and not the key, so the localizer would look the text up as if it were a key. Pass the generated ...Key constant, or read the text from the unit directly with CurrentValue.

The first localizer accesses strings using the options provided as global in the setup code below.

The second localizer uses options provided for the given scope. The "Strings" type here is an autogenerated class that includes a property named "TranslationManager". The IStringLocalizer implementation will take the TranslationManager instance and use it to find the translated string with the "Welcome" name. The use of the autogenerated class also enables referencing translation units or string constants for the purpose of syntax checking.

Web-safe Formatting

To conveniently use Tlumach in provision of text for Razor and Blazor pages, you can make all localized strings related to a certain Translation Manager instance return web-safe data. For this, set the WebEncodeValues property of the translation manager in question to true. After that, when you access the strings via a localizer, these instances will return web-safe text strings.

More About Options

TlumachLocalizationOptions objects include different properties that let you reference an instance of TranslationManager or create a new instance. If the TranslationManager property is set, an IStringLocalizer implementation will use the value from this property and ignore the other settings. If the TranslationManager property is null and Configuration is not null, an IStringLocalizer implementation will use this configuration to create a new instance of TranslationManager. And if Configuration is null, the other properties are used to create an instance of TranslationConfiguration and then this new configuration is used to create an instance of TranslationManager. Note that in the latter scenario you don't need a configuration file, you need only the default translation file.

Each instance of TlumachLocalizationOptions may include a reference to a different translation set.

Culture

Unless the application asks for a particular culture, a localizer reads CultureInfo.CurrentCulture at the moment of every call rather than at the moment it was created. A localizer that a container creates while the application starts therefore follows the culture of each request of a web application, which is what the request localization middleware of ASP.NET Core sets.

To pin a localizer to one culture, call WithCulture(CultureInfo). It returns a new localizer and leaves the one whose method was called untouched, as does WithTextProcessingMode(TextFormat).

Values and Missing Keys

The indexer without arguments returns the text of the translation entry as it stands, with the placeholders it contains left untouched, so that the value can be used as a format string. Use the indexer that takes arguments to have the placeholders replaced with values.

When a key is present in no translation at all, the value is the key itself and LocalizedString.ResourceNotFound is true, which is the behaviour that the consumers of IStringLocalizer rely on. A text that comes from the default translation rather than from the translation of the requested culture is a text that was found: ResourceNotFound stays false.

GetAllStrings returns every key once. When several translations of the culture chain carry one key, the value of the most specific culture wins, and the default translation is the last fallback.

Localization of Data Annotations

Because the localizer follows the contract of IStringLocalizer, the localization of data annotations that ASP.NET Core provides works on top of Tlumach with no further code:

builder.Services.AddTlumachLocalization(options => options.TranslationManager = Strings.TranslationManager);
builder.Services.AddRazorPages().AddDataAnnotationsLocalization();

The model metadata then resolves DisplayAttribute.Name through the localizer whenever ResourceType is not set, and the validation adapters resolve ErrorMessage the same way, so the text of an annotation becomes a translation key. See Localization of Data Annotations for this and for the two other routes, which also cover validation outside ASP.NET.