From a26223b57b7d180fbbbd58d4e0a11d5ba4e6a72b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 17 Dec 2017 22:54:00 +0300 Subject: [PATCH] Allow to extend/override existing resources --- .../ApplicationConfigurationBuilder.cs | 2 +- .../Localization/AbpLocalizationOptions.cs | 4 +- .../Localization/AbpStringLocalizerFactory.cs | 12 ++--- .../ILocalizationDictionaryProvider.cs | 4 +- ...eddedFileLocalizationDictionaryProvider.cs | 4 +- .../LocalizationDictionaryProviderBase.cs | 27 +++++++--- .../Abp/Localization/LocalizationResource.cs | 11 ++++ .../LocalizationResourceDictionary.cs | 10 ++++ .../Localization/LocalizationResourceList.cs | 9 ---- .../LocalizationResourceListExtensions.cs | 50 ++++++++++++++----- test/Volo.Abp.Tests/Volo.Abp.Tests.csproj | 9 +--- .../Abp/Localization/AbpLocalization_Tests.cs | 28 ++++++++++- .../Volo/Abp/Localization/Source/en.json | 2 +- .../Volo/Abp/Localization/Source/tr.json | 2 +- .../Abp/Localization/SourceExt/SourceExt.cs | 7 +++ .../Volo/Abp/Localization/SourceExt/en.json | 6 +++ .../Volo/Abp/Localization/SourceExt/it.json | 8 +++ 17 files changed, 143 insertions(+), 52 deletions(-) create mode 100644 src/Volo.Abp/Volo/Abp/Localization/LocalizationResourceDictionary.cs delete mode 100644 src/Volo.Abp/Volo/Abp/Localization/LocalizationResourceList.cs create mode 100644 test/Volo.Abp.Tests/Volo/Abp/Localization/SourceExt/SourceExt.cs create mode 100644 test/Volo.Abp.Tests/Volo/Abp/Localization/SourceExt/en.json create mode 100644 test/Volo.Abp.Tests/Volo/Abp/Localization/SourceExt/it.json diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationBuilder.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationBuilder.cs index 2b6176fbe0..58f9f41348 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationBuilder.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationBuilder.cs @@ -37,7 +37,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations var localizationConfig = new ApplicationLocalizationConfigurationDto(); - foreach (var resource in _localizationOptions.Resources) + foreach (var resource in _localizationOptions.Resources.Values) { var dictionary = new Dictionary(); diff --git a/src/Volo.Abp/Volo/Abp/Localization/AbpLocalizationOptions.cs b/src/Volo.Abp/Volo/Abp/Localization/AbpLocalizationOptions.cs index 8375a557f9..387eccff7a 100644 --- a/src/Volo.Abp/Volo/Abp/Localization/AbpLocalizationOptions.cs +++ b/src/Volo.Abp/Volo/Abp/Localization/AbpLocalizationOptions.cs @@ -2,11 +2,11 @@ { public class AbpLocalizationOptions { - public LocalizationResourceList Resources { get; } + public LocalizationResourceDictionary Resources { get; } public AbpLocalizationOptions() { - Resources = new LocalizationResourceList(); + Resources = new LocalizationResourceDictionary(); } } } diff --git a/src/Volo.Abp/Volo/Abp/Localization/AbpStringLocalizerFactory.cs b/src/Volo.Abp/Volo/Abp/Localization/AbpStringLocalizerFactory.cs index 64e617929d..481fa530e4 100644 --- a/src/Volo.Abp/Volo/Abp/Localization/AbpStringLocalizerFactory.cs +++ b/src/Volo.Abp/Volo/Abp/Localization/AbpStringLocalizerFactory.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; -using Volo.Abp.Localization.Json; namespace Volo.Abp.Localization { @@ -29,17 +29,15 @@ namespace Volo.Abp.Localization _localizerCache = new ConcurrentDictionary();; } - public virtual IStringLocalizer Create(Type resourceSource) + public virtual IStringLocalizer Create(Type resourceType) { - //TODO: Optimize! - - var localizationResource = _abpLocalizationOptions.Resources.FirstOrDefault(l => l.ResourceType == resourceSource); + var localizationResource = _abpLocalizationOptions.Resources.GetOrDefault(resourceType); if (localizationResource == null) { - return _innerFactory.Create(resourceSource); + return _innerFactory.Create(resourceType); } - return _localizerCache.GetOrAdd(resourceSource, _ => CreateAbpStringLocalizer(localizationResource)); + return _localizerCache.GetOrAdd(resourceType, _ => CreateAbpStringLocalizer(localizationResource)); } private AbpDictionaryBasedStringLocalizer CreateAbpStringLocalizer(LocalizationResource resource) diff --git a/src/Volo.Abp/Volo/Abp/Localization/ILocalizationDictionaryProvider.cs b/src/Volo.Abp/Volo/Abp/Localization/ILocalizationDictionaryProvider.cs index 8a133825db..6b63386c05 100644 --- a/src/Volo.Abp/Volo/Abp/Localization/ILocalizationDictionaryProvider.cs +++ b/src/Volo.Abp/Volo/Abp/Localization/ILocalizationDictionaryProvider.cs @@ -7,7 +7,7 @@ namespace Volo.Abp.Localization IDictionary Dictionaries { get; } void Initialize(); - - void Extend(ILocalizationDictionary dictionary); + + void Extend(ILocalizationDictionaryProvider dictionaryProvider); } } \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Localization/Json/JsonEmbeddedFileLocalizationDictionaryProvider.cs b/src/Volo.Abp/Volo/Abp/Localization/Json/JsonEmbeddedFileLocalizationDictionaryProvider.cs index 2a4cb2758f..701c39e1a7 100644 --- a/src/Volo.Abp/Volo/Abp/Localization/Json/JsonEmbeddedFileLocalizationDictionaryProvider.cs +++ b/src/Volo.Abp/Volo/Abp/Localization/Json/JsonEmbeddedFileLocalizationDictionaryProvider.cs @@ -19,10 +19,12 @@ namespace Volo.Abp.Localization.Json public override void Initialize() { + var rootNameSpaceWithDot = _rootNamespace + "."; + var resourceNames = _assembly.GetManifestResourceNames(); foreach (var resourceName in resourceNames) { - if (resourceName.StartsWith(_rootNamespace)) + if (resourceName.StartsWith(rootNameSpaceWithDot)) { using (var stream = _assembly.GetManifestResourceStream(resourceName)) { diff --git a/src/Volo.Abp/Volo/Abp/Localization/LocalizationDictionaryProviderBase.cs b/src/Volo.Abp/Volo/Abp/Localization/LocalizationDictionaryProviderBase.cs index 622d946226..f07f962e57 100644 --- a/src/Volo.Abp/Volo/Abp/Localization/LocalizationDictionaryProviderBase.cs +++ b/src/Volo.Abp/Volo/Abp/Localization/LocalizationDictionaryProviderBase.cs @@ -17,19 +17,30 @@ namespace Volo.Abp.Localization { } - public void Extend(ILocalizationDictionary dictionary) + public virtual void Extend(ILocalizationDictionaryProvider dictionaryProvider) { - //Add - ILocalizationDictionary existingDictionary; - if (!Dictionaries.TryGetValue(dictionary.CultureName, out existingDictionary)) + foreach (var dictionary in dictionaryProvider.Dictionaries.Values) + { + Extend(dictionary); + } + } + + protected virtual void Extend(ILocalizationDictionary dictionary) + { + var existingDictionary = Dictionaries.GetOrDefault(dictionary.CultureName); + if (existingDictionary == null) { Dictionaries[dictionary.CultureName] = dictionary; - return; } + else + { + Overwrite(existingDictionary, dictionary); + } + } - //Override - var localizedStrings = dictionary.GetAllStrings(); - foreach (var localizedString in localizedStrings) + protected virtual void Overwrite(ILocalizationDictionary existingDictionary, ILocalizationDictionary dictionary) + { + foreach (var localizedString in dictionary.GetAllStrings()) { existingDictionary[localizedString.Name] = localizedString; } diff --git a/src/Volo.Abp/Volo/Abp/Localization/LocalizationResource.cs b/src/Volo.Abp/Volo/Abp/Localization/LocalizationResource.cs index dd44c67166..2e2dbd6fd4 100644 --- a/src/Volo.Abp/Volo/Abp/Localization/LocalizationResource.cs +++ b/src/Volo.Abp/Volo/Abp/Localization/LocalizationResource.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using JetBrains.Annotations; namespace Volo.Abp.Localization @@ -20,6 +21,8 @@ namespace Volo.Abp.Localization } private ILocalizationDictionaryProvider _dictionaryProvider; + public List Extensions { get; } + public LocalizationResource([NotNull] Type resourceType, [NotNull] string defaultCultureName, [NotNull] ILocalizationDictionaryProvider dictionaryProvider) { Check.NotNull(resourceType, nameof(resourceType)); @@ -29,11 +32,19 @@ namespace Volo.Abp.Localization ResourceType = resourceType; DefaultCultureName = defaultCultureName; DictionaryProvider = dictionaryProvider; + + Extensions = new List(); } public virtual void Initialize(IServiceProvider serviceProvider) //TODO: Create a LocalizationResourceInitializationContext! { DictionaryProvider.Initialize(); + + foreach (var extension in Extensions) + { + extension.Initialize(); + DictionaryProvider.Extend(extension); + } } } } \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Localization/LocalizationResourceDictionary.cs b/src/Volo.Abp/Volo/Abp/Localization/LocalizationResourceDictionary.cs new file mode 100644 index 0000000000..1c378b78b6 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Localization/LocalizationResourceDictionary.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Localization +{ + public class LocalizationResourceDictionary : Dictionary + { + + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Localization/LocalizationResourceList.cs b/src/Volo.Abp/Volo/Abp/Localization/LocalizationResourceList.cs deleted file mode 100644 index 331c1946be..0000000000 --- a/src/Volo.Abp/Volo/Abp/Localization/LocalizationResourceList.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -namespace Volo.Abp.Localization -{ - public class LocalizationResourceList : List - { - - } -} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Localization/LocalizationResourceListExtensions.cs b/src/Volo.Abp/Volo/Abp/Localization/LocalizationResourceListExtensions.cs index f5166ae7f4..f02881035e 100644 --- a/src/Volo.Abp/Volo/Abp/Localization/LocalizationResourceListExtensions.cs +++ b/src/Volo.Abp/Volo/Abp/Localization/LocalizationResourceListExtensions.cs @@ -1,24 +1,50 @@ -using JetBrains.Annotations; +using System.Collections.Generic; +using JetBrains.Annotations; using Volo.Abp.Localization.Json; namespace Volo.Abp.Localization { public static class LocalizationResourceListExtensions { - public static void AddJson(this LocalizationResourceList resourceList, [NotNull] string defaultCultureName) + public static void AddJson([NotNull] this LocalizationResourceDictionary resourceDictionary, [NotNull] string defaultCultureName) { - var type = typeof(TResource); - - resourceList.Add( - new LocalizationResource( - type, - defaultCultureName, - new JsonEmbeddedFileLocalizationDictionaryProvider( - type.Assembly, - type.Namespace - ) + Check.NotNull(resourceDictionary, nameof(resourceDictionary)); + Check.NotNull(defaultCultureName, nameof(defaultCultureName)); + + var resourceType = typeof(TResource); + + if (resourceDictionary.ContainsKey(resourceType)) + { + throw new AbpException("There is already a resource with given type: " + resourceType.AssemblyQualifiedName); + } + + resourceDictionary[resourceType] = new LocalizationResource( + resourceType, + defaultCultureName, + new JsonEmbeddedFileLocalizationDictionaryProvider( + resourceType.Assembly, + resourceType.Namespace ) ); } + + public static void ExtendWithJson([NotNull] this LocalizationResourceDictionary resourceDictionary) + { + Check.NotNull(resourceDictionary, nameof(resourceDictionary)); + + var resourceType = typeof(TResource); + var resourceExtType = typeof(TResourceExt); + + var resource = resourceDictionary.GetOrDefault(resourceType); + if (resource == null) + { + throw new AbpException("Can not find a resource with given type: " + resourceType.AssemblyQualifiedName); + } + + resource.Extensions.Add(new JsonEmbeddedFileLocalizationDictionaryProvider( + resourceExtType.Assembly, + resourceExtType.Namespace + )); + } } } \ No newline at end of file diff --git a/test/Volo.Abp.Tests/Volo.Abp.Tests.csproj b/test/Volo.Abp.Tests/Volo.Abp.Tests.csproj index 851d4bdcbb..693dc2a605 100644 --- a/test/Volo.Abp.Tests/Volo.Abp.Tests.csproj +++ b/test/Volo.Abp.Tests/Volo.Abp.Tests.csproj @@ -12,13 +12,8 @@ - - - - - - - + + diff --git a/test/Volo.Abp.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs b/test/Volo.Abp.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs index 88346e2521..3697b6aa17 100644 --- a/test/Volo.Abp.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs +++ b/test/Volo.Abp.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Shouldly; using Volo.Abp.Localization.Source; +using Volo.Abp.Localization.SourceExt; using Volo.Abp.Modularity; using Volo.Abp.TestBase; using Xunit; @@ -40,6 +41,30 @@ namespace Volo.Abp.Localization _localizer["Car"].Value.ShouldBe("Araba"); _localizer["CarPlural"].Value.ShouldBe("Araba"); } + + using (AbpCultureHelper.Use("it")) + { + _localizer["Car"].Value.ShouldBe("Auto"); + } + } + + [Fact] + public void Should_Get_Extension_Texts() + { + using (AbpCultureHelper.Use("en")) + { + _localizer["SeeYou"].Value.ShouldBe("See you"); + } + + using (AbpCultureHelper.Use("tr")) + { + _localizer["SeeYou"].Value.ShouldBe("See you"); //Not defined in tr + } + + using (AbpCultureHelper.Use("it")) + { + _localizer["SeeYou"].Value.ShouldBe("Ci vediamo"); + } } [Fact] @@ -48,7 +73,7 @@ namespace Volo.Abp.Localization _localizer.WithCulture(CultureInfo.GetCultureInfo("en"))["Car"].Value.ShouldBe("Car"); _localizer.WithCulture(CultureInfo.GetCultureInfo("en"))["CarPlural"].Value.ShouldBe("Cars"); - _localizer.WithCulture(CultureInfo.GetCultureInfo("tr"))["CarPlural"].Value.ShouldBe("Araba"); + _localizer.WithCulture(CultureInfo.GetCultureInfo("tr"))["Car"].Value.ShouldBe("Araba"); _localizer.WithCulture(CultureInfo.GetCultureInfo("tr"))["CarPlural"].Value.ShouldBe("Araba"); } @@ -60,6 +85,7 @@ namespace Volo.Abp.Localization services.Configure(options => { options.Resources.AddJson("en"); + options.Resources.ExtendWithJson(); }); } } diff --git a/test/Volo.Abp.Tests/Volo/Abp/Localization/Source/en.json b/test/Volo.Abp.Tests/Volo/Abp/Localization/Source/en.json index a94360c161..988c2f6278 100644 --- a/test/Volo.Abp.Tests/Volo/Abp/Localization/Source/en.json +++ b/test/Volo.Abp.Tests/Volo/Abp/Localization/Source/en.json @@ -1,7 +1,7 @@ { "culture": "en", "texts": { - "Hello {0}.": "Hello {0}", + "Hello {0}.": "Hello {0}.", "Car": "Car", "CarPlural": "Cars" } diff --git a/test/Volo.Abp.Tests/Volo/Abp/Localization/Source/tr.json b/test/Volo.Abp.Tests/Volo/Abp/Localization/Source/tr.json index caf947e79e..ff92ed51bd 100644 --- a/test/Volo.Abp.Tests/Volo/Abp/Localization/Source/tr.json +++ b/test/Volo.Abp.Tests/Volo/Abp/Localization/Source/tr.json @@ -1,7 +1,7 @@ { "culture": "tr", "texts": { - "Hello {0}.": "Merhaba {0}", + "Hello {0}.": "Merhaba {0}.", "Car": "Araba", "CarPlural": "Araba" } diff --git a/test/Volo.Abp.Tests/Volo/Abp/Localization/SourceExt/SourceExt.cs b/test/Volo.Abp.Tests/Volo/Abp/Localization/SourceExt/SourceExt.cs new file mode 100644 index 0000000000..0e7f2b8087 --- /dev/null +++ b/test/Volo.Abp.Tests/Volo/Abp/Localization/SourceExt/SourceExt.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Localization.SourceExt +{ + internal sealed class LocalizationTestResourceExt + { + + } +} diff --git a/test/Volo.Abp.Tests/Volo/Abp/Localization/SourceExt/en.json b/test/Volo.Abp.Tests/Volo/Abp/Localization/SourceExt/en.json new file mode 100644 index 0000000000..bcf2a995df --- /dev/null +++ b/test/Volo.Abp.Tests/Volo/Abp/Localization/SourceExt/en.json @@ -0,0 +1,6 @@ +{ + "culture": "en", + "texts": { + "SeeYou": "See you" + } +} \ No newline at end of file diff --git a/test/Volo.Abp.Tests/Volo/Abp/Localization/SourceExt/it.json b/test/Volo.Abp.Tests/Volo/Abp/Localization/SourceExt/it.json new file mode 100644 index 0000000000..38d5f1ff1f --- /dev/null +++ b/test/Volo.Abp.Tests/Volo/Abp/Localization/SourceExt/it.json @@ -0,0 +1,8 @@ +{ + "culture": "it", + "texts": { + "Hello {0}.": "Ciao {0}.", + "Car": "Auto", + "SeeYou": "Ci vediamo" + } +} \ No newline at end of file