ABP Framework ile Dinamik Lokalizasyon: Veritabanı Tabanlı Esnek Dil Yönetimi
🎯 Giriş
ABP Framework, güçlü bir yerelleştirme (localization) altyapısı ile gelir. Ancak varsayılan olarak çeviri metinleri .json
dosyalarında sabitlenmiştir. Bu yapı, çevrimiçi yönetim paneli üzerinden dil içeriğini değiştirme, çok kiracılı uygulamalarda tenant bazlı çeviri sağlama gibi esneklikler sunmaz.
Bu yazıda, ABP Framework’te dinamik olarak veritabanı destekli lokalizasyon işlemini nasıl gerçekleştirebileceğimizi adım adım ele alacağız. Örnek olarak oluşturulmuş AbpDynamicResourceManagement
projesinden kod parçalarıyla ilerleyeceğiz.
🧱 Genel Mimari
Dinamik lokalizasyon sistemi şu temel bileşenlerden oluşur:
Language
: Tanımlı dillerin tutulduğu entityLanguageText
: Her dile ait çevirilerin tutulduğu entityILanguageTextRepository
: Veri erişim işlemleri için repository arayüzüDynamicLocalizationResourceContributor
: ABP'nin localization pipeline’ına entegre edilen özel sağlayıcıAbpLocalizationOptions.GlobalContributors
: Yeni provider’ı tanıtan yapılandırma
🗣️ Dil Tanımı: Language
Entity'si
public class Language : FullAuditedAggregateRoot<Guid>, ILanguageInfo, IMultiTenant
{
public Guid? TenantId { get; private set; }
public string CultureName { get; protected set; }
public string UiCultureName { get; protected set; }
public string DisplayName { get; protected set; }
public string? FlagIcon { get; set; }
public bool IsEnabled { get; set; }
// ...
}
Bu sınıf, sistemdeki dillerin tanımlandığı temel varlıktır. Çoklu tenant desteği için IMultiTenant arayüzü uygulanmıştır.
📝 Çeviri Metinleri: LanguageText Entity'si
public class LanguageText : AuditedAggregateRoot<Guid>, IMultiTenant
{
public string ResourceName { get; set; }
public string CultureName { get; set; }
public string Name { get; set; }
public string Value { get; set; }
// ...
}
Her bir çeviri metni, ilgili kültür (CultureName) ve anahtar (Name) ile ilişkilendirilmiştir. Örneğin:
CultureName | Name | Value |
---|---|---|
tr | HelloMessage | Merhaba |
en | HelloMessage | Hello |
📦 Repository Katmanı
Arayüz
public interface ILanguageTextRepository : IRepository<LanguageText, Guid>
{
Task<LanguageText> GetdByCultureNameAndNameAsync(string cultureName, string name);
Task<List<LanguageText>> GetListByCultureNameAsync(string cultureName);
}
Uygulama
public class LanguageTextRepository : EfCoreRepository<AbpDynamicResourceManagementDbContext, LanguageText, Guid>, ILanguageTextRepository
{
public async Task<LanguageText> GetdByCultureNameAndNameAsync(string cultureName, string name)
{
var dbSet = await GetDbSetAsync();
return await dbSet.FirstOrDefaultAsync(l => l.CultureName == cultureName && l.Name == name);
}
public async Task<List<LanguageText>> GetListByCultureNameAsync(string cultureName)
{
var dbSet = await GetDbSetAsync();
return await dbSet.Where(l => l.CultureName == cultureName).ToListAsync();
}
}
Bu sınıf, veritabanı üzerinden dil verilerine ulaşmak için gerekli temel metodları sağlar.
🧩 ABP’ye Entegrasyon: DynamicLocalizationResourceContributor
public class DynamicLocalizationResourceContributor : ILocalizationResourceContributor
{
private ILanguageTextRepository _languageTextRepository;
public void Initialize(LocalizationResourceInitializationContext context)
{
_languageTextRepository = context.ServiceProvider.GetRequiredService<ILanguageTextRepository>();
}
public void Fill(string cultureName, Dictionary<string, LocalizedString> dictionary)
{
var texts = AsyncHelper.RunSync(() => _languageTextRepository.GetListByCultureNameAsync(cultureName));
foreach (var item in texts)
{
dictionary[item.Name] = new LocalizedString(item.Name, item.Value);
}
}
public async Task FillAsync(string cultureName, Dictionary<string, LocalizedString> dictionary)
{
var texts = await _languageTextRepository.GetListByCultureNameAsync(cultureName);
foreach (var item in texts)
{
dictionary[item.Name] = new LocalizedString(item.Name, item.Value);
}
}
public LocalizedString? GetOrNull(string cultureName, string name)
{
var item = AsyncHelper.RunSync(() => _languageTextRepository.GetdByCultureNameAndNameAsync(cultureName, name));
return item == null ? null : new LocalizedString(name, item.Value);
}
public async Task<IEnumerable<string>> GetSupportedCulturesAsync()
{
return (await _languageTextRepository.GetListAsync()).Select(x => x.CultureName).Distinct();
}
}
Bu sınıf, ABP’nin localization altyapısına bağlanarak .json yerine veritabanından verileri alır.
⚙️ Modül Yapılandırması
public class AbpDynamicResourceManagementDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpLocalizationOptions>(options =>
{
options.Languages.Add(new LanguageInfo("en", "en", "English"));
options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe"));
options.GlobalContributors.Add<DynamicLocalizationResourceContributor>();
});
}
}
GlobalContributors koleksiyonuna DynamicLocalizationResourceContributor eklenerek sistemde kullanılacak lokalizasyon sağlayıcısı tanıtılır.
🧪 Kullanım Örneği
Bir sayfada lokalize bir mesaj göstermek istediğinizde ABP’nin sağladığı IStringLocalizer
public class MyService : ITransientDependency
{
private readonly IStringLocalizer<TestResource> _localizer;
public MyService(IStringLocalizer<TestResource> localizer)
{
_localizer = localizer;
}
public void Foo()
{
var str = _localizer["HelloWorld"];
}
}
🧩 Çok Kiracılı (Multi-Tenant) Destek
Her iki entity (Language, LanguageText) IMultiTenant arayüzünü uyguladığı için ABP’nin tenant filtreleme altyapısından otomatik olarak faydalanır.
📌 Sonuç
Bu yapı sayesinde:
- .json dosyalarına bağlı kalmadan içerikleri dinamik olarak yönetebilirsiniz.
- Yönetici paneli üzerinden canlı dil güncellemeleri yapabilirsiniz.
- Her tenant için farklı dil çevirileri sunabilirsiniz.
ABP Framework’ün sunduğu altyapı ile birlikte dinamik lokalizasyon, özellikle SaaS mimarilerinde çok güçlü bir özelliktir.
Comments
No one has commented yet, be the first to comment!