sozsoft-platform/api/modules/Sozsoft.Languages/Sozsoft.Languages.Domain/Localization/DatabaseResourceLocalizer.cs

124 lines
4.6 KiB
C#
Raw Normal View History

using System;
2026-10-05 12:58:59 +00:00
using System.Collections.Concurrent;
2026-02-24 20:44:16 +00:00
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Localization;
using Sozsoft.Languages.Entities;
2026-02-24 20:44:16 +00:00
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Localization;
using Volo.Abp.Threading;
namespace Sozsoft.Languages.Localization;
public class DatabaseResourceLocalizer : IDatabaseResourceLocalizer, ISingletonDependency
{
2026-10-05 12:58:59 +00:00
// Her metin aramasında sözlüğün tamamı Redis'ten çekilip çözülüyordu. Süreç içi katman bunu önler.
// Kısa ömürlüdür: metinlerin bir kısmı ham SQL ile yazılır (olay üretmez) ve diğer API örnekleri ile
// DbMigrator ayrı süreçtir; onların temizliği buraya en geç bu süre sonunda yansır.
private static readonly long LocalCacheDurationMs = (long)TimeSpan.FromSeconds(30).TotalMilliseconds;
2026-02-24 20:44:16 +00:00
private readonly IRepository<LanguageText, Guid> languageTextRepository;
private readonly IDistributedCache<LanguageTextCacheItem> cache;
2026-10-05 12:58:59 +00:00
private readonly ConcurrentDictionary<string, (LanguageTextCacheItem Item, long ExpiresAt)> localCache = new();
2026-02-24 20:44:16 +00:00
public DatabaseResourceLocalizer(
IRepository<LanguageText, Guid> languageTextRepository,
IDistributedCache<LanguageTextCacheItem> cache)
{
this.languageTextRepository = languageTextRepository;
this.cache = cache;
}
public LocalizedString GetOrNull(LocalizationResourceBase resource, string cultureName, string name)
{
2026-10-05 12:58:59 +00:00
var value = GetCacheItem(resource, cultureName)?.Dictionary?.GetOrDefault(name);
return value == null ? null : new LocalizedString(name, value);
2026-02-24 20:44:16 +00:00
}
2026-10-05 12:58:59 +00:00
/// <summary>Süreç içi katmanı boşaltır; dağıtık önbellek temizlendiğinde ve metin değiştiğinde çağrılır.</summary>
public void ClearLocalCache() => localCache.Clear();
2026-02-24 20:44:16 +00:00
protected LanguageTextCacheItem GetCacheItem(LocalizationResourceBase resource, string cultureName)
{
// culture, resource, key
var key = LanguageTextCacheItem.CalculateCacheKey(resource.ResourceName, cultureName);
2026-10-05 12:58:59 +00:00
if (TryGetLocal(key, out var local))
{
return local;
}
return SetLocal(key, cache.GetOrAdd(key, () => CreateCacheItem(resource, cultureName)));
}
private bool TryGetLocal(string key, out LanguageTextCacheItem item)
{
if (localCache.TryGetValue(key, out var entry) && entry.ExpiresAt > Environment.TickCount64)
{
item = entry.Item;
return true;
}
item = null;
return false;
}
private LanguageTextCacheItem SetLocal(string key, LanguageTextCacheItem item)
{
if (item != null)
{
localCache[key] = (item, Environment.TickCount64 + LocalCacheDurationMs);
}
return item;
2026-02-24 20:44:16 +00:00
}
public void Fill(LocalizationResourceBase resource, string cultureName, Dictionary<string, LocalizedString> dictionary)
{
var item = GetCacheItem(resource, cultureName);
2026-10-05 12:58:59 +00:00
foreach (var kvp in item?.Dictionary ?? [])
2026-02-24 20:44:16 +00:00
{
dictionary[kvp.Key] = new LocalizedString(kvp.Key, kvp.Value);
}
}
protected virtual LanguageTextCacheItem CreateCacheItem(LocalizationResourceBase resource, string cultureName)
{
var task = languageTextRepository.GetListAsync(a => a.ResourceName == resource.ResourceName && a.CultureName == cultureName);
var texts = AsyncHelper.RunSync(() => task);
return new LanguageTextCacheItem
{
Dictionary = texts.ToDictionary(a => a.Key, a => a.Value)
};
}
public async Task FillAsync(LocalizationResourceBase resource, string cultureName, Dictionary<string, LocalizedString> dictionary)
{
var key = LanguageTextCacheItem.CalculateCacheKey(resource.ResourceName, cultureName);
2026-10-05 12:58:59 +00:00
if (!TryGetLocal(key, out var item))
{
item = SetLocal(key, await cache.GetOrAddAsync(key, () => CreateCacheItemAsync(resource, cultureName)));
}
2026-02-24 20:44:16 +00:00
2026-10-05 12:58:59 +00:00
foreach (var kvp in item?.Dictionary ?? [])
2026-02-24 20:44:16 +00:00
{
dictionary[kvp.Key] = new LocalizedString(kvp.Key, kvp.Value);
}
}
protected virtual async Task<LanguageTextCacheItem> CreateCacheItemAsync(LocalizationResourceBase resource, string cultureName)
{
var texts = await languageTextRepository.GetListAsync(a => a.ResourceName == resource.ResourceName && a.CultureName == cultureName);
return new LanguageTextCacheItem
{
Dictionary = texts.ToDictionary(a => a.Key, a => a.Value)
};
}
}