committed by
GitHub
30 changed files with 781 additions and 24 deletions
@ -0,0 +1,25 @@ |
|||||
|
using LINGYUN.Abp.LocalizationManagement.Localization; |
||||
|
using Volo.Abp.Features; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Validation.StringValues; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.Features; |
||||
|
|
||||
|
public class LocalizationManagementFeatureDefinitionProvider : FeatureDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(IFeatureDefinitionContext context) |
||||
|
{ |
||||
|
var group = context.AddGroup(LocalizationManagementFeatures.GroupName, |
||||
|
L("Feature:LocalizationManagement")); |
||||
|
group.AddFeature(LocalizationManagementFeatures.Enable, |
||||
|
"true", |
||||
|
L("Feature:LocalizationManagementEnable"), |
||||
|
L("Feature:LocalizationManagementEnableDesc"), |
||||
|
new ToggleStringValueType()); |
||||
|
} |
||||
|
|
||||
|
private static LocalizableString L(string name) |
||||
|
{ |
||||
|
return LocalizableString.Create<LocalizationManagementResource>(name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
namespace LINGYUN.Abp.LocalizationManagement.Features; |
||||
|
|
||||
|
public static class LocalizationManagementFeatures |
||||
|
{ |
||||
|
public const string GroupName = "LocalizationManagement"; |
||||
|
|
||||
|
public const string Enable = GroupName + ".Enable"; |
||||
|
} |
||||
@ -0,0 +1,69 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.External; |
||||
|
|
||||
|
public class ExternalLocalizationResourceContributor : ILocalizationResourceContributor |
||||
|
{ |
||||
|
public bool IsDynamic => false; |
||||
|
|
||||
|
protected LocalizationResourceBase Resource { get; private set; } |
||||
|
protected IExternalLocalizationStoreCache StoreCache { get; private set; } |
||||
|
protected IExternalLocalizationTextStoreCache TextStoreCache { get; private set; } |
||||
|
|
||||
|
public virtual void Fill(string cultureName, Dictionary<string, LocalizedString> dictionary) |
||||
|
{ |
||||
|
var texts = TextStoreCache.GetTexts(Resource, cultureName); |
||||
|
|
||||
|
foreach (var text in texts) |
||||
|
{ |
||||
|
dictionary[text.Key] = new LocalizedString(text.Key, text.Value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public async virtual Task FillAsync(string cultureName, Dictionary<string, LocalizedString> dictionary) |
||||
|
{ |
||||
|
var texts = await TextStoreCache.GetTextsAsync(Resource, cultureName); |
||||
|
|
||||
|
foreach (var text in texts) |
||||
|
{ |
||||
|
dictionary[text.Key] = new LocalizedString(text.Key, text.Value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public virtual LocalizedString GetOrNull(string cultureName, string name) |
||||
|
{ |
||||
|
var texts = TextStoreCache.GetTexts(Resource, cultureName); |
||||
|
|
||||
|
var text = texts.GetOrDefault(name); |
||||
|
if (text == null) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return new LocalizedString(name, text); |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<IEnumerable<string>> GetSupportedCulturesAsync() |
||||
|
{ |
||||
|
var cacheItem = await StoreCache.GetResourceOrNullAsync(Resource.ResourceName); |
||||
|
|
||||
|
if (cacheItem == null || !cacheItem.IsEnabled) |
||||
|
{ |
||||
|
return Array.Empty<string>(); |
||||
|
} |
||||
|
|
||||
|
return cacheItem.SupportedCultures; |
||||
|
} |
||||
|
|
||||
|
public void Initialize(LocalizationResourceInitializationContext context) |
||||
|
{ |
||||
|
Resource = context.Resource; |
||||
|
StoreCache = context.ServiceProvider.GetRequiredService<IExternalLocalizationStoreCache>(); |
||||
|
TextStoreCache = context.ServiceProvider.GetRequiredService<IExternalLocalizationTextStoreCache>(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,90 @@ |
|||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Localization.External; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.External; |
||||
|
|
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
public class ExternalLocalizationStore : IExternalLocalizationStore, ITransientDependency |
||||
|
{ |
||||
|
protected AbpLocalizationOptions LocalizationOptions { get; } |
||||
|
protected IExternalLocalizationStoreCache StoreCache { get; } |
||||
|
|
||||
|
public ExternalLocalizationStore( |
||||
|
IOptions<AbpLocalizationOptions> localizationOptions, |
||||
|
IExternalLocalizationStoreCache storeCache) |
||||
|
{ |
||||
|
LocalizationOptions = localizationOptions.Value; |
||||
|
StoreCache = storeCache; |
||||
|
} |
||||
|
|
||||
|
public virtual LocalizationResourceBase GetResourceOrNull(string resourceName) |
||||
|
{ |
||||
|
var cacheItem = StoreCache.GetResourceOrNull(resourceName); |
||||
|
|
||||
|
if (cacheItem == null || LocalizationOptions.Resources.ContainsKey(cacheItem.Name)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return CreateNonTypedLocalizationResource(cacheItem); |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<LocalizationResourceBase> GetResourceOrNullAsync(string resourceName) |
||||
|
{ |
||||
|
var cacheItem = await StoreCache.GetResourceOrNullAsync(resourceName); |
||||
|
|
||||
|
if (cacheItem == null || LocalizationOptions.Resources.ContainsKey(cacheItem.Name)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return CreateNonTypedLocalizationResource(cacheItem); |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<string[]> GetResourceNamesAsync() |
||||
|
{ |
||||
|
var cacheNames = await StoreCache.GetResourceNamesAsync(); |
||||
|
|
||||
|
return cacheNames |
||||
|
.Where(name => !LocalizationOptions.Resources.ContainsKey(name)) |
||||
|
.ToArray(); |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<LocalizationResourceBase[]> GetResourcesAsync() |
||||
|
{ |
||||
|
var cacheItem = await StoreCache.GetResourcesAsync(); |
||||
|
|
||||
|
|
||||
|
if (cacheItem == null) |
||||
|
{ |
||||
|
return Array.Empty<LocalizationResourceBase>(); |
||||
|
} |
||||
|
|
||||
|
return cacheItem |
||||
|
.Where(x => x.IsEnabled) |
||||
|
.Where(x => !LocalizationOptions.Resources.ContainsKey(x.Name)) |
||||
|
.Select(CreateNonTypedLocalizationResource) |
||||
|
.ToArray(); |
||||
|
} |
||||
|
|
||||
|
protected virtual NonTypedLocalizationResource CreateNonTypedLocalizationResource(LocalizationResourceCacheItem cacheItem) |
||||
|
{ |
||||
|
var localizationResource = new NonTypedLocalizationResource( |
||||
|
cacheItem.Name, |
||||
|
cacheItem.DefaultCulture); |
||||
|
|
||||
|
if (cacheItem.BaseResources.Length > 0) |
||||
|
{ |
||||
|
localizationResource.AddBaseResources(cacheItem.BaseResources); |
||||
|
} |
||||
|
|
||||
|
localizationResource.Contributors.Add(new ExternalLocalizationResourceContributor()); |
||||
|
|
||||
|
return localizationResource; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,146 @@ |
|||||
|
using Microsoft.Extensions.Options; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Caching; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.External; |
||||
|
|
||||
|
public class ExternalLocalizationStoreCache : IExternalLocalizationStoreCache, ITransientDependency |
||||
|
{ |
||||
|
protected IDistributedCache<LocalizationResourceCacheItem> ResourceCache { get; } |
||||
|
protected IDistributedCache<LocalizationResourcesCacheItem> ResourcesCache { get; } |
||||
|
protected AbpLocalizationOptions LocalizationOptions { get; } |
||||
|
protected IResourceRepository ResourceRepository { get; } |
||||
|
|
||||
|
public ExternalLocalizationStoreCache( |
||||
|
IDistributedCache<LocalizationResourcesCacheItem> resourcesCache, |
||||
|
IDistributedCache<LocalizationResourceCacheItem> resourceCache, |
||||
|
IOptions<AbpLocalizationOptions> localizationOptions, |
||||
|
IResourceRepository resourceRepository) |
||||
|
{ |
||||
|
ResourceCache = resourceCache; |
||||
|
ResourcesCache = resourcesCache; |
||||
|
ResourceRepository = resourceRepository; |
||||
|
LocalizationOptions = localizationOptions.Value; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task RemoveAsync(string resourceName) |
||||
|
{ |
||||
|
await ResourceCache.RemoveAsync(resourceName); |
||||
|
await ResourcesCache.RemoveAsync(LocalizationResourcesCacheItem.CacheKey); |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<string[]> GetResourceNamesAsync() |
||||
|
{ |
||||
|
var cacheItem = await GetResourcesCacheItemAsync(); |
||||
|
|
||||
|
return cacheItem.Resources |
||||
|
.Where(x => x.IsEnabled) |
||||
|
.Where(x => !LocalizationOptions.Resources.ContainsKey(x.Name)) |
||||
|
.Select(x => x.Name) |
||||
|
.ToArray(); |
||||
|
} |
||||
|
|
||||
|
public virtual LocalizationResourceCacheItem GetResourceOrNull(string resourceName) |
||||
|
{ |
||||
|
var cacheItem = GetResourceCacheItem(resourceName); |
||||
|
|
||||
|
return cacheItem.IsEnabled ? cacheItem : null; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<LocalizationResourceCacheItem> GetResourceOrNullAsync(string resourceName) |
||||
|
{ |
||||
|
var cacheItem = await GetResourceCacheItemAsync(resourceName); |
||||
|
|
||||
|
return cacheItem.IsEnabled ? cacheItem : null; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<LocalizationResourceCacheItem[]> GetResourcesAsync() |
||||
|
{ |
||||
|
var cacheItem = await GetResourcesCacheItemAsync(); |
||||
|
|
||||
|
return cacheItem.Resources |
||||
|
.Where(x => x.IsEnabled) |
||||
|
.Where(x => !LocalizationOptions.Resources.ContainsKey(x.Name)) |
||||
|
.ToArray(); |
||||
|
} |
||||
|
|
||||
|
protected virtual LocalizationResourceCacheItem GetResourceCacheItem(string resourceName) |
||||
|
{ |
||||
|
var cacheItem = ResourceCache.Get(resourceName); |
||||
|
if (cacheItem == null) |
||||
|
{ |
||||
|
var resource = ResourceRepository.FindByName(resourceName); |
||||
|
|
||||
|
if (resource == null) |
||||
|
{ |
||||
|
cacheItem = new LocalizationResourceCacheItem(resourceName) |
||||
|
{ |
||||
|
IsEnabled = false |
||||
|
}; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
cacheItem = new LocalizationResourceCacheItem(resource.Name, resource.DefaultCultureName) |
||||
|
{ |
||||
|
IsEnabled = resource.Enable |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
ResourceCache.Set(resourceName, cacheItem); |
||||
|
} |
||||
|
|
||||
|
return cacheItem; |
||||
|
} |
||||
|
|
||||
|
protected async virtual Task<LocalizationResourceCacheItem> GetResourceCacheItemAsync(string resourceName) |
||||
|
{ |
||||
|
var cacheItem = await ResourceCache.GetAsync(resourceName); |
||||
|
if (cacheItem == null) |
||||
|
{ |
||||
|
var resource = await ResourceRepository.FindByNameAsync(resourceName); |
||||
|
if (resource == null) |
||||
|
{ |
||||
|
cacheItem = new LocalizationResourceCacheItem(resourceName) |
||||
|
{ |
||||
|
IsEnabled = false |
||||
|
}; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
cacheItem = new LocalizationResourceCacheItem(resource.Name, resource.DefaultCultureName) |
||||
|
{ |
||||
|
IsEnabled = resource.Enable |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
await ResourceCache.SetAsync(resourceName, cacheItem); |
||||
|
} |
||||
|
|
||||
|
return cacheItem; |
||||
|
} |
||||
|
|
||||
|
protected async virtual Task<LocalizationResourcesCacheItem> GetResourcesCacheItemAsync() |
||||
|
{ |
||||
|
var cacheItem = await ResourcesCache.GetAsync(LocalizationResourcesCacheItem.CacheKey); |
||||
|
if (cacheItem == null) |
||||
|
{ |
||||
|
var resources = await ResourceRepository.GetListAsync(); |
||||
|
var resourceItems = resources |
||||
|
.Where(x => !LocalizationOptions.Resources.ContainsKey(x.Name)) |
||||
|
.Select(x => new LocalizationResourceCacheItem(x.Name, x.DefaultCultureName) |
||||
|
{ |
||||
|
IsEnabled = x.Enable, |
||||
|
}) |
||||
|
.ToList(); |
||||
|
|
||||
|
cacheItem = new LocalizationResourcesCacheItem(resourceItems); |
||||
|
|
||||
|
await ResourcesCache.SetAsync(LocalizationResourcesCacheItem.CacheKey, cacheItem); |
||||
|
} |
||||
|
|
||||
|
return cacheItem; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Caching; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.External; |
||||
|
|
||||
|
[Serializable] |
||||
|
[IgnoreMultiTenancy] |
||||
|
[CacheName("AbpExternalLocalizationTexts")] |
||||
|
public class ExternalLocalizationTextCacheItem |
||||
|
{ |
||||
|
private const string CacheKeyFormat = "r:{0},c:{1}"; |
||||
|
|
||||
|
public Dictionary<string, string> Texts { get; set; } |
||||
|
public ExternalLocalizationTextCacheItem() |
||||
|
{ |
||||
|
Texts = new Dictionary<string, string>(); |
||||
|
} |
||||
|
public ExternalLocalizationTextCacheItem(Dictionary<string, string> texts) |
||||
|
{ |
||||
|
Texts = texts; |
||||
|
} |
||||
|
|
||||
|
public static string CalculateCacheKey(string resourceName, string cultureName) |
||||
|
{ |
||||
|
return string.Format(CacheKeyFormat, resourceName, cultureName); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Caching; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.External; |
||||
|
|
||||
|
[Serializable] |
||||
|
[IgnoreMultiTenancy] |
||||
|
[CacheName("AbpExternalLocalizationTextsStamp")] |
||||
|
public class ExternalLocalizationTextStampCacheItem |
||||
|
{ |
||||
|
private const string CacheKeyFormat = "r:{0},c:{1}"; |
||||
|
public string Stamp { get; set; } |
||||
|
|
||||
|
public ExternalLocalizationTextStampCacheItem() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public ExternalLocalizationTextStampCacheItem(string stamp) |
||||
|
{ |
||||
|
Stamp = stamp; |
||||
|
} |
||||
|
|
||||
|
public static string CalculateCacheKey(string resourceName, string cultureName) |
||||
|
{ |
||||
|
return string.Format(CacheKeyFormat, resourceName, cultureName); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,143 @@ |
|||||
|
using AsyncKeyedLock; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using System; |
||||
|
using System.Collections.Concurrent; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Caching; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.DistributedLocking; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Uow; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.External; |
||||
|
|
||||
|
public class ExternalLocalizationTextStoreCache : IExternalLocalizationTextStoreCache, ISingletonDependency |
||||
|
{ |
||||
|
protected AsyncKeyedLocker<string> AsyncKeyedLocker; |
||||
|
protected ConcurrentDictionary<string, LocalizationTextMemoryCacheItem> MemoryCache { get; } |
||||
|
|
||||
|
protected IAbpDistributedLock DistributedLock { get; } |
||||
|
protected IServiceScopeFactory ServiceScopeFactory { get; } |
||||
|
protected IDistributedCache<ExternalLocalizationTextCacheItem> DistributedCache { get; } |
||||
|
protected IDistributedCache<ExternalLocalizationTextStampCacheItem> StampCache { get; } |
||||
|
|
||||
|
public ExternalLocalizationTextStoreCache( |
||||
|
IAbpDistributedLock distributedLock, |
||||
|
IServiceScopeFactory serviceScopeFactory, |
||||
|
IDistributedCache<ExternalLocalizationTextCacheItem> distributedCache, |
||||
|
IDistributedCache<ExternalLocalizationTextStampCacheItem> stampCache) |
||||
|
{ |
||||
|
DistributedLock = distributedLock; |
||||
|
ServiceScopeFactory = serviceScopeFactory; |
||||
|
DistributedCache = distributedCache; |
||||
|
StampCache = stampCache; |
||||
|
|
||||
|
AsyncKeyedLocker = new AsyncKeyedLocker<string>(o => |
||||
|
{ |
||||
|
o.PoolSize = 20; |
||||
|
o.PoolInitialFill = 1; |
||||
|
}); |
||||
|
MemoryCache = new ConcurrentDictionary<string, LocalizationTextMemoryCacheItem>(); |
||||
|
} |
||||
|
|
||||
|
public async virtual Task RemoveAsync(string resourceName, string cultureName) |
||||
|
{ |
||||
|
var cacheKey = ExternalLocalizationTextCacheItem.CalculateCacheKey(resourceName, cultureName); |
||||
|
var stampCacheKey = ExternalLocalizationTextStampCacheItem.CalculateCacheKey(resourceName, cultureName); |
||||
|
|
||||
|
await using var lockHandle = await DistributedLock.TryAcquireAsync(cacheKey, TimeSpan.FromMinutes(1d)); |
||||
|
|
||||
|
await DistributedCache.RemoveAsync(cacheKey); |
||||
|
|
||||
|
await StampCache.SetAsync(stampCacheKey, new ExternalLocalizationTextStampCacheItem(Guid.NewGuid().ToString())); |
||||
|
} |
||||
|
|
||||
|
public Dictionary<string, string> GetTexts(LocalizationResourceBase resource, string cultureName) |
||||
|
{ |
||||
|
var cacheKey = ExternalLocalizationTextCacheItem.CalculateCacheKey(resource.ResourceName, cultureName); |
||||
|
var memoryCacheItem = MemoryCache.GetOrDefault(cacheKey); |
||||
|
if (memoryCacheItem != null && !IsShouldCheck(memoryCacheItem)) |
||||
|
{ |
||||
|
return memoryCacheItem.Texts; |
||||
|
} |
||||
|
|
||||
|
return new Dictionary<string, string>(); |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<Dictionary<string, string>> GetTextsAsync(LocalizationResourceBase resource, string cultureName) |
||||
|
{ |
||||
|
var cacheKey = ExternalLocalizationTextCacheItem.CalculateCacheKey(resource.ResourceName, cultureName); |
||||
|
var memoryCacheItem = MemoryCache.GetOrDefault(cacheKey); |
||||
|
if (memoryCacheItem != null && !IsShouldCheck(memoryCacheItem)) |
||||
|
{ |
||||
|
return memoryCacheItem.Texts; |
||||
|
} |
||||
|
|
||||
|
using (await AsyncKeyedLocker.LockAsync(cacheKey)) |
||||
|
{ |
||||
|
memoryCacheItem = MemoryCache.GetOrDefault(cacheKey); |
||||
|
if (memoryCacheItem != null && !IsShouldCheck(memoryCacheItem)) |
||||
|
{ |
||||
|
return memoryCacheItem.Texts; |
||||
|
} |
||||
|
|
||||
|
var stampCacheKey = ExternalLocalizationTextStampCacheItem.CalculateCacheKey(resource.ResourceName, cultureName); |
||||
|
var stampCacheItem = await StampCache.GetAsync(stampCacheKey); |
||||
|
if (memoryCacheItem != null && memoryCacheItem.CacheStamp == stampCacheItem.Stamp) |
||||
|
{ |
||||
|
memoryCacheItem.LastCheckTime = DateTime.Now; |
||||
|
return memoryCacheItem.Texts; |
||||
|
} |
||||
|
|
||||
|
var distributeCacheItem = await DistributedCache.GetAsync(cacheKey); |
||||
|
if (distributeCacheItem != null) |
||||
|
{ |
||||
|
MemoryCache[cacheKey] = new LocalizationTextMemoryCacheItem(distributeCacheItem.Texts, stampCacheItem.Stamp); |
||||
|
|
||||
|
return distributeCacheItem.Texts; |
||||
|
} |
||||
|
|
||||
|
await using var lockHandle = await DistributedLock.TryAcquireAsync(cacheKey, TimeSpan.FromMinutes(1d)); |
||||
|
|
||||
|
if (lockHandle == null) |
||||
|
{ |
||||
|
return new Dictionary<string, string>(); |
||||
|
} |
||||
|
|
||||
|
distributeCacheItem = await CreateCacheItemAsync(resource, cultureName); |
||||
|
|
||||
|
await DistributedCache.SetAsync(cacheKey, distributeCacheItem); |
||||
|
|
||||
|
stampCacheItem = new ExternalLocalizationTextStampCacheItem(Guid.NewGuid().ToString()); |
||||
|
await StampCache.SetAsync(stampCacheKey, stampCacheItem); |
||||
|
|
||||
|
MemoryCache[cacheKey] = new LocalizationTextMemoryCacheItem(distributeCacheItem.Texts, stampCacheItem.Stamp); |
||||
|
|
||||
|
return distributeCacheItem.Texts; |
||||
|
} |
||||
|
} |
||||
|
private static bool IsShouldCheck(LocalizationTextMemoryCacheItem memoryCacheItem) |
||||
|
{ |
||||
|
return DateTime.Now.Subtract(memoryCacheItem.LastCheckTime).TotalSeconds >= 30.0; |
||||
|
} |
||||
|
|
||||
|
protected async virtual Task<ExternalLocalizationTextCacheItem> CreateCacheItemAsync(LocalizationResourceBase resource, string cultureName) |
||||
|
{ |
||||
|
using var scope = ServiceScopeFactory.CreateScope(); |
||||
|
var unitOfWorkManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>(); |
||||
|
using var unitOfWork = unitOfWorkManager.Begin(requiresNew: true); |
||||
|
var repo = scope.ServiceProvider.GetRequiredService<ITextRepository>(); |
||||
|
|
||||
|
var texts = await repo.GetListAsync(resource.ResourceName, cultureName); |
||||
|
|
||||
|
var fillTexts = new Dictionary<string, string>(); |
||||
|
|
||||
|
foreach (var text in texts) |
||||
|
{ |
||||
|
fillTexts[text.Key] = text.Value; |
||||
|
} |
||||
|
|
||||
|
return new ExternalLocalizationTextCacheItem(fillTexts); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.External; |
||||
|
|
||||
|
public interface IExternalLocalizationStoreCache |
||||
|
{ |
||||
|
LocalizationResourceCacheItem GetResourceOrNull(string resourceName); |
||||
|
|
||||
|
Task<LocalizationResourceCacheItem> GetResourceOrNullAsync(string resourceName); |
||||
|
|
||||
|
Task<string[]> GetResourceNamesAsync(); |
||||
|
|
||||
|
Task<LocalizationResourceCacheItem[]> GetResourcesAsync(); |
||||
|
|
||||
|
Task RemoveAsync(string resourceName); |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.External; |
||||
|
|
||||
|
public interface IExternalLocalizationTextStoreCache |
||||
|
{ |
||||
|
Dictionary<string, string> GetTexts(LocalizationResourceBase resource, string cultureName); |
||||
|
|
||||
|
Task<Dictionary<string, string>> GetTextsAsync(LocalizationResourceBase resource, string cultureName); |
||||
|
|
||||
|
Task RemoveAsync(string resourceName, string cultureName); |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Domain.Entities.Events; |
||||
|
using Volo.Abp.EventBus; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.External; |
||||
|
|
||||
|
public class LocalizationResourceCacheInvalidator : |
||||
|
ILocalEventHandler<EntityChangedEventData<Resource>>, |
||||
|
ITransientDependency |
||||
|
{ |
||||
|
private readonly IExternalLocalizationTextStoreCache _externalLocalizationTextStoreCache; |
||||
|
private readonly ILocalizationLanguageStoreCache _localizationLanguageStoreCache; |
||||
|
private readonly IExternalLocalizationStoreCache _externalLocalizationStoreCache; |
||||
|
public LocalizationResourceCacheInvalidator( |
||||
|
IExternalLocalizationTextStoreCache externalLocalizationTextStoreCache, |
||||
|
ILocalizationLanguageStoreCache localizationLanguageStoreCache, |
||||
|
IExternalLocalizationStoreCache externalLocalizationStoreCache) |
||||
|
{ |
||||
|
_externalLocalizationTextStoreCache = externalLocalizationTextStoreCache; |
||||
|
_localizationLanguageStoreCache = localizationLanguageStoreCache; |
||||
|
_externalLocalizationStoreCache = externalLocalizationStoreCache; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task HandleEventAsync(EntityChangedEventData<Resource> eventData) |
||||
|
{ |
||||
|
await _externalLocalizationStoreCache.RemoveAsync(eventData.Entity.Name); |
||||
|
; |
||||
|
var languages = await _localizationLanguageStoreCache.GetLanguagesAsync(); |
||||
|
|
||||
|
foreach (var language in languages) |
||||
|
{ |
||||
|
await _externalLocalizationTextStoreCache.RemoveAsync(eventData.Entity.Name, language.CultureName); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Caching; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.External; |
||||
|
|
||||
|
[IgnoreMultiTenancy] |
||||
|
[CacheName("AbpExternalLocalizationResource")] |
||||
|
public class LocalizationResourceCacheItem |
||||
|
{ |
||||
|
public virtual string Name { get; set; } |
||||
|
|
||||
|
public virtual string DefaultCulture { get; set; } |
||||
|
|
||||
|
public virtual string[] BaseResources { get; set; } |
||||
|
|
||||
|
public virtual string[] SupportedCultures { get; set; } |
||||
|
|
||||
|
public bool IsEnabled { get; set; } |
||||
|
|
||||
|
public LocalizationResourceCacheItem( |
||||
|
string name, |
||||
|
string defaultCulture = null, |
||||
|
string[] baseResources = null, |
||||
|
string[] supportedCultures = null) |
||||
|
{ |
||||
|
Name = name; |
||||
|
DefaultCulture = defaultCulture; |
||||
|
BaseResources = baseResources ?? Array.Empty<string>(); |
||||
|
SupportedCultures = supportedCultures ?? Array.Empty<string>(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp.Caching; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.External; |
||||
|
|
||||
|
[IgnoreMultiTenancy] |
||||
|
[CacheName("AbpExternalLocalizationResources")] |
||||
|
public class LocalizationResourcesCacheItem |
||||
|
{ |
||||
|
public const string CacheKey = "All"; |
||||
|
|
||||
|
public List<LocalizationResourceCacheItem> Resources { get; set; } |
||||
|
|
||||
|
public LocalizationResourcesCacheItem() |
||||
|
{ |
||||
|
Resources = new List<LocalizationResourceCacheItem>(); |
||||
|
} |
||||
|
|
||||
|
public LocalizationResourcesCacheItem(List<LocalizationResourceCacheItem> resources) |
||||
|
{ |
||||
|
Resources = resources; |
||||
|
} |
||||
|
|
||||
|
public LocalizationResourceCacheItem GetResourceOrNull(string name) |
||||
|
{ |
||||
|
return Resources?.FirstOrDefault(x => x.Name == name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.LocalizationManagement.External; |
||||
|
|
||||
|
public class LocalizationTextMemoryCacheItem |
||||
|
{ |
||||
|
private const string CacheKeyFormat = "r:{0},c:{1}"; |
||||
|
|
||||
|
public string CacheStamp { get; } |
||||
|
|
||||
|
public DateTime LastCheckTime { get; set; } |
||||
|
|
||||
|
public Dictionary<string, string> Texts { get; set; } |
||||
|
|
||||
|
public LocalizationTextMemoryCacheItem() |
||||
|
{ |
||||
|
Texts = new Dictionary<string, string>(); |
||||
|
} |
||||
|
public LocalizationTextMemoryCacheItem(Dictionary<string, string> texts, string cacheStamp) |
||||
|
{ |
||||
|
Texts = texts; |
||||
|
CacheStamp = cacheStamp; |
||||
|
LastCheckTime = DateTime.Now; |
||||
|
} |
||||
|
|
||||
|
public static string CalculateCacheKey(string resourceName, string cultureName) |
||||
|
{ |
||||
|
return string.Format(CacheKeyFormat, resourceName, cultureName); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue