Browse Source

Allow to work with non-typed localization resources

pull/13845/head
Halil İbrahim Kalkan 4 years ago
parent
commit
560ba6fce3
  1. 13
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
  2. 4
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLocalizationScriptController.cs
  3. 2
      framework/src/Volo.Abp.Core/System/AbpObjectExtensions.cs
  4. 6
      framework/src/Volo.Abp.Localization.Abstractions/Microsoft/Extensions/Localization/AbpStringLocalizerFactoryExtensions.cs
  5. 54
      framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs
  6. 5
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs
  7. 41
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerFactory.cs
  8. 9
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/IExternalLocalizationStore.cs
  9. 12
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableStringSerializer.cs
  10. 28
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResource.cs
  11. 25
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceBase.cs
  12. 5
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs
  13. 30
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceExtensions.cs
  14. 11
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/NullExternalLocalizationStore.cs
  15. 10
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationExtensions.cs
  16. 1
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpStringLocalizerFactory_Tests.cs
  17. 2
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/LocalizableStringSerializer_Tests.cs

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

@ -218,14 +218,13 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
foreach (var resource in _localizationOptions.Resources.Values)
{
var dictionary = new Dictionary<string, string>();
var localizer = (IStringLocalizer) _serviceProvider.GetRequiredService(
typeof(IStringLocalizer<>).MakeGenericType(resource.ResourceType)
);
foreach (var localizedString in localizer.GetAllStrings())
var localizer = StringLocalizerFactory.CreateByResourceNameOrNull(resource.ResourceName);
if (localizer != null)
{
dictionary[localizedString.Name] = localizedString.Value;
foreach (var localizedString in localizer.GetAllStrings())
{
dictionary[localizedString.Name] = localizedString.Value;
}
}
localizationConfig.Values[resource.ResourceName] = dictionary;

4
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLocalizationScriptController.cs

@ -34,8 +34,8 @@ public class AbpLocalizationScriptController
{
foreach (var resource in LocalizationOptions.Resources.Values)
{
var localizer = StringLocalizerFactory.Create(resource.ResourceType);
localizer.GetAllStrings();
var localizer = StringLocalizerFactory.CreateByResourceNameOrNull(resource.ResourceName);
localizer?.GetAllStrings();
}
}
}

2
framework/src/Volo.Abp.Core/System/AbpObjectExtensions.cs

