Browse Source

Introduce async version of StringLocalizerFactory.CreateByResourceNameOrNull.

pull/13845/head
Halil İbrahim Kalkan 4 years ago
parent
commit
ccd13c95d5
  1. 9
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/AbpPageToolbarButtonViewComponent.cs
  2. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
  3. 13
      framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor.cs
  4. 31
      framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/AbpStringLocalizerFactoryExtensions.cs
  5. 6
      framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/IAbpStringLocalizerFactory.cs
  6. 9
      framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/IAsyncLocalizableString.cs
  7. 2
      framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/ILocalizableString.cs
  8. 45
      framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs
  9. 19
      framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableStringExtensions.cs
  10. 143
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerFactory.cs
  11. 3
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/External/IExternalLocalizationStore.cs
  12. 5
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/External/NullExternalLocalizationStore.cs
  13. 22
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpStringLocalizerFactory_Tests.cs

9
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/AbpPageToolbarButtonViewComponent.cs

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button;
@ -15,7 +16,7 @@ public class AbpPageToolbarButtonViewComponent : AbpViewComponent
StringLocalizerFactory = stringLocalizerFactory; StringLocalizerFactory = stringLocalizerFactory;
} }
public IViewComponentResult Invoke( public async Task<IViewComponentResult> InvokeAsync(
ILocalizableString text, ILocalizableString text,
string name, string name,
string icon, string icon,
@ -31,11 +32,11 @@ public class AbpPageToolbarButtonViewComponent : AbpViewComponent
return View( return View(
"~/Pages/Shared/Components/AbpPageToolbar/Button/Default.cshtml", "~/Pages/Shared/Components/AbpPageToolbar/Button/Default.cshtml",
new AbpPageToolbarButtonViewModel( new AbpPageToolbarButtonViewModel(
text.Localize(StringLocalizerFactory), await text.LocalizeAsync(StringLocalizerFactory),
name, name,
icon, icon,
id, id,
busyText?.Localize(StringLocalizerFactory), busyText == null ? null : await busyText.LocalizeAsync(StringLocalizerFactory),
iconType, iconType,
type, type,
size, size,

2
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs

@ -229,7 +229,7 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
{ {
var dictionary = new Dictionary<string, string>(); var dictionary = new Dictionary<string, string>();
var localizer = StringLocalizerFactory.CreateByResourceNameOrNull(resourceName); var localizer = await StringLocalizerFactory.CreateByResourceNameOrNullAsync(resourceName);
if (localizer != null) if (localizer != null)
{ {
foreach (var localizedString in await localizer.GetAllStringsAsync()) foreach (var localizedString in await localizer.GetAllStringsAsync())

13
framework/src/Volo.Abp.BlazoriseUI/Components/UiNotificationAlert.razor.cs

@ -5,6 +5,7 @@ using Blazorise.Snackbar;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Volo.Abp.AspNetCore.Components.Notifications; using Volo.Abp.AspNetCore.Components.Notifications;
using Volo.Abp.Localization;
namespace Volo.Abp.BlazoriseUI.Components; namespace Volo.Abp.BlazoriseUI.Components;
@ -54,13 +55,15 @@ public partial class UiNotificationAlert : ComponentBase, IDisposable
Title = e.Title; Title = e.Title;
Options = e.Options; Options = e.Options;
var okButtonText = Options?.OkButtonText?.Localize(StringLocalizerFactory); var okButtonText = Options?.OkButtonText == null
? null
: await Options.OkButtonText.LocalizeAsync(StringLocalizerFactory);
await SnackbarStack.PushAsync(Message, Title, GetSnackbarColor(e.NotificationType), (options) => await SnackbarStack.PushAsync(Message, Title, GetSnackbarColor(e.NotificationType), (options) =>
{ {
options.CloseButtonIcon = IconName.Times; options.CloseButtonIcon = IconName.Times;
options.ActionButtonText = okButtonText; options.ActionButtonText = okButtonText;
}); });
} }
public virtual void Dispose() public virtual void Dispose()

31
framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/AbpStringLocalizerFactoryExtensions.cs

@ -1,4 +1,5 @@
using JetBrains.Annotations; using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp; using Volo.Abp;
namespace Microsoft.Extensions.Localization; namespace Microsoft.Extensions.Localization;
@ -34,4 +35,32 @@ public static class AbpStringLocalizerFactoryExtensions
return localizer; return localizer;
} }
[ItemCanBeNull]
public static async Task<IStringLocalizer> CreateByResourceNameOrNullAsync(
this IStringLocalizerFactory localizerFactory,
string resourceName)
{
var abpLocalizerFactory = localizerFactory as IAbpStringLocalizerFactory;
if (abpLocalizerFactory == null)
{
return null;
}
return await abpLocalizerFactory.CreateByResourceNameOrNullAsync(resourceName);
}
[NotNull]
public async static Task<IStringLocalizer> CreateByResourceNameAsync(
this IStringLocalizerFactory localizerFactory,
string resourceName)
{
var localizer = await localizerFactory.CreateByResourceNameOrNullAsync(resourceName);
if (localizer == null)
{
throw new AbpException("Couldn't find a localizer with given resource name: " + resourceName);
}
return localizer;
}
} }

6
framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/IAbpStringLocalizerFactory.cs

@ -1,4 +1,5 @@
using JetBrains.Annotations; using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Microsoft.Extensions.Localization; namespace Microsoft.Extensions.Localization;
@ -9,4 +10,7 @@ public interface IAbpStringLocalizerFactory
[CanBeNull] [CanBeNull]
IStringLocalizer CreateByResourceNameOrNull([NotNull] string resourceName); IStringLocalizer CreateByResourceNameOrNull([NotNull] string resourceName);
[ItemCanBeNull]
Task<IStringLocalizer> CreateByResourceNameOrNullAsync([NotNull] string resourceName);
} }

9
framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/IAsyncLocalizableString.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Localization;
namespace Volo.Abp.Localization;
public interface IAsyncLocalizableString
{
Task<LocalizedString> LocalizeAsync(IStringLocalizerFactory stringLocalizerFactory);
}

2
framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/ILocalizableString.cs

@ -5,4 +5,4 @@ namespace Volo.Abp.Localization;
public interface ILocalizableString public interface ILocalizableString
{ {
LocalizedString Localize(IStringLocalizerFactory stringLocalizerFactory); LocalizedString Localize(IStringLocalizerFactory stringLocalizerFactory);
} }

45
framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs

@ -1,10 +1,11 @@
using System; using System;
using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
namespace Volo.Abp.Localization; namespace Volo.Abp.Localization;
public class LocalizableString : ILocalizableString public class LocalizableString : ILocalizableString, IAsyncLocalizableString
{ {
[CanBeNull] [CanBeNull]
public string ResourceName { get; } public string ResourceName { get; }
@ -54,6 +55,29 @@ public class LocalizableString : ILocalizableString
return result; return result;
} }
public async Task<LocalizedString> LocalizeAsync(IStringLocalizerFactory stringLocalizerFactory)
{
var localizer = await CreateStringLocalizerOrNullAsync(stringLocalizerFactory);
if (localizer == null)
{
throw new AbpException($"Set {nameof(ResourceName)} or configure the default localization resource type (in the AbpLocalizationOptions)!");
}
var result = localizer[Name];
if (result.ResourceNotFound && ResourceName != null)
{
/* Search in the default resource if not found in the provided resource */
localizer = stringLocalizerFactory.CreateDefaultOrNull();
if (localizer != null)
{
result = localizer[Name];
}
}
return result;
}
private IStringLocalizer CreateStringLocalizerOrNull(IStringLocalizerFactory stringLocalizerFactory) private IStringLocalizer CreateStringLocalizerOrNull(IStringLocalizerFactory stringLocalizerFactory)
{ {
@ -73,6 +97,25 @@ public class LocalizableString : ILocalizableString
return stringLocalizerFactory.CreateDefaultOrNull(); return stringLocalizerFactory.CreateDefaultOrNull();
} }
private async Task<IStringLocalizer> CreateStringLocalizerOrNullAsync(IStringLocalizerFactory stringLocalizerFactory)
{
if (ResourceType != null)
{
return stringLocalizerFactory.Create(ResourceType);
}
if (ResourceName != null)
{
var localizerByName = await stringLocalizerFactory.CreateByResourceNameOrNullAsync(ResourceName);
if (localizerByName != null)
{
return localizerByName;
}
}
return stringLocalizerFactory.CreateDefaultOrNull();
}
public static LocalizableString Create<TResource>([NotNull] string name) public static LocalizableString Create<TResource>([NotNull] string name)
{ {

19
framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableStringExtensions.cs

@ -0,0 +1,19 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Localization;
namespace Volo.Abp.Localization;
public static class LocalizableStringExtensions
{
public static async Task<LocalizedString> LocalizeAsync(
this ILocalizableString localizableString,
IStringLocalizerFactory stringLocalizerFactory)
{
if (localizableString is IAsyncLocalizableString asyncLocalizableString)
{
return await asyncLocalizableString.LocalizeAsync(stringLocalizerFactory);
}
return localizableString.Localize(stringLocalizerFactory);
}
}

143
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerFactory.cs

@ -2,11 +2,14 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.Localization.External; using Volo.Abp.Localization.External;
using Volo.Abp.Threading;
namespace Volo.Abp.Localization; namespace Volo.Abp.Localization;
@ -17,6 +20,7 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca
protected IServiceProvider ServiceProvider { get; } protected IServiceProvider ServiceProvider { get; }
protected IExternalLocalizationStore ExternalLocalizationStore { get; } protected IExternalLocalizationStore ExternalLocalizationStore { get; }
protected ConcurrentDictionary<string, StringLocalizerCacheItem> LocalizerCache { get; } protected ConcurrentDictionary<string, StringLocalizerCacheItem> LocalizerCache { get; }
protected SemaphoreSlim LocalizerCacheSemaphore { get; } = new(1, 1);
public AbpStringLocalizerFactory( public AbpStringLocalizerFactory(
ResourceManagerStringLocalizerFactory innerFactory, ResourceManagerStringLocalizerFactory innerFactory,
@ -33,6 +37,11 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca
} }
public virtual IStringLocalizer Create(Type resourceType) public virtual IStringLocalizer Create(Type resourceType)
{
return Create(resourceType, lockCache: true);
}
private IStringLocalizer Create(Type resourceType, bool lockCache)
{ {
var resource = AbpLocalizationOptions.Resources.GetOrNull(resourceType); var resource = AbpLocalizationOptions.Resources.GetOrNull(resourceType);
if (resource == null) if (resource == null)
@ -40,10 +49,17 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca
return InnerFactory.Create(resourceType); return InnerFactory.Create(resourceType);
} }
return CreateInternal(resource.ResourceName, resource); return CreateInternal(resource.ResourceName, resource, lockCache);
} }
public IStringLocalizer CreateByResourceNameOrNull(string resourceName) public IStringLocalizer CreateByResourceNameOrNull(string resourceName)
{
return CreateByResourceNameOrNullInternal(resourceName, lockCache: true);
}
private IStringLocalizer CreateByResourceNameOrNullInternal(
string resourceName,
bool lockCache)
{ {
var resource = AbpLocalizationOptions.Resources.GetOrDefault(resourceName); var resource = AbpLocalizationOptions.Resources.GetOrDefault(resourceName);
if (resource == null) if (resource == null)
@ -55,23 +71,102 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca
} }
} }
return CreateInternal(resourceName, resource); return CreateInternal(resourceName, resource, lockCache);
}
public Task<IStringLocalizer> CreateByResourceNameOrNullAsync(string resourceName)
{
return CreateByResourceNameOrNullInternalAsync(resourceName, lockCache: true);
} }
private IStringLocalizer CreateInternal(string resourceName, LocalizationResourceBase resource) private async Task<IStringLocalizer> CreateByResourceNameOrNullInternalAsync(
string resourceName,
bool lockCache)
{
var resource = AbpLocalizationOptions.Resources.GetOrDefault(resourceName);
if (resource == null)
{
resource = await ExternalLocalizationStore.GetResourceOrNullAsync(resourceName);
if (resource == null)
{
return null;
}
}
return await CreateInternalAsync(resourceName, resource, lockCache);
}
private IStringLocalizer CreateInternal(
string resourceName,
LocalizationResourceBase resource,
bool lockCache)
{ {
if (LocalizerCache.TryGetValue(resourceName, out var cacheItem)) if (LocalizerCache.TryGetValue(resourceName, out var cacheItem))
{ {
return cacheItem.Localizer; return cacheItem.Localizer;
} }
lock (LocalizerCache) IStringLocalizer GetOrCreateLocalizer()
{ {
// Double check
if (LocalizerCache.TryGetValue(resourceName, out var cacheItem2))
{
return cacheItem2.Localizer;
}
return LocalizerCache.GetOrAdd( return LocalizerCache.GetOrAdd(
resourceName, resourceName,
_ => CreateStringLocalizerCacheItem(resource) _ => CreateStringLocalizerCacheItem(resource)
).Localizer; ).Localizer;
} }
if (lockCache)
{
using (LocalizerCacheSemaphore.Lock())
{
return GetOrCreateLocalizer();
}
}
else
{
return GetOrCreateLocalizer();
}
}
private async Task<IStringLocalizer> CreateInternalAsync(
string resourceName,
LocalizationResourceBase resource,
bool lockCache)
{
if (LocalizerCache.TryGetValue(resourceName, out var cacheItem))
{
return cacheItem.Localizer;
}
async Task<IStringLocalizer> GetOrCreateLocalizerAsync()
{
// Double check
if (LocalizerCache.TryGetValue(resourceName, out var cacheItem2))
{
return cacheItem2.Localizer;
}
var newCacheItem = await CreateStringLocalizerCacheItemAsync(resource);
LocalizerCache[resourceName] = newCacheItem;
return newCacheItem.Localizer;
}
if (lockCache)
{
using (await LocalizerCacheSemaphore.LockAsync())
{
return await GetOrCreateLocalizerAsync();
}
}
else
{
return await GetOrCreateLocalizerAsync();
}
} }
private StringLocalizerCacheItem CreateStringLocalizerCacheItem(LocalizationResourceBase resource) private StringLocalizerCacheItem CreateStringLocalizerCacheItem(LocalizationResourceBase resource)
@ -97,13 +192,51 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca
resource, resource,
resource resource
.BaseResourceNames .BaseResourceNames
.Select(CreateByResourceNameOrNull) .Select(x => CreateByResourceNameOrNullInternal(x, lockCache: false))
.Where(x => x != null) .Where(x => x != null)
.ToList(), .ToList(),
AbpLocalizationOptions AbpLocalizationOptions
) )
); );
} }
private async Task<StringLocalizerCacheItem> CreateStringLocalizerCacheItemAsync(LocalizationResourceBase resource)
{
foreach (var globalContributorType in AbpLocalizationOptions.GlobalContributors)
{
resource.Contributors.Add(
Activator
.CreateInstance(globalContributorType)
.As<ILocalizationResourceContributor>()
);
}
var context = new LocalizationResourceInitializationContext(resource, ServiceProvider);
foreach (var contributor in resource.Contributors)
{
contributor.Initialize(context);
}
var baseLocalizers = new List<IStringLocalizer>();
foreach (var baseResourceName in resource.BaseResourceNames)
{
var baseLocalizer = await CreateByResourceNameOrNullInternalAsync(baseResourceName, lockCache: false);
if (baseLocalizer != null)
{
baseLocalizers.Add(baseLocalizer);
}
}
return new StringLocalizerCacheItem(
new AbpDictionaryBasedStringLocalizer(
resource,
baseLocalizers,
AbpLocalizationOptions
)
);
}
public virtual IStringLocalizer Create(string baseName, string location) public virtual IStringLocalizer Create(string baseName, string location)
{ {

3
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/External/IExternalLocalizationStore.cs

@ -8,5 +8,8 @@ public interface IExternalLocalizationStore
[CanBeNull] [CanBeNull]
LocalizationResourceBase GetResourceOrNull([NotNull] string resourceName); LocalizationResourceBase GetResourceOrNull([NotNull] string resourceName);
[ItemCanBeNull]
Task<LocalizationResourceBase> GetResourceOrNullAsync([NotNull] string resourceName);
Task<string[]> GetResourceNamesAsync(); Task<string[]> GetResourceNamesAsync();
} }

5
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/External/NullExternalLocalizationStore.cs

@ -11,6 +11,11 @@ public class NullExternalLocalizationStore : IExternalLocalizationStore, ISingle
return null; return null;
} }
public Task<LocalizationResourceBase> GetResourceOrNullAsync(string resourceName)
{
return Task.FromResult<LocalizationResourceBase>(null);
}
public Task<string[]> GetResourceNamesAsync() public Task<string[]> GetResourceNamesAsync()
{ {
return Task.FromResult(Array.Empty<string>()); return Task.FromResult(Array.Empty<string>());

22
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpStringLocalizerFactory_Tests.cs

@ -1,4 +1,5 @@
using Microsoft.Extensions.Localization; using System.Threading.Tasks;
using Microsoft.Extensions.Localization;
using Shouldly; using Shouldly;
using Volo.Abp.DynamicProxy; using Volo.Abp.DynamicProxy;
using Volo.Abp.Testing; using Volo.Abp.Testing;
@ -32,6 +33,17 @@ public class AbpStringLocalizerFactory_Tests : AbpIntegratedTest<AbpLocalization
} }
} }
[Fact]
public async Task Should_Create_Resource_By_Name_Async()
{
using (CultureHelper.Use("en"))
{
var localizer = await _factory.CreateByResourceNameOrNullAsync("Test");
localizer.ShouldNotBeNull();
localizer["CarPlural"].Value.ShouldBe("Cars");
}
}
[Fact] [Fact]
public void Should_Throw_Exception_For_Unknown_Resource_Names() public void Should_Throw_Exception_For_Unknown_Resource_Names()
{ {
@ -39,4 +51,12 @@ public class AbpStringLocalizerFactory_Tests : AbpIntegratedTest<AbpLocalization
() => _factory.CreateByResourceName("UnknownResourceName") () => _factory.CreateByResourceName("UnknownResourceName")
); );
} }
[Fact]
public async Task Should_Throw_Exception_For_Unknown_Resource_Names_Async()
{
await Assert.ThrowsAsync<AbpException>(
async () => await _factory.CreateByResourceNameAsync("UnknownResourceName")
);
}
} }
Loading…
Cancel
Save