<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel xmlns:media="http://search.yahoo.com/mrss/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <title>ABP.IO Stories</title>
    <link>https://abp.io/community/articles</link>
    <description>A hub for ABP Framework, .NET, and software development. Access articles, tutorials, news, and contribute to the ABP community.</description>
    <lastBuildDate>Mon, 26 Oct 2026 11:50:01 Z</lastBuildDate>
    <generator>Community - ABP.IO</generator>
    <image>
      <url>https://abp.io/assets/favicon.ico/favicon-32x32.png</url>
      <title>ABP.IO Stories</title>
      <link>https://abp.io/community/articles</link>
    </image>
    <a10:link rel="self" type="application/rss+xml" title="self" href="https://abp.io/community/rss?member=icagatay" />
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-framework-ile-dinamik-lokalizasyon-veritabani-tabanli-esnek-dil-yonetimi-g1a6lgdl</guid>
      <link>https://abp.io/community/posts/abp-framework-ile-dinamik-lokalizasyon-veritabani-tabanli-esnek-dil-yonetimi-g1a6lgdl</link>
      <a10:author>
        <a10:name>icagatay</a10:name>
        <a10:uri>https://abp.io/community/members/icagatay</a10:uri>
      </a10:author>
      <title>ABP Framework ile Dinamik Lokalizasyon: Veritabanı Tabanlı Esnek Dil Yönetimi</title>
      <description>ABP Framework ile dinamik olarak veritabanı destekli lokalizasyon işlemini nasıl gerçekleştirebileceğimizi adım adım ele alacağız.</description>
      <pubDate>Thu, 26 Jun 2025 12:14:49 Z</pubDate>
      <a10:updated>2026-10-26T09:47:14Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP Framework ile Dinamik Lokalizasyon: Veritabanı Tabanlı Esnek Dil Yönetimi</h1>
