diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/AbpLocalizationManagementOptions.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/AbpLocalizationManagementOptions.cs
new file mode 100644
index 000000000..1008e21ec
--- /dev/null
+++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/AbpLocalizationManagementOptions.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace LINGYUN.Abp.LocalizationManagement;
+
+public class AbpLocalizationManagementOptions
+{
+ ///
+ /// 申请时间戳超时时间
+ /// default: 2 minutes
+ ///
+ public TimeSpan LocalizationCacheStampTimeOut { get; set; }
+ ///
+ /// 时间戳过期时间
+ /// default: 30 minutes
+ ///
+ public TimeSpan LocalizationCacheStampExpiration { get; set; }
+
+ public AbpLocalizationManagementOptions()
+ {
+ LocalizationCacheStampTimeOut = TimeSpan.FromMinutes(2);
+ // 30分钟过期重新刷新缓存
+ LocalizationCacheStampExpiration = TimeSpan.FromMinutes(30);
+ }
+}
diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ILocalizationStoreCache.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ILocalizationStoreCache.cs
new file mode 100644
index 000000000..649a6666e
--- /dev/null
+++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/ILocalizationStoreCache.cs
@@ -0,0 +1,21 @@
+using Microsoft.Extensions.Localization;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.LocalizationManagement;
+
+public interface ILocalizationStoreCache
+{
+ Task InitializeAsync(LocalizationStoreCacheInitializeContext context);
+
+ LocalizationResourceBase GetResourceOrNull(string resourceName);
+
+ LocalizedString GetLocalizedStringOrNull(string resourceName, string cultureName, string name);
+
+ IReadOnlyList GetResources();
+
+ IReadOnlyList GetLanguages();
+
+ IDictionary GetAllLocalizedStrings(string cultureName);
+}
diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionary.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionary.cs
new file mode 100644
index 000000000..e98f9fe40
--- /dev/null
+++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionary.cs
@@ -0,0 +1,8 @@
+using Microsoft.Extensions.Localization;
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.LocalizationManagement;
+public class LocalizationDictionary : Dictionary
+{
+
+}
diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionaryWithCulture.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionaryWithCulture.cs
new file mode 100644
index 000000000..68152887c
--- /dev/null
+++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionaryWithCulture.cs
@@ -0,0 +1,6 @@
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.LocalizationManagement;
+public class LocalizationDictionaryWithCulture : Dictionary
+{
+}
diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionaryWithResource.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionaryWithResource.cs
new file mode 100644
index 000000000..901aa2d35
--- /dev/null
+++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationDictionaryWithResource.cs
@@ -0,0 +1,6 @@
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.LocalizationManagement;
+public class LocalizationDictionaryWithResource : Dictionary
+{
+}
diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageDictionary.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageDictionary.cs
new file mode 100644
index 000000000..d05c7d52f
--- /dev/null
+++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationLanguageDictionary.cs
@@ -0,0 +1,7 @@
+using System.Collections.Generic;
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.LocalizationManagement;
+public class LocalizationLanguageDictionary : Dictionary
+{
+}
diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationManagementExternalContributor.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationManagementExternalContributor.cs
index 3dac52cc7..2f0c334ed 100644
--- a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationManagementExternalContributor.cs
+++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationManagementExternalContributor.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Localization;
+using Volo.Abp.Threading;
namespace LINGYUN.Abp.LocalizationManagement;
@@ -12,81 +13,53 @@ public class LocalizationManagementExternalContributor : ILocalizationResourceCo
public bool IsDynamic => true;
private LocalizationResourceBase _resource;
- private ITextRepository _textRepository;
- private IResourceRepository _resourceRepository;
- private ILanguageRepository _languageRepository;
+
+ private ILocalizationStoreCache _localizationStoreCache;
+ private LocalizationStoreCacheInitializeContext _cacheInitializeContext;
public void Initialize(LocalizationResourceInitializationContext context)
{
_resource = context.Resource;
- _textRepository = context.ServiceProvider.GetRequiredService();
- _resourceRepository = context.ServiceProvider.GetRequiredService();
- _languageRepository = context.ServiceProvider.GetRequiredService();
+
+ _cacheInitializeContext = new LocalizationStoreCacheInitializeContext(context.ServiceProvider);
+ _localizationStoreCache = context.ServiceProvider.GetRequiredService();
}
public virtual void Fill(string cultureName, Dictionary dictionary)
{
- FillInternalAsync(_resource.ResourceName, cultureName, dictionary).GetAwaiter().GetResult();
+ AsyncHelper.RunSync(async () => await FillAsync(cultureName, dictionary));
}
public async virtual Task FillAsync(string cultureName, Dictionary dictionary)
{
- await FillInternalAsync(_resource.ResourceName, cultureName, dictionary);
- }
+ await _localizationStoreCache.InitializeAsync(_cacheInitializeContext);
- public virtual LocalizedString GetOrNull(string cultureName, string name)
- {
- return GetOrNullInternal(_resource.ResourceName, cultureName, name);
- }
+ var localizedStrings = _localizationStoreCache.GetAllLocalizedStrings(cultureName);
- protected virtual LocalizedString GetOrNullInternal(string resourceName, string cultureName, string name)
- {
- var resource = GetResourceOrNullAsync(name).GetAwaiter().GetResult();
- if (resource == null)
+ var localizedStringsInResource = localizedStrings.GetOrDefault(_resource.ResourceName);
+ if (localizedStringsInResource != null)
{
- return null;
+ foreach (var localizedString in localizedStringsInResource)
+ {
+ dictionary[localizedString.Key] = localizedString.Value;
+ }
}
- var text = _textRepository.GetByCultureKeyAsync(resourceName, cultureName, name).GetAwaiter().GetResult();
- if (text != null)
- {
- return new LocalizedString(name, text.Value);
- }
-
- return null;
}
- public async virtual Task> GetSupportedCulturesAsync()
+ public virtual LocalizedString GetOrNull(string cultureName, string name)
{
- var languages = await _languageRepository.GetActivedListAsync();
-
- return languages
- .Select(x => x.CultureName)
- .ToList();
+ return _localizationStoreCache
+ .GetLocalizedStringOrNull(_resource.ResourceName, cultureName, name);
}
- protected async virtual Task FillInternalAsync(string resourceName, string cultureName, Dictionary dictionary)
+ public virtual Task> GetSupportedCulturesAsync()
{
- var resource = await GetResourceOrNullAsync(resourceName);
- if (resource == null)
- {
- return;
- }
-
- var texts = await GetTextListByResourceAsync(resourceName, cultureName);
+ var languageInfos = _localizationStoreCache.GetLanguages();
- foreach (var text in texts)
- {
- dictionary[text.Key] = new LocalizedString(text.Key, text.Value);
- }
- }
-
- protected async virtual Task GetResourceOrNullAsync(string resourceName)
- {
- return await _resourceRepository.FindByNameAsync(resourceName);
- }
+ IEnumerable languages = languageInfos
+ .Select(x => x.CultureName)
+ .ToList();
- protected async virtual Task> GetTextListByResourceAsync(string resourceName, string cultureName = null)
- {
- return await _textRepository.GetListAsync(resourceName, cultureName);
+ return Task.FromResult(languages);
}
}
diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStore.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStore.cs
index 4bd7ce6bc..9989560b1 100644
--- a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStore.cs
+++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStore.cs
@@ -2,6 +2,7 @@
using Microsoft.Extensions.Localization;
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -18,29 +19,25 @@ namespace LINGYUN.Abp.LocalizationManagement
typeof(LocalizationStore))]
public class LocalizationStore : IExternalLocalizationStore
{
- protected ILanguageRepository LanguageRepository { get; }
- protected ITextRepository TextRepository { get; }
- protected IResourceRepository ResourceRepository { get; }
+ protected IServiceProvider ServiceProvider { get; }
+ protected ILocalizationStoreCache LocalizationStoreCache { get; }
- public LocalizationStore(
- ILanguageRepository languageRepository,
- ITextRepository textRepository,
- IResourceRepository resourceRepository)
- {
- TextRepository = textRepository;
- LanguageRepository = languageRepository;
- ResourceRepository = resourceRepository;
+ public LocalizationStore(
+ IServiceProvider serviceProvider,
+ ILocalizationStoreCache localizationStoreCache)
+ {
+ ServiceProvider = serviceProvider;
+ LocalizationStoreCache = localizationStoreCache;
}
[Obsolete("The framework already supports dynamic languages and will be deprecated in the next release")]
public async virtual Task> GetLanguageListAsync(
CancellationToken cancellationToken = default)
{
- var languages = await LanguageRepository.GetActivedListAsync(cancellationToken);
+ var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
+ await LocalizationStoreCache.InitializeAsync(context);
- return languages
- .Select(x => new LanguageInfo(x.CultureName, x.UiCultureName, x.DisplayName, x.FlagIcon))
- .ToList();
+ return LocalizationStoreCache.GetLanguages().ToList();
}
[Obsolete("The framework already supports dynamic languages and will be deprecated in the next release")]
@@ -48,26 +45,31 @@ namespace LINGYUN.Abp.LocalizationManagement
string resourceName,
CancellationToken cancellationToken = default)
{
- // TODO: 引用缓存?
var dictionaries = new Dictionary();
- var resource = await ResourceRepository.FindByNameAsync(resourceName, cancellationToken);
- if (resource == null || !resource.Enable)
+
+ var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
+ await LocalizationStoreCache.InitializeAsync(context);
+
+ var resource = LocalizationStoreCache.GetResourceOrNull(resourceName);
+
+ if (resource == null)
{
// 资源不存在或未启用返回空
return dictionaries;
}
- var texts = await TextRepository.GetListAsync(resourceName, null, cancellationToken);
+ var texts = LocalizationStoreCache.GetAllLocalizedStrings(CultureInfo.CurrentCulture.Name);
- foreach (var textGroup in texts.GroupBy(x => x.CultureName))
+ foreach (var textGroup in texts)
{
var cultureTextDictionaires = new Dictionary();
- foreach (var text in textGroup)
+
+ foreach (var text in textGroup.Value)
{
// 本地化名称去重
if (!cultureTextDictionaires.ContainsKey(text.Key))
{
- cultureTextDictionaires[text.Key] = new LocalizedString(text.Key, text.Value.NormalizeLineEndings());
+ cultureTextDictionaires[text.Key] = new LocalizedString(text.Key, text.Value.Value.NormalizeLineEndings());
}
}
@@ -85,27 +87,28 @@ namespace LINGYUN.Abp.LocalizationManagement
public async virtual Task>> GetAllLocalizationDictionaryAsync(CancellationToken cancellationToken = default)
{
var result = new Dictionary>();
- var textList = await TextRepository.GetListAsync(resourceName: null, cancellationToken: cancellationToken);
- foreach (var resourcesGroup in textList.GroupBy(x => x.ResourceName))
+ var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
+ await LocalizationStoreCache.InitializeAsync(context);
+
+ var textList = LocalizationStoreCache.GetAllLocalizedStrings(CultureInfo.CurrentCulture.Name);
+
+ foreach (var resourcesGroup in textList)
{
var dictionaries = new Dictionary();
- foreach (var textGroup in resourcesGroup.GroupBy(x => x.CultureName))
+ foreach (var text in resourcesGroup.Value)
{
var cultureTextDictionaires = new Dictionary();
- foreach (var text in textGroup)
+ // 本地化名称去重
+ if (!cultureTextDictionaires.ContainsKey(text.Key))
{
- // 本地化名称去重
- if (!cultureTextDictionaires.ContainsKey(text.Key))
- {
- cultureTextDictionaires[text.Key] = new LocalizedString(text.Key, text.Value.NormalizeLineEndings());
- }
+ cultureTextDictionaires[text.Key] = new LocalizedString(text.Key, text.Value.Value.NormalizeLineEndings());
}
// 本地化语言去重
- if (!dictionaries.ContainsKey(textGroup.Key))
+ if (!dictionaries.ContainsKey(text.Key))
{
- dictionaries[textGroup.Key] = new StaticLocalizationDictionary(textGroup.Key, cultureTextDictionaires);
+ dictionaries[text.Key] = new StaticLocalizationDictionary(text.Key, cultureTextDictionaires);
}
}
@@ -118,40 +121,41 @@ namespace LINGYUN.Abp.LocalizationManagement
[Obsolete("The framework already supports dynamic languages and will be deprecated in the next release")]
public async virtual Task ResourceExistsAsync(string resourceName, CancellationToken cancellationToken = default)
{
- return await ResourceRepository.ExistsAsync(resourceName, cancellationToken);
+ var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
+ await LocalizationStoreCache.InitializeAsync(context);
+
+ return LocalizationStoreCache.GetResourceOrNull(resourceName) != null;
}
public LocalizationResourceBase GetResourceOrNull(string resourceName)
{
- return GetResourceOrNullAsync(resourceName)
- .ConfigureAwait(continueOnCapturedContext: false)
- .GetAwaiter()
- .GetResult();
+ return AsyncHelper.RunSync(async () => await GetResourceOrNullAsync(resourceName));
}
public async virtual Task GetResourceOrNullAsync(string resourceName)
{
- var resource = await ResourceRepository.FindByNameAsync(resourceName);
- if (resource == null)
- {
- return null;
- }
+ var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
+ await LocalizationStoreCache.InitializeAsync(context);
- return new NonTypedLocalizationResource(resource.Name);
+ return LocalizationStoreCache.GetResourceOrNull(resourceName);
}
public async virtual Task GetResourceNamesAsync()
{
- var resources = await ResourceRepository.GetListAsync();
+ var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
+ await LocalizationStoreCache.InitializeAsync(context);
- return resources.Select(r => r.Name).ToArray();
+ return LocalizationStoreCache.GetResources()
+ .Select(x => x.ResourceName)
+ .ToArray();
}
public async virtual Task GetResourcesAsync()
{
- var resources = await ResourceRepository.GetListAsync();
+ var context = new LocalizationStoreCacheInitializeContext(ServiceProvider);
+ await LocalizationStoreCache.InitializeAsync(context);
- return resources.Select(r => new NonTypedLocalizationResource(r.Name)).ToArray();
+ return LocalizationStoreCache.GetResources().ToArray();
}
}
}
diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStoreCacheInitializeContext.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStoreCacheInitializeContext.cs
new file mode 100644
index 000000000..462a2ca56
--- /dev/null
+++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStoreCacheInitializeContext.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace LINGYUN.Abp.LocalizationManagement;
+
+public class LocalizationStoreCacheInitializeContext
+{
+ public IServiceProvider ServiceProvider { get; }
+ public LocalizationStoreCacheInitializeContext(IServiceProvider serviceProvider)
+ {
+ ServiceProvider = serviceProvider;
+ }
+}
diff --git a/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStoreInMemoryCache.cs b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStoreInMemoryCache.cs
new file mode 100644
index 000000000..91a2d0b24
--- /dev/null
+++ b/aspnet-core/modules/lt/LINGYUN.Abp.LocalizationManagement.Domain/LINGYUN/Abp/LocalizationManagement/LocalizationStoreInMemoryCache.cs
@@ -0,0 +1,242 @@
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Localization;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Caching;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.DistributedLocking;
+using Volo.Abp.Localization;
+using Volo.Abp.Threading;
+using Volo.Abp.Timing;
+
+namespace LINGYUN.Abp.LocalizationManagement;
+
+[ExposeServices(
+ typeof(ILocalizationStoreCache),
+ typeof(LocalizationStoreInMemoryCache))]
+public class LocalizationStoreInMemoryCache : ILocalizationStoreCache, ISingletonDependency
+{
+ private string _cacheStamp;
+ private DateTime? _lastCheckTime;
+ private readonly SemaphoreSlim _syncSemaphore;
+
+ protected LocalizationResourceDictionary Resources { get; }
+ protected LocalizationLanguageDictionary Languages { get; }
+ protected LocalizationDictionaryWithResource LocalizedStrings { get; }
+
+ private readonly IClock _clock;
+ private readonly IDistributedCache _distributedCache;
+ private readonly IAbpDistributedLock _distributedLock;
+ private readonly AbpDistributedCacheOptions _distributedCacheOptions;
+ private readonly AbpLocalizationManagementOptions _managementOptions;
+
+ public LocalizationStoreInMemoryCache(
+ IClock clock,
+ IDistributedCache distributedCache,
+ IAbpDistributedLock distributedLock,
+ IOptions distributedCacheOptions,
+ IOptions managementOptions)
+ {
+ _clock = clock;
+ _distributedCache = distributedCache;
+ _distributedLock = distributedLock;
+ _distributedCacheOptions = distributedCacheOptions.Value;
+ _managementOptions = managementOptions.Value;
+
+ _syncSemaphore = new SemaphoreSlim(1, 1);
+ Resources = new LocalizationResourceDictionary();
+ Languages = new LocalizationLanguageDictionary();
+ LocalizedStrings = new LocalizationDictionaryWithResource();
+ }
+
+ public async virtual Task InitializeAsync(LocalizationStoreCacheInitializeContext context)
+ {
+ using (await _syncSemaphore.LockAsync())
+ {
+ await EnsureCacheIsUptoDateAsync(context);
+ }
+ }
+
+ public virtual IDictionary GetAllLocalizedStrings(string cultureName)
+ {
+ var localizedStrings = new Dictionary();
+
+ foreach (var resource in Resources)
+ {
+ var localizedStringsInResource = LocalizedStrings.GetOrDefault(resource.Key);
+ if (localizedStringsInResource == null)
+ {
+ continue;
+ }
+
+ var localizedStringsInCurrentCulture = localizedStringsInResource.GetOrDefault(cultureName);
+ if (localizedStringsInCurrentCulture == null)
+ {
+ continue;
+ }
+
+ var currentCultureLocalizedStrings = new LocalizationDictionary();
+
+ foreach (var localizedString in localizedStringsInCurrentCulture)
+ {
+ if (!currentCultureLocalizedStrings.ContainsKey(localizedString.Key))
+ {
+ currentCultureLocalizedStrings.Add(localizedString.Key, localizedString.Value);
+ }
+ }
+
+ localizedStrings[resource.Key] = currentCultureLocalizedStrings;
+ }
+
+ return localizedStrings;
+ }
+
+ public virtual LocalizedString GetLocalizedStringOrNull(string resourceName, string cultureName, string name)
+ {
+ var localizedStringsInResource = LocalizedStrings.GetOrDefault(resourceName);
+ if (localizedStringsInResource == null)
+ {
+ return null;
+ }
+
+ var currentCultureLocalizedStrings = localizedStringsInResource.GetOrDefault(cultureName);
+ if (currentCultureLocalizedStrings == null)
+ {
+ return null;
+ }
+
+ return currentCultureLocalizedStrings.GetOrDefault(name);
+ }
+
+ public virtual LocalizationResourceBase GetResourceOrNull(string resourceName)
+ {
+ return Resources.GetOrDefault(resourceName);
+ }
+
+ public virtual IReadOnlyList GetResources()
+ {
+ return Resources.Values.ToImmutableList();
+ }
+
+ public virtual IReadOnlyList GetLanguages()
+ {
+ return Languages.Values.ToImmutableList();
+ }
+
+ protected async virtual Task EnsureCacheIsUptoDateAsync(LocalizationStoreCacheInitializeContext context)
+ {
+ if (_lastCheckTime.HasValue &&
+ _clock.Now.Subtract(_lastCheckTime.Value).TotalSeconds < 30)
+ {
+ return;
+ }
+
+ var stampInDistributedCache = await GetOrSetStampInDistributedCache();
+
+ if (stampInDistributedCache == _cacheStamp)
+ {
+ _lastCheckTime = _clock.Now;
+ return;
+ }
+
+ await UpdateInMemoryStoreCache(context);
+
+ _cacheStamp = stampInDistributedCache;
+ _lastCheckTime = _clock.Now;
+ }
+
+ protected async virtual Task UpdateInMemoryStoreCache(LocalizationStoreCacheInitializeContext context)
+ {
+ var textRepository = context.ServiceProvider.GetRequiredService();
+ var languageRepository = context.ServiceProvider.GetRequiredService();
+ var resourceRepository = context.ServiceProvider.GetRequiredService();
+
+ var resourceRecords = await resourceRepository.GetListAsync();
+ var languageRecords = await languageRepository.GetActivedListAsync();
+ var textRecords = await textRepository.GetListAsync();
+
+ Resources.Clear();
+ Languages.Clear();
+
+ foreach (var resourceRecord in resourceRecords)
+ {
+ Resources[resourceRecord.Name] = new NonTypedLocalizationResource(resourceRecord.Name, resourceRecord.DefaultCultureName);
+
+ var localizedStrings = LocalizedStrings.GetOrDefault(resourceRecord.Name);
+
+ localizedStrings ??= new LocalizationDictionaryWithCulture();
+ localizedStrings.Clear();
+
+ var currentCultureLocalizedStrings = new LocalizationDictionary();
+
+ foreach (var textRecord in textRecords.Where(x => x.ResourceName == resourceRecord.Name))
+ {
+ currentCultureLocalizedStrings[textRecord.Key] = new LocalizedString(textRecord.Key, textRecord.Value);
+ }
+
+ localizedStrings[CultureInfo.CurrentCulture.Name] = currentCultureLocalizedStrings;
+
+ LocalizedStrings[resourceRecord.Name] = localizedStrings;
+ }
+
+ foreach (var language in languageRecords)
+ {
+ Languages[language.CultureName] = new LanguageInfo(
+ language.CultureName,
+ language.UiCultureName,
+ language.DisplayName,
+ language.FlagIcon);
+ }
+ }
+
+ protected async virtual Task GetOrSetStampInDistributedCache()
+ {
+ var cacheKey = $"{_distributedCacheOptions.KeyPrefix}_AbpInMemoryLocalizationCacheStamp";
+
+ var stampInDistributedCache = await _distributedCache.GetStringAsync(cacheKey);
+ if (stampInDistributedCache != null)
+ {
+ return stampInDistributedCache;
+ }
+
+ var distributedLockKey = $"{_distributedCacheOptions.KeyPrefix}_AbpLocalizationUpdateLock";
+ await using (var commonLockHandle = await _distributedLock
+ .TryAcquireAsync(distributedLockKey, _managementOptions.LocalizationCacheStampTimeOut))
+ {
+ if (commonLockHandle == null)
+ {
+ /* This request will fail */
+ throw new AbpException(
+ "Could not acquire distributed lock for localization stamp check!"
+ );
+ }
+
+ stampInDistributedCache = await _distributedCache.GetStringAsync(cacheKey);
+ if (stampInDistributedCache != null)
+ {
+ return stampInDistributedCache;
+ }
+
+ stampInDistributedCache = Guid.NewGuid().ToString();
+
+ await _distributedCache.SetStringAsync(
+ cacheKey,
+ stampInDistributedCache,
+ new DistributedCacheEntryOptions
+ {
+ SlidingExpiration = _managementOptions.LocalizationCacheStampExpiration
+ }
+ );
+ }
+
+ return stampInDistributedCache;
+ }
+}