@ -3,6 +3,7 @@ using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace System;
@ -17,6 +18,7 @@ public static class AbpObjectExtensions
/// <typeparam name="T">Type to be casted</typeparam>
/// <param name="obj">Object to cast</param>
/// <returns>Casted object</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T As<T>(this object obj)
where T : class
{

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

@ -1,15 +1,18 @@
using Volo.Abp;
using JetBrains.Annotations;
using Volo.Abp;
namespace Microsoft.Extensions.Localization;
public static class AbpStringLocalizerFactoryExtensions
{
[CanBeNull]
public static IStringLocalizer CreateDefaultOrNull(this IStringLocalizerFactory localizerFactory)
{
return (localizerFactory as IAbpStringLocalizerFactory)
?.CreateDefaultOrNull();
}
[CanBeNull]
public static IStringLocalizer CreateByResourceNameOrNull(
this IStringLocalizerFactory localizerFactory,
string resourceName)
@ -18,6 +21,7 @@ public static class AbpStringLocalizerFactoryExtensions
?.CreateByResourceNameOrNull(resourceName);
}
[NotNull]
public static IStringLocalizer CreateByResourceName(
this IStringLocalizerFactory localizerFactory,
string resourceName)

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

@ -6,6 +6,9 @@ namespace Volo.Abp.Localization;
public class LocalizableString : ILocalizableString
{
[CanBeNull]
public string ResourceName { get; }
[CanBeNull]
public Type ResourceType { get; }
@ -16,22 +19,30 @@ public class LocalizableString : ILocalizableString
{
Name = Check.NotNullOrEmpty(name, nameof(name));
ResourceType = resourceType;
if (resourceType != null)
{
ResourceName = LocalizationResourceNameAttribute.GetName(resourceType);
}
}
public LocalizedString Localize(IStringLocalizerFactory stringLocalizerFactory)
public LocalizableString([NotNull] string name, [CanBeNull] string resourceName = null)
{
var localizer = ResourceType != null
? stringLocalizerFactory.Create(ResourceType)
: stringLocalizerFactory.CreateDefaultOrNull();
Name = Check.NotNullOrEmpty(name, nameof(name));
ResourceName = resourceName;
}
public LocalizedString Localize(IStringLocalizerFactory stringLocalizerFactory)
{
var localizer = CreateStringLocalizerOrNull(stringLocalizerFactory);
if (localizer == null)
{
throw new AbpException($"Set {nameof(ResourceType)} or configure the default localization resource type (in the AbpLocalizationOptions)!");
throw new AbpException($"Set {nameof(ResourceName)} or configure the default localization resource type (in the AbpLocalizationOptions)!");
}
var result = localizer[Name];
if (result.ResourceNotFound && ResourceType != null)
if (result.ResourceNotFound && ResourceName != null)
{
/* Search in the default resource if not found in the provided resource */
localizer = stringLocalizerFactory.CreateDefaultOrNull();
@ -44,8 +55,37 @@ public class LocalizableString : ILocalizableString
return result;
}
private IStringLocalizer CreateStringLocalizerOrNull(IStringLocalizerFactory stringLocalizerFactory)
{
if (ResourceType != null)
{
return stringLocalizerFactory.Create(ResourceType);
}
if (ResourceName != null)
{
var localizerByName = stringLocalizerFactory.CreateByResourceNameOrNull(ResourceName);
if (localizerByName != null)
{
return localizerByName;
}
}
return stringLocalizerFactory.CreateDefaultOrNull();
}
public static LocalizableString Create<TResource>([NotNull] string name)
{
return new LocalizableString(typeof(TResource), name);
return Create(typeof(TResource), name);
}
public static LocalizableString Create(Type resourceType,[NotNull] string name)
{
return new LocalizableString(resourceType, name);
}
public static LocalizableString Create([NotNull] string name, [CanBeNull] string resourceName = null)
{
return new LocalizableString(name, resourceName);
}
}

5
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs

@ -20,7 +20,10 @@ public class AbpDictionaryBasedStringLocalizer : IStringLocalizer, IStringLocali
public virtual LocalizedString this[string name, params object[] arguments] => GetLocalizedStringFormatted(name, arguments);
public AbpDictionaryBasedStringLocalizer(LocalizationResource resource, List<IStringLocalizer> baseLocalizers, AbpLocalizationOptions abpLocalizationOptions)
public AbpDictionaryBasedStringLocalizer(
LocalizationResource resource,
List<IStringLocalizer> baseLocalizers,
AbpLocalizationOptions abpLocalizationOptions)
{
Resource = resource;
BaseLocalizers = baseLocalizers;

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

@ -14,19 +14,22 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca
protected internal AbpLocalizationOptions AbpLocalizationOptions { get; }
protected ResourceManagerStringLocalizerFactory InnerFactory { get; }
protected IServiceProvider ServiceProvider { get; }
protected ConcurrentDictionary<Type, StringLocalizerCacheItem> LocalizerCache { get; }
protected IExternalLocalizationStore ExternalLocalizationStore { get; }
protected ConcurrentDictionary<string, StringLocalizerCacheItem> LocalizerCache { get; }
//TODO: It's better to use decorator pattern for IStringLocalizerFactory instead of getting ResourceManagerStringLocalizerFactory as a dependency.
public AbpStringLocalizerFactory(
ResourceManagerStringLocalizerFactory innerFactory,
IOptions<AbpLocalizationOptions> abpLocalizationOptions,
IServiceProvider serviceProvider)
IServiceProvider serviceProvider,
IExternalLocalizationStore externalLocalizationStore)
{
InnerFactory = innerFactory;
ServiceProvider = serviceProvider;
ExternalLocalizationStore = externalLocalizationStore;
AbpLocalizationOptions = abpLocalizationOptions.Value;
LocalizerCache = new ConcurrentDictionary<Type, StringLocalizerCacheItem>();
LocalizerCache = new ConcurrentDictionary<string, StringLocalizerCacheItem>();
}
public virtual IStringLocalizer Create(Type resourceType)
@ -37,7 +40,7 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca
return InnerFactory.Create(resourceType);
}
return CreateInternal(resourceType, resource);
return CreateInternal(resource.ResourceName, resource);
}
public IStringLocalizer CreateByResourceNameOrNull(string resourceName)
@ -45,15 +48,19 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca
var resource = AbpLocalizationOptions.Resources.GetOrNull(resourceName);
if (resource == null)
{
return null;
resource = ExternalLocalizationStore.GetResourceOrNull(resourceName);
if (resource == null)
{
return null;
}
}
return CreateInternal(resource.ResourceType, resource);
return CreateInternal(resourceName, resource);
}
private IStringLocalizer CreateInternal(Type resourceType, LocalizationResource resource)
private IStringLocalizer CreateInternal(string resourceName, LocalizationResource resource)
{
if (LocalizerCache.TryGetValue(resourceType, out var cacheItem))
if (LocalizerCache.TryGetValue(resourceName, out var cacheItem))
{
return cacheItem.Localizer;
}
@ -61,7 +68,7 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca
lock (LocalizerCache)
{
return LocalizerCache.GetOrAdd(
resourceType,
resourceName,
_ => CreateStringLocalizerCacheItem(resource)
).Localizer;
}
@ -69,9 +76,13 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca
private StringLocalizerCacheItem CreateStringLocalizerCacheItem(LocalizationResource resource)
{
foreach (var globalContributor in AbpLocalizationOptions.GlobalContributors)
foreach (var globalContributorType in AbpLocalizationOptions.GlobalContributors)
{
resource.Contributors.Add((ILocalizationResourceContributor)Activator.CreateInstance(globalContributor));
resource.Contributors.Add(
Activator
.CreateInstance(globalContributorType)
.As<ILocalizationResourceContributor>()
);
}
var context = new LocalizationResourceInitializationContext(resource, ServiceProvider);
@ -84,7 +95,11 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca
return new StringLocalizerCacheItem(
new AbpDictionaryBasedStringLocalizer(
resource,
resource.BaseResourceTypes.Select(Create).ToList(),
resource
.BaseResourceNames
.Select(CreateByResourceNameOrNull)
.Where(x => x != null)
.ToList(),
AbpLocalizationOptions
)
);
@ -122,4 +137,4 @@ public class AbpStringLocalizerFactory : IStringLocalizerFactory, IAbpStringLoca
return Create(AbpLocalizationOptions.DefaultResourceType);
}
}
}

