• Lexical
Show / Hide Table of Contents
  • Lexical.FileSystem
    • Introduction
    • Abstractions
      • IFileSystem
        • IFileSystem
        • IFileSystemBrowse
        • IFileSystemCreateDirectory
        • IFileSystemDelete
        • IFileSystemFileAttribute
        • IFileSystemMount
        • IFileSystemMove
        • IFileSystemObserve
        • IFileSystemOpen
      • IEvent
      • IEntry
      • IOption
      • IToken
    • FileSystem
    • VirtualFileSystem
    • MemoryFileSystem
    • EmbeddedFileSystem
    • HttpFileSystem
    • Decoration
    • IFileProvider
    • Utility
      • DisposeList
      • FileScanner
      • VisitTree
      • File Operation
  • Lexical.FileProvider
    • Introduction
    • Package
    • Package.Abstractions
    • Root
    • Zip
    • Dll
    • SharpCompress
    • SharpZipLib
    • FileScanner
    • Utils
  • Lexical.Localization
    • Introduction
    • Tutorial
    • Asset
      • IAsset
      • IStringAsset
    • Line
      • ILine
      • ILineFactory
      • ILineRoot
      • ILineFormat
      • ILineLogger
      • LineComparer
    • File
      • ILineReader
      • ILineWriter
      • Ini
      • Json
      • Xml
      • Resx
      • Resources
    • Miscellaneous
      • Plurality
      • ICulturePolicy
      • IStringFormat
      • Dependency Injection
    • Practices
      • Class Library
      • Class Library DI
      • Class Library DI opt.
  • Lexical.Utilities
    • Introduction
    • UnicodeString
    • FileScanner
    • Permutation
    • Tuples
    • StructList

Class Library with Dependency Injection optional

This article describes recommended practice for writing a class library that, for purposes of localization, is compatible with and without Dependency Injection.

Localization Sources

The class library may want to provide builtin localizations. The recommended practice is to create a public class AssetSources which implements IAssetSources as a signal to indicate that this class provides localizations for this class library.

Internal localization files are typically embedded resources.

using System.Collections.Generic;
using Lexical.Localization;
using Lexical.Localization.Asset;
using Microsoft.Extensions.FileProviders;

namespace TutorialLibrary3
{
    public class AssetSources : List<IAssetSource>, ILibraryAssetSources
    {
        /// <summary>
        /// Localization source reference to embedded resource.
        /// </summary>
        public readonly LineEmbeddedSource LocalizationSource = 
            LineReaderMap.Default.EmbeddedAssetSource(typeof(AssetSources).Assembly, "docs.TutorialLibrary3-de.xml");

        /// <summary>
        /// (Optional) External file localization source.
        /// </summary>
        public readonly LineFileProviderSource ExternalLocalizationSource;

        public AssetSources() : base()
        {
            // Add internal localization source
            Add(LocalizationSource);
        }

        public AssetSources(IFileProvider fileProvider) : this()
        {
            // Use file provider from dependency injection and search for an optional external localization source
            if (fileProvider != null)
            {
                ExternalLocalizationSource = 
                    XmlLinesReader.Default.FileProviderAssetSource(fileProvider, "Resources/TutorialLibrary3.xml", throwIfNotFound: false);
                Add(ExternalLocalizationSource);
            }
        }
    }
}
The example localization file TutorialLibrary3-de.xml.
<?xml version="1.0" encoding="UTF-8"?>
<Localization xmlns:Culture="urn:lexical.fi:Culture"
              xmlns:Type="urn:lexical.fi:Type"
              xmlns:Key="urn:lexical.fi:Key"
              xmlns="urn:lexical.fi">

  <!-- Example: Localization string for Culture "de" -->
  <Type:TutorialLibrary3.MyClass Culture="de">
    <Key:OK>Erfolgreich!</Key:OK>
  </Type:TutorialLibrary3.MyClass>

</Localization>

Localization Root

There should be another class called Localization that is used as the ILineRoot for the classes that use localization. This root can be linked to the global static root and shares its assets.

using Lexical.Localization;
using Lexical.Localization.Asset;

namespace TutorialLibrary3
{
    internal class Localization : StringLocalizerRoot.LinkedTo, ILineRoot
    {
        private static readonly Localization instance = new Localization(LineRoot.Global);

        /// <summary>
        /// Singleton instance to localization root for this class library.
        /// </summary>
        public static Localization Root => instance;

        /// <summary>
        /// Add asset sources here. Then call <see cref="IAssetBuilder.Build"/> to make effective.
        /// </summary>
        public new static IAssetBuilder Builder => LineRoot.Builder;

        Localization(ILineRoot linkedTo) : base(null, linkedTo)
        {
            // Add library's internal assets here
            Builder.AddSources(new AssetSources());
            // Apply changes
            Builder.Build();
        }
    }
}

Classes

For inversion of control, the class library can use IStringLocalizer abstractions. The non-dependency injection instance is acquired from Localization if localizer is null.

using Lexical.Localization;
using Microsoft.Extensions.Localization;

namespace TutorialLibrary3
{
    public class MyClass
    {
        IStringLocalizer<MyClass> localizer;

        public MyClass(IStringLocalizer<MyClass> localizer = default)
        {
            this.localizer = localizer ?? (Localization.Root.Type<MyClass>().AsStringLocalizer<MyClass>());
        }

        public string Do()
        {
            return localizer["OK"];
        }
    }
}

... or alternatively Lexical.Localization.Abstractions.

using Lexical.Localization;

namespace TutorialLibrary3
{
    public class MyClassB
    {
        ILine<MyClass> localizer;

        public MyClassB(ILine<MyClass> localizer = default)
        {
            this.localizer = localizer ?? Localization.Root.Type<MyClass>();
        }

        public string Do()
        {
            return localizer.Key("OK").Format("Operation Successful").ToString();
        }
    }
}

Application

Application that deploys the localizer must include the internal localizations with IAssetBuilder.AddAssetSources(Assembly) which searches the IAssetSources from the library.

  • Snippet
  • Full Code
// Create class without localizer
MyClass myClass1 = new MyClass(default);

// Create localizer
IAssetBuilder builder = new AssetBuilder.OneBuildInstance();
IAsset asset = builder.Build();
IStringLocalizerFactory localizer = new StringLocalizerRoot(asset, new CulturePolicy());

// Install TutorialLibrary's IAssetSources
Assembly library = typeof(MyClass).Assembly;
builder.AddLibraryAssetSources(library).Build();

// Create class with localizer
IStringLocalizer<MyClass> classLocalizer = localizer.Create(typeof(MyClass)) as IStringLocalizer<MyClass>;
MyClass myClass2 = new MyClass(classLocalizer);

/// Use culture that was provided with the class library
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de");
Console.WriteLine(myClass1.Do());
Console.WriteLine(myClass2.Do());
using Lexical.Localization;
using Lexical.Localization.Asset;
using Microsoft.Extensions.Localization;
using System;
using System.Globalization;
using System.Reflection;
using TutorialLibrary3;

namespace TutorialProject3
{
    public class Program1
    {
        public static void Main(string[] args)
        {
            #region Snippet
            // Create class without localizer
            MyClass myClass1 = new MyClass(default);
            
            // Create localizer
            IAssetBuilder builder = new AssetBuilder.OneBuildInstance();
            IAsset asset = builder.Build();
            IStringLocalizerFactory localizer = new StringLocalizerRoot(asset, new CulturePolicy());

            // Install TutorialLibrary's IAssetSources
            Assembly library = typeof(MyClass).Assembly;
            builder.AddLibraryAssetSources(library).Build();

            // Create class with localizer
            IStringLocalizer<MyClass> classLocalizer = localizer.Create(typeof(MyClass)) as IStringLocalizer<MyClass>;
            MyClass myClass2 = new MyClass(classLocalizer);

            /// Use culture that was provided with the class library
            CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de");
            Console.WriteLine(myClass1.Do());
            Console.WriteLine(myClass2.Do());
            #endregion Snippet
        }
    }
}


The application can supply additional localization sources with IAssetBuilder.AddSource(IAssetSource)

  • Snippet
  • Full Code

// Create class without localizer
MyClass myClass1 = new MyClass(default);

// Use the culture that was provided by with the class library (AssetSources)
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de");
Console.WriteLine(myClass1.Do());

// Install additional localization that was not available in the TutorialLibrary.
IAssetSource assetSource = XmlLinesReader.Default.FileAssetSource("TutorialLibrary3-fi.xml");
// Add to global localizer instance for the non-DI case
StringLocalizerRoot.Builder.AddSource(assetSource).Build();
// Add to local localizer instance for the DI case.
builder.AddSource(assetSource).Build();

// Use the culture that we just supplied
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("fi");
// Try the class without localizer
Console.WriteLine(myClass1.Do());
// Try the class with localizer
IStringLocalizer<MyClass> classLocalizer = localizer.Create(typeof(MyClass)) as IStringLocalizer<MyClass>;
MyClass myClass2 = new MyClass(classLocalizer);
Console.WriteLine(myClass2.Do());
using Lexical.Localization;
using Lexical.Localization.Asset;
using Microsoft.Extensions.Localization;
using System;
using System.Globalization;
using System.Reflection;
using TutorialLibrary3;

namespace TutorialProject3
{
    public class Program2
    {
        public static void Main(string[] args)
        {
            // Create localizer
            IAssetBuilder builder = new AssetBuilder.OneBuildInstance();
            IAsset asset = builder.Build();
            IStringLocalizerFactory localizer = new StringLocalizerRoot(asset, new CulturePolicy());
            // Install TutorialLibrary's IAssetSources
            Assembly library = typeof(MyClass).Assembly;
            builder.AddLibraryAssetSources(library).Build();
            #region Snippet

            // Create class without localizer
            MyClass myClass1 = new MyClass(default);

            // Use the culture that was provided by with the class library (AssetSources)
            CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de");
            Console.WriteLine(myClass1.Do());

            // Install additional localization that was not available in the TutorialLibrary.
            IAssetSource assetSource = XmlLinesReader.Default.FileAssetSource("TutorialLibrary3-fi.xml");
            // Add to global localizer instance for the non-DI case
            StringLocalizerRoot.Builder.AddSource(assetSource).Build();
            // Add to local localizer instance for the DI case.
            builder.AddSource(assetSource).Build();

            // Use the culture that we just supplied
            CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("fi");
            // Try the class without localizer
            Console.WriteLine(myClass1.Do());
            // Try the class with localizer
            IStringLocalizer<MyClass> classLocalizer = localizer.Create(typeof(MyClass)) as IStringLocalizer<MyClass>;
            MyClass myClass2 = new MyClass(classLocalizer);
            Console.WriteLine(myClass2.Do());
            #endregion Snippet
        }
    }
}

The example localization file TutorialLibrary3-fi.xml.

<?xml version="1.0" encoding="UTF-8"?>
<Localization xmlns:Culture="urn:lexical.fi:Culture"
              xmlns:Type="urn:lexical.fi:Type"
              xmlns:Key="urn:lexical.fi:Key"
              xmlns="urn:lexical.fi">

  <!-- Example: Localization string for Culture "fi" -->
  <Type:TutorialLibrary3.MyClass Culture="fi">
    <Key:OK>Toiminto onnistui!</Key:OK>
  </Type:TutorialLibrary3.MyClass>

</Localization>


When class is initialized with IServiceProvider, additional localizations are added to IServiceCollection as IAssetSources. The extension method AddLexicalLocalization(this IServiceCollection) adds the default services.

  • Snippet
  • Full Code
IServiceCollection services = new ServiceCollection();

// Install default IStringLocalizerFactory
services.AddLexicalLocalization(
    addStringLocalizerService: true,
    addCulturePolicyService: true,
    useGlobalInstance: false,
    addCache: false);

// Install TutorialLibrary's IAssetSources.
Assembly library = typeof(MyClass).Assembly;
services.AddLibraryAssetSources(library);

// Install additional localization that was not available in the TutorialLibrary.
services.AddSingleton<IAssetSource>(XmlLinesReader.Default.FileAssetSource("TutorialLibrary3-fi.xml"));

// Service MyClass
services.AddTransient<MyClass, MyClass>();

// Create instance container
using (var provider = services.BuildServiceProvider())
{
    // Create class
    MyClass myClass = provider.GetService<MyClass>();

    // Use the culture that was provided by with the class library (AssetSources)
    CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de");
    Console.WriteLine(myClass.Do());

    // Use the culture that was supplied above
    CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("fi");
    Console.WriteLine(myClass.Do());
}
using Lexical.Localization;
using Lexical.Localization.Asset;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Globalization;
using System.Reflection;
using TutorialLibrary3;

namespace TutorialProject3
{
    public class Program3
    {
        public static void Main(string[] args)
        {
            #region Snippet
            IServiceCollection services = new ServiceCollection();

            // Install default IStringLocalizerFactory
            services.AddLexicalLocalization(
                addStringLocalizerService: true,
                addCulturePolicyService: true,
                useGlobalInstance: false,
                addCache: false);

            // Install TutorialLibrary's IAssetSources.
            Assembly library = typeof(MyClass).Assembly;
            services.AddLibraryAssetSources(library);

            // Install additional localization that was not available in the TutorialLibrary.
            services.AddSingleton<IAssetSource>(XmlLinesReader.Default.FileAssetSource("TutorialLibrary3-fi.xml"));

            // Service MyClass
            services.AddTransient<MyClass, MyClass>();

            // Create instance container
            using (var provider = services.BuildServiceProvider())
            {
                // Create class
                MyClass myClass = provider.GetService<MyClass>();

                // Use the culture that was provided by with the class library (AssetSources)
                CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de");
                Console.WriteLine(myClass.Do());

                // Use the culture that was supplied above
                CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("fi");
                Console.WriteLine(myClass.Do());
            }
            #endregion Snippet
        }
    }
}
Back to top Copyright © 2015-2020 Toni Kalajainen