From 73a21a816de6e6b8cfe5d6d4a5785d7ace682d6e Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Wed, 17 Apr 2024 10:21:10 +0300 Subject: [PATCH 01/14] Add generic localizer --- Insight.Localizer.sln | 6 ++ .../Insight.Localizer.Extensions.csproj | 28 ++++++++ .../ServiceCollectionExtensions.cs | 43 +++++++++++ src/Insight.Localizer/Block.cs | 2 +- src/Insight.Localizer/ILocalizer.cs | 4 +- src/Insight.Localizer/ILocalizerCulture.cs | 7 ++ src/Insight.Localizer/ILocalizer{T}.cs | 10 +++ .../Insight.Localizer.csproj | 2 +- src/Insight.Localizer/Localizer.cs | 52 ++++++++++---- ...urrentCulture.cs => LocalizerConstants.cs} | 1 - src/Insight.Localizer/LocalizerCulture.cs | 19 +++++ ...erConfiguration.cs => LocalizerOptions.cs} | 2 +- src/Insight.Localizer/Localizer{T}.cs | 11 +++ .../Insight.Localizer.Tests.csproj | 13 +++- .../Insight.Localizer.Tests/LocalizerTest.cs | 71 +++++++++++-------- .../LocalizierExtensionsTests.cs | 64 +++++++++++++++++ .../GenericLocalizerTests.ru-ru.json | 3 + 17 files changed, 291 insertions(+), 47 deletions(-) create mode 100644 src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj create mode 100644 src/Insight.Localizer.Extensions/ServiceCollectionExtensions.cs create mode 100644 src/Insight.Localizer/ILocalizerCulture.cs create mode 100644 src/Insight.Localizer/ILocalizer{T}.cs rename src/Insight.Localizer/{CurrentCulture.cs => LocalizerConstants.cs} (98%) create mode 100644 src/Insight.Localizer/LocalizerCulture.cs rename src/Insight.Localizer/{LocalizerConfiguration.cs => LocalizerOptions.cs} (89%) create mode 100644 src/Insight.Localizer/Localizer{T}.cs create mode 100644 tests/Insight.Localizer.Tests/LocalizierExtensionsTests.cs create mode 100644 tests/Insight.Localizer.Tests/Resources/GenericLocalizerTests.ru-ru.json diff --git a/Insight.Localizer.sln b/Insight.Localizer.sln index 2d6457e..c076c34 100644 --- a/Insight.Localizer.sln +++ b/Insight.Localizer.sln @@ -13,6 +13,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Solution Items", "_Solutio README.md = README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Insight.Localizer.Extensions", "src\Insight.Localizer.Extensions\Insight.Localizer.Extensions.csproj", "{91FBA765-5291-4A11-9A55-7E4B932E3F7A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,5 +29,9 @@ Global {C8EDC5BA-AA36-44C6-A4D5-E45695EF5867}.Debug|Any CPU.Build.0 = Debug|Any CPU {C8EDC5BA-AA36-44C6-A4D5-E45695EF5867}.Release|Any CPU.ActiveCfg = Release|Any CPU {C8EDC5BA-AA36-44C6-A4D5-E45695EF5867}.Release|Any CPU.Build.0 = Release|Any CPU + {91FBA765-5291-4A11-9A55-7E4B932E3F7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91FBA765-5291-4A11-9A55-7E4B932E3F7A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91FBA765-5291-4A11-9A55-7E4B932E3F7A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91FBA765-5291-4A11-9A55-7E4B932E3F7A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj b/src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj new file mode 100644 index 0000000..fdf265c --- /dev/null +++ b/src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj @@ -0,0 +1,28 @@ + + + + netstandard2.1 + Insight.Localizer's extensions + Copyright © 2023 Sergey Nazarov + https://github.com/nazarovsa/Insight.Localizer + https://github.com/nazarovsa/Insight.Localizer + Github + + + + + + + + + + + <_Parameter1>Insight.Localizer.Tests + + + + + + + + diff --git a/src/Insight.Localizer.Extensions/ServiceCollectionExtensions.cs b/src/Insight.Localizer.Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..2e0eadd --- /dev/null +++ b/src/Insight.Localizer.Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,43 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; + +namespace Insight.Localizer.Extensions +{ + internal class LocalizerInitializerBackgroundService : BackgroundService + { + private readonly LocalizerOptions _options; + + public LocalizerInitializerBackgroundService(IOptions options) + { + _options = options?.Value ?? throw new ArgumentNullException(nameof(options)); + } + + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + Localizer.Initialize(_options); + return Task.CompletedTask; + } + } + + public static class ServiceCollectionExtensions + { + public static IServiceCollection AddLocalizer(this IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + services.TryAddSingleton(); + services.AddTransient(typeof(ILocalizer<>), typeof(Localizer<>)); + services.AddHostedService(); + + return services; + } + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/Block.cs b/src/Insight.Localizer/Block.cs index a8833fa..951abdb 100644 --- a/src/Insight.Localizer/Block.cs +++ b/src/Insight.Localizer/Block.cs @@ -11,7 +11,7 @@ public sealed class Block private Block() { - _localizations = new Dictionary>(StringComparer.InvariantCultureIgnoreCase); + _localizations = new Dictionary>(StringComparer.OrdinalIgnoreCase); } internal Block(string name) : this() diff --git a/src/Insight.Localizer/ILocalizer.cs b/src/Insight.Localizer/ILocalizer.cs index f4fdd8b..43dfefc 100644 --- a/src/Insight.Localizer/ILocalizer.cs +++ b/src/Insight.Localizer/ILocalizer.cs @@ -8,7 +8,7 @@ public interface ILocalizer /// Curent culture of the localizer. /// public static string? CurrentCulture { get; set; } - + /// /// Loaded blocks /// @@ -25,7 +25,7 @@ public interface ILocalizer /// Block name /// Key string Get(string block, string key); - + /// /// Get value by block-key for any culture /// diff --git a/src/Insight.Localizer/ILocalizerCulture.cs b/src/Insight.Localizer/ILocalizerCulture.cs new file mode 100644 index 0000000..3db77a6 --- /dev/null +++ b/src/Insight.Localizer/ILocalizerCulture.cs @@ -0,0 +1,7 @@ +namespace Insight.Localizer +{ + public interface ILocalizerCulture + { + string Value { get; } + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/ILocalizer{T}.cs b/src/Insight.Localizer/ILocalizer{T}.cs new file mode 100644 index 0000000..c1a3c23 --- /dev/null +++ b/src/Insight.Localizer/ILocalizer{T}.cs @@ -0,0 +1,10 @@ +namespace Insight.Localizer +{ + public interface ILocalizer : ILocalizer where T : class + { + /// + /// Get ket from context - using nameof(T) as block name + /// + string Get(string key); + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/Insight.Localizer.csproj b/src/Insight.Localizer/Insight.Localizer.csproj index 1a0ea2a..5a5798e 100644 --- a/src/Insight.Localizer/Insight.Localizer.csproj +++ b/src/Insight.Localizer/Insight.Localizer.csproj @@ -1,7 +1,7 @@ - netstandard2.1;net6.0; + netstandard2.1 Abstraction to localize your app from json files Copyright © 2023 Sergey Nazarov https://github.com/InsightAppDev/Insight.Localizer diff --git a/src/Insight.Localizer/Localizer.cs b/src/Insight.Localizer/Localizer.cs index f1ad86d..c702e3a 100644 --- a/src/Insight.Localizer/Localizer.cs +++ b/src/Insight.Localizer/Localizer.cs @@ -8,8 +8,10 @@ namespace Insight.Localizer { - public sealed class Localizer : ILocalizer + public class Localizer : ILocalizer { + private static object _syncRoot = new object(); + private static IDictionary _blocks; private static readonly AsyncLocal _currentCulture = new AsyncLocal(); @@ -36,13 +38,39 @@ public static string? CurrentCulture public IDictionary Blocks => _blocks; - public static void Initialize(LocalizerConfiguration configuration) + + public Localizer() + { + } + + public Localizer(ILocalizerCulture culture) { - if (configuration == null) - throw new ArgumentNullException(nameof(configuration)); + } + + public static void Initialize(LocalizerOptions options) + { + if (options == null) + throw new ArgumentNullException(nameof(options)); + + if (_blocks == null) + { + lock (_syncRoot) + { + if (_blocks != null) + return; - _blocks = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - Build(configuration); + _blocks = new Dictionary(StringComparer.OrdinalIgnoreCase); + Build(options); + } + } + } + + public static void Clear() + { + lock (_syncRoot) + { + _blocks = null; + } } public string Get(string block, string key) @@ -65,16 +93,16 @@ public string Get(string culture, string block, string key) ? value : throw new MissingBlockException($"Block `{name}` missing"); - private static void Build(LocalizerConfiguration configuration) + private static void Build(LocalizerOptions options) { - var pattern = string.IsNullOrWhiteSpace(configuration.Pattern) + var pattern = string.IsNullOrWhiteSpace(options.Pattern) ? "*.json" - : $"{configuration.Pattern}.*.json"; - var searchOption = configuration.ReadNestedFolders + : $"{options.Pattern}.*.json"; + var searchOption = options.ReadNestedFolders ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; - var files = Directory.GetFiles(configuration.Path, pattern, searchOption); + var files = Directory.GetFiles(options.Path, pattern, searchOption); foreach (var file in files) { var localeRegex = new Regex(@"^(.{1,})\.(.{2,})\.json$"); @@ -87,7 +115,7 @@ private static void Build(LocalizerConfiguration configuration) var json = File.ReadAllText(file); var jObject = JObject.Parse(json); - var blockContent = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + var blockContent = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var (key, value) in jObject) blockContent.Add(key, value.ToString()); diff --git a/src/Insight.Localizer/CurrentCulture.cs b/src/Insight.Localizer/LocalizerConstants.cs similarity index 98% rename from src/Insight.Localizer/CurrentCulture.cs rename to src/Insight.Localizer/LocalizerConstants.cs index 1a7b103..649af5b 100644 --- a/src/Insight.Localizer/CurrentCulture.cs +++ b/src/Insight.Localizer/LocalizerConstants.cs @@ -1,6 +1,5 @@ namespace Insight.Localizer { - public static class LocalizerConstants { public static string AnyCultureKey = "any"; diff --git a/src/Insight.Localizer/LocalizerCulture.cs b/src/Insight.Localizer/LocalizerCulture.cs new file mode 100644 index 0000000..039259b --- /dev/null +++ b/src/Insight.Localizer/LocalizerCulture.cs @@ -0,0 +1,19 @@ +using System; + +namespace Insight.Localizer +{ + public sealed class LocalizerCulture : ILocalizerCulture + { + public LocalizerCulture(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentNullException(nameof(value)); + } + + Value = value; + } + + public string Value { get; } + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/LocalizerConfiguration.cs b/src/Insight.Localizer/LocalizerOptions.cs similarity index 89% rename from src/Insight.Localizer/LocalizerConfiguration.cs rename to src/Insight.Localizer/LocalizerOptions.cs index 92e7ba4..cc8e2a0 100644 --- a/src/Insight.Localizer/LocalizerConfiguration.cs +++ b/src/Insight.Localizer/LocalizerOptions.cs @@ -1,6 +1,6 @@ namespace Insight.Localizer { - public sealed class LocalizerConfiguration + public sealed class LocalizerOptions { public string Path { get; set; } diff --git a/src/Insight.Localizer/Localizer{T}.cs b/src/Insight.Localizer/Localizer{T}.cs new file mode 100644 index 0000000..5224a3b --- /dev/null +++ b/src/Insight.Localizer/Localizer{T}.cs @@ -0,0 +1,11 @@ +namespace Insight.Localizer +{ + public class Localizer : Localizer, ILocalizer where T : class + { + public string Get(string key) + { + var block = typeof(T).Name; + return Get(block, key); + } + } +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj b/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj index 99ad63a..c485bf7 100644 --- a/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj +++ b/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj @@ -2,11 +2,11 @@ net6.0 - false + @@ -14,6 +14,7 @@ + @@ -32,4 +33,14 @@ + + + + PreserveNewest + + + PreserveNewest + + + diff --git a/tests/Insight.Localizer.Tests/LocalizerTest.cs b/tests/Insight.Localizer.Tests/LocalizerTest.cs index 9deca15..97bdeb0 100644 --- a/tests/Insight.Localizer.Tests/LocalizerTest.cs +++ b/tests/Insight.Localizer.Tests/LocalizerTest.cs @@ -4,9 +4,34 @@ namespace Insight.Localizer.Tests { + public sealed class GenericLocalizerTests + { + private readonly Localizer _localizer; + + private static LocalizerOptions Options => new LocalizerOptions + { + Path = "Resources", + ReadNestedFolders = true + }; + + public GenericLocalizerTests() + { + Localizer.Initialize(Options); + Localizer.CurrentCulture = "ru-ru"; + _localizer = new Localizer(); + } + + [Fact] + public void Get_returns_value_based_on_generic_argument_name() + { + var value = _localizer.Get("test"); + Assert.Equal("Hello!", value); + } + } + public sealed class LocalizerTest { - private static LocalizerConfiguration Configuration => new LocalizerConfiguration + private static LocalizerOptions Options => new LocalizerOptions { Path = "Resources", ReadNestedFolders = true @@ -14,7 +39,7 @@ public sealed class LocalizerTest public LocalizerTest() { - Localizer.Initialize(Configuration); + Localizer.Initialize(Options); Localizer.CurrentCulture = "ru-ru"; } @@ -56,78 +81,68 @@ public void Should_throw_MissingLocalizationException_if_there_is_no_key() public void Should_read_all_files() { var localizer = new Localizer(); - AssertLocalizer(localizer, 3); + AssertLocalizer(localizer, 4); } [Fact] public void Should_get_available_block_names_from_all_Files() { var localizer = new Localizer(); - AssertLocalizer(localizer, 3); + AssertLocalizer(localizer, 4); var names = localizer.AvailableBlockNames; Assert.NotNull(names); Assert.NotEmpty(names); - Assert.Equal(3, names.Count); - Assert.NotNull(names.FirstOrDefault(x => x.Equals("test", StringComparison.InvariantCultureIgnoreCase))); + Assert.Equal(4, names.Count); + Assert.NotNull(names.FirstOrDefault(x => x.Equals("test", StringComparison.OrdinalIgnoreCase))); Assert.NotNull(names.FirstOrDefault(x => - x.Equals("messages", StringComparison.InvariantCultureIgnoreCase))); + x.Equals("messages", StringComparison.OrdinalIgnoreCase))); Assert.NotNull(names.FirstOrDefault(x => - x.Equals("language", StringComparison.InvariantCultureIgnoreCase))); - } - - [Fact] - public void Should_read_files_by_pattern() - { - var config = Configuration; - config.Pattern = "test"; - Localizer.Initialize(config); - var localizer = new Localizer(); - AssertLocalizer(localizer, 1); + x.Equals("language", StringComparison.OrdinalIgnoreCase))); } [Fact] public void Should_get_value_in_all_languages() { var localizer = new Localizer(); - AssertLocalizer(localizer, 3); + AssertLocalizer(localizer, 4); var en = localizer.Get("en-us", "test", "Hello"); var ru = localizer.Get("test", "Hello"); - Assert.Equal("Hi", en, StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("Привет", ru, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Hi", en, StringComparer.OrdinalIgnoreCase); + Assert.Equal("Привет", ru, StringComparer.OrdinalIgnoreCase); } [Fact] public void Should_get_any_value() { var localizer = new Localizer(); - AssertLocalizer(localizer, 3); + AssertLocalizer(localizer, 4); var russianLanguage = localizer.GetAny("language", "Russian"); var englishLanguage = localizer.GetAny("language", "English"); - Assert.Equal("Русский", russianLanguage, StringComparer.InvariantCultureIgnoreCase); - Assert.Equal("English", englishLanguage, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("Русский", russianLanguage, StringComparer.OrdinalIgnoreCase); + Assert.Equal("English", englishLanguage, StringComparer.OrdinalIgnoreCase); } [Fact] public void Should_change_current_culture() { var localizer = new Localizer(); - AssertLocalizer(localizer, 3); + AssertLocalizer(localizer, 4); - Assert.Equal("ru-ru", Localizer.CurrentCulture, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("ru-ru", Localizer.CurrentCulture, StringComparer.OrdinalIgnoreCase); Localizer.CurrentCulture = "en-us"; - Assert.Equal("en-us", Localizer.CurrentCulture, StringComparer.InvariantCultureIgnoreCase); + Assert.Equal("en-us", Localizer.CurrentCulture, StringComparer.OrdinalIgnoreCase); } [Fact] public void Should_throw_ANE_on_set_culture_if_culture_is_null() { var localizer = new Localizer(); - AssertLocalizer(localizer, 3); + AssertLocalizer(localizer, 4); Assert.Throws(() => Localizer.CurrentCulture = null); } diff --git a/tests/Insight.Localizer.Tests/LocalizierExtensionsTests.cs b/tests/Insight.Localizer.Tests/LocalizierExtensionsTests.cs new file mode 100644 index 0000000..4283d55 --- /dev/null +++ b/tests/Insight.Localizer.Tests/LocalizierExtensionsTests.cs @@ -0,0 +1,64 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Insight.Localizer.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; + +namespace Insight.Localizer.Tests; + +public class LocalizierExtensionsTests +{ + [Fact] + public void AddLocalizer_registers_non_generic_implementation() + { + var sp = BuildServiceProvider(); + var localizer = sp.GetRequiredService(); + + Assert.NotNull(localizer); + } + + [Fact] + public void AddLocalizer_registers_generic_implementation() + { + var sp = BuildServiceProvider(); + var localizer = sp.GetRequiredService>(); + + Assert.NotNull(localizer); + } + + [Fact] + public void AddLocalizer_registers_localizer_initializer() + { + var sp = BuildServiceProvider(); + var localizer = sp.GetRequiredService>(); + + Assert.NotNull(localizer); + } + + [Fact] + public async Task LocalizerInitializer_initializes_localizer() + { + var sp = BuildServiceProvider(); + var localizer = sp.GetRequiredService>(); + + var initializer = sp.GetRequiredService(); + await initializer.StartAsync(CancellationToken.None); + + Assert.NotNull(localizer.AvailableBlockNames); + } + + private static IServiceProvider BuildServiceProvider() + { + IServiceCollection services = new ServiceCollection(); + services.Configure(cfg => + { + cfg.Path = "Resources"; + cfg.ReadNestedFolders = true; + }); + services.AddLocalizer(); + + return services.BuildServiceProvider(); + } +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/Resources/GenericLocalizerTests.ru-ru.json b/tests/Insight.Localizer.Tests/Resources/GenericLocalizerTests.ru-ru.json new file mode 100644 index 0000000..36e4e4a --- /dev/null +++ b/tests/Insight.Localizer.Tests/Resources/GenericLocalizerTests.ru-ru.json @@ -0,0 +1,3 @@ +{ + "Test": "Hello!" +} \ No newline at end of file From 4a0570aa5d43cedbae2b4b3733e425d3cdbe34e8 Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Wed, 24 Apr 2024 15:52:45 +0400 Subject: [PATCH 02/14] Extend generic localizer methods --- src/Insight.Localizer/ILocalizer.cs | 2 +- src/Insight.Localizer/ILocalizer{T}.cs | 14 +++++++++++++- src/Insight.Localizer/Localizer.cs | 2 +- src/Insight.Localizer/Localizer{T}.cs | 19 ++++++++++++++++++- .../Insight.Localizer.Tests/LocalizerTest.cs | 2 +- 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/Insight.Localizer/ILocalizer.cs b/src/Insight.Localizer/ILocalizer.cs index 43dfefc..fad4591 100644 --- a/src/Insight.Localizer/ILocalizer.cs +++ b/src/Insight.Localizer/ILocalizer.cs @@ -41,6 +41,6 @@ public interface ILocalizer /// Block name /// Key /// - string Get(string culture, string block, string key); + string GetByCulture(string culture, string block, string key); } } \ No newline at end of file diff --git a/src/Insight.Localizer/ILocalizer{T}.cs b/src/Insight.Localizer/ILocalizer{T}.cs index c1a3c23..d9b27d5 100644 --- a/src/Insight.Localizer/ILocalizer{T}.cs +++ b/src/Insight.Localizer/ILocalizer{T}.cs @@ -3,8 +3,20 @@ namespace Insight.Localizer public interface ILocalizer : ILocalizer where T : class { /// - /// Get ket from context - using nameof(T) as block name + /// Get value by key from context - using nameof(T) as block name /// string Get(string key); + + /// + /// Get any value by key from context for any culture - using nameof(T) as block name + /// + /// Key + /// + string GetAny(string key); + + /// + /// Get value from context - using nameof(T) as block name + /// + string GetByCulture(string culture, string key); } } \ No newline at end of file diff --git a/src/Insight.Localizer/Localizer.cs b/src/Insight.Localizer/Localizer.cs index c702e3a..d91dde1 100644 --- a/src/Insight.Localizer/Localizer.cs +++ b/src/Insight.Localizer/Localizer.cs @@ -83,7 +83,7 @@ public string GetAny(string block, string key) return this[block].Get(LocalizerConstants.AnyCultureKey, key); } - public string Get(string culture, string block, string key) + public string GetByCulture(string culture, string block, string key) { return this[block].Get(culture, key); } diff --git a/src/Insight.Localizer/Localizer{T}.cs b/src/Insight.Localizer/Localizer{T}.cs index 5224a3b..33f17fc 100644 --- a/src/Insight.Localizer/Localizer{T}.cs +++ b/src/Insight.Localizer/Localizer{T}.cs @@ -4,8 +4,25 @@ public class Localizer : Localizer, ILocalizer where T : class { public string Get(string key) { - var block = typeof(T).Name; + var block = GetBlockName(); return Get(block, key); } + + public string GetAny(string key) + { + var block = GetBlockName(); + return GetAny(block, key); + } + + public string GetByCulture(string culture, string key) + { + var block = GetBlockName(); + return GetByCulture(culture, block, key); + } + + private static string GetBlockName() + { + return typeof(T).Name; + } } } \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/LocalizerTest.cs b/tests/Insight.Localizer.Tests/LocalizerTest.cs index 97bdeb0..0cce0cc 100644 --- a/tests/Insight.Localizer.Tests/LocalizerTest.cs +++ b/tests/Insight.Localizer.Tests/LocalizerTest.cs @@ -107,7 +107,7 @@ public void Should_get_value_in_all_languages() var localizer = new Localizer(); AssertLocalizer(localizer, 4); - var en = localizer.Get("en-us", "test", "Hello"); + var en = localizer.GetByCulture("en-us", "test", "Hello"); var ru = localizer.Get("test", "Hello"); Assert.Equal("Hi", en, StringComparer.OrdinalIgnoreCase); From 27cf1f1586ed30766e31d39ab69da6df09d2df05 Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Thu, 25 Apr 2024 00:47:42 +0400 Subject: [PATCH 03/14] Intermediate commit --- src/Insight.Localizer/Localizer.cs | 87 ++++++++++++++----- src/Insight.Localizer/LocalizerOptions.cs | 14 ++- .../GenericLocalizerTests.cs | 30 +++++++ .../Insight.Localizer.Tests.csproj | 22 +---- ....cs => LocalizerOneLanguageInFileTests.cs} | 41 +++++---- .../MultipleLanguagesInFile/test.lclzr.json | 8 ++ .../GenericLocalizerTests.ru-ru.json | 0 .../{ => OneLanguageInFile}/language.any.json | 0 .../messages.ru-ru.json | 0 .../{ => OneLanguageInFile}/test.en-us.json | 0 .../{ => OneLanguageInFile}/test.ru-ru.json | 0 11 files changed, 142 insertions(+), 60 deletions(-) create mode 100644 tests/Insight.Localizer.Tests/GenericLocalizerTests.cs rename tests/Insight.Localizer.Tests/{LocalizerTest.cs => LocalizerOneLanguageInFileTests.cs} (76%) create mode 100644 tests/Insight.Localizer.Tests/Resources/MultipleLanguagesInFile/test.lclzr.json rename tests/Insight.Localizer.Tests/Resources/{ => OneLanguageInFile}/GenericLocalizerTests.ru-ru.json (100%) rename tests/Insight.Localizer.Tests/Resources/{ => OneLanguageInFile}/language.any.json (100%) rename tests/Insight.Localizer.Tests/Resources/{ => OneLanguageInFile}/messages.ru-ru.json (100%) rename tests/Insight.Localizer.Tests/Resources/{ => OneLanguageInFile}/test.en-us.json (100%) rename tests/Insight.Localizer.Tests/Resources/{ => OneLanguageInFile}/test.ru-ru.json (100%) diff --git a/src/Insight.Localizer/Localizer.cs b/src/Insight.Localizer/Localizer.cs index d91dde1..a2606fc 100644 --- a/src/Insight.Localizer/Localizer.cs +++ b/src/Insight.Localizer/Localizer.cs @@ -16,6 +16,9 @@ public class Localizer : ILocalizer private static readonly AsyncLocal _currentCulture = new AsyncLocal(); + private static readonly Regex OneFilePerLanguageNameRegex = + new Regex(@"^(.{1,})\.(.{2,})\.json$", RegexOptions.Compiled); + public static string? CurrentCulture { get => _currentCulture.Value; @@ -95,39 +98,81 @@ public string GetByCulture(string culture, string block, string key) private static void Build(LocalizerOptions options) { - var pattern = string.IsNullOrWhiteSpace(options.Pattern) - ? "*.json" - : $"{options.Pattern}.*.json"; + if (string.IsNullOrWhiteSpace(options.FileEndsWith)) + { + throw new InvalidOperationException($"{nameof(LocalizerOptions.FileEndsWith)} should be specified"); + } + var searchOption = options.ReadNestedFolders ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; - var files = Directory.GetFiles(options.Path, pattern, searchOption); + var files = Directory.GetFiles(options.Path, "*", searchOption) + .Where(x => x.EndsWith(options.FileEndsWith, StringComparison.OrdinalIgnoreCase)); + foreach (var file in files) { - var localeRegex = new Regex(@"^(.{1,})\.(.{2,})\.json$"); - var filename = Path.GetFileName(file); - var match = localeRegex.Match(filename); - if (match.Success) + if (options.OneLanguageInFile) + { + InitializeForOneLanguageInFile(file); + continue; + } + + InitializeForAllLanguagesInOneFile(file, options.FileEndsWith); + } + } + + private static void InitializeForAllLanguagesInOneFile(string file, string fileEndsWith) + { + var filename = Path.GetFileName(file); + var blockName = filename.Replace(fileEndsWith, string.Empty); + + var json = File.ReadAllText(file); + var jObject = JObject.Parse(json); + + foreach (var (culture, blockJToken) in jObject) + { + var blockContent = new Dictionary(StringComparer.OrdinalIgnoreCase); + var blockJObject = blockJToken as JObject; + foreach (var (key, value) in blockJObject) { - var blockName = match.Groups[1].Value; - var cultureString = match.Groups[^1]?.Value; + blockContent.Add(key, value.ToString()); + } - var json = File.ReadAllText(file); - var jObject = JObject.Parse(json); - var blockContent = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (!_blocks.ContainsKey(blockName)) + { + var block = new Block(blockName); + _blocks.Add(block.Name, block); + } - foreach (var (key, value) in jObject) - blockContent.Add(key, value.ToString()); + _blocks[blockName].Add(culture, blockContent); + } + } - if (!_blocks.ContainsKey(blockName)) - { - var block = new Block(blockName); - _blocks.Add(block.Name, block); - } + private static void InitializeForOneLanguageInFile(string file) + { + var localeRegex = OneFilePerLanguageNameRegex; + var filename = Path.GetFileName(file); + var match = localeRegex.Match(filename); + if (match.Success) + { + var blockName = match.Groups[1].Value; + var cultureString = match.Groups[^1]?.Value; + + var json = File.ReadAllText(file); + var jObject = JObject.Parse(json); + var blockContent = new Dictionary(StringComparer.OrdinalIgnoreCase); - _blocks[blockName].Add(cultureString, blockContent); + foreach (var (key, value) in jObject) + blockContent.Add(key, value.ToString()); + + if (!_blocks.ContainsKey(blockName)) + { + var block = new Block(blockName); + _blocks.Add(block.Name, block); } + + _blocks[blockName].Add(cultureString, blockContent); } } } diff --git a/src/Insight.Localizer/LocalizerOptions.cs b/src/Insight.Localizer/LocalizerOptions.cs index cc8e2a0..3c9de61 100644 --- a/src/Insight.Localizer/LocalizerOptions.cs +++ b/src/Insight.Localizer/LocalizerOptions.cs @@ -2,13 +2,23 @@ namespace Insight.Localizer { public sealed class LocalizerOptions { + public const string DefaultResourceEndWith = ".lclzr.json"; + public string Path { get; set; } + public bool ReadNestedFolders { get; set; } + + public bool OneLanguageInFile { get; set; } = true; + + public bool ReadFromFiles { get; set; } = true; + /// /// File pattern. Example 'message' will read all files started with 'message.' like 'message.en-us.json', etc. If null reads all files in directory /// - public string Pattern { get; set; } + public string FileEndsWith { get; set; } = DefaultResourceEndWith; - public bool ReadNestedFolders { get; set; } + public bool ReadFromEmbedded { get; set; } = false; + + public string EmbeddedEndWith { get; set; } = DefaultResourceEndWith; } } \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/GenericLocalizerTests.cs b/tests/Insight.Localizer.Tests/GenericLocalizerTests.cs new file mode 100644 index 0000000..c38a78b --- /dev/null +++ b/tests/Insight.Localizer.Tests/GenericLocalizerTests.cs @@ -0,0 +1,30 @@ +using Xunit; + +namespace Insight.Localizer.Tests; + +[Collection("LocalizerTests")] +public sealed class GenericLocalizerTests +{ + private readonly Localizer _localizer; + + private static LocalizerOptions Options => new() + { + Path = "Resources", + FileEndsWith = ".json", + ReadNestedFolders = true + }; + + public GenericLocalizerTests() + { + Localizer.Initialize(Options); + Localizer.CurrentCulture = "ru-ru"; + _localizer = new Localizer(); + } + + [Fact] + public void Get_returns_value_based_on_generic_argument_name() + { + var value = _localizer.Get("test"); + Assert.Equal("Hello!", value); + } +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj b/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj index c485bf7..c169f9e 100644 --- a/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj +++ b/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj @@ -19,27 +19,9 @@ - + + Always - - - Always - - - Always - - - Always - - - - - - - PreserveNewest - - - PreserveNewest diff --git a/tests/Insight.Localizer.Tests/LocalizerTest.cs b/tests/Insight.Localizer.Tests/LocalizerOneLanguageInFileTests.cs similarity index 76% rename from tests/Insight.Localizer.Tests/LocalizerTest.cs rename to tests/Insight.Localizer.Tests/LocalizerOneLanguageInFileTests.cs index 0cce0cc..dcb47d0 100644 --- a/tests/Insight.Localizer.Tests/LocalizerTest.cs +++ b/tests/Insight.Localizer.Tests/LocalizerOneLanguageInFileTests.cs @@ -1,43 +1,50 @@ using System; +using System.IO; using System.Linq; +using Microsoft.Extensions.Options; using Xunit; namespace Insight.Localizer.Tests { - public sealed class GenericLocalizerTests + [Collection("LocalizerTests")] + public sealed class LocalizerMultipleLanguagesInFileTests { - private readonly Localizer _localizer; - - private static LocalizerOptions Options => new LocalizerOptions + private static LocalizerOptions _options = new() { - Path = "Resources", + Path = "Resources" + Path.DirectorySeparatorChar + "MultipleLanguagesInFile", + OneLanguageInFile = false, ReadNestedFolders = true }; - public GenericLocalizerTests() + public LocalizerMultipleLanguagesInFileTests() { - Localizer.Initialize(Options); - Localizer.CurrentCulture = "ru-ru"; - _localizer = new Localizer(); + Localizer.Initialize(_options); + Localizer.CurrentCulture = "ru"; } [Fact] - public void Get_returns_value_based_on_generic_argument_name() + public void Ctor_initializes_block_test_with_two_cultures_and_hello_key_from_single_file() { - var value = _localizer.Get("test"); - Assert.Equal("Hello!", value); + var localizer = new Localizer(); + + Assert.Equal(1, localizer.AvailableBlockNames.Count); + Assert.Equal("test", localizer.AvailableBlockNames.First(), StringComparer.OrdinalIgnoreCase); + Assert.Equal("Привет", localizer.GetByCulture("ru", "test", "Hello"), StringComparer.OrdinalIgnoreCase); + Assert.Equal("Hi", localizer.GetByCulture("en", "test", "Hello"), StringComparer.OrdinalIgnoreCase); } } - public sealed class LocalizerTest + [Collection("LocalizerTests")] + public sealed class LocalizerOneLanguageInFileTests { - private static LocalizerOptions Options => new LocalizerOptions + private static readonly LocalizerOptions Options = new() { - Path = "Resources", + Path = "Resources" + Path.DirectorySeparatorChar + "OneLanguageInFile", + FileEndsWith = ".json", ReadNestedFolders = true }; - public LocalizerTest() + public LocalizerOneLanguageInFileTests() { Localizer.Initialize(Options); Localizer.CurrentCulture = "ru-ru"; @@ -54,7 +61,7 @@ public void Should_throw_ANE_if_name_at_block_ctor_is_null() { Assert.Throws(() => new Block(null)); } - + [Fact] public void Should_throw_MissingBlockException() { diff --git a/tests/Insight.Localizer.Tests/Resources/MultipleLanguagesInFile/test.lclzr.json b/tests/Insight.Localizer.Tests/Resources/MultipleLanguagesInFile/test.lclzr.json new file mode 100644 index 0000000..8b16d23 --- /dev/null +++ b/tests/Insight.Localizer.Tests/Resources/MultipleLanguagesInFile/test.lclzr.json @@ -0,0 +1,8 @@ +{ + "ru": { + "Hello": "Привет" + }, + "en": { + "Hello": "Hi" + } +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/Resources/GenericLocalizerTests.ru-ru.json b/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/GenericLocalizerTests.ru-ru.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/GenericLocalizerTests.ru-ru.json rename to tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/GenericLocalizerTests.ru-ru.json diff --git a/tests/Insight.Localizer.Tests/Resources/language.any.json b/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/language.any.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/language.any.json rename to tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/language.any.json diff --git a/tests/Insight.Localizer.Tests/Resources/messages.ru-ru.json b/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/messages.ru-ru.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/messages.ru-ru.json rename to tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/messages.ru-ru.json diff --git a/tests/Insight.Localizer.Tests/Resources/test.en-us.json b/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/test.en-us.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/test.en-us.json rename to tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/test.en-us.json diff --git a/tests/Insight.Localizer.Tests/Resources/test.ru-ru.json b/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/test.ru-ru.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/test.ru-ru.json rename to tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/test.ru-ru.json From 6f713eba7bb8deb8734616eef28ea3485897552f Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Thu, 25 Apr 2024 15:52:43 +0400 Subject: [PATCH 04/14] Fix Initialize method --- src/Insight.Localizer/Localizer.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Insight.Localizer/Localizer.cs b/src/Insight.Localizer/Localizer.cs index a2606fc..f518296 100644 --- a/src/Insight.Localizer/Localizer.cs +++ b/src/Insight.Localizer/Localizer.cs @@ -17,7 +17,7 @@ public class Localizer : ILocalizer private static readonly AsyncLocal _currentCulture = new AsyncLocal(); private static readonly Regex OneFilePerLanguageNameRegex = - new Regex(@"^(.{1,})\.(.{2,})\.json$", RegexOptions.Compiled); + new Regex(@"^(.{1,})\.(.{2,})$", RegexOptions.Compiled); public static string? CurrentCulture { @@ -114,7 +114,7 @@ private static void Build(LocalizerOptions options) { if (options.OneLanguageInFile) { - InitializeForOneLanguageInFile(file); + InitializeForOneLanguageInFile(file, options.FileEndsWith); continue; } @@ -149,10 +149,11 @@ private static void InitializeForAllLanguagesInOneFile(string file, string fileE } } - private static void InitializeForOneLanguageInFile(string file) + private static void InitializeForOneLanguageInFile(string file, string fileEndsWith) { var localeRegex = OneFilePerLanguageNameRegex; - var filename = Path.GetFileName(file); + var filename = Path.GetFileName(file) + .Replace(fileEndsWith, string.Empty, StringComparison.OrdinalIgnoreCase); var match = localeRegex.Match(filename); if (match.Success) { From 980fa2d4ac1725741b997aa619fdbe3be584aede Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Sat, 27 Apr 2024 16:20:54 +0400 Subject: [PATCH 05/14] Change concept --- .../Insight.Localizer.Extensions.csproj | 5 - .../RegistryInitializerBackgroundService.cs | 31 +++ .../ServiceCollectionExtensions.cs | 38 ++- src/Insight.Localizer/Block.cs | 4 + .../Exceptions/LocalizerException.cs | 15 ++ src/Insight.Localizer/ILocalizer.cs | 7 +- .../Infrastructure/IInitializable.cs | 12 + .../Insight.Localizer.csproj | 7 +- src/Insight.Localizer/Localizer.cs | 225 ++++-------------- src/Insight.Localizer/LocalizerOptions.cs | 24 -- src/Insight.Localizer/Localizer{T}.cs | 11 + .../Providers/BlockCultureData.cs | 35 +++ .../EmbeddedResourcesBlocksProvider.cs | 97 ++++++++ .../EmbeddedResourcesBlocksProviderOptions.cs | 11 + .../Providers/Files/FilesBlocksProvider.cs | 36 +++ .../Files/RawFiles/RawFilesBlocksProvider.cs | 83 +++++++ .../RawFiles/RawFilesBlocksProviderOptions.cs | 9 + .../Providers/IBlocksProvider.cs | 9 + .../Registries/ILocalizerRegistry.cs | 34 +++ .../Registries/LocalizerRegistry.cs | 88 +++++++ tests/Insight.Localizer.Tests/BlockTests.cs | 19 ++ .../lclzr.embedded.json | 8 + .../lclzr.embedded.en-us.json | 3 + .../lclzr.embedded.ru-ru.json | 3 + .../GenericLocalizerTests.cs | 15 +- .../LocalizerWithRawFilesProviderSut.cs | 23 ++ .../Infratructure/TestBlocksProvider.cs | 19 ++ .../Insight.Localizer.Tests.csproj | 30 ++- .../LocalizerMultipleLanguagesInFileTests.cs | 37 +++ .../LocalizerOneLanguageInFileTests.cs | 179 +++----------- .../LocalizerRegistryTests.cs | 38 +++ .../LocalizierExtensionsTests.cs | 7 +- .../EmbeddedResourceBlocksProviderTests.cs | 53 +++++ .../Providers/RawFilesBlocksProviderTests.cs | 24 ++ .../{test.lclzr.json => lclzr.multiple.json} | 0 ...=> lclzr.GenericLocalizerTests.ru-ru.json} | 0 ...guage.any.json => lclzr.language.any.json} | 0 ...s.ru-ru.json => lclzr.messages.ru-ru.json} | 0 ...{test.en-us.json => lclzr.test.en-us.json} | 0 ...{test.ru-ru.json => lclzr.test.ru-ru.json} | 0 40 files changed, 829 insertions(+), 410 deletions(-) create mode 100644 src/Insight.Localizer.Extensions/RegistryInitializerBackgroundService.cs create mode 100644 src/Insight.Localizer/Exceptions/LocalizerException.cs create mode 100644 src/Insight.Localizer/Infrastructure/IInitializable.cs delete mode 100644 src/Insight.Localizer/LocalizerOptions.cs create mode 100644 src/Insight.Localizer/Providers/BlockCultureData.cs create mode 100644 src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs create mode 100644 src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProviderOptions.cs create mode 100644 src/Insight.Localizer/Providers/Files/FilesBlocksProvider.cs create mode 100644 src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProvider.cs create mode 100644 src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProviderOptions.cs create mode 100644 src/Insight.Localizer/Providers/IBlocksProvider.cs create mode 100644 src/Insight.Localizer/Registries/ILocalizerRegistry.cs create mode 100644 src/Insight.Localizer/Registries/LocalizerRegistry.cs create mode 100644 tests/Insight.Localizer.Tests/BlockTests.cs create mode 100644 tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedMultipleLanguagesInFile/lclzr.embedded.json create mode 100644 tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.en-us.json create mode 100644 tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.ru-ru.json create mode 100644 tests/Insight.Localizer.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs create mode 100644 tests/Insight.Localizer.Tests/Infratructure/TestBlocksProvider.cs create mode 100644 tests/Insight.Localizer.Tests/LocalizerMultipleLanguagesInFileTests.cs create mode 100644 tests/Insight.Localizer.Tests/LocalizerRegistryTests.cs create mode 100644 tests/Insight.Localizer.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs create mode 100644 tests/Insight.Localizer.Tests/Providers/RawFilesBlocksProviderTests.cs rename tests/Insight.Localizer.Tests/Resources/MultipleLanguagesInFile/{test.lclzr.json => lclzr.multiple.json} (100%) rename tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/{GenericLocalizerTests.ru-ru.json => lclzr.GenericLocalizerTests.ru-ru.json} (100%) rename tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/{language.any.json => lclzr.language.any.json} (100%) rename tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/{messages.ru-ru.json => lclzr.messages.ru-ru.json} (100%) rename tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/{test.en-us.json => lclzr.test.en-us.json} (100%) rename tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/{test.ru-ru.json => lclzr.test.ru-ru.json} (100%) diff --git a/src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj b/src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj index fdf265c..bb97f91 100644 --- a/src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj +++ b/src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj @@ -2,11 +2,6 @@ netstandard2.1 - Insight.Localizer's extensions - Copyright © 2023 Sergey Nazarov - https://github.com/nazarovsa/Insight.Localizer - https://github.com/nazarovsa/Insight.Localizer - Github diff --git a/src/Insight.Localizer.Extensions/RegistryInitializerBackgroundService.cs b/src/Insight.Localizer.Extensions/RegistryInitializerBackgroundService.cs new file mode 100644 index 0000000..b828dae --- /dev/null +++ b/src/Insight.Localizer.Extensions/RegistryInitializerBackgroundService.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Insight.Localizer.Infrastructure; +using Insight.Localizer.Registries; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Insight.Localizer.Extensions +{ + internal class RegistryInitializerBackgroundService : BackgroundService + { + private readonly IServiceProvider _serviceProvider; + + public RegistryInitializerBackgroundService(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + } + + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + var registry = _serviceProvider.GetRequiredService(); + if (registry is IInitializable initializable) + { + return initializable.Initialize(); + } + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/Insight.Localizer.Extensions/ServiceCollectionExtensions.cs b/src/Insight.Localizer.Extensions/ServiceCollectionExtensions.cs index 2e0eadd..ce5c217 100644 --- a/src/Insight.Localizer.Extensions/ServiceCollectionExtensions.cs +++ b/src/Insight.Localizer.Extensions/ServiceCollectionExtensions.cs @@ -1,41 +1,37 @@ using System; -using System.Threading; -using System.Threading.Tasks; +using Insight.Localizer.Providers; +using Insight.Localizer.Registries; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; namespace Insight.Localizer.Extensions { - internal class LocalizerInitializerBackgroundService : BackgroundService + public static class ServiceCollectionExtensions { - private readonly LocalizerOptions _options; - - public LocalizerInitializerBackgroundService(IOptions options) + public static IServiceCollection AddLocalizer(this IServiceCollection services) { - _options = options?.Value ?? throw new ArgumentNullException(nameof(options)); - } + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } - protected override Task ExecuteAsync(CancellationToken stoppingToken) - { - Localizer.Initialize(_options); - return Task.CompletedTask; + services.TryAddSingleton(); + services.TryAddScoped(); + services.AddTransient(typeof(ILocalizer<>), typeof(Localizer<>)); + services.AddHostedService(); + + return services; } - } - public static class ServiceCollectionExtensions - { - public static IServiceCollection AddLocalizer(this IServiceCollection services) + public static IServiceCollection AddLocalizerProvider(this IServiceCollection services) + where T : class, IBlocksProvider { if (services == null) { throw new ArgumentNullException(nameof(services)); } - services.TryAddSingleton(); - services.AddTransient(typeof(ILocalizer<>), typeof(Localizer<>)); - services.AddHostedService(); + services.AddSingleton(); return services; } diff --git a/src/Insight.Localizer/Block.cs b/src/Insight.Localizer/Block.cs index 951abdb..9782575 100644 --- a/src/Insight.Localizer/Block.cs +++ b/src/Insight.Localizer/Block.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; namespace Insight.Localizer { @@ -7,6 +8,9 @@ public sealed class Block { public string Name { get; } + public IReadOnlyCollection AvailableCultures => + new Lazy>(() => _localizations.Keys.ToList().AsReadOnly()).Value; + private readonly IDictionary> _localizations; private Block() diff --git a/src/Insight.Localizer/Exceptions/LocalizerException.cs b/src/Insight.Localizer/Exceptions/LocalizerException.cs new file mode 100644 index 0000000..55085f8 --- /dev/null +++ b/src/Insight.Localizer/Exceptions/LocalizerException.cs @@ -0,0 +1,15 @@ +using System; + +namespace Insight.Localizer +{ + public class LocalizerException : Exception + { + public LocalizerException(string message) : base(message) + { + } + + public LocalizerException(string message, Exception inner) : base(message, inner) + { + } + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/ILocalizer.cs b/src/Insight.Localizer/ILocalizer.cs index fad4591..e17368d 100644 --- a/src/Insight.Localizer/ILocalizer.cs +++ b/src/Insight.Localizer/ILocalizer.cs @@ -7,12 +7,7 @@ public interface ILocalizer /// /// Curent culture of the localizer. /// - public static string? CurrentCulture { get; set; } - - /// - /// Loaded blocks - /// - IDictionary Blocks { get; } + public ILocalizerCulture? CurrentCulture { get; set; } /// /// Available block names diff --git a/src/Insight.Localizer/Infrastructure/IInitializable.cs b/src/Insight.Localizer/Infrastructure/IInitializable.cs new file mode 100644 index 0000000..ae14e52 --- /dev/null +++ b/src/Insight.Localizer/Infrastructure/IInitializable.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace Insight.Localizer.Infrastructure +{ + /// + /// Marks that implementation requires initialization + /// + public interface IInitializable + { + Task Initialize(); + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/Insight.Localizer.csproj b/src/Insight.Localizer/Insight.Localizer.csproj index 5a5798e..6ab42a1 100644 --- a/src/Insight.Localizer/Insight.Localizer.csproj +++ b/src/Insight.Localizer/Insight.Localizer.csproj @@ -2,15 +2,10 @@ netstandard2.1 - Abstraction to localize your app from json files - Copyright © 2023 Sergey Nazarov - https://github.com/InsightAppDev/Insight.Localizer - https://github.com/InsightAppDev/Insight.Localizer - Github - + diff --git a/src/Insight.Localizer/Localizer.cs b/src/Insight.Localizer/Localizer.cs index f518296..39bcab2 100644 --- a/src/Insight.Localizer/Localizer.cs +++ b/src/Insight.Localizer/Localizer.cs @@ -1,180 +1,47 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using System.Threading; -using Newtonsoft.Json.Linq; - -namespace Insight.Localizer -{ - public class Localizer : ILocalizer - { - private static object _syncRoot = new object(); - - private static IDictionary _blocks; - - private static readonly AsyncLocal _currentCulture = new AsyncLocal(); - - private static readonly Regex OneFilePerLanguageNameRegex = - new Regex(@"^(.{1,})\.(.{2,})$", RegexOptions.Compiled); - - public static string? CurrentCulture - { - get => _currentCulture.Value; - set - { - if (string.IsNullOrWhiteSpace(value)) - { - throw new ArgumentNullException(nameof(value)); - } - - _currentCulture.Value = value.ToLower(); - } - } - - public IReadOnlyCollection AvailableBlockNames => new Lazy>( - () => Blocks - .Select(x => x.Key) - .ToList()) - .Value; - - public IDictionary Blocks => _blocks; - - - public Localizer() - { - } - - public Localizer(ILocalizerCulture culture) - { - } - - public static void Initialize(LocalizerOptions options) - { - if (options == null) - throw new ArgumentNullException(nameof(options)); - - if (_blocks == null) - { - lock (_syncRoot) - { - if (_blocks != null) - return; - - _blocks = new Dictionary(StringComparer.OrdinalIgnoreCase); - Build(options); - } - } - } - - public static void Clear() - { - lock (_syncRoot) - { - _blocks = null; - } - } - - public string Get(string block, string key) - { - return this[block].Get(_currentCulture.Value, key); - } - - public string GetAny(string block, string key) - { - return this[block].Get(LocalizerConstants.AnyCultureKey, key); - } - - public string GetByCulture(string culture, string block, string key) - { - return this[block].Get(culture, key); - } - - private Block this[string name] => - Blocks.TryGetValue(name, out var value) - ? value - : throw new MissingBlockException($"Block `{name}` missing"); - - private static void Build(LocalizerOptions options) - { - if (string.IsNullOrWhiteSpace(options.FileEndsWith)) - { - throw new InvalidOperationException($"{nameof(LocalizerOptions.FileEndsWith)} should be specified"); - } - - var searchOption = options.ReadNestedFolders - ? SearchOption.AllDirectories - : SearchOption.TopDirectoryOnly; - - var files = Directory.GetFiles(options.Path, "*", searchOption) - .Where(x => x.EndsWith(options.FileEndsWith, StringComparison.OrdinalIgnoreCase)); - - foreach (var file in files) - { - if (options.OneLanguageInFile) - { - InitializeForOneLanguageInFile(file, options.FileEndsWith); - continue; - } - - InitializeForAllLanguagesInOneFile(file, options.FileEndsWith); - } - } - - private static void InitializeForAllLanguagesInOneFile(string file, string fileEndsWith) - { - var filename = Path.GetFileName(file); - var blockName = filename.Replace(fileEndsWith, string.Empty); - - var json = File.ReadAllText(file); - var jObject = JObject.Parse(json); - - foreach (var (culture, blockJToken) in jObject) - { - var blockContent = new Dictionary(StringComparer.OrdinalIgnoreCase); - var blockJObject = blockJToken as JObject; - foreach (var (key, value) in blockJObject) - { - blockContent.Add(key, value.ToString()); - } - - if (!_blocks.ContainsKey(blockName)) - { - var block = new Block(blockName); - _blocks.Add(block.Name, block); - } - - _blocks[blockName].Add(culture, blockContent); - } - } - - private static void InitializeForOneLanguageInFile(string file, string fileEndsWith) - { - var localeRegex = OneFilePerLanguageNameRegex; - var filename = Path.GetFileName(file) - .Replace(fileEndsWith, string.Empty, StringComparison.OrdinalIgnoreCase); - var match = localeRegex.Match(filename); - if (match.Success) - { - var blockName = match.Groups[1].Value; - var cultureString = match.Groups[^1]?.Value; - - var json = File.ReadAllText(file); - var jObject = JObject.Parse(json); - var blockContent = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var (key, value) in jObject) - blockContent.Add(key, value.ToString()); - - if (!_blocks.ContainsKey(blockName)) - { - var block = new Block(blockName); - _blocks.Add(block.Name, block); - } - - _blocks[blockName].Add(cultureString, blockContent); - } - } - } +using System; +using System.Collections.Generic; +using Insight.Localizer.Registries; + +namespace Insight.Localizer +{ + public class Localizer : ILocalizer + { + private readonly ILocalizerRegistry _registry; + + public ILocalizerCulture? CurrentCulture { get; set; } + + public IReadOnlyDictionary Blocks => _registry.Blocks; + + public IReadOnlyCollection AvailableBlockNames => _registry.AvailableBlockNames; + + public Localizer(ILocalizerRegistry registry) + { + _registry = registry ?? throw new ArgumentNullException(nameof(registry)); + } + + public Localizer(ILocalizerRegistry registry, ILocalizerCulture localizerCulture) : this(registry) + { + CurrentCulture = localizerCulture ?? throw new ArgumentNullException(nameof(localizerCulture)); + } + + public string Get(string block, string key) + { + if (CurrentCulture == null) + { + throw new InvalidOperationException($"{nameof(CurrentCulture)} is null"); + } + + return _registry.GetByCulture(CurrentCulture.Value, block, key); + } + + public string GetAny(string block, string key) + { + return _registry.GetAny(block, key); + } + + public string GetByCulture(string culture, string block, string key) + { + return _registry.GetByCulture(culture, block, key); + } + } } \ No newline at end of file diff --git a/src/Insight.Localizer/LocalizerOptions.cs b/src/Insight.Localizer/LocalizerOptions.cs deleted file mode 100644 index 3c9de61..0000000 --- a/src/Insight.Localizer/LocalizerOptions.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Insight.Localizer -{ - public sealed class LocalizerOptions - { - public const string DefaultResourceEndWith = ".lclzr.json"; - - public string Path { get; set; } - - public bool ReadNestedFolders { get; set; } - - public bool OneLanguageInFile { get; set; } = true; - - public bool ReadFromFiles { get; set; } = true; - - /// - /// File pattern. Example 'message' will read all files started with 'message.' like 'message.en-us.json', etc. If null reads all files in directory - /// - public string FileEndsWith { get; set; } = DefaultResourceEndWith; - - public bool ReadFromEmbedded { get; set; } = false; - - public string EmbeddedEndWith { get; set; } = DefaultResourceEndWith; - } -} \ No newline at end of file diff --git a/src/Insight.Localizer/Localizer{T}.cs b/src/Insight.Localizer/Localizer{T}.cs index 33f17fc..582814c 100644 --- a/src/Insight.Localizer/Localizer{T}.cs +++ b/src/Insight.Localizer/Localizer{T}.cs @@ -1,7 +1,18 @@ +using Insight.Localizer.Registries; + namespace Insight.Localizer { public class Localizer : Localizer, ILocalizer where T : class { + public Localizer(ILocalizerRegistry registry) : base(registry) + { + } + + public Localizer(ILocalizerRegistry registry, ILocalizerCulture localizerCulture) : base(registry, + localizerCulture) + { + } + public string Get(string key) { var block = GetBlockName(); diff --git a/src/Insight.Localizer/Providers/BlockCultureData.cs b/src/Insight.Localizer/Providers/BlockCultureData.cs new file mode 100644 index 0000000..f97c0b9 --- /dev/null +++ b/src/Insight.Localizer/Providers/BlockCultureData.cs @@ -0,0 +1,35 @@ +using System; + +namespace Insight.Localizer.Providers +{ + public readonly struct BlockCultureData : IEquatable + { + public BlockCultureData(string block, string culture, string content) + { + Block = block; + Culture = culture; + Content = content; + } + + public string Block { get; } + + public string Culture { get; } + + public string Content { get; } + + public bool Equals(BlockCultureData other) + { + return Block == other.Block && Culture == other.Culture && Content == other.Content; + } + + public override bool Equals(object obj) + { + return obj is BlockCultureData other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Block, Culture, Content); + } + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs b/src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs new file mode 100644 index 0000000..13acf8b --- /dev/null +++ b/src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Insight.Localizer.Infrastructure; +using Newtonsoft.Json.Linq; + +namespace Insight.Localizer.Providers.Files.EmbeddedResources +{ + public class EmbeddedResourcesBlocksProvider : BlocksProvider, IInitializable + { + private static readonly Regex OneFilePerCultureNameRegex = + new Regex(@"lclzr.([A-z0-9_-]{1,})\.([a-z\-]{2,})\.json$", RegexOptions.Compiled); + + private static readonly Regex OneFilePerMultipleCulturesNameRegex = + new Regex(@"lclzr.([A-z0-9_-]{1,})\.json$", RegexOptions.Compiled); + + private readonly EmbeddedResourcesBlocksProviderOptions _options; + + public EmbeddedResourcesBlocksProvider(EmbeddedResourcesBlocksProviderOptions options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + } + + public async Task Initialize() + { + var encoding = Encoding.GetEncoding(_options.ResourceEncodingWebName); + foreach (var assembly in _options.Assemblies.Select(Assembly.Load)) + { + var resourceNames = assembly.GetManifestResourceNames(); + + foreach (var resourceName in resourceNames) + { + try + { + using (var resourceStream = assembly.GetManifestResourceStream(resourceName)) + { + if (resourceStream == null) + // TODO: Log + continue; + + using (var streamReader = new StreamReader(resourceStream, encoding)) + { + var content = await streamReader.ReadToEndAsync(); + + var oneFilePerCultureMatch = OneFilePerCultureNameRegex.Match(resourceName); + if (oneFilePerCultureMatch.Success) + { + var block = oneFilePerCultureMatch.Groups[1].Value; + var culture = oneFilePerCultureMatch.Groups[2].Value; + var info = new BlockCultureData(block, culture, content); + + await InitializeBlockCulture(in info); + // TODO: Write log + + continue; + } + + + var oneFilePerMultipleCulturesMatch = + OneFilePerMultipleCulturesNameRegex.Match(resourceName); + if (oneFilePerMultipleCulturesMatch.Success) + { + var block = oneFilePerMultipleCulturesMatch.Groups[1].Value; + var json = content; + var jObject = JObject.Parse(json); + foreach (var (culture, blockJToken) in jObject) + { + var info = new BlockCultureData(block, culture, blockJToken.ToString()); + await InitializeBlockCulture(in info); + } + + continue; + } + + // TODO: Log + } + } + } + catch (Exception ex) + { + throw new LocalizerException($"Failed to process embedded resource: {resourceName}", ex); + } + } + } + } + + public override IReadOnlyCollection GetBlocks() + { + return Blocks.Values.ToArray(); + } + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProviderOptions.cs b/src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProviderOptions.cs new file mode 100644 index 0000000..4ebe690 --- /dev/null +++ b/src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProviderOptions.cs @@ -0,0 +1,11 @@ +using System.Text; + +namespace Insight.Localizer.Providers.Files.EmbeddedResources +{ + public class EmbeddedResourcesBlocksProviderOptions + { + public string ResourceEncodingWebName { get; set; } = Encoding.UTF8.WebName; + + public string[] Assemblies { get; set; } + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/Providers/Files/FilesBlocksProvider.cs b/src/Insight.Localizer/Providers/Files/FilesBlocksProvider.cs new file mode 100644 index 0000000..3ad59ac --- /dev/null +++ b/src/Insight.Localizer/Providers/Files/FilesBlocksProvider.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; + +namespace Insight.Localizer.Providers.Files +{ + public abstract class BlocksProvider : IBlocksProvider + { + protected readonly IDictionary Blocks = new Dictionary(); + + public abstract IReadOnlyCollection GetBlocks(); + + protected Task InitializeBlockCulture(in BlockCultureData cultureData) + { + var blockName = cultureData.Block; + var cultureString = cultureData.Culture; + + var json = cultureData.Content; + var jObject = JObject.Parse(json); + var blockContent = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var (key, value) in jObject) + blockContent.Add(key, value.ToString()); + + if (!Blocks.ContainsKey(blockName)) + { + var block = new Block(blockName); + Blocks.Add(block.Name, block); + } + + Blocks[blockName].Add(cultureString, blockContent); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProvider.cs b/src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProvider.cs new file mode 100644 index 0000000..f5f2d9c --- /dev/null +++ b/src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProvider.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Insight.Localizer.Infrastructure; +using Newtonsoft.Json.Linq; + +namespace Insight.Localizer.Providers.Files.RawFiles +{ + public class RawFilesBlocksProvider : BlocksProvider, IInitializable + { + private static readonly Regex OneFilePerCultureNameRegex = + new Regex(@"^lclzr.([A-z0-9_-]{1,})\.([a-z\-]{2,})\.json$", RegexOptions.Compiled); + + private static readonly Regex OneFilePerMultipleCulturesNameRegex = + new Regex(@"^lclzr.([A-z0-9_-]{1,})\.json$", RegexOptions.Compiled); + + private readonly RawFilesBlocksProviderOptions _options; + + public RawFilesBlocksProvider(RawFilesBlocksProviderOptions options) + { + _options = options ?? throw new ArgumentNullException(nameof(options)); + } + + public async Task Initialize() + { + var searchOption = _options.ReadNestedFolders + ? SearchOption.AllDirectories + : SearchOption.TopDirectoryOnly; + + var files = Directory.GetFiles(_options.Path, "*", searchOption); + + foreach (var file in files) + { + try + { + var filename = Path.GetFileName(file); + // TODO: Lazy or different way not to load + string? content = null; + var oneFilePerMultipleLanguagesMatch = OneFilePerMultipleCulturesNameRegex.Match(filename); + if (oneFilePerMultipleLanguagesMatch.Success) + { + content = await File.ReadAllTextAsync(file); + var blockName = oneFilePerMultipleLanguagesMatch.Groups[1].Value; + var jObject = JObject.Parse(content); + foreach (var (culture, blockJToken) in jObject) + { + var info = new BlockCultureData(blockName, culture, blockJToken.ToString()); + await InitializeBlockCulture(in info); + } + + continue; + } + + var oneFilePerLanguageMatch = OneFilePerCultureNameRegex.Match(filename); + if (oneFilePerLanguageMatch.Success) + { + content = await File.ReadAllTextAsync(file); + var block = oneFilePerLanguageMatch.Groups[1].Value; + var culture = oneFilePerLanguageMatch.Groups[2].Value; + var info = new BlockCultureData(block, culture, content); + + await InitializeBlockCulture(in info); + continue; + } + + // TODO: Log block not added + } + catch (Exception ex) + { + throw new LocalizerException($"Failed to process file: {file}", ex); + } + } + } + + public override IReadOnlyCollection GetBlocks() + { + return Blocks.Values.ToArray(); + } + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProviderOptions.cs b/src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProviderOptions.cs new file mode 100644 index 0000000..b00b48a --- /dev/null +++ b/src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProviderOptions.cs @@ -0,0 +1,9 @@ +namespace Insight.Localizer.Providers.Files.RawFiles +{ + public class RawFilesBlocksProviderOptions + { + public string Path { get; set; } + + public bool ReadNestedFolders { get; set; } + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/Providers/IBlocksProvider.cs b/src/Insight.Localizer/Providers/IBlocksProvider.cs new file mode 100644 index 0000000..7d7db2e --- /dev/null +++ b/src/Insight.Localizer/Providers/IBlocksProvider.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Insight.Localizer.Providers +{ + public interface IBlocksProvider + { + IReadOnlyCollection GetBlocks(); + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/Registries/ILocalizerRegistry.cs b/src/Insight.Localizer/Registries/ILocalizerRegistry.cs new file mode 100644 index 0000000..a0c116f --- /dev/null +++ b/src/Insight.Localizer/Registries/ILocalizerRegistry.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; + +namespace Insight.Localizer.Registries +{ + public interface ILocalizerRegistry + { + /// + /// Loaded blocks + /// + IReadOnlyDictionary Blocks { get; } + + /// + /// Available block names + /// + IReadOnlyCollection AvailableBlockNames { get; } + + /// + /// Get value by block-key for any culture + /// + /// Block name + /// Key + /// + string GetAny(string block, string key); + + /// + /// Get value by culture-block-key for any culture + /// + /// Culture + /// Block name + /// Key + /// + string GetByCulture(string culture, string block, string key); + } +} \ No newline at end of file diff --git a/src/Insight.Localizer/Registries/LocalizerRegistry.cs b/src/Insight.Localizer/Registries/LocalizerRegistry.cs new file mode 100644 index 0000000..0cd7423 --- /dev/null +++ b/src/Insight.Localizer/Registries/LocalizerRegistry.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using Insight.Localizer.Infrastructure; +using Insight.Localizer.Providers; + +namespace Insight.Localizer.Registries +{ + public class LocalizerRegistry : ILocalizerRegistry, IInitializable + { + private Dictionary _blocks = new Dictionary(); + + private IBlocksProvider[]? _blockProviders; + + private bool _initialized; + + public IReadOnlyCollection AvailableBlockNames => new Lazy>( + () => Blocks + .Select(x => x.Key) + .ToList() + .AsReadOnly()) + .Value; + + public IReadOnlyDictionary Blocks => new ReadOnlyDictionary(_blocks); + + public LocalizerRegistry(IEnumerable blocksProviders) : this(blocksProviders.ToArray()) + { + } + + public LocalizerRegistry(params IBlocksProvider[] blocksProviders) + { + _blockProviders = blocksProviders; + } + + // TODO: Think about thread safety + public async Task Initialize() + { + if (_initialized) + throw new InvalidOperationException($"{nameof(LocalizerRegistry)} already initialized"); + + if (_blockProviders == null) + { + throw new InvalidOperationException("There is no block providers"); + } + + foreach (var blocksProvider in _blockProviders) + { + if (blocksProvider is IInitializable initializable) + await initializable.Initialize(); + + var currentProviderBlocks = blocksProvider.GetBlocks(); + foreach (var block in currentProviderBlocks) + { + if (_blocks.ContainsKey(block.Name)) + { + // TODO: Write log + throw new InvalidOperationException( + $"Error occured while adding blocks from {blocksProvider.GetType().Name}. Block already added."); + } + + _blocks.Add(block.Name, block); + } + } + + // Clear providers to collect them + // TODO: Think how to refactor this + _blockProviders = null; + _initialized = true; + } + + public string GetAny(string block, string key) + { + return this[block].Get(LocalizerConstants.AnyCultureKey, key); + } + + public string GetByCulture(string culture, string block, string key) + { + return this[block].Get(culture, key); + } + + private Block this[string name] => + Blocks.TryGetValue(name, out var value) + ? value + : throw new MissingBlockException($"Block `{name}` missing"); + } +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/BlockTests.cs b/tests/Insight.Localizer.Tests/BlockTests.cs new file mode 100644 index 0000000..ddef8d0 --- /dev/null +++ b/tests/Insight.Localizer.Tests/BlockTests.cs @@ -0,0 +1,19 @@ +using Xunit; + +namespace Insight.Localizer.Tests; + +public sealed class BlockTests +{ + private readonly Block _block; + + public BlockTests() + { + _block = new Block("messages"); + } + + [Fact] + public void Get_throws_MissingLocalizationException_when_key_is_missing() + { + Assert.Throws(() => _block.Get("ru-ru", "Hello")); + } +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedMultipleLanguagesInFile/lclzr.embedded.json b/tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedMultipleLanguagesInFile/lclzr.embedded.json new file mode 100644 index 0000000..2f5ec04 --- /dev/null +++ b/tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedMultipleLanguagesInFile/lclzr.embedded.json @@ -0,0 +1,8 @@ +{ + "ru-ru": { + "Hello": "Привет" + }, + "en-us": { + "Hello": "Hi" + } +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.en-us.json b/tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.en-us.json new file mode 100644 index 0000000..ae44cc6 --- /dev/null +++ b/tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.en-us.json @@ -0,0 +1,3 @@ +{ + "Hello": "Hi" +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.ru-ru.json b/tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.ru-ru.json new file mode 100644 index 0000000..a57e5b4 --- /dev/null +++ b/tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.ru-ru.json @@ -0,0 +1,3 @@ +{ + "Hello": "Привет" +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/GenericLocalizerTests.cs b/tests/Insight.Localizer.Tests/GenericLocalizerTests.cs index c38a78b..19e21f6 100644 --- a/tests/Insight.Localizer.Tests/GenericLocalizerTests.cs +++ b/tests/Insight.Localizer.Tests/GenericLocalizerTests.cs @@ -1,24 +1,27 @@ +using Insight.Localizer.Providers.Files.RawFiles; +using Insight.Localizer.Registries; using Xunit; namespace Insight.Localizer.Tests; -[Collection("LocalizerTests")] public sealed class GenericLocalizerTests { private readonly Localizer _localizer; - private static LocalizerOptions Options => new() + private static readonly RawFilesBlocksProviderOptions Options = new() { Path = "Resources", - FileEndsWith = ".json", ReadNestedFolders = true }; public GenericLocalizerTests() { - Localizer.Initialize(Options); - Localizer.CurrentCulture = "ru-ru"; - _localizer = new Localizer(); + var provider = new RawFilesBlocksProvider(Options); + var registry = new LocalizerRegistry(provider); + registry.Initialize().GetAwaiter().GetResult(); + + _localizer = new Localizer(registry); + _localizer.CurrentCulture = new LocalizerCulture("ru-ru"); } [Fact] diff --git a/tests/Insight.Localizer.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs b/tests/Insight.Localizer.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs new file mode 100644 index 0000000..26ae6b2 --- /dev/null +++ b/tests/Insight.Localizer.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs @@ -0,0 +1,23 @@ +using Insight.Localizer.Providers.Files.RawFiles; +using Insight.Localizer.Registries; + +namespace Insight.Localizer.Tests; + +public sealed class LocalizerWithRawFilesProviderSut +{ + public ILocalizer Localizer { get; } + + private static readonly RawFilesBlocksProviderOptions Options = new() + { + Path = "Resources", + ReadNestedFolders = true + }; + + public LocalizerWithRawFilesProviderSut(RawFilesBlocksProviderOptions? options = null) + { + var provider = new RawFilesBlocksProvider(options ?? Options); + var registry = new LocalizerRegistry(provider); + Localizer = new Localizer(registry); + registry.Initialize().GetAwaiter().GetResult(); + } +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/Infratructure/TestBlocksProvider.cs b/tests/Insight.Localizer.Tests/Infratructure/TestBlocksProvider.cs new file mode 100644 index 0000000..31d89f4 --- /dev/null +++ b/tests/Insight.Localizer.Tests/Infratructure/TestBlocksProvider.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Insight.Localizer.Providers; + +namespace Insight.Localizer.Tests; + +internal sealed class TestBlocksProvider : IBlocksProvider +{ + private readonly Block[] _blocks; + + public TestBlocksProvider(params Block[] blocks) + { + _blocks = blocks; + } + + public IReadOnlyCollection GetBlocks() + { + return _blocks; + } +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj b/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj index c169f9e..4c341d0 100644 --- a/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj +++ b/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj @@ -7,22 +7,32 @@ - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - - + + - - - Always - + + + Always + + + + + + + Always + diff --git a/tests/Insight.Localizer.Tests/LocalizerMultipleLanguagesInFileTests.cs b/tests/Insight.Localizer.Tests/LocalizerMultipleLanguagesInFileTests.cs new file mode 100644 index 0000000..e2e9267 --- /dev/null +++ b/tests/Insight.Localizer.Tests/LocalizerMultipleLanguagesInFileTests.cs @@ -0,0 +1,37 @@ +using System; +using System.IO; +using System.Linq; +using Insight.Localizer.Providers.Files.RawFiles; +using Xunit; + +namespace Insight.Localizer.Tests; + +public sealed class LocalizerMultipleLanguagesInFileTests +{ + private static RawFilesBlocksProviderOptions _options = new() + { + Path = "Resources" + Path.DirectorySeparatorChar + "MultipleLanguagesInFile", + }; + + private readonly ILocalizer _localizer; + + public LocalizerMultipleLanguagesInFileTests() + { + var sut = new LocalizerWithRawFilesProviderSut(_options); + _localizer = sut.Localizer; + } + + [Fact] + public void Ctor_initializes_block_named_multiple() + { + Assert.Single(_localizer.AvailableBlockNames); + Assert.Equal("multiple", _localizer.AvailableBlockNames.First(), StringComparer.OrdinalIgnoreCase); + } + + [Fact] + public void Ctor_initializes_block_named_multiple_with_two_cultures_and_hello_key() + { + Assert.Equal("Привет", _localizer.GetByCulture("ru", "multiple", "Hello"), StringComparer.OrdinalIgnoreCase); + Assert.Equal("Hi", _localizer.GetByCulture("en", "multiple", "Hello"), StringComparer.OrdinalIgnoreCase); + } +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/LocalizerOneLanguageInFileTests.cs b/tests/Insight.Localizer.Tests/LocalizerOneLanguageInFileTests.cs index dcb47d0..87a69b2 100644 --- a/tests/Insight.Localizer.Tests/LocalizerOneLanguageInFileTests.cs +++ b/tests/Insight.Localizer.Tests/LocalizerOneLanguageInFileTests.cs @@ -1,164 +1,47 @@ using System; using System.IO; using System.Linq; -using Microsoft.Extensions.Options; +using Insight.Localizer.Providers.Files.RawFiles; using Xunit; -namespace Insight.Localizer.Tests +namespace Insight.Localizer.Tests; + +public sealed class LocalizerOneLanguageInFileTests { - [Collection("LocalizerTests")] - public sealed class LocalizerMultipleLanguagesInFileTests + private static RawFilesBlocksProviderOptions _options = new() { - private static LocalizerOptions _options = new() - { - Path = "Resources" + Path.DirectorySeparatorChar + "MultipleLanguagesInFile", - OneLanguageInFile = false, - ReadNestedFolders = true - }; - - public LocalizerMultipleLanguagesInFileTests() - { - Localizer.Initialize(_options); - Localizer.CurrentCulture = "ru"; - } + Path = "Resources" + Path.DirectorySeparatorChar + "OneLanguageInFile", + ReadNestedFolders = true + }; - [Fact] - public void Ctor_initializes_block_test_with_two_cultures_and_hello_key_from_single_file() - { - var localizer = new Localizer(); + private readonly ILocalizer _localizer; - Assert.Equal(1, localizer.AvailableBlockNames.Count); - Assert.Equal("test", localizer.AvailableBlockNames.First(), StringComparer.OrdinalIgnoreCase); - Assert.Equal("Привет", localizer.GetByCulture("ru", "test", "Hello"), StringComparer.OrdinalIgnoreCase); - Assert.Equal("Hi", localizer.GetByCulture("en", "test", "Hello"), StringComparer.OrdinalIgnoreCase); - } + public LocalizerOneLanguageInFileTests() + { + var sut = new LocalizerWithRawFilesProviderSut(_options); + _localizer = sut.Localizer; + _localizer.CurrentCulture = new LocalizerCulture("ru-ru"); } - [Collection("LocalizerTests")] - public sealed class LocalizerOneLanguageInFileTests + [Fact] + public void AvailableBlockNames_returns_all_block_names() { - private static readonly LocalizerOptions Options = new() - { - Path = "Resources" + Path.DirectorySeparatorChar + "OneLanguageInFile", - FileEndsWith = ".json", - ReadNestedFolders = true - }; - - public LocalizerOneLanguageInFileTests() - { - Localizer.Initialize(Options); - Localizer.CurrentCulture = "ru-ru"; - } - - [Fact] - public void Should_throw_ANE_if_configuration_at_Initialize_is_null() - { - Assert.Throws(() => Localizer.Initialize(null)); - } - - [Fact] - public void Should_throw_ANE_if_name_at_block_ctor_is_null() - { - Assert.Throws(() => new Block(null)); - } - - [Fact] - public void Should_throw_MissingBlockException() - { - var localizer = new Localizer(); - Assert.Throws(() => localizer.Get("there_is_no_block", "Hello")); - } - - [Fact] - public void Should_throw_MissingLocalizationException_if_there_is_no_culture() - { - Localizer.CurrentCulture = "ke-ke"; - var localizer = new Localizer(); - Assert.Throws(() => localizer.Get("messages", "Hello")); - } - - [Fact] - public void Should_throw_MissingLocalizationException_if_there_is_no_key() - { - var localizer = new Localizer(); - Assert.Throws(() => localizer.Get("messages", "there_is_no_localization")); - } - - [Fact] - public void Should_read_all_files() - { - var localizer = new Localizer(); - AssertLocalizer(localizer, 4); - } - - [Fact] - public void Should_get_available_block_names_from_all_Files() - { - var localizer = new Localizer(); - AssertLocalizer(localizer, 4); - - var names = localizer.AvailableBlockNames; - Assert.NotNull(names); - Assert.NotEmpty(names); - Assert.Equal(4, names.Count); - Assert.NotNull(names.FirstOrDefault(x => x.Equals("test", StringComparison.OrdinalIgnoreCase))); - Assert.NotNull(names.FirstOrDefault(x => - x.Equals("messages", StringComparison.OrdinalIgnoreCase))); - Assert.NotNull(names.FirstOrDefault(x => - x.Equals("language", StringComparison.OrdinalIgnoreCase))); - } - - [Fact] - public void Should_get_value_in_all_languages() - { - var localizer = new Localizer(); - AssertLocalizer(localizer, 4); - - var en = localizer.GetByCulture("en-us", "test", "Hello"); - var ru = localizer.Get("test", "Hello"); - - Assert.Equal("Hi", en, StringComparer.OrdinalIgnoreCase); - Assert.Equal("Привет", ru, StringComparer.OrdinalIgnoreCase); - } - - [Fact] - public void Should_get_any_value() - { - var localizer = new Localizer(); - AssertLocalizer(localizer, 4); - - var russianLanguage = localizer.GetAny("language", "Russian"); - var englishLanguage = localizer.GetAny("language", "English"); - - Assert.Equal("Русский", russianLanguage, StringComparer.OrdinalIgnoreCase); - Assert.Equal("English", englishLanguage, StringComparer.OrdinalIgnoreCase); - } - - [Fact] - public void Should_change_current_culture() - { - var localizer = new Localizer(); - AssertLocalizer(localizer, 4); - - Assert.Equal("ru-ru", Localizer.CurrentCulture, StringComparer.OrdinalIgnoreCase); - Localizer.CurrentCulture = "en-us"; - Assert.Equal("en-us", Localizer.CurrentCulture, StringComparer.OrdinalIgnoreCase); - } - - [Fact] - public void Should_throw_ANE_on_set_culture_if_culture_is_null() - { - var localizer = new Localizer(); - AssertLocalizer(localizer, 4); + var names = _localizer.AvailableBlockNames; + Assert.NotNull(names); + Assert.NotEmpty(names); + Assert.Equal(4, names.Count); + Assert.NotNull(names.FirstOrDefault(x => x.Equals("test", StringComparison.OrdinalIgnoreCase))); + Assert.NotNull(names.FirstOrDefault(x => + x.Equals("messages", StringComparison.OrdinalIgnoreCase))); + Assert.NotNull(names.FirstOrDefault(x => + x.Equals("language", StringComparison.OrdinalIgnoreCase))); + } - Assert.Throws(() => Localizer.CurrentCulture = null); - } + [Fact] + public void Get_returns_value_for_current_culture() + { + var value = _localizer.Get("test", "Hello"); - private void AssertLocalizer(ILocalizer localizer, int expectedBlocksCount) - { - Assert.NotNull(localizer); - Assert.NotEmpty(localizer.Blocks); - Assert.Equal(expectedBlocksCount, localizer.Blocks.Count); - } + Assert.Equal("Привет", value, StringComparer.OrdinalIgnoreCase); } } \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/LocalizerRegistryTests.cs b/tests/Insight.Localizer.Tests/LocalizerRegistryTests.cs new file mode 100644 index 0000000..73c3487 --- /dev/null +++ b/tests/Insight.Localizer.Tests/LocalizerRegistryTests.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Insight.Localizer.Registries; +using Xunit; + +namespace Insight.Localizer.Tests; + +public sealed class LocalizerRegistryTests +{ + [Fact] + public void Get_throws_MissingBlockException_when_block_is_missing() + { + Assert.Throws(() => + new LocalizerRegistry().GetByCulture("ru-ru", "there_is_no_block", "Hello")); + } + + [Fact] + public async Task Get_any_returns_correct_value() + { + var languageBlock = new Block("language"); + languageBlock.Add(LocalizerConstants.AnyCultureKey, new Dictionary + { + {"Russian", "Русский"}, + {"English", "English"} + }); + + var testProvider = new TestBlocksProvider(languageBlock); + var registry = new LocalizerRegistry(testProvider); + await registry.Initialize(); + + var russianLanguage = registry.GetAny("language", "Russian"); + var englishLanguage = registry.GetAny("language", "English"); + + Assert.Equal("Русский", russianLanguage, StringComparer.OrdinalIgnoreCase); + Assert.Equal("English", englishLanguage, StringComparer.OrdinalIgnoreCase); + } +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/LocalizierExtensionsTests.cs b/tests/Insight.Localizer.Tests/LocalizierExtensionsTests.cs index 4283d55..5742592 100644 --- a/tests/Insight.Localizer.Tests/LocalizierExtensionsTests.cs +++ b/tests/Insight.Localizer.Tests/LocalizierExtensionsTests.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Insight.Localizer.Extensions; +using Insight.Localizer.Providers.Files.RawFiles; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Xunit; @@ -52,11 +53,7 @@ public async Task LocalizerInitializer_initializes_localizer() private static IServiceProvider BuildServiceProvider() { IServiceCollection services = new ServiceCollection(); - services.Configure(cfg => - { - cfg.Path = "Resources"; - cfg.ReadNestedFolders = true; - }); + services.AddLocalizer(); return services.BuildServiceProvider(); diff --git a/tests/Insight.Localizer.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs b/tests/Insight.Localizer.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs new file mode 100644 index 0000000..dae78b3 --- /dev/null +++ b/tests/Insight.Localizer.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs @@ -0,0 +1,53 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Insight.Localizer.Providers.Files.EmbeddedResources; +using Xunit; + +namespace Insight.Localizer.Tests.Providers; + +public class EmbeddedResourceBlocksProviderTests +{ + [Fact] + public async Task Initialize_with_one_language_in_file_creates_provider_with_single_block_named_embedded_and_two_cultures() + { + var fileBlocksProviderOptions = new EmbeddedResourcesBlocksProviderOptions() + { + Assemblies = new[] { Assembly.GetExecutingAssembly().FullName } + }; + + var provider = new EmbeddedResourcesBlocksProvider(fileBlocksProviderOptions); + + await provider.Initialize(); + + var blocks = provider.GetBlocks(); + Assert.Single(blocks); + var block = blocks.Single(); + + Assert.Equal(2, block.AvailableCultures.Count); + Assert.Contains("en-us", block.AvailableCultures, StringComparer.OrdinalIgnoreCase); + Assert.Contains("ru-ru", block.AvailableCultures, StringComparer.OrdinalIgnoreCase); + } + + [Fact] + public async Task Initialize_with_multiple_languages_in_file_creates_provider_with_single_block_named_embedded_and_two_cultures() + { + var fileBlocksProviderOptions = new EmbeddedResourcesBlocksProviderOptions() + { + Assemblies = new[] { Assembly.GetExecutingAssembly().FullName } + }; + + var provider = new EmbeddedResourcesBlocksProvider(fileBlocksProviderOptions); + + await provider.Initialize(); + + var blocks = provider.GetBlocks(); + Assert.Single(blocks); + var block = blocks.Single(); + + Assert.Equal(2, block.AvailableCultures.Count); + Assert.Contains("en-us", block.AvailableCultures, StringComparer.OrdinalIgnoreCase); + Assert.Contains("ru-ru", block.AvailableCultures, StringComparer.OrdinalIgnoreCase); + } +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/Providers/RawFilesBlocksProviderTests.cs b/tests/Insight.Localizer.Tests/Providers/RawFilesBlocksProviderTests.cs new file mode 100644 index 0000000..a74a7e2 --- /dev/null +++ b/tests/Insight.Localizer.Tests/Providers/RawFilesBlocksProviderTests.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Insight.Localizer.Providers.Files.RawFiles; +using Xunit; + +namespace Insight.Localizer.Tests.Providers; + +public class RawFilesBlocksProviderTests +{ + [Fact] + public async Task Initialize_with_one() + { + var fileBlocksProviderOptions = new RawFilesBlocksProviderOptions + { + Path = "Resources", + ReadNestedFolders = true + }; + + var provider = new RawFilesBlocksProvider(fileBlocksProviderOptions); + await provider.Initialize(); + + var blocks = provider.GetBlocks(); + Assert.Equal(5, blocks.Count); + } +} \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/Resources/MultipleLanguagesInFile/test.lclzr.json b/tests/Insight.Localizer.Tests/Resources/MultipleLanguagesInFile/lclzr.multiple.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/MultipleLanguagesInFile/test.lclzr.json rename to tests/Insight.Localizer.Tests/Resources/MultipleLanguagesInFile/lclzr.multiple.json diff --git a/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/GenericLocalizerTests.ru-ru.json b/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.GenericLocalizerTests.ru-ru.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/GenericLocalizerTests.ru-ru.json rename to tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.GenericLocalizerTests.ru-ru.json diff --git a/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/language.any.json b/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.language.any.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/language.any.json rename to tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.language.any.json diff --git a/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/messages.ru-ru.json b/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.messages.ru-ru.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/messages.ru-ru.json rename to tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.messages.ru-ru.json diff --git a/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/test.en-us.json b/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.test.en-us.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/test.en-us.json rename to tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.test.en-us.json diff --git a/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/test.ru-ru.json b/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.test.ru-ru.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/test.ru-ru.json rename to tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.test.ru-ru.json From c5c8d63fe8242cbb5a3ab2a2b1b377f49562c65b Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Sat, 27 Apr 2024 16:25:19 +0400 Subject: [PATCH 06/14] Add central package management --- Directory.Build.props | 5 +++++ Directory.Packages.props | 22 +++++++++++++++++++ Insight.Localizer.sln | 2 ++ .../Insight.Localizer.Extensions.csproj | 6 ++--- .../Insight.Localizer.csproj | 2 +- .../Insight.Localizer.csproj.DotSettings | 2 -- .../Insight.Localizer.Tests.csproj | 22 +++++++++---------- 7 files changed, 44 insertions(+), 17 deletions(-) create mode 100644 Directory.Build.props create mode 100644 Directory.Packages.props delete mode 100644 src/Insight.Localizer/Insight.Localizer.csproj.DotSettings diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..e4951e3 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,5 @@ + + + true + + \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..4eea325 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,22 @@ + + + 8.0.0 + 12 + netstandard2.1 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Insight.Localizer.sln b/Insight.Localizer.sln index c076c34..1757a16 100644 --- a/Insight.Localizer.sln +++ b/Insight.Localizer.sln @@ -11,6 +11,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Solution Items", "_Solutio .github\workflows\publish.yml = .github\workflows\publish.yml .gitignore = .gitignore README.md = README.md + Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Insight.Localizer.Extensions", "src\Insight.Localizer.Extensions\Insight.Localizer.Extensions.csproj", "{91FBA765-5291-4A11-9A55-7E4B932E3F7A}" diff --git a/src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj b/src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj index bb97f91..b2d1eae 100644 --- a/src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj +++ b/src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj @@ -5,9 +5,9 @@ - - - + + + diff --git a/src/Insight.Localizer/Insight.Localizer.csproj b/src/Insight.Localizer/Insight.Localizer.csproj index 6ab42a1..cdbd4ef 100644 --- a/src/Insight.Localizer/Insight.Localizer.csproj +++ b/src/Insight.Localizer/Insight.Localizer.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Insight.Localizer/Insight.Localizer.csproj.DotSettings b/src/Insight.Localizer/Insight.Localizer.csproj.DotSettings deleted file mode 100644 index 374f4af..0000000 --- a/src/Insight.Localizer/Insight.Localizer.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - True \ No newline at end of file diff --git a/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj b/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj index 4c341d0..d044b93 100644 --- a/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj +++ b/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj @@ -6,30 +6,30 @@ - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - + Always - + Always From b460002e4c598292238d1442c06022618a7ed9d7 Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Sat, 27 Apr 2024 16:27:14 +0400 Subject: [PATCH 07/14] Rename to lclzr --- Insight.Localizer.sln => Lclzr.sln | 6 +++--- src/Lclzr.Extensions/AssemblyInfo.cs | 3 +++ .../Lclzr.Extensions.csproj} | 10 ++-------- .../RegistryInitializerBackgroundService.cs | 6 +++--- .../ServiceCollectionExtensions.cs | 6 +++--- src/Lclzr/AssemblyInfo.cs | 3 +++ src/{Insight.Localizer => Lclzr}/Block.cs | 3 ++- .../Exceptions/LocalizerException.cs | 2 +- .../Exceptions/MissingBlockException.cs | 2 +- .../Exceptions/MissingLocalizationException.cs | 2 +- src/{Insight.Localizer => Lclzr}/ILocalizer.cs | 2 +- .../ILocalizerCulture.cs | 2 +- .../ILocalizer{T}.cs | 2 +- .../Infrastructure/IInitializable.cs | 2 +- .../Lclzr.csproj} | 6 ------ src/{Insight.Localizer => Lclzr}/Localizer.cs | 4 ++-- .../LocalizerConstants.cs | 2 +- .../LocalizerCulture.cs | 2 +- src/{Insight.Localizer => Lclzr}/Localizer{T}.cs | 4 ++-- .../Providers/BlockCultureData.cs | 2 +- .../EmbeddedResourcesBlocksProvider.cs | 5 +++-- .../EmbeddedResourcesBlocksProviderOptions.cs | 2 +- .../Providers/Files/FilesBlocksProvider.cs | 2 +- .../Files/RawFiles/RawFilesBlocksProvider.cs | 5 +++-- .../RawFiles/RawFilesBlocksProviderOptions.cs | 2 +- .../Providers/IBlocksProvider.cs | 2 +- .../Registries/ILocalizerRegistry.cs | 2 +- .../Registries/LocalizerRegistry.cs | 7 ++++--- .../BlockTests.cs | 3 ++- .../lclzr.embedded.json | 0 .../lclzr.embedded.en-us.json | 0 .../lclzr.embedded.ru-ru.json | 0 .../GenericLocalizerTests.cs | 6 +++--- .../LocalizerWithRawFilesProviderSut.cs | 6 +++--- .../Infratructure/TestBlocksProvider.cs | 4 ++-- .../Lclzr.Tests.csproj} | 16 ++++++++-------- .../LocalizerMultipleLanguagesInFileTests.cs | 5 +++-- .../LocalizerOneLanguageInFileTests.cs | 5 +++-- .../LocalizerRegistryTests.cs | 6 ++++-- .../LocalizierExtensionsTests.cs | 5 ++--- .../EmbeddedResourceBlocksProviderTests.cs | 4 ++-- .../Providers/RawFilesBlocksProviderTests.cs | 4 ++-- .../MultipleLanguagesInFile/lclzr.multiple.json | 0 .../lclzr.GenericLocalizerTests.ru-ru.json | 0 .../OneLanguageInFile/lclzr.language.any.json | 0 .../OneLanguageInFile/lclzr.messages.ru-ru.json | 0 .../OneLanguageInFile/lclzr.test.en-us.json | 0 .../OneLanguageInFile/lclzr.test.ru-ru.json | 0 48 files changed, 82 insertions(+), 80 deletions(-) rename Insight.Localizer.sln => Lclzr.sln (77%) create mode 100644 src/Lclzr.Extensions/AssemblyInfo.cs rename src/{Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj => Lclzr.Extensions/Lclzr.Extensions.csproj} (59%) rename src/{Insight.Localizer.Extensions => Lclzr.Extensions}/RegistryInitializerBackgroundService.cs (88%) rename src/{Insight.Localizer.Extensions => Lclzr.Extensions}/ServiceCollectionExtensions.cs (91%) create mode 100644 src/Lclzr/AssemblyInfo.cs rename src/{Insight.Localizer => Lclzr}/Block.cs (97%) rename src/{Insight.Localizer => Lclzr}/Exceptions/LocalizerException.cs (90%) rename src/{Insight.Localizer => Lclzr}/Exceptions/MissingBlockException.cs (85%) rename src/{Insight.Localizer => Lclzr}/Exceptions/MissingLocalizationException.cs (86%) rename src/{Insight.Localizer => Lclzr}/ILocalizer.cs (97%) rename src/{Insight.Localizer => Lclzr}/ILocalizerCulture.cs (75%) rename src/{Insight.Localizer => Lclzr}/ILocalizer{T}.cs (95%) rename src/{Insight.Localizer => Lclzr}/Infrastructure/IInitializable.cs (82%) rename src/{Insight.Localizer/Insight.Localizer.csproj => Lclzr/Lclzr.csproj} (52%) rename src/{Insight.Localizer => Lclzr}/Localizer.cs (95%) rename src/{Insight.Localizer => Lclzr}/LocalizerConstants.cs (79%) rename src/{Insight.Localizer => Lclzr}/LocalizerCulture.cs (92%) rename src/{Insight.Localizer => Lclzr}/Localizer{T}.cs (93%) rename src/{Insight.Localizer => Lclzr}/Providers/BlockCultureData.cs (95%) rename src/{Insight.Localizer => Lclzr}/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs (97%) rename src/{Insight.Localizer => Lclzr}/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProviderOptions.cs (78%) rename src/{Insight.Localizer => Lclzr}/Providers/Files/FilesBlocksProvider.cs (96%) rename src/{Insight.Localizer => Lclzr}/Providers/Files/RawFiles/RawFilesBlocksProvider.cs (97%) rename src/{Insight.Localizer => Lclzr}/Providers/Files/RawFiles/RawFilesBlocksProviderOptions.cs (74%) rename src/{Insight.Localizer => Lclzr}/Providers/IBlocksProvider.cs (78%) rename src/{Insight.Localizer => Lclzr}/Registries/ILocalizerRegistry.cs (96%) rename src/{Insight.Localizer => Lclzr}/Registries/LocalizerRegistry.cs (96%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/BlockTests.cs (88%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/EmbeddedResources/EmbeddedMultipleLanguagesInFile/lclzr.embedded.json (100%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.en-us.json (100%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.ru-ru.json (100%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/GenericLocalizerTests.cs (86%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/Infratructure/LocalizerWithRawFilesProviderSut.cs (82%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/Infratructure/TestBlocksProvider.cs (82%) rename tests/{Insight.Localizer.Tests/Insight.Localizer.Tests.csproj => Lclzr.Tests/Lclzr.Tests.csproj} (65%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/LocalizerMultipleLanguagesInFileTests.cs (92%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/LocalizerOneLanguageInFileTests.cs (93%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/LocalizerRegistryTests.cs (91%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/LocalizierExtensionsTests.cs (92%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/Providers/EmbeddedResourceBlocksProviderTests.cs (94%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/Providers/RawFilesBlocksProviderTests.cs (84%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/Resources/MultipleLanguagesInFile/lclzr.multiple.json (100%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/Resources/OneLanguageInFile/lclzr.GenericLocalizerTests.ru-ru.json (100%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/Resources/OneLanguageInFile/lclzr.language.any.json (100%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/Resources/OneLanguageInFile/lclzr.messages.ru-ru.json (100%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/Resources/OneLanguageInFile/lclzr.test.en-us.json (100%) rename tests/{Insight.Localizer.Tests => Lclzr.Tests}/Resources/OneLanguageInFile/lclzr.test.ru-ru.json (100%) diff --git a/Insight.Localizer.sln b/Lclzr.sln similarity index 77% rename from Insight.Localizer.sln rename to Lclzr.sln index 1757a16..14c2204 100644 --- a/Insight.Localizer.sln +++ b/Lclzr.sln @@ -1,8 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Insight.Localizer", "src\Insight.Localizer\Insight.Localizer.csproj", "{192A4E10-2498-409B-A537-F27150C31C1C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lclzr", "src\Lclzr\Lclzr.csproj", "{192A4E10-2498-409B-A537-F27150C31C1C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Insight.Localizer.Tests", "tests\Insight.Localizer.Tests\Insight.Localizer.Tests.csproj", "{C8EDC5BA-AA36-44C6-A4D5-E45695EF5867}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lclzr.Tests", "tests\Lclzr.Tests\Lclzr.Tests.csproj", "{C8EDC5BA-AA36-44C6-A4D5-E45695EF5867}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Solution Items", "_Solution Items", "{7C43D58B-6E27-40AC-B59E-D9FE1AB66DB3}" ProjectSection(SolutionItems) = preProject @@ -15,7 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Solution Items", "_Solutio Directory.Packages.props = Directory.Packages.props EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Insight.Localizer.Extensions", "src\Insight.Localizer.Extensions\Insight.Localizer.Extensions.csproj", "{91FBA765-5291-4A11-9A55-7E4B932E3F7A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lclzr.Extensions", "src\Lclzr.Extensions\Lclzr.Extensions.csproj", "{91FBA765-5291-4A11-9A55-7E4B932E3F7A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Lclzr.Extensions/AssemblyInfo.cs b/src/Lclzr.Extensions/AssemblyInfo.cs new file mode 100644 index 0000000..db8e4ef --- /dev/null +++ b/src/Lclzr.Extensions/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly:InternalsVisibleTo("Lclzr.Tests")] \ No newline at end of file diff --git a/src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj b/src/Lclzr.Extensions/Lclzr.Extensions.csproj similarity index 59% rename from src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj rename to src/Lclzr.Extensions/Lclzr.Extensions.csproj index b2d1eae..fbbf0f9 100644 --- a/src/Insight.Localizer.Extensions/Insight.Localizer.Extensions.csproj +++ b/src/Lclzr.Extensions/Lclzr.Extensions.csproj @@ -6,18 +6,12 @@ - + - - <_Parameter1>Insight.Localizer.Tests - - - - - + diff --git a/src/Insight.Localizer.Extensions/RegistryInitializerBackgroundService.cs b/src/Lclzr.Extensions/RegistryInitializerBackgroundService.cs similarity index 88% rename from src/Insight.Localizer.Extensions/RegistryInitializerBackgroundService.cs rename to src/Lclzr.Extensions/RegistryInitializerBackgroundService.cs index b828dae..d96dd81 100644 --- a/src/Insight.Localizer.Extensions/RegistryInitializerBackgroundService.cs +++ b/src/Lclzr.Extensions/RegistryInitializerBackgroundService.cs @@ -1,12 +1,12 @@ using System; using System.Threading; using System.Threading.Tasks; -using Insight.Localizer.Infrastructure; -using Insight.Localizer.Registries; +using Lclzr.Infrastructure; +using Lclzr.Registries; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace Insight.Localizer.Extensions +namespace Lclzr.Extensions { internal class RegistryInitializerBackgroundService : BackgroundService { diff --git a/src/Insight.Localizer.Extensions/ServiceCollectionExtensions.cs b/src/Lclzr.Extensions/ServiceCollectionExtensions.cs similarity index 91% rename from src/Insight.Localizer.Extensions/ServiceCollectionExtensions.cs rename to src/Lclzr.Extensions/ServiceCollectionExtensions.cs index ce5c217..6e33a79 100644 --- a/src/Insight.Localizer.Extensions/ServiceCollectionExtensions.cs +++ b/src/Lclzr.Extensions/ServiceCollectionExtensions.cs @@ -1,10 +1,10 @@ using System; -using Insight.Localizer.Providers; -using Insight.Localizer.Registries; +using Lclzr.Providers; +using Lclzr.Registries; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -namespace Insight.Localizer.Extensions +namespace Lclzr.Extensions { public static class ServiceCollectionExtensions { diff --git a/src/Lclzr/AssemblyInfo.cs b/src/Lclzr/AssemblyInfo.cs new file mode 100644 index 0000000..db8e4ef --- /dev/null +++ b/src/Lclzr/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly:InternalsVisibleTo("Lclzr.Tests")] \ No newline at end of file diff --git a/src/Insight.Localizer/Block.cs b/src/Lclzr/Block.cs similarity index 97% rename from src/Insight.Localizer/Block.cs rename to src/Lclzr/Block.cs index 9782575..69a52aa 100644 --- a/src/Insight.Localizer/Block.cs +++ b/src/Lclzr/Block.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using Lclzr.Exceptions; -namespace Insight.Localizer +namespace Lclzr { public sealed class Block { diff --git a/src/Insight.Localizer/Exceptions/LocalizerException.cs b/src/Lclzr/Exceptions/LocalizerException.cs similarity index 90% rename from src/Insight.Localizer/Exceptions/LocalizerException.cs rename to src/Lclzr/Exceptions/LocalizerException.cs index 55085f8..dda7e1e 100644 --- a/src/Insight.Localizer/Exceptions/LocalizerException.cs +++ b/src/Lclzr/Exceptions/LocalizerException.cs @@ -1,6 +1,6 @@ using System; -namespace Insight.Localizer +namespace Lclzr.Exceptions { public class LocalizerException : Exception { diff --git a/src/Insight.Localizer/Exceptions/MissingBlockException.cs b/src/Lclzr/Exceptions/MissingBlockException.cs similarity index 85% rename from src/Insight.Localizer/Exceptions/MissingBlockException.cs rename to src/Lclzr/Exceptions/MissingBlockException.cs index 00da805..29cb19d 100644 --- a/src/Insight.Localizer/Exceptions/MissingBlockException.cs +++ b/src/Lclzr/Exceptions/MissingBlockException.cs @@ -1,6 +1,6 @@ using System; -namespace Insight.Localizer +namespace Lclzr.Exceptions { public class MissingBlockException : Exception { diff --git a/src/Insight.Localizer/Exceptions/MissingLocalizationException.cs b/src/Lclzr/Exceptions/MissingLocalizationException.cs similarity index 86% rename from src/Insight.Localizer/Exceptions/MissingLocalizationException.cs rename to src/Lclzr/Exceptions/MissingLocalizationException.cs index d1ba9f8..de291c2 100644 --- a/src/Insight.Localizer/Exceptions/MissingLocalizationException.cs +++ b/src/Lclzr/Exceptions/MissingLocalizationException.cs @@ -1,6 +1,6 @@ using System; -namespace Insight.Localizer +namespace Lclzr.Exceptions { public class MissingLocalizationException : Exception { diff --git a/src/Insight.Localizer/ILocalizer.cs b/src/Lclzr/ILocalizer.cs similarity index 97% rename from src/Insight.Localizer/ILocalizer.cs rename to src/Lclzr/ILocalizer.cs index e17368d..8817af4 100644 --- a/src/Insight.Localizer/ILocalizer.cs +++ b/src/Lclzr/ILocalizer.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Insight.Localizer +namespace Lclzr { public interface ILocalizer { diff --git a/src/Insight.Localizer/ILocalizerCulture.cs b/src/Lclzr/ILocalizerCulture.cs similarity index 75% rename from src/Insight.Localizer/ILocalizerCulture.cs rename to src/Lclzr/ILocalizerCulture.cs index 3db77a6..1dc3088 100644 --- a/src/Insight.Localizer/ILocalizerCulture.cs +++ b/src/Lclzr/ILocalizerCulture.cs @@ -1,4 +1,4 @@ -namespace Insight.Localizer +namespace Lclzr { public interface ILocalizerCulture { diff --git a/src/Insight.Localizer/ILocalizer{T}.cs b/src/Lclzr/ILocalizer{T}.cs similarity index 95% rename from src/Insight.Localizer/ILocalizer{T}.cs rename to src/Lclzr/ILocalizer{T}.cs index d9b27d5..405264c 100644 --- a/src/Insight.Localizer/ILocalizer{T}.cs +++ b/src/Lclzr/ILocalizer{T}.cs @@ -1,4 +1,4 @@ -namespace Insight.Localizer +namespace Lclzr { public interface ILocalizer : ILocalizer where T : class { diff --git a/src/Insight.Localizer/Infrastructure/IInitializable.cs b/src/Lclzr/Infrastructure/IInitializable.cs similarity index 82% rename from src/Insight.Localizer/Infrastructure/IInitializable.cs rename to src/Lclzr/Infrastructure/IInitializable.cs index ae14e52..85aa213 100644 --- a/src/Insight.Localizer/Infrastructure/IInitializable.cs +++ b/src/Lclzr/Infrastructure/IInitializable.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; -namespace Insight.Localizer.Infrastructure +namespace Lclzr.Infrastructure { /// /// Marks that implementation requires initialization diff --git a/src/Insight.Localizer/Insight.Localizer.csproj b/src/Lclzr/Lclzr.csproj similarity index 52% rename from src/Insight.Localizer/Insight.Localizer.csproj rename to src/Lclzr/Lclzr.csproj index cdbd4ef..6cd3b64 100644 --- a/src/Insight.Localizer/Insight.Localizer.csproj +++ b/src/Lclzr/Lclzr.csproj @@ -8,10 +8,4 @@ - - - <_Parameter1>Insight.Localizer.Tests - - - diff --git a/src/Insight.Localizer/Localizer.cs b/src/Lclzr/Localizer.cs similarity index 95% rename from src/Insight.Localizer/Localizer.cs rename to src/Lclzr/Localizer.cs index 39bcab2..cce1ce4 100644 --- a/src/Insight.Localizer/Localizer.cs +++ b/src/Lclzr/Localizer.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using Insight.Localizer.Registries; +using Lclzr.Registries; -namespace Insight.Localizer +namespace Lclzr { public class Localizer : ILocalizer { diff --git a/src/Insight.Localizer/LocalizerConstants.cs b/src/Lclzr/LocalizerConstants.cs similarity index 79% rename from src/Insight.Localizer/LocalizerConstants.cs rename to src/Lclzr/LocalizerConstants.cs index 649af5b..510e880 100644 --- a/src/Insight.Localizer/LocalizerConstants.cs +++ b/src/Lclzr/LocalizerConstants.cs @@ -1,4 +1,4 @@ -namespace Insight.Localizer +namespace Lclzr { public static class LocalizerConstants { diff --git a/src/Insight.Localizer/LocalizerCulture.cs b/src/Lclzr/LocalizerCulture.cs similarity index 92% rename from src/Insight.Localizer/LocalizerCulture.cs rename to src/Lclzr/LocalizerCulture.cs index 039259b..e9ede00 100644 --- a/src/Insight.Localizer/LocalizerCulture.cs +++ b/src/Lclzr/LocalizerCulture.cs @@ -1,6 +1,6 @@ using System; -namespace Insight.Localizer +namespace Lclzr { public sealed class LocalizerCulture : ILocalizerCulture { diff --git a/src/Insight.Localizer/Localizer{T}.cs b/src/Lclzr/Localizer{T}.cs similarity index 93% rename from src/Insight.Localizer/Localizer{T}.cs rename to src/Lclzr/Localizer{T}.cs index 582814c..34edbca 100644 --- a/src/Insight.Localizer/Localizer{T}.cs +++ b/src/Lclzr/Localizer{T}.cs @@ -1,6 +1,6 @@ -using Insight.Localizer.Registries; +using Lclzr.Registries; -namespace Insight.Localizer +namespace Lclzr { public class Localizer : Localizer, ILocalizer where T : class { diff --git a/src/Insight.Localizer/Providers/BlockCultureData.cs b/src/Lclzr/Providers/BlockCultureData.cs similarity index 95% rename from src/Insight.Localizer/Providers/BlockCultureData.cs rename to src/Lclzr/Providers/BlockCultureData.cs index f97c0b9..a27f35e 100644 --- a/src/Insight.Localizer/Providers/BlockCultureData.cs +++ b/src/Lclzr/Providers/BlockCultureData.cs @@ -1,6 +1,6 @@ using System; -namespace Insight.Localizer.Providers +namespace Lclzr.Providers { public readonly struct BlockCultureData : IEquatable { diff --git a/src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs b/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs similarity index 97% rename from src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs rename to src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs index 13acf8b..632645b 100644 --- a/src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs +++ b/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs @@ -6,10 +6,11 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Insight.Localizer.Infrastructure; +using Lclzr.Exceptions; +using Lclzr.Infrastructure; using Newtonsoft.Json.Linq; -namespace Insight.Localizer.Providers.Files.EmbeddedResources +namespace Lclzr.Providers.Files.EmbeddedResources { public class EmbeddedResourcesBlocksProvider : BlocksProvider, IInitializable { diff --git a/src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProviderOptions.cs b/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProviderOptions.cs similarity index 78% rename from src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProviderOptions.cs rename to src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProviderOptions.cs index 4ebe690..3f1ff7b 100644 --- a/src/Insight.Localizer/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProviderOptions.cs +++ b/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProviderOptions.cs @@ -1,6 +1,6 @@ using System.Text; -namespace Insight.Localizer.Providers.Files.EmbeddedResources +namespace Lclzr.Providers.Files.EmbeddedResources { public class EmbeddedResourcesBlocksProviderOptions { diff --git a/src/Insight.Localizer/Providers/Files/FilesBlocksProvider.cs b/src/Lclzr/Providers/Files/FilesBlocksProvider.cs similarity index 96% rename from src/Insight.Localizer/Providers/Files/FilesBlocksProvider.cs rename to src/Lclzr/Providers/Files/FilesBlocksProvider.cs index 3ad59ac..ab8cda2 100644 --- a/src/Insight.Localizer/Providers/Files/FilesBlocksProvider.cs +++ b/src/Lclzr/Providers/Files/FilesBlocksProvider.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Newtonsoft.Json.Linq; -namespace Insight.Localizer.Providers.Files +namespace Lclzr.Providers.Files { public abstract class BlocksProvider : IBlocksProvider { diff --git a/src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProvider.cs b/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs similarity index 97% rename from src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProvider.cs rename to src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs index f5f2d9c..bfb1f0a 100644 --- a/src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProvider.cs +++ b/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs @@ -4,10 +4,11 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Insight.Localizer.Infrastructure; +using Lclzr.Exceptions; +using Lclzr.Infrastructure; using Newtonsoft.Json.Linq; -namespace Insight.Localizer.Providers.Files.RawFiles +namespace Lclzr.Providers.Files.RawFiles { public class RawFilesBlocksProvider : BlocksProvider, IInitializable { diff --git a/src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProviderOptions.cs b/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProviderOptions.cs similarity index 74% rename from src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProviderOptions.cs rename to src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProviderOptions.cs index b00b48a..14f8811 100644 --- a/src/Insight.Localizer/Providers/Files/RawFiles/RawFilesBlocksProviderOptions.cs +++ b/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProviderOptions.cs @@ -1,4 +1,4 @@ -namespace Insight.Localizer.Providers.Files.RawFiles +namespace Lclzr.Providers.Files.RawFiles { public class RawFilesBlocksProviderOptions { diff --git a/src/Insight.Localizer/Providers/IBlocksProvider.cs b/src/Lclzr/Providers/IBlocksProvider.cs similarity index 78% rename from src/Insight.Localizer/Providers/IBlocksProvider.cs rename to src/Lclzr/Providers/IBlocksProvider.cs index 7d7db2e..b8066a8 100644 --- a/src/Insight.Localizer/Providers/IBlocksProvider.cs +++ b/src/Lclzr/Providers/IBlocksProvider.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Insight.Localizer.Providers +namespace Lclzr.Providers { public interface IBlocksProvider { diff --git a/src/Insight.Localizer/Registries/ILocalizerRegistry.cs b/src/Lclzr/Registries/ILocalizerRegistry.cs similarity index 96% rename from src/Insight.Localizer/Registries/ILocalizerRegistry.cs rename to src/Lclzr/Registries/ILocalizerRegistry.cs index a0c116f..630a27e 100644 --- a/src/Insight.Localizer/Registries/ILocalizerRegistry.cs +++ b/src/Lclzr/Registries/ILocalizerRegistry.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Insight.Localizer.Registries +namespace Lclzr.Registries { public interface ILocalizerRegistry { diff --git a/src/Insight.Localizer/Registries/LocalizerRegistry.cs b/src/Lclzr/Registries/LocalizerRegistry.cs similarity index 96% rename from src/Insight.Localizer/Registries/LocalizerRegistry.cs rename to src/Lclzr/Registries/LocalizerRegistry.cs index 0cd7423..492f2c5 100644 --- a/src/Insight.Localizer/Registries/LocalizerRegistry.cs +++ b/src/Lclzr/Registries/LocalizerRegistry.cs @@ -3,10 +3,11 @@ using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; -using Insight.Localizer.Infrastructure; -using Insight.Localizer.Providers; +using Lclzr.Exceptions; +using Lclzr.Infrastructure; +using Lclzr.Providers; -namespace Insight.Localizer.Registries +namespace Lclzr.Registries { public class LocalizerRegistry : ILocalizerRegistry, IInitializable { diff --git a/tests/Insight.Localizer.Tests/BlockTests.cs b/tests/Lclzr.Tests/BlockTests.cs similarity index 88% rename from tests/Insight.Localizer.Tests/BlockTests.cs rename to tests/Lclzr.Tests/BlockTests.cs index ddef8d0..0a2a560 100644 --- a/tests/Insight.Localizer.Tests/BlockTests.cs +++ b/tests/Lclzr.Tests/BlockTests.cs @@ -1,6 +1,7 @@ +using Lclzr.Exceptions; using Xunit; -namespace Insight.Localizer.Tests; +namespace Lclzr.Tests; public sealed class BlockTests { diff --git a/tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedMultipleLanguagesInFile/lclzr.embedded.json b/tests/Lclzr.Tests/EmbeddedResources/EmbeddedMultipleLanguagesInFile/lclzr.embedded.json similarity index 100% rename from tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedMultipleLanguagesInFile/lclzr.embedded.json rename to tests/Lclzr.Tests/EmbeddedResources/EmbeddedMultipleLanguagesInFile/lclzr.embedded.json diff --git a/tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.en-us.json b/tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.en-us.json similarity index 100% rename from tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.en-us.json rename to tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.en-us.json diff --git a/tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.ru-ru.json b/tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.ru-ru.json similarity index 100% rename from tests/Insight.Localizer.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.ru-ru.json rename to tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.ru-ru.json diff --git a/tests/Insight.Localizer.Tests/GenericLocalizerTests.cs b/tests/Lclzr.Tests/GenericLocalizerTests.cs similarity index 86% rename from tests/Insight.Localizer.Tests/GenericLocalizerTests.cs rename to tests/Lclzr.Tests/GenericLocalizerTests.cs index 19e21f6..21c80f6 100644 --- a/tests/Insight.Localizer.Tests/GenericLocalizerTests.cs +++ b/tests/Lclzr.Tests/GenericLocalizerTests.cs @@ -1,8 +1,8 @@ -using Insight.Localizer.Providers.Files.RawFiles; -using Insight.Localizer.Registries; +using Lclzr.Providers.Files.RawFiles; +using Lclzr.Registries; using Xunit; -namespace Insight.Localizer.Tests; +namespace Lclzr.Tests; public sealed class GenericLocalizerTests { diff --git a/tests/Insight.Localizer.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs b/tests/Lclzr.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs similarity index 82% rename from tests/Insight.Localizer.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs rename to tests/Lclzr.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs index 26ae6b2..8c65d72 100644 --- a/tests/Insight.Localizer.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs +++ b/tests/Lclzr.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs @@ -1,7 +1,7 @@ -using Insight.Localizer.Providers.Files.RawFiles; -using Insight.Localizer.Registries; +using Lclzr.Providers.Files.RawFiles; +using Lclzr.Registries; -namespace Insight.Localizer.Tests; +namespace Lclzr.Tests.Infratructure; public sealed class LocalizerWithRawFilesProviderSut { diff --git a/tests/Insight.Localizer.Tests/Infratructure/TestBlocksProvider.cs b/tests/Lclzr.Tests/Infratructure/TestBlocksProvider.cs similarity index 82% rename from tests/Insight.Localizer.Tests/Infratructure/TestBlocksProvider.cs rename to tests/Lclzr.Tests/Infratructure/TestBlocksProvider.cs index 31d89f4..b1c62f0 100644 --- a/tests/Insight.Localizer.Tests/Infratructure/TestBlocksProvider.cs +++ b/tests/Lclzr.Tests/Infratructure/TestBlocksProvider.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using Insight.Localizer.Providers; +using Lclzr.Providers; -namespace Insight.Localizer.Tests; +namespace Lclzr.Tests.Infratructure; internal sealed class TestBlocksProvider : IBlocksProvider { diff --git a/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj b/tests/Lclzr.Tests/Lclzr.Tests.csproj similarity index 65% rename from tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj rename to tests/Lclzr.Tests/Lclzr.Tests.csproj index d044b93..f874736 100644 --- a/tests/Insight.Localizer.Tests/Insight.Localizer.Tests.csproj +++ b/tests/Lclzr.Tests/Lclzr.Tests.csproj @@ -6,10 +6,10 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -17,19 +17,19 @@ - - + + - + Always - + Always diff --git a/tests/Insight.Localizer.Tests/LocalizerMultipleLanguagesInFileTests.cs b/tests/Lclzr.Tests/LocalizerMultipleLanguagesInFileTests.cs similarity index 92% rename from tests/Insight.Localizer.Tests/LocalizerMultipleLanguagesInFileTests.cs rename to tests/Lclzr.Tests/LocalizerMultipleLanguagesInFileTests.cs index e2e9267..151d931 100644 --- a/tests/Insight.Localizer.Tests/LocalizerMultipleLanguagesInFileTests.cs +++ b/tests/Lclzr.Tests/LocalizerMultipleLanguagesInFileTests.cs @@ -1,10 +1,11 @@ using System; using System.IO; using System.Linq; -using Insight.Localizer.Providers.Files.RawFiles; +using Lclzr.Providers.Files.RawFiles; +using Lclzr.Tests.Infratructure; using Xunit; -namespace Insight.Localizer.Tests; +namespace Lclzr.Tests; public sealed class LocalizerMultipleLanguagesInFileTests { diff --git a/tests/Insight.Localizer.Tests/LocalizerOneLanguageInFileTests.cs b/tests/Lclzr.Tests/LocalizerOneLanguageInFileTests.cs similarity index 93% rename from tests/Insight.Localizer.Tests/LocalizerOneLanguageInFileTests.cs rename to tests/Lclzr.Tests/LocalizerOneLanguageInFileTests.cs index 87a69b2..67d27ea 100644 --- a/tests/Insight.Localizer.Tests/LocalizerOneLanguageInFileTests.cs +++ b/tests/Lclzr.Tests/LocalizerOneLanguageInFileTests.cs @@ -1,10 +1,11 @@ using System; using System.IO; using System.Linq; -using Insight.Localizer.Providers.Files.RawFiles; +using Lclzr.Providers.Files.RawFiles; +using Lclzr.Tests.Infratructure; using Xunit; -namespace Insight.Localizer.Tests; +namespace Lclzr.Tests; public sealed class LocalizerOneLanguageInFileTests { diff --git a/tests/Insight.Localizer.Tests/LocalizerRegistryTests.cs b/tests/Lclzr.Tests/LocalizerRegistryTests.cs similarity index 91% rename from tests/Insight.Localizer.Tests/LocalizerRegistryTests.cs rename to tests/Lclzr.Tests/LocalizerRegistryTests.cs index 73c3487..58fad91 100644 --- a/tests/Insight.Localizer.Tests/LocalizerRegistryTests.cs +++ b/tests/Lclzr.Tests/LocalizerRegistryTests.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Insight.Localizer.Registries; +using Lclzr.Exceptions; +using Lclzr.Registries; +using Lclzr.Tests.Infratructure; using Xunit; -namespace Insight.Localizer.Tests; +namespace Lclzr.Tests; public sealed class LocalizerRegistryTests { diff --git a/tests/Insight.Localizer.Tests/LocalizierExtensionsTests.cs b/tests/Lclzr.Tests/LocalizierExtensionsTests.cs similarity index 92% rename from tests/Insight.Localizer.Tests/LocalizierExtensionsTests.cs rename to tests/Lclzr.Tests/LocalizierExtensionsTests.cs index 5742592..8127391 100644 --- a/tests/Insight.Localizer.Tests/LocalizierExtensionsTests.cs +++ b/tests/Lclzr.Tests/LocalizierExtensionsTests.cs @@ -1,13 +1,12 @@ using System; using System.Threading; using System.Threading.Tasks; -using Insight.Localizer.Extensions; -using Insight.Localizer.Providers.Files.RawFiles; +using Lclzr.Extensions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Xunit; -namespace Insight.Localizer.Tests; +namespace Lclzr.Tests; public class LocalizierExtensionsTests { diff --git a/tests/Insight.Localizer.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs b/tests/Lclzr.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs similarity index 94% rename from tests/Insight.Localizer.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs rename to tests/Lclzr.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs index dae78b3..430577a 100644 --- a/tests/Insight.Localizer.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs +++ b/tests/Lclzr.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs @@ -2,10 +2,10 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; -using Insight.Localizer.Providers.Files.EmbeddedResources; +using Lclzr.Providers.Files.EmbeddedResources; using Xunit; -namespace Insight.Localizer.Tests.Providers; +namespace Lclzr.Tests.Providers; public class EmbeddedResourceBlocksProviderTests { diff --git a/tests/Insight.Localizer.Tests/Providers/RawFilesBlocksProviderTests.cs b/tests/Lclzr.Tests/Providers/RawFilesBlocksProviderTests.cs similarity index 84% rename from tests/Insight.Localizer.Tests/Providers/RawFilesBlocksProviderTests.cs rename to tests/Lclzr.Tests/Providers/RawFilesBlocksProviderTests.cs index a74a7e2..5d78050 100644 --- a/tests/Insight.Localizer.Tests/Providers/RawFilesBlocksProviderTests.cs +++ b/tests/Lclzr.Tests/Providers/RawFilesBlocksProviderTests.cs @@ -1,8 +1,8 @@ using System.Threading.Tasks; -using Insight.Localizer.Providers.Files.RawFiles; +using Lclzr.Providers.Files.RawFiles; using Xunit; -namespace Insight.Localizer.Tests.Providers; +namespace Lclzr.Tests.Providers; public class RawFilesBlocksProviderTests { diff --git a/tests/Insight.Localizer.Tests/Resources/MultipleLanguagesInFile/lclzr.multiple.json b/tests/Lclzr.Tests/Resources/MultipleLanguagesInFile/lclzr.multiple.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/MultipleLanguagesInFile/lclzr.multiple.json rename to tests/Lclzr.Tests/Resources/MultipleLanguagesInFile/lclzr.multiple.json diff --git a/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.GenericLocalizerTests.ru-ru.json b/tests/Lclzr.Tests/Resources/OneLanguageInFile/lclzr.GenericLocalizerTests.ru-ru.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.GenericLocalizerTests.ru-ru.json rename to tests/Lclzr.Tests/Resources/OneLanguageInFile/lclzr.GenericLocalizerTests.ru-ru.json diff --git a/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.language.any.json b/tests/Lclzr.Tests/Resources/OneLanguageInFile/lclzr.language.any.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.language.any.json rename to tests/Lclzr.Tests/Resources/OneLanguageInFile/lclzr.language.any.json diff --git a/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.messages.ru-ru.json b/tests/Lclzr.Tests/Resources/OneLanguageInFile/lclzr.messages.ru-ru.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.messages.ru-ru.json rename to tests/Lclzr.Tests/Resources/OneLanguageInFile/lclzr.messages.ru-ru.json diff --git a/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.test.en-us.json b/tests/Lclzr.Tests/Resources/OneLanguageInFile/lclzr.test.en-us.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.test.en-us.json rename to tests/Lclzr.Tests/Resources/OneLanguageInFile/lclzr.test.en-us.json diff --git a/tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.test.ru-ru.json b/tests/Lclzr.Tests/Resources/OneLanguageInFile/lclzr.test.ru-ru.json similarity index 100% rename from tests/Insight.Localizer.Tests/Resources/OneLanguageInFile/lclzr.test.ru-ru.json rename to tests/Lclzr.Tests/Resources/OneLanguageInFile/lclzr.test.ru-ru.json From c400e5aa77d40772102e4eeb5df019f9457fac09 Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Sat, 27 Apr 2024 17:21:49 +0400 Subject: [PATCH 08/14] Add localizer builder --- .../ServiceCollectionExtensions.cs | 33 ++++--- src/Lclzr/AssemblyInfo.cs | 3 +- src/Lclzr/Block.cs | 5 +- src/Lclzr/Localizer.cs | 4 +- src/Lclzr/LocalizerBuilder.cs | 92 +++++++++++++++++++ src/Lclzr/Localizer{T}.cs | 2 +- src/Lclzr/Providers/BlockCultureData.cs | 2 +- ...lesBlocksProvider.cs => BlocksProvider.cs} | 4 +- .../EmbeddedResourcesBlocksProvider.cs | 2 +- .../Files/RawFiles/RawFilesBlocksProvider.cs | 2 +- src/Lclzr/Registries/ILocalizerRegistry.cs | 5 - src/Lclzr/Registries/LocalizerRegistry.cs | 12 +-- .../LocalizerWithRawFilesProviderSut.cs | 8 +- .../Lclzr.Tests/LocalizierExtensionsTests.cs | 16 +++- 14 files changed, 142 insertions(+), 48 deletions(-) create mode 100644 src/Lclzr/LocalizerBuilder.cs rename src/Lclzr/Providers/{Files/FilesBlocksProvider.cs => BlocksProvider.cs} (92%) diff --git a/src/Lclzr.Extensions/ServiceCollectionExtensions.cs b/src/Lclzr.Extensions/ServiceCollectionExtensions.cs index 6e33a79..28524f1 100644 --- a/src/Lclzr.Extensions/ServiceCollectionExtensions.cs +++ b/src/Lclzr.Extensions/ServiceCollectionExtensions.cs @@ -1,5 +1,4 @@ using System; -using Lclzr.Providers; using Lclzr.Registries; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -8,30 +7,34 @@ namespace Lclzr.Extensions { public static class ServiceCollectionExtensions { - public static IServiceCollection AddLocalizer(this IServiceCollection services) + public static IServiceCollection AddLocalizer(this IServiceCollection services, + Action builder) { if (services == null) { throw new ArgumentNullException(nameof(services)); } - services.TryAddSingleton(); - services.TryAddScoped(); - services.AddTransient(typeof(ILocalizer<>), typeof(Localizer<>)); - services.AddHostedService(); + var localizerBuilder = new LocalizerBuilder(); + builder.Invoke(localizerBuilder); - return services; - } - - public static IServiceCollection AddLocalizerProvider(this IServiceCollection services) - where T : class, IBlocksProvider - { - if (services == null) + foreach (var provider in localizerBuilder.Providers) { - throw new ArgumentNullException(nameof(services)); + services.AddSingleton(provider); } - services.AddSingleton(); + services.TryAddSingleton(); + + Func factory = ctx => + localizerBuilder + .WithRegistry(ctx.GetRequiredService()) + .Build(); + + var descriptor = new ServiceDescriptor(typeof(ILocalizer), factory, ServiceLifetime.Scoped); + services.TryAdd(descriptor); + + services.AddTransient(typeof(ILocalizer<>), typeof(Localizer<>)); + services.AddHostedService(); return services; } diff --git a/src/Lclzr/AssemblyInfo.cs b/src/Lclzr/AssemblyInfo.cs index db8e4ef..548c792 100644 --- a/src/Lclzr/AssemblyInfo.cs +++ b/src/Lclzr/AssemblyInfo.cs @@ -1,3 +1,4 @@ using System.Runtime.CompilerServices; -[assembly:InternalsVisibleTo("Lclzr.Tests")] \ No newline at end of file +[assembly:InternalsVisibleTo("Lclzr.Tests")] +[assembly:InternalsVisibleTo("Lclzr.Extensions")] \ No newline at end of file diff --git a/src/Lclzr/Block.cs b/src/Lclzr/Block.cs index 69a52aa..873f68a 100644 --- a/src/Lclzr/Block.cs +++ b/src/Lclzr/Block.cs @@ -10,7 +10,10 @@ public sealed class Block public string Name { get; } public IReadOnlyCollection AvailableCultures => - new Lazy>(() => _localizations.Keys.ToList().AsReadOnly()).Value; + new Lazy>(() => _localizations.Keys + .ToList() + .AsReadOnly()) + .Value; private readonly IDictionary> _localizations; diff --git a/src/Lclzr/Localizer.cs b/src/Lclzr/Localizer.cs index cce1ce4..b48e3c2 100644 --- a/src/Lclzr/Localizer.cs +++ b/src/Lclzr/Localizer.cs @@ -4,14 +4,12 @@ namespace Lclzr { - public class Localizer : ILocalizer + internal class Localizer : ILocalizer { private readonly ILocalizerRegistry _registry; public ILocalizerCulture? CurrentCulture { get; set; } - public IReadOnlyDictionary Blocks => _registry.Blocks; - public IReadOnlyCollection AvailableBlockNames => _registry.AvailableBlockNames; public Localizer(ILocalizerRegistry registry) diff --git a/src/Lclzr/LocalizerBuilder.cs b/src/Lclzr/LocalizerBuilder.cs new file mode 100644 index 0000000..668ac8e --- /dev/null +++ b/src/Lclzr/LocalizerBuilder.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Lclzr.Providers; +using Lclzr.Providers.Files.EmbeddedResources; +using Lclzr.Providers.Files.RawFiles; +using Lclzr.Registries; + +namespace Lclzr +{ + public sealed class LocalizerBuilder + { + internal readonly List Providers = new List(); + + private ILocalizerRegistry? _registry; + + public LocalizerBuilder WithProvider(TProvider provider) where TProvider : class, IBlocksProvider + { + if (provider == null) + { + throw new ArgumentNullException(nameof(provider)); + } + + Providers.Add(provider); + return this; + } + + public LocalizerBuilder WithRawFilesProvider(RawFilesBlocksProviderOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + Providers.Add(new RawFilesBlocksProvider(options)); + return this; + } + + public LocalizerBuilder WithEmbeddedResourcesProvider(EmbeddedResourcesBlocksProviderOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + Providers.Add(new EmbeddedResourcesBlocksProvider(options)); + return this; + } + + public ILocalizer Build() + { + var registry = _registry ?? BuildAndInitializeRegistry().GetAwaiter().GetResult(); + + return new Localizer(registry); + } + + public ILocalizer Build() where T : class + { + var registry = _registry ?? BuildAndInitializeRegistry().GetAwaiter().GetResult(); + + return new Localizer(registry); + } + + public async Task BuildAsync() + { + var registry = _registry ?? await BuildAndInitializeRegistry(); + + return new Localizer(registry); + } + + public async Task> BuildAsync() where T : class + { + var registry = _registry ?? await BuildAndInitializeRegistry(); + + return new Localizer(registry); + } + + internal LocalizerBuilder WithRegistry(TRegistry registry) where TRegistry : class, ILocalizerRegistry + { + _registry = registry ?? throw new ArgumentNullException(nameof(registry)); + return this; + } + + private async Task BuildAndInitializeRegistry() + { + var registry = new LocalizerRegistry(Providers); + await registry.Initialize(); + + return registry; + } + } +} \ No newline at end of file diff --git a/src/Lclzr/Localizer{T}.cs b/src/Lclzr/Localizer{T}.cs index 34edbca..ab790cb 100644 --- a/src/Lclzr/Localizer{T}.cs +++ b/src/Lclzr/Localizer{T}.cs @@ -2,7 +2,7 @@ namespace Lclzr { - public class Localizer : Localizer, ILocalizer where T : class + internal class Localizer : Localizer, ILocalizer where T : class { public Localizer(ILocalizerRegistry registry) : base(registry) { diff --git a/src/Lclzr/Providers/BlockCultureData.cs b/src/Lclzr/Providers/BlockCultureData.cs index a27f35e..4613a68 100644 --- a/src/Lclzr/Providers/BlockCultureData.cs +++ b/src/Lclzr/Providers/BlockCultureData.cs @@ -2,7 +2,7 @@ namespace Lclzr.Providers { - public readonly struct BlockCultureData : IEquatable + internal readonly struct BlockCultureData : IEquatable { public BlockCultureData(string block, string culture, string content) { diff --git a/src/Lclzr/Providers/Files/FilesBlocksProvider.cs b/src/Lclzr/Providers/BlocksProvider.cs similarity index 92% rename from src/Lclzr/Providers/Files/FilesBlocksProvider.cs rename to src/Lclzr/Providers/BlocksProvider.cs index ab8cda2..4b8db0d 100644 --- a/src/Lclzr/Providers/Files/FilesBlocksProvider.cs +++ b/src/Lclzr/Providers/BlocksProvider.cs @@ -3,9 +3,9 @@ using System.Threading.Tasks; using Newtonsoft.Json.Linq; -namespace Lclzr.Providers.Files +namespace Lclzr.Providers { - public abstract class BlocksProvider : IBlocksProvider + internal abstract class BlocksProvider : IBlocksProvider { protected readonly IDictionary Blocks = new Dictionary(); diff --git a/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs b/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs index 632645b..b96400e 100644 --- a/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs +++ b/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs @@ -12,7 +12,7 @@ namespace Lclzr.Providers.Files.EmbeddedResources { - public class EmbeddedResourcesBlocksProvider : BlocksProvider, IInitializable + internal class EmbeddedResourcesBlocksProvider : BlocksProvider, IInitializable { private static readonly Regex OneFilePerCultureNameRegex = new Regex(@"lclzr.([A-z0-9_-]{1,})\.([a-z\-]{2,})\.json$", RegexOptions.Compiled); diff --git a/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs b/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs index bfb1f0a..ef46147 100644 --- a/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs +++ b/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs @@ -10,7 +10,7 @@ namespace Lclzr.Providers.Files.RawFiles { - public class RawFilesBlocksProvider : BlocksProvider, IInitializable + internal class RawFilesBlocksProvider : BlocksProvider, IInitializable { private static readonly Regex OneFilePerCultureNameRegex = new Regex(@"^lclzr.([A-z0-9_-]{1,})\.([a-z\-]{2,})\.json$", RegexOptions.Compiled); diff --git a/src/Lclzr/Registries/ILocalizerRegistry.cs b/src/Lclzr/Registries/ILocalizerRegistry.cs index 630a27e..48b6dd8 100644 --- a/src/Lclzr/Registries/ILocalizerRegistry.cs +++ b/src/Lclzr/Registries/ILocalizerRegistry.cs @@ -4,11 +4,6 @@ namespace Lclzr.Registries { public interface ILocalizerRegistry { - /// - /// Loaded blocks - /// - IReadOnlyDictionary Blocks { get; } - /// /// Available block names /// diff --git a/src/Lclzr/Registries/LocalizerRegistry.cs b/src/Lclzr/Registries/LocalizerRegistry.cs index 492f2c5..04254b4 100644 --- a/src/Lclzr/Registries/LocalizerRegistry.cs +++ b/src/Lclzr/Registries/LocalizerRegistry.cs @@ -9,23 +9,21 @@ namespace Lclzr.Registries { - public class LocalizerRegistry : ILocalizerRegistry, IInitializable + internal class LocalizerRegistry : ILocalizerRegistry, IInitializable { private Dictionary _blocks = new Dictionary(); - + private IBlocksProvider[]? _blockProviders; - + private bool _initialized; public IReadOnlyCollection AvailableBlockNames => new Lazy>( - () => Blocks + () => _blocks .Select(x => x.Key) .ToList() .AsReadOnly()) .Value; - public IReadOnlyDictionary Blocks => new ReadOnlyDictionary(_blocks); - public LocalizerRegistry(IEnumerable blocksProviders) : this(blocksProviders.ToArray()) { } @@ -82,7 +80,7 @@ public string GetByCulture(string culture, string block, string key) } private Block this[string name] => - Blocks.TryGetValue(name, out var value) + _blocks.TryGetValue(name, out var value) ? value : throw new MissingBlockException($"Block `{name}` missing"); } diff --git a/tests/Lclzr.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs b/tests/Lclzr.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs index 8c65d72..e61a2db 100644 --- a/tests/Lclzr.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs +++ b/tests/Lclzr.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs @@ -1,5 +1,4 @@ using Lclzr.Providers.Files.RawFiles; -using Lclzr.Registries; namespace Lclzr.Tests.Infratructure; @@ -15,9 +14,8 @@ public sealed class LocalizerWithRawFilesProviderSut public LocalizerWithRawFilesProviderSut(RawFilesBlocksProviderOptions? options = null) { - var provider = new RawFilesBlocksProvider(options ?? Options); - var registry = new LocalizerRegistry(provider); - Localizer = new Localizer(registry); - registry.Initialize().GetAwaiter().GetResult(); + Localizer = new LocalizerBuilder() + .WithRawFilesProvider(options ?? Options) + .Build(); } } \ No newline at end of file diff --git a/tests/Lclzr.Tests/LocalizierExtensionsTests.cs b/tests/Lclzr.Tests/LocalizierExtensionsTests.cs index 8127391..fdae8b6 100644 --- a/tests/Lclzr.Tests/LocalizierExtensionsTests.cs +++ b/tests/Lclzr.Tests/LocalizierExtensionsTests.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; +using System.Security; using System.Threading; using System.Threading.Tasks; using Lclzr.Extensions; +using Lclzr.Tests.Infratructure; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Xunit; @@ -29,12 +32,13 @@ public void AddLocalizer_registers_generic_implementation() } [Fact] - public void AddLocalizer_registers_localizer_initializer() + public void AddLocalizer_registers_registry_initializer() { var sp = BuildServiceProvider(); - var localizer = sp.GetRequiredService>(); + var initializer = sp.GetRequiredService(); - Assert.NotNull(localizer); + Assert.NotNull(initializer); + Assert.Equal(typeof(RegistryInitializerBackgroundService), initializer.GetType()); } [Fact] @@ -45,7 +49,7 @@ public async Task LocalizerInitializer_initializes_localizer() var initializer = sp.GetRequiredService(); await initializer.StartAsync(CancellationToken.None); - + Assert.NotNull(localizer.AvailableBlockNames); } @@ -53,7 +57,9 @@ private static IServiceProvider BuildServiceProvider() { IServiceCollection services = new ServiceCollection(); - services.AddLocalizer(); + var block = new Block("test"); + block.Add("ru-ru", new Dictionary() ); + services.AddLocalizer(builder => builder.WithProvider(new TestBlocksProvider(block))); return services.BuildServiceProvider(); } From 8e2fcf9c6de753650f00a2871e37e4493cb5eb65 Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Mon, 29 Apr 2024 12:39:16 +0400 Subject: [PATCH 09/14] Remove todos --- src/Lclzr/Providers/BlocksProvider.cs | 6 +++++- .../EmbeddedResourcesBlocksProvider.cs | 12 ------------ .../Files/RawFiles/RawFilesBlocksProvider.cs | 9 --------- src/Lclzr/Registries/LocalizerRegistry.cs | 2 -- 4 files changed, 5 insertions(+), 24 deletions(-) diff --git a/src/Lclzr/Providers/BlocksProvider.cs b/src/Lclzr/Providers/BlocksProvider.cs index 4b8db0d..720fc48 100644 --- a/src/Lclzr/Providers/BlocksProvider.cs +++ b/src/Lclzr/Providers/BlocksProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json.Linq; @@ -9,7 +10,10 @@ internal abstract class BlocksProvider : IBlocksProvider { protected readonly IDictionary Blocks = new Dictionary(); - public abstract IReadOnlyCollection GetBlocks(); + public virtual IReadOnlyCollection GetBlocks() + { + return Blocks.Values.ToArray(); + } protected Task InitializeBlockCulture(in BlockCultureData cultureData) { diff --git a/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs b/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs index b96400e..1ac3528 100644 --- a/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs +++ b/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs @@ -41,7 +41,6 @@ public async Task Initialize() using (var resourceStream = assembly.GetManifestResourceStream(resourceName)) { if (resourceStream == null) - // TODO: Log continue; using (var streamReader = new StreamReader(resourceStream, encoding)) @@ -56,8 +55,6 @@ public async Task Initialize() var info = new BlockCultureData(block, culture, content); await InitializeBlockCulture(in info); - // TODO: Write log - continue; } @@ -74,11 +71,7 @@ public async Task Initialize() var info = new BlockCultureData(block, culture, blockJToken.ToString()); await InitializeBlockCulture(in info); } - - continue; } - - // TODO: Log } } } @@ -89,10 +82,5 @@ public async Task Initialize() } } } - - public override IReadOnlyCollection GetBlocks() - { - return Blocks.Values.ToArray(); - } } } \ No newline at end of file diff --git a/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs b/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs index ef46147..8a5e15a 100644 --- a/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs +++ b/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs @@ -38,7 +38,6 @@ public async Task Initialize() try { var filename = Path.GetFileName(file); - // TODO: Lazy or different way not to load string? content = null; var oneFilePerMultipleLanguagesMatch = OneFilePerMultipleCulturesNameRegex.Match(filename); if (oneFilePerMultipleLanguagesMatch.Success) @@ -64,10 +63,7 @@ public async Task Initialize() var info = new BlockCultureData(block, culture, content); await InitializeBlockCulture(in info); - continue; } - - // TODO: Log block not added } catch (Exception ex) { @@ -75,10 +71,5 @@ public async Task Initialize() } } } - - public override IReadOnlyCollection GetBlocks() - { - return Blocks.Values.ToArray(); - } } } \ No newline at end of file diff --git a/src/Lclzr/Registries/LocalizerRegistry.cs b/src/Lclzr/Registries/LocalizerRegistry.cs index 04254b4..b9caa12 100644 --- a/src/Lclzr/Registries/LocalizerRegistry.cs +++ b/src/Lclzr/Registries/LocalizerRegistry.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using Lclzr.Exceptions; @@ -54,7 +53,6 @@ public async Task Initialize() { if (_blocks.ContainsKey(block.Name)) { - // TODO: Write log throw new InvalidOperationException( $"Error occured while adding blocks from {blocksProvider.GetType().Name}. Block already added."); } From 7fb377d27b15d4880fefbbb2ff705d44685c9eb7 Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Mon, 29 Apr 2024 12:46:42 +0400 Subject: [PATCH 10/14] Remove todos --- src/Lclzr/Registries/LocalizerRegistry.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Lclzr/Registries/LocalizerRegistry.cs b/src/Lclzr/Registries/LocalizerRegistry.cs index b9caa12..e46cb8b 100644 --- a/src/Lclzr/Registries/LocalizerRegistry.cs +++ b/src/Lclzr/Registries/LocalizerRegistry.cs @@ -32,16 +32,13 @@ public LocalizerRegistry(params IBlocksProvider[] blocksProviders) _blockProviders = blocksProviders; } - // TODO: Think about thread safety public async Task Initialize() { if (_initialized) throw new InvalidOperationException($"{nameof(LocalizerRegistry)} already initialized"); if (_blockProviders == null) - { throw new InvalidOperationException("There is no block providers"); - } foreach (var blocksProvider in _blockProviders) { @@ -61,8 +58,6 @@ public async Task Initialize() } } - // Clear providers to collect them - // TODO: Think how to refactor this _blockProviders = null; _initialized = true; } From ae1e8cc69d5851496164d43f8539390a1a798247 Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Thu, 2 May 2024 17:58:59 +0300 Subject: [PATCH 11/14] Fix generic localizer lifetime --- src/Lclzr.Extensions/ServiceCollectionExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Lclzr.Extensions/ServiceCollectionExtensions.cs b/src/Lclzr.Extensions/ServiceCollectionExtensions.cs index 28524f1..4d83aa4 100644 --- a/src/Lclzr.Extensions/ServiceCollectionExtensions.cs +++ b/src/Lclzr.Extensions/ServiceCollectionExtensions.cs @@ -33,7 +33,7 @@ public static IServiceCollection AddLocalizer(this IServiceCollection services, var descriptor = new ServiceDescriptor(typeof(ILocalizer), factory, ServiceLifetime.Scoped); services.TryAdd(descriptor); - services.AddTransient(typeof(ILocalizer<>), typeof(Localizer<>)); + services.AddScoped(typeof(ILocalizer<>), typeof(Localizer<>)); services.AddHostedService(); return services; From 011dd3c75a136fff1336799de20b5f193f10a066 Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Thu, 2 May 2024 18:03:06 +0300 Subject: [PATCH 12/14] Fix namespace --- src/Lclzr/Registries/LocalizerRegistry.cs | 2 +- .../LocalizerWithRawFilesProviderSut.cs | 2 +- .../{Infratructure => Infrastructure}/TestBlocksProvider.cs | 2 +- tests/Lclzr.Tests/LocalizerMultipleLanguagesInFileTests.cs | 2 +- tests/Lclzr.Tests/LocalizerOneLanguageInFileTests.cs | 2 +- tests/Lclzr.Tests/LocalizerRegistryTests.cs | 2 +- tests/Lclzr.Tests/LocalizierExtensionsTests.cs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename tests/Lclzr.Tests/{Infratructure => Infrastructure}/LocalizerWithRawFilesProviderSut.cs (93%) rename tests/Lclzr.Tests/{Infratructure => Infrastructure}/TestBlocksProvider.cs (90%) diff --git a/src/Lclzr/Registries/LocalizerRegistry.cs b/src/Lclzr/Registries/LocalizerRegistry.cs index e46cb8b..c78ebca 100644 --- a/src/Lclzr/Registries/LocalizerRegistry.cs +++ b/src/Lclzr/Registries/LocalizerRegistry.cs @@ -51,7 +51,7 @@ public async Task Initialize() if (_blocks.ContainsKey(block.Name)) { throw new InvalidOperationException( - $"Error occured while adding blocks from {blocksProvider.GetType().Name}. Block already added."); + $"Error occured while adding blocks from {blocksProvider.GetType().Name}. Block already exists."); } _blocks.Add(block.Name, block); diff --git a/tests/Lclzr.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs b/tests/Lclzr.Tests/Infrastructure/LocalizerWithRawFilesProviderSut.cs similarity index 93% rename from tests/Lclzr.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs rename to tests/Lclzr.Tests/Infrastructure/LocalizerWithRawFilesProviderSut.cs index e61a2db..674114a 100644 --- a/tests/Lclzr.Tests/Infratructure/LocalizerWithRawFilesProviderSut.cs +++ b/tests/Lclzr.Tests/Infrastructure/LocalizerWithRawFilesProviderSut.cs @@ -1,6 +1,6 @@ using Lclzr.Providers.Files.RawFiles; -namespace Lclzr.Tests.Infratructure; +namespace Lclzr.Tests.Infrastructure; public sealed class LocalizerWithRawFilesProviderSut { diff --git a/tests/Lclzr.Tests/Infratructure/TestBlocksProvider.cs b/tests/Lclzr.Tests/Infrastructure/TestBlocksProvider.cs similarity index 90% rename from tests/Lclzr.Tests/Infratructure/TestBlocksProvider.cs rename to tests/Lclzr.Tests/Infrastructure/TestBlocksProvider.cs index b1c62f0..e780d06 100644 --- a/tests/Lclzr.Tests/Infratructure/TestBlocksProvider.cs +++ b/tests/Lclzr.Tests/Infrastructure/TestBlocksProvider.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Lclzr.Providers; -namespace Lclzr.Tests.Infratructure; +namespace Lclzr.Tests.Infrastructure; internal sealed class TestBlocksProvider : IBlocksProvider { diff --git a/tests/Lclzr.Tests/LocalizerMultipleLanguagesInFileTests.cs b/tests/Lclzr.Tests/LocalizerMultipleLanguagesInFileTests.cs index 151d931..f3ea628 100644 --- a/tests/Lclzr.Tests/LocalizerMultipleLanguagesInFileTests.cs +++ b/tests/Lclzr.Tests/LocalizerMultipleLanguagesInFileTests.cs @@ -2,7 +2,7 @@ using System.IO; using System.Linq; using Lclzr.Providers.Files.RawFiles; -using Lclzr.Tests.Infratructure; +using Lclzr.Tests.Infrastructure; using Xunit; namespace Lclzr.Tests; diff --git a/tests/Lclzr.Tests/LocalizerOneLanguageInFileTests.cs b/tests/Lclzr.Tests/LocalizerOneLanguageInFileTests.cs index 67d27ea..2c2553f 100644 --- a/tests/Lclzr.Tests/LocalizerOneLanguageInFileTests.cs +++ b/tests/Lclzr.Tests/LocalizerOneLanguageInFileTests.cs @@ -2,7 +2,7 @@ using System.IO; using System.Linq; using Lclzr.Providers.Files.RawFiles; -using Lclzr.Tests.Infratructure; +using Lclzr.Tests.Infrastructure; using Xunit; namespace Lclzr.Tests; diff --git a/tests/Lclzr.Tests/LocalizerRegistryTests.cs b/tests/Lclzr.Tests/LocalizerRegistryTests.cs index 58fad91..710f935 100644 --- a/tests/Lclzr.Tests/LocalizerRegistryTests.cs +++ b/tests/Lclzr.Tests/LocalizerRegistryTests.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Lclzr.Exceptions; using Lclzr.Registries; -using Lclzr.Tests.Infratructure; +using Lclzr.Tests.Infrastructure; using Xunit; namespace Lclzr.Tests; diff --git a/tests/Lclzr.Tests/LocalizierExtensionsTests.cs b/tests/Lclzr.Tests/LocalizierExtensionsTests.cs index fdae8b6..cdc09ac 100644 --- a/tests/Lclzr.Tests/LocalizierExtensionsTests.cs +++ b/tests/Lclzr.Tests/LocalizierExtensionsTests.cs @@ -4,7 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Lclzr.Extensions; -using Lclzr.Tests.Infratructure; +using Lclzr.Tests.Infrastructure; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Xunit; From 1fac0de978acbec4b11deb759d0dd008b954a4a3 Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Thu, 2 May 2024 18:14:16 +0300 Subject: [PATCH 13/14] Generic localizer now use ILocalizer in ctor --- src/Lclzr/LocalizerBuilder.cs | 14 ---------- src/Lclzr/Localizer{T}.cs | 32 ++++++++++++++++++---- tests/Lclzr.Tests/GenericLocalizerTests.cs | 3 +- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/Lclzr/LocalizerBuilder.cs b/src/Lclzr/LocalizerBuilder.cs index 668ac8e..d980371 100644 --- a/src/Lclzr/LocalizerBuilder.cs +++ b/src/Lclzr/LocalizerBuilder.cs @@ -54,13 +54,6 @@ public ILocalizer Build() return new Localizer(registry); } - public ILocalizer Build() where T : class - { - var registry = _registry ?? BuildAndInitializeRegistry().GetAwaiter().GetResult(); - - return new Localizer(registry); - } - public async Task BuildAsync() { var registry = _registry ?? await BuildAndInitializeRegistry(); @@ -68,13 +61,6 @@ public async Task BuildAsync() return new Localizer(registry); } - public async Task> BuildAsync() where T : class - { - var registry = _registry ?? await BuildAndInitializeRegistry(); - - return new Localizer(registry); - } - internal LocalizerBuilder WithRegistry(TRegistry registry) where TRegistry : class, ILocalizerRegistry { _registry = registry ?? throw new ArgumentNullException(nameof(registry)); diff --git a/src/Lclzr/Localizer{T}.cs b/src/Lclzr/Localizer{T}.cs index ab790cb..c23c3b5 100644 --- a/src/Lclzr/Localizer{T}.cs +++ b/src/Lclzr/Localizer{T}.cs @@ -1,16 +1,23 @@ -using Lclzr.Registries; +using System; +using System.Collections.Generic; namespace Lclzr { - internal class Localizer : Localizer, ILocalizer where T : class + internal class Localizer : ILocalizer where T : class { - public Localizer(ILocalizerRegistry registry) : base(registry) + private readonly ILocalizer _localizer; + + public ILocalizerCulture CurrentCulture { + get => _localizer.CurrentCulture; + set => _localizer.CurrentCulture = value; } - public Localizer(ILocalizerRegistry registry, ILocalizerCulture localizerCulture) : base(registry, - localizerCulture) + public IReadOnlyCollection AvailableBlockNames => _localizer.AvailableBlockNames; + + public Localizer(ILocalizer localizer) { + _localizer = localizer ?? throw new ArgumentNullException(nameof(localizer)); } public string Get(string key) @@ -31,6 +38,21 @@ public string GetByCulture(string culture, string key) return GetByCulture(culture, block, key); } + public string Get(string block, string key) + { + return _localizer.Get(block, key); + } + + public string GetAny(string block, string key) + { + return _localizer.GetAny(block, key); + } + + public string GetByCulture(string culture, string block, string key) + { + return _localizer.GetByCulture(culture, block, key); + } + private static string GetBlockName() { return typeof(T).Name; diff --git a/tests/Lclzr.Tests/GenericLocalizerTests.cs b/tests/Lclzr.Tests/GenericLocalizerTests.cs index 21c80f6..7879778 100644 --- a/tests/Lclzr.Tests/GenericLocalizerTests.cs +++ b/tests/Lclzr.Tests/GenericLocalizerTests.cs @@ -20,7 +20,8 @@ public GenericLocalizerTests() var registry = new LocalizerRegistry(provider); registry.Initialize().GetAwaiter().GetResult(); - _localizer = new Localizer(registry); + var lclzr = new Localizer(registry); + _localizer = new Localizer(lclzr); _localizer.CurrentCulture = new LocalizerCulture("ru-ru"); } From 694bcf2c4b422fb01075a02095e85ce1f3f2d0bf Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Fri, 3 May 2024 16:05:40 +0300 Subject: [PATCH 14/14] Remove initialize methods from block providers --- src/Lclzr/Infrastructure/IInitializable.cs | 5 +---- src/Lclzr/Providers/BlocksProvider.cs | 4 ++-- .../EmbeddedResourcesBlocksProvider.cs | 18 +++++++++++++++--- .../Files/RawFiles/RawFilesBlocksProvider.cs | 19 +++++++++++++++---- src/Lclzr/Providers/IBlocksProvider.cs | 3 ++- src/Lclzr/Registries/LocalizerRegistry.cs | 5 +---- ...dded.en-us.json => lclzr.embedded.en.json} | 0 ...dded.ru-ru.json => lclzr.embedded.ru.json} | 0 .../Infrastructure/TestBlocksProvider.cs | 7 ++++--- .../EmbeddedResourceBlocksProviderTests.cs | 8 ++------ .../Providers/RawFilesBlocksProviderTests.cs | 3 +-- 11 files changed, 43 insertions(+), 29 deletions(-) rename tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/{lclzr.embedded.en-us.json => lclzr.embedded.en.json} (100%) rename tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/{lclzr.embedded.ru-ru.json => lclzr.embedded.ru.json} (100%) diff --git a/src/Lclzr/Infrastructure/IInitializable.cs b/src/Lclzr/Infrastructure/IInitializable.cs index 85aa213..31b8f33 100644 --- a/src/Lclzr/Infrastructure/IInitializable.cs +++ b/src/Lclzr/Infrastructure/IInitializable.cs @@ -2,10 +2,7 @@ namespace Lclzr.Infrastructure { - /// - /// Marks that implementation requires initialization - /// - public interface IInitializable + internal interface IInitializable { Task Initialize(); } diff --git a/src/Lclzr/Providers/BlocksProvider.cs b/src/Lclzr/Providers/BlocksProvider.cs index 720fc48..4022e2d 100644 --- a/src/Lclzr/Providers/BlocksProvider.cs +++ b/src/Lclzr/Providers/BlocksProvider.cs @@ -10,9 +10,9 @@ internal abstract class BlocksProvider : IBlocksProvider { protected readonly IDictionary Blocks = new Dictionary(); - public virtual IReadOnlyCollection GetBlocks() + public virtual Task> GetBlocks() { - return Blocks.Values.ToArray(); + return Task.FromResult>(Blocks.Values.ToArray()); } protected Task InitializeBlockCulture(in BlockCultureData cultureData) diff --git a/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs b/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs index 1ac3528..8e7b7cc 100644 --- a/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs +++ b/src/Lclzr/Providers/Files/EmbeddedResources/EmbeddedResourcesBlocksProvider.cs @@ -7,12 +7,11 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Lclzr.Exceptions; -using Lclzr.Infrastructure; using Newtonsoft.Json.Linq; namespace Lclzr.Providers.Files.EmbeddedResources { - internal class EmbeddedResourcesBlocksProvider : BlocksProvider, IInitializable + internal class EmbeddedResourcesBlocksProvider : BlocksProvider { private static readonly Regex OneFilePerCultureNameRegex = new Regex(@"lclzr.([A-z0-9_-]{1,})\.([a-z\-]{2,})\.json$", RegexOptions.Compiled); @@ -21,13 +20,26 @@ internal class EmbeddedResourcesBlocksProvider : BlocksProvider, IInitializable new Regex(@"lclzr.([A-z0-9_-]{1,})\.json$", RegexOptions.Compiled); private readonly EmbeddedResourcesBlocksProviderOptions _options; + + private bool _initialized; public EmbeddedResourcesBlocksProvider(EmbeddedResourcesBlocksProviderOptions options) { _options = options ?? throw new ArgumentNullException(nameof(options)); } - public async Task Initialize() + public override async Task> GetBlocks() + { + if (!_initialized) + { + await Initialize(); + _initialized = true; + } + + return await base.GetBlocks(); + } + + private async Task Initialize() { var encoding = Encoding.GetEncoding(_options.ResourceEncodingWebName); foreach (var assembly in _options.Assemblies.Select(Assembly.Load)) diff --git a/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs b/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs index 8a5e15a..9e031ba 100644 --- a/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs +++ b/src/Lclzr/Providers/Files/RawFiles/RawFilesBlocksProvider.cs @@ -1,16 +1,14 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using Lclzr.Exceptions; -using Lclzr.Infrastructure; using Newtonsoft.Json.Linq; namespace Lclzr.Providers.Files.RawFiles { - internal class RawFilesBlocksProvider : BlocksProvider, IInitializable + internal class RawFilesBlocksProvider : BlocksProvider { private static readonly Regex OneFilePerCultureNameRegex = new Regex(@"^lclzr.([A-z0-9_-]{1,})\.([a-z\-]{2,})\.json$", RegexOptions.Compiled); @@ -20,12 +18,25 @@ internal class RawFilesBlocksProvider : BlocksProvider, IInitializable private readonly RawFilesBlocksProviderOptions _options; + private bool _initialied; + public RawFilesBlocksProvider(RawFilesBlocksProviderOptions options) { _options = options ?? throw new ArgumentNullException(nameof(options)); } - public async Task Initialize() + public override async Task> GetBlocks() + { + if (!_initialied) + { + await Initialize(); + _initialied = true; + } + + return await base.GetBlocks(); + } + + private async Task Initialize() { var searchOption = _options.ReadNestedFolders ? SearchOption.AllDirectories diff --git a/src/Lclzr/Providers/IBlocksProvider.cs b/src/Lclzr/Providers/IBlocksProvider.cs index b8066a8..ef30241 100644 --- a/src/Lclzr/Providers/IBlocksProvider.cs +++ b/src/Lclzr/Providers/IBlocksProvider.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; +using System.Threading.Tasks; namespace Lclzr.Providers { public interface IBlocksProvider { - IReadOnlyCollection GetBlocks(); + Task> GetBlocks(); } } \ No newline at end of file diff --git a/src/Lclzr/Registries/LocalizerRegistry.cs b/src/Lclzr/Registries/LocalizerRegistry.cs index c78ebca..c19b1b7 100644 --- a/src/Lclzr/Registries/LocalizerRegistry.cs +++ b/src/Lclzr/Registries/LocalizerRegistry.cs @@ -42,10 +42,7 @@ public async Task Initialize() foreach (var blocksProvider in _blockProviders) { - if (blocksProvider is IInitializable initializable) - await initializable.Initialize(); - - var currentProviderBlocks = blocksProvider.GetBlocks(); + var currentProviderBlocks = await blocksProvider.GetBlocks(); foreach (var block in currentProviderBlocks) { if (_blocks.ContainsKey(block.Name)) diff --git a/tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.en-us.json b/tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.en.json similarity index 100% rename from tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.en-us.json rename to tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.en.json diff --git a/tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.ru-ru.json b/tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.ru.json similarity index 100% rename from tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.ru-ru.json rename to tests/Lclzr.Tests/EmbeddedResources/EmbeddedOneLanguageInFile/lclzr.embedded.ru.json diff --git a/tests/Lclzr.Tests/Infrastructure/TestBlocksProvider.cs b/tests/Lclzr.Tests/Infrastructure/TestBlocksProvider.cs index e780d06..b52feb6 100644 --- a/tests/Lclzr.Tests/Infrastructure/TestBlocksProvider.cs +++ b/tests/Lclzr.Tests/Infrastructure/TestBlocksProvider.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading.Tasks; using Lclzr.Providers; namespace Lclzr.Tests.Infrastructure; @@ -11,9 +12,9 @@ public TestBlocksProvider(params Block[] blocks) { _blocks = blocks; } - - public IReadOnlyCollection GetBlocks() + + public Task> GetBlocks() { - return _blocks; + return Task.FromResult>(_blocks); } } \ No newline at end of file diff --git a/tests/Lclzr.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs b/tests/Lclzr.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs index 430577a..821ed99 100644 --- a/tests/Lclzr.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs +++ b/tests/Lclzr.Tests/Providers/EmbeddedResourceBlocksProviderTests.cs @@ -19,9 +19,7 @@ public async Task Initialize_with_one_language_in_file_creates_provider_with_sin var provider = new EmbeddedResourcesBlocksProvider(fileBlocksProviderOptions); - await provider.Initialize(); - - var blocks = provider.GetBlocks(); + var blocks = await provider.GetBlocks(); Assert.Single(blocks); var block = blocks.Single(); @@ -40,9 +38,7 @@ public async Task Initialize_with_multiple_languages_in_file_creates_provider_wi var provider = new EmbeddedResourcesBlocksProvider(fileBlocksProviderOptions); - await provider.Initialize(); - - var blocks = provider.GetBlocks(); + var blocks = await provider.GetBlocks(); Assert.Single(blocks); var block = blocks.Single(); diff --git a/tests/Lclzr.Tests/Providers/RawFilesBlocksProviderTests.cs b/tests/Lclzr.Tests/Providers/RawFilesBlocksProviderTests.cs index 5d78050..398a142 100644 --- a/tests/Lclzr.Tests/Providers/RawFilesBlocksProviderTests.cs +++ b/tests/Lclzr.Tests/Providers/RawFilesBlocksProviderTests.cs @@ -16,9 +16,8 @@ public async Task Initialize_with_one() }; var provider = new RawFilesBlocksProvider(fileBlocksProviderOptions); - await provider.Initialize(); - var blocks = provider.GetBlocks(); + var blocks = await provider.GetBlocks(); Assert.Equal(5, blocks.Count); } } \ No newline at end of file