9
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/IExternalLocalizationStore.cs

@ -0,0 +1,9 @@
using JetBrains.Annotations;
namespace Volo.Abp.Localization;
public interface IExternalLocalizationStore
{
[CanBeNull]
LocalizationResource GetResourceOrNull([NotNull] string resourceName);
}

12
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableStringSerializer.cs

@ -55,13 +55,13 @@ public class LocalizableStringSerializer : ILocalizableStringSerializer, ITransi
{
throw new AbpException("Invalid LocalizableString value: " + value);
}
if (!LocalizationOptions.Resources.ContainsResource(resourceName))
{
resourceName = null;
}
var resourceType = LocalizationOptions.Resources.GetOrNull(resourceName)?.ResourceType;
return new LocalizableString(
resourceType,
name
);
return LocalizableString.Create(name, resourceName);
default:
return new FixedLocalizableString(value);
}

28
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResource.cs

@ -5,40 +5,26 @@ using JetBrains.Annotations;
namespace Volo.Abp.Localization;
public class LocalizationResource
public class LocalizationResource : LocalizationResourceBase
{
[NotNull]
public Type ResourceType { get; }
[NotNull]
public string ResourceName => LocalizationResourceNameAttribute.GetName(ResourceType);
[CanBeNull]
public string DefaultCultureName { get; set; }
[NotNull]
public LocalizationResourceContributorList Contributors { get; }
[NotNull]
public List<Type> BaseResourceTypes { get; }
private Type ResourceType { get; }
public LocalizationResource(
[NotNull] Type resourceType,
[CanBeNull] string defaultCultureName = null,
[CanBeNull] ILocalizationResourceContributor initialContributor = null)
: base(LocalizationResourceNameAttribute.GetName(resourceType))
{
ResourceType = Check.NotNull(resourceType, nameof(resourceType));
DefaultCultureName = defaultCultureName;
BaseResourceTypes = new List<Type>();
Contributors = new LocalizationResourceContributorList();
AddBaseResourceTypes();
if (initialContributor != null)
{
Contributors.Add(initialContributor);
}
AddBaseResourceTypes();
}
protected virtual void AddBaseResourceTypes()
@ -51,8 +37,8 @@ public class LocalizationResource
{
foreach (var baseResourceType in descriptor.GetInheritedResourceTypes())
{
BaseResourceTypes.AddIfNotContains(baseResourceType);
BaseResourceNames.AddIfNotContains(LocalizationResourceNameAttribute.GetName(baseResourceType));
}
}
}
}
}

25
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceBase.cs

@ -0,0 +1,25 @@
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.Localization;
public abstract class LocalizationResourceBase
{
[NotNull]
public string ResourceName { get; }
public List<string> BaseResourceNames { get; }
[CanBeNull]
public string DefaultCultureName { get; set; }
[NotNull]
public LocalizationResourceContributorList Contributors { get; }
public LocalizationResourceBase([NotNull] string resourceName)
{
ResourceName = Check.NotNullOrWhiteSpace(resourceName, nameof(resourceName));
Contributors = new LocalizationResourceContributorList();
BaseResourceNames = new();
}
}

5
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs

@ -56,4 +56,9 @@ public class LocalizationResourceDictionary : Dictionary<Type, LocalizationResou
{
return _resourcesByNames.GetOrDefault(resourceName);
}
public bool ContainsResource(string resourceName)
{
return _resourcesByNames.ContainsKey(resourceName);
}
}

30
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceExtensions.cs

@ -7,9 +7,10 @@ namespace Volo.Abp.Localization;
public static class LocalizationResourceExtensions
{
public static LocalizationResource AddVirtualJson(
[NotNull] this LocalizationResource localizationResource,
public static TLocalizationResource AddVirtualJson<TLocalizationResource>(
[NotNull] this TLocalizationResource localizationResource,
[NotNull] string virtualPath)
where TLocalizationResource : LocalizationResourceBase
{
Check.NotNull(localizationResource, nameof(localizationResource));
Check.NotNull(virtualPath, nameof(virtualPath));
@ -21,16 +22,35 @@ public static class LocalizationResourceExtensions
return localizationResource;
}
public static LocalizationResource AddBaseTypes(
[NotNull] this LocalizationResource localizationResource,
public static TLocalizationResource AddBaseTypes<TLocalizationResource>(
[NotNull] this TLocalizationResource localizationResource,
[NotNull] params Type[] types)
where TLocalizationResource : LocalizationResourceBase
{
Check.NotNull(localizationResource, nameof(localizationResource));
Check.NotNull(types, nameof(types));
foreach (var type in types)
{
localizationResource.BaseResourceTypes.AddIfNotContains(type);
localizationResource
.BaseResourceNames
.AddIfNotContains(LocalizationResourceNameAttribute.GetName(type));
}
return localizationResource;
}
public static TLocalizationResource AddBaseResources<TLocalizationResource>(
[NotNull] this TLocalizationResource localizationResource,
[NotNull] params string[] baseResourceNames)
where TLocalizationResource : LocalizationResourceBase
{
Check.NotNull(localizationResource, nameof(localizationResource));
Check.NotNull(baseResourceNames, nameof(baseResourceNames));
foreach (var baseResourceName in baseResourceNames)
{
localizationResource.BaseResourceNames.AddIfNotContains(baseResourceName);
}
return localizationResource;

11
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/NullExternalLocalizationStore.cs

@ -0,0 +1,11 @@
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Localization;
public class NullExternalLocalizationStore : IExternalLocalizationStore, ISingletonDependency
{
public LocalizationResource GetResourceOrNull(string resourceName)
{
return null;
}
}

10
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationExtensions.cs

@ -8,20 +8,18 @@ public static class ExtensionPropertyConfigurationExtensions
public static string GetLocalizationResourceNameOrNull(
this ExtensionPropertyConfiguration property)
{
var resourceType = property.GetLocalizationResourceTypeOrNull();
if (resourceType == null)
if (property.DisplayName is LocalizableString localizableString)
{
return null;
return localizableString.ResourceName;
}
return LocalizationResourceNameAttribute.GetName(resourceType);
return null;
}
public static Type GetLocalizationResourceTypeOrNull(
this ExtensionPropertyConfiguration property)
{
if (property.DisplayName != null &&
property.DisplayName is LocalizableString localizableString)
if (property.DisplayName is LocalizableString localizableString)
{
return localizableString.ResourceType;
}

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

@ -32,7 +32,6 @@ public class AbpStringLocalizerFactory_Tests : AbpIntegratedTest<AbpLocalization
}
}
[Fact]
public void Should_Throw_Exception_For_Unknown_Resource_Names()
{

2
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/LocalizableStringSerializer_Tests.cs

@ -59,7 +59,7 @@ public class LocalizableStringSerializer_Tests : AbpIntegratedTest<AbpLocalizati
var localizableString = _serializer
.Deserialize("L:Test,Car")
.ShouldBeOfType<LocalizableString>();
localizableString.ResourceType.ShouldBe(typeof(LocalizationTestResource));
localizableString.ResourceName.ShouldBe("Test");
localizableString.Name.ShouldBe("Car");
Assert.Throws<AbpException>(() =>

Loading…
Cancel
Save