sozsoft-platform/api/modules/Sozsoft.Languages/Sozsoft.Languages.Domain/Localization/DatabaseResourceLocalizer.cs
2026-10-05 15:58:59 +03:00

123 lines
4.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Localization;
using Sozsoft.Languages.Entities;
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
{
// 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;
private readonly IRepository<LanguageText, Guid> languageTextRepository;
private readonly IDistributedCache<LanguageTextCacheItem> cache;
private readonly ConcurrentDictionary<string, (LanguageTextCacheItem Item, long ExpiresAt)> localCache = new();
public DatabaseResourceLocalizer(
IRepository<LanguageText, Guid> languageTextRepository,
IDistributedCache<LanguageTextCacheItem> cache)
{
this.languageTextRepository = languageTextRepository;
this.cache = cache;
}
public LocalizedString GetOrNull(LocalizationResourceBase resource, string cultureName, string name)
{
var value = GetCacheItem(resource, cultureName)?.Dictionary?.GetOrDefault(name);
return value == null ? null : new LocalizedString(name, value);
}
/// <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();
protected LanguageTextCacheItem GetCacheItem(LocalizationResourceBase resource, string cultureName)
{
// culture, resource, key
var key = LanguageTextCacheItem.CalculateCacheKey(resource.ResourceName, cultureName);
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;
}
public void Fill(LocalizationResourceBase resource, string cultureName, Dictionary<string, LocalizedString> dictionary)
{
var item = GetCacheItem(resource, cultureName);
foreach (var kvp in item?.Dictionary ?? [])
{
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);
if (!TryGetLocal(key, out var item))
{
item = SetLocal(key, await cache.GetOrAddAsync(key, () => CreateCacheItemAsync(resource, cultureName)));
}
foreach (var kvp in item?.Dictionary ?? [])
{
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)
};
}
}