<h2>🎯 Giriş</h2>
<p>ABP Framework, güçlü bir yerelleştirme (localization) altyapısı ile gelir. Ancak varsayılan olarak çeviri metinleri <code>.json</code> 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.</p>
<p>Bu yazıda, <strong>ABP Framework’te dinamik olarak veritabanı destekli lokalizasyon</strong> işlemini nasıl gerçekleştirebileceğimizi adım adım ele alacağız. Örnek olarak oluşturulmuş <code>AbpDynamicResourceManagement</code> projesinden kod parçalarıyla ilerleyeceğiz.</p>
<hr />
<h2>🧱 Genel Mimari</h2>
<p>Dinamik lokalizasyon sistemi şu temel bileşenlerden oluşur:</p>
<ul>
<li><code>Language</code>: Tanımlı dillerin tutulduğu entity</li>
<li><code>LanguageText</code>: Her dile ait çevirilerin tutulduğu entity</li>
<li><code>ILanguageTextRepository</code>: Veri erişim işlemleri için repository arayüzü</li>
<li><code>DynamicLocalizationResourceContributor</code>: ABP'nin localization pipeline’ına entegre edilen özel sağlayıcı</li>
<li><code>AbpLocalizationOptions.GlobalContributors</code>: Yeni provider’ı tanıtan yapılandırma</li>
</ul>
<hr />
<h2>🗣️ Dil Tanımı: <code>Language</code> Entity'si</h2>
<pre><code class="language-csharp">public class Language : FullAuditedAggregateRoot&lt;Guid&gt;, 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; }
    
    // ...
}
</code></pre>
<p>Bu sınıf, sistemdeki dillerin tanımlandığı temel varlıktır. Çoklu tenant desteği için IMultiTenant arayüzü uygulanmıştır.</p>
<h2>📝 Çeviri Metinleri: LanguageText Entity'si</h2>
<pre><code class="language-csharp">public class LanguageText : AuditedAggregateRoot&lt;Guid&gt;, IMultiTenant
{
    public string ResourceName { get; set; }
    public string CultureName { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
    // ...
}
</code></pre>
<p>Her bir çeviri metni, ilgili kültür (CultureName) ve anahtar (Name) ile ilişkilendirilmiştir. Örneğin:</p>
<p>| CultureName | Name         | Value   |
| ----------- | ------------ | ------- |
| tr          | HelloMessage | Merhaba |
| en          | HelloMessage | Hello   |</p>
<h2>📦 Repository Katmanı</h2>
<p>Arayüz</p>
<pre><code class="language-csharp">public interface ILanguageTextRepository : IRepository&lt;LanguageText, Guid&gt;
{
    Task&lt;LanguageText&gt; GetdByCultureNameAndNameAsync(string cultureName, string name);
    Task&lt;List&lt;LanguageText&gt;&gt; GetListByCultureNameAsync(string cultureName);
}
</code></pre>
<p>Uygulama</p>
<pre><code class="language-csharp">public class LanguageTextRepository : EfCoreRepository&lt;AbpDynamicResourceManagementDbContext, LanguageText, Guid&gt;, ILanguageTextRepository
{
    public async Task&lt;LanguageText&gt; GetdByCultureNameAndNameAsync(string cultureName, string name)
    {
        var dbSet = await GetDbSetAsync();
        return await dbSet.FirstOrDefaultAsync(l =&gt; l.CultureName == cultureName &amp;&amp; l.Name == name);
    }

    public async Task&lt;List&lt;LanguageText&gt;&gt; GetListByCultureNameAsync(string cultureName)
    {
        var dbSet = await GetDbSetAsync();
        return await dbSet.Where(l =&gt; l.CultureName == cultureName).ToListAsync();
    }
}
</code></pre>
<p>Bu sınıf, veritabanı üzerinden dil verilerine ulaşmak için gerekli temel metodları sağlar.</p>
<h2>🧩 ABP’ye Entegrasyon: DynamicLocalizationResourceContributor</h2>
<pre><code class="language-csharp">public class DynamicLocalizationResourceContributor : ILocalizationResourceContributor
{
    private ILanguageTextRepository _languageTextRepository;

    public void Initialize(LocalizationResourceInitializationContext context)
    {
        _languageTextRepository = context.ServiceProvider.GetRequiredService&lt;ILanguageTextRepository&gt;();
    }

    public void Fill(string cultureName, Dictionary&lt;string, LocalizedString&gt; dictionary)
    {
        var texts = AsyncHelper.RunSync(() =&gt; _languageTextRepository.GetListByCultureNameAsync(cultureName));
        foreach (var item in texts)
        {
            dictionary[item.Name] = new LocalizedString(item.Name, item.Value);
        }
    }

    public async Task FillAsync(string cultureName, Dictionary&lt;string, LocalizedString&gt; 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(() =&gt; _languageTextRepository.GetdByCultureNameAndNameAsync(cultureName, name));
        return item == null ? null : new LocalizedString(name, item.Value);
    }

    public async Task&lt;IEnumerable&lt;string&gt;&gt; GetSupportedCulturesAsync()
    {
        return (await _languageTextRepository.GetListAsync()).Select(x =&gt; x.CultureName).Distinct();
    }
}
</code></pre>
<p>Bu sınıf, ABP’nin localization altyapısına bağlanarak .json yerine veritabanından verileri alır.</p>
<h2>⚙️ Modül Yapılandırması</h2>
<pre><code class="language-csharp">public class AbpDynamicResourceManagementDomainModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        Configure&lt;AbpLocalizationOptions&gt;(options =&gt;
        {
            options.Languages.Add(new LanguageInfo(&quot;en&quot;, &quot;en&quot;, &quot;English&quot;));
            options.Languages.Add(new LanguageInfo(&quot;tr&quot;, &quot;tr&quot;, &quot;Türkçe&quot;));

            options.GlobalContributors.Add&lt;DynamicLocalizationResourceContributor&gt;();
        });
    }
}
</code></pre>
<p>GlobalContributors koleksiyonuna DynamicLocalizationResourceContributor eklenerek sistemde kullanılacak lokalizasyon sağlayıcısı tanıtılır.</p>
<h2>🧪 Kullanım Örneği</h2>
<p>Bir sayfada lokalize bir mesaj göstermek istediğinizde ABP’nin sağladığı IStringLocalizer<T> ya da IStringLocalizerFactory kullanılabilir:</p>
<pre><code class="language-csharp">public class MyService : ITransientDependency
{
    private readonly IStringLocalizer&lt;TestResource&gt; _localizer;

    public MyService(IStringLocalizer&lt;TestResource&gt; localizer)
    {
        _localizer = localizer;
    }

    public void Foo()
    {
        var str = _localizer[&quot;HelloWorld&quot;];
    }
}
</code></pre>
<h2>🧩 Çok Kiracılı (Multi-Tenant) Destek</h2>
<p>Her iki entity (Language, LanguageText) IMultiTenant arayüzünü uyguladığı için ABP’nin tenant filtreleme altyapısından otomatik olarak faydalanır.</p>
<h2>📌 Sonuç</h2>
<p>Bu yapı sayesinde:</p>
<ul>
<li>.json dosyalarına bağlı kalmadan içerikleri dinamik olarak yönetebilirsiniz.</li>
<li>Yönetici paneli üzerinden canlı dil güncellemeleri yapabilirsiniz.</li>
<li>Her tenant için farklı dil çevirileri sunabilirsiniz.</li>
</ul>
<p>ABP Framework’ün sunduğu altyapı ile birlikte dinamik lokalizasyon, özellikle SaaS mimarilerinde çok güçlü bir özelliktir.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/images/others/blank-cover-image-150_79.png" />
      <media:content url="https://abp.io/images/others/blank-cover-image-150_79.png" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-razor-pages-ile-kullanici-bazli-tema-tercihi-uygulamasi-sjpxbyq7</guid>
      <link>https://abp.io/community/posts/abp-razor-pages-ile-kullanici-bazli-tema-tercihi-uygulamasi-sjpxbyq7</link>
      <a10:author>
        <a10:name>icagatay</a10:name>
        <a10:uri>https://abp.io/community/members/icagatay</a10:uri>
      </a10:author>
      <title>ABP Razor Pages ile Kullanıcı Bazlı Tema Tercihi Uygulaması</title>
      <description>ABP Framework kullanan bir Single Layer Razor Pages projesinde kullanıcıya özel ayarların nasıl yönetileceğini öğreneceğiz.</description>
      <pubDate>Tue, 24 Jun 2025 06:38:11 Z</pubDate>
      <a10:updated>2026-10-26T09:47:53Z</a10:updated>
      <content:encoded><![CDATA[<h1>ABP Razor Pages ile Kullanıcı Bazlı Tema Tercihi Uygulaması</h1>
<p>🎯 <strong>Amaç</strong></p>
<p>Bu yazıda, <strong>ABP Framework</strong> kullanan bir <strong>Single Layer Razor Pages</strong> projesinde <strong>kullanıcıya özel ayarların nasıl yönetileceğini</strong> öğreneceğiz. Örnek senaryo olarak kullanıcıların açık veya koyu tema tercihini uygulama arayüzüne yansıttığımız bir yapı geliştireceğiz.</p>
<p>🧩 Bu uygulamayla neler yapacağız?</p>
<ul>
<li>Her kullanıcı kendi tema tercihini seçebilecek</li>
<li>Seçilen tema <strong>veritabanında kullanıcıya özel olarak saklanacak</strong></li>
<li>Kullanıcı uygulamaya tekrar girdiğinde, <strong>seçtiği tema otomatik olarak yüklenecek</strong></li>
</ul>
<p>Bu senaryo, kişisel ayarların kullanıcı bazlı nasıl yönetileceğini ve ABP Framework’ün <code>ISettingManager</code> servisiyle nasıl çalıştığını pratik bir şekilde gösterecek.</p>
<hr />
<h2>📁 Proje Yapısı</h2>
<pre><code>UserBasedSetting.Web/
├── Pages/
│ ├── Index.cshtml / Index.cshtml.cs
│ └── Settings.cshtml / Settings.cshtml.cs
├── Settings/
│ ├── UserSettings.cs
│ └── UserThemeSettingDefinitionProvider.cs
└── UserBasedSettingWebModule.cs

</code></pre>
<p>📌 1. Ayar Anahtarının Tanımı</p>
<pre><code class="language-csharp">public static class UserSettings
{
    public const string PreferredTheme = &quot;AbpUserBasedSetting.User.PreferredTheme&quot;;
}
</code></pre>
<p>🧠 2. Ayar Tanımını Kaydet (UserThemeSettingDefinitionProvider)</p>
<pre><code class="language-csharp">public class UserThemeSettingDefinitionProvider : SettingDefinitionProvider
{
    public override void Define(ISettingDefinitionContext context)
    {
        context.Add(
            new SettingDefinition(
                UserSettings.PreferredTheme,
                defaultValue: &quot;light&quot;,
                isVisibleToClients: true
            )
        );
    }
}
</code></pre>
<p>⚙️ 3. Ayar Sağlayıcısını Module Sınıfında Kaydet</p>
<pre><code class="language-csharp">Configure&lt;AbpSettingOptions&gt;(options =&gt;
{
    options.DefinitionProviders.Add&lt;UserThemeSettingDefinitionProvider&gt;();
});
</code></pre>
<p>💾 4. Tema Seçimi Sayfası (Settings.cshtml)</p>
<p>📄 Pages/Settings.cshtml</p>
<pre><code class="language-csharp">@page
@model AbpUserBasedSetting.Pages.SettingsModel
@{
}

&lt;h3&gt;Theme Preferences&lt;/h3&gt;

&lt;form method=&quot;post&quot;&gt;
    &lt;select name=&quot;theme&quot; asp-for=&quot;Theme&quot;&gt;
        &lt;option value=&quot;light&quot;&gt;Light Theme&lt;/option&gt;
        &lt;option value=&quot;dark&quot;&gt;Dark Theme&lt;/option&gt;
    &lt;/select&gt;
    &lt;button type=&quot;submit&quot;&gt;Save&lt;/button&gt;
&lt;/form&gt;
</code></pre>
<p>📄 Pages/Settings.cshtml.cs</p>
<pre><code class="language-csharp">[Authorize]
public class SettingsModel : PageModel
{
    private readonly ISettingManager _settingManager;
    private readonly ICurrentUser _currentUser;

    public SettingsModel(ISettingManager settingManager, ICurrentUser currentUser)
    {
        _settingManager = settingManager;
        _currentUser = currentUser;
    }

    [BindProperty]
    public string Theme { get; set; }


    public async Task OnGetAsync()
    {
        if (_currentUser.IsAuthenticated)
        {
            Theme = await _settingManager.GetOrNullForUserAsync(UserSettings.PreferredTheme, _currentUser.Id.Value);
        }
    }

    public async Task&lt;IActionResult&gt; OnPostAsync()
    {
        if (_currentUser.IsAuthenticated)
        {
            await _settingManager.SetForUserAsync(_currentUser.Id.Value, UserSettings.PreferredTheme, Theme);
        }

        return RedirectToPage(&quot;/Index&quot;);
    }
}
</code></pre>
<p>🏠 5. Tema Uygulaması (Ana Sayfa Index.cshtml)</p>
<p>📄 Pages/Index.cshtml</p>
<pre><code class="language-csharp">@{
    var theme = Model.Theme ?? &quot;light&quot;;
}
&lt;a href=&quot;/settings&quot; class=&quot;btn btn-primary mb-2 mt-2 ml-2 @theme&quot;&gt;Settings&lt;/a&gt;
</code></pre>
<p>📄 Pages/Index.cshtml.cs</p>
<pre><code class="language-csharp">public class IndexModel : AbpPageModel
{
    private readonly ISettingManager _settingManager;
    private readonly ICurrentUser _currentUser;

    public IndexModel(ISettingManager settingManager, ICurrentUser currentUser)
    {
        _settingManager = settingManager;
        _currentUser = currentUser;
    }

    public string Theme { get; set; }

    public async Task OnGetAsync()
    {
        if (_currentUser.IsAuthenticated)
        {
            Theme = await _settingManager.GetOrNullForUserAsync(UserSettings.PreferredTheme, _currentUser.Id.Value);
        }

        Theme ??= &quot;light&quot;;
    }
}
</code></pre>
<p>📄 Pages/Index.cshtml.css</p>
<pre><code class="language-css">a.light {
    background-color: white;
    color: black;
}

a.dark {
    background-color: #121212;
    color: white;
}
</code></pre>
<p>🔍 Nasıl Çalışır?
AbpUserBasedSetting.User.PreferredTheme adlı özel bir ayar tanımlanır.
/Settings sayfasında kullanıcı tema tercihini belirler.
Tercih ISettingManager.SetForUserAsync ile kullanıcıya özel kaydedilir.
/Index sayfası açıldığında tema tercihi uygulanır.</p>
<hr />
<p>🚀 Kurulum Adımları</p>
<ol>
<li>Repoyu Klonla</li>
</ol>
<pre><code class="language-bash">git clone https://github.com/icagatay/AbpUserBasedSetting.git
cd AbpUserBasedSetting
</code></pre>
<ol start="2">
<li>Veritabanı Güncelle ve Projeyi Başlat</li>
</ol>
<pre><code class="language-bash">dotnet ef database update
dotnet run
</code></pre>
<p>🎉 Sonuç
Bu örnekle, ABP Framework’te ISettingManager kullanarak kullanıcı bazlı ayarların nasıl geliştirileceğini pratik bir şekilde uyguladık. Benzer şekilde dil tercihi, e-posta bildirim tercihi gibi birçok kişisel ayarı da aynı yöntemle yönetebilirsiniz.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/images/others/blank-cover-image-150_79.png" />
      <media:content url="https://abp.io/images/others/blank-cover-image-150_79.png" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/abp-framework-ile-paylasimli-hosting-ortaminda-pfx-sertifikasi-olmadan-openiddict-yayinlamak-iw6sp6ls</guid>
      <link>https://abp.io/community/posts/abp-framework-ile-paylasimli-hosting-ortaminda-pfx-sertifikasi-olmadan-openiddict-yayinlamak-iw6sp6ls</link>
      <a10:author>
        <a10:name>icagatay</a10:name>
        <a10:uri>https://abp.io/community/members/icagatay</a10:uri>
      </a10:author>
      <category>openiddict</category>
      <category>security</category>
      <title>ABP Framework ile Paylaşımlı Hosting Ortamında PFX Sertifikası Olmadan OpenIddict Yayınlamak</title>
      <description>Bazı paylaşımlı hosting ortamları .pfx sertifika dosyalarının yüklenmesine veya çalıştırılmasına izin vermez. Bu durumda OpenIddict ile JWT üretimi ve imzalama için alternatif çözümler gerekir. Bu çözüm, önceden oluşturulmuş RSA anahtar çifti kullanarak .pfx olmadan JWT altyapısı kurulabileceğini anlatır.</description>
      <pubDate>Wed, 04 Jun 2025 07:22:12 Z</pubDate>
      <a10:updated>2026-10-26T05:37:39Z</a10:updated>
      <content:encoded><![CDATA[<h1>abp-openiddict-rsa-sample</h1>
<h1>ABP Framework: Paylaşımlı Hosting Ortamında PFX Sertifikası Olmadan OpenIddict Yayınlamak</h1>
<h2>🎯 Amaç</h2>
<p>Bu repo, ABP Framework kullanılarak geliştirilen bir uygulamanın paylaşımlı hosting ortamında <code>.pfx</code> sertifikasına ihtiyaç duymadan OpenIddict ile token imzalama işlemini nasıl gerçekleştireceğini gösterir.</p>
<hr />
<h2>⚠️ Neden <code>.pfx</code> Dosyası Sorun Olur?</h2>
<p>Paylaşımlı hosting ortamlarında:</p>
<ul>
<li><code>.pfx</code> dosyasını okumak genellikle yasaktır,</li>
<li>Uygulama başlatılamaz veya token üretimi başarısız olur.</li>
</ul>
<hr />
<h2>✅ Çözüm: Kalıcı RSA Anahtarı ile Sertifikasız Token İmzalama</h2>
<p>OpenIddict, <code>.pfx</code> yerine <strong>kalıcı bir RSA özel anahtar dosyasını (PEM)</strong> doğrudan kabul eder.</p>
<h3>1. RSA Anahtarı Oluştur</h3>
<p>openssl genpkey -algorithm RSA -out signing-key.pem -pkeyopt rsa_keygen_bits:2048</p>
<p>Oluşan dosyayı keys/ klasörüne yerleştirin.</p>
<h3>2. Anahtarı Yükle (C#)</h3>
<pre><code class="language-csharp">using System.Security.Cryptography;

public static class RsaKeyLoader
{
    public static RSA LoadPrivateKey(string filePath)
    {
        var keyBytes = File.ReadAllBytes(filePath);
        var rsa = RSA.Create();
        rsa.ImportFromPem(System.Text.Encoding.UTF8.GetString(keyBytes));
        return rsa;
    }
}
</code></pre>
<p>ImportFromPem() .NET 5+ sürümlerinde desteklenir.</p>
<h3>3. OpenIddict’e Entegre Et</h3>
<p>MyProjectWebModule dosyasında:</p>
<pre><code class="language-csharp">PreConfigure&lt;OpenIddictServerBuilder&gt;(builder =&gt;
{
    var rsa = RsaKeyLoader.LoadPrivateKey(Path.Combine(&quot;keys&quot;, &quot;signing-key.pem&quot;));

    builder.AddSigningKey(new RsaSecurityKey(rsa)
    {
        KeyId = &quot;my-signing-key&quot;
    });
});
</code></pre>
<p>Güvenlik Tavsiyeleri
keys/signing-key.pem dosyasını .gitignore ile gizleyin:</p>
<pre><code class="language-bash">/keys/*.pem
</code></pre>
<p>Üretim ortamında bu dosyayı kimseyle paylaşmayın.</p>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/images/others/blank-cover-image-150_79.png" />
      <media:content url="https://abp.io/images/others/blank-cover-image-150_79.png" medium="image" />
    </item>
  </channel>
</rss>