Yazılım Mimarileri ve Tasarım Desenleri Üzerine

Asp.NET Core’da Çok Dilli Uygulamalar Geliştirme

Merhaba,

Her web uygulamasının ideali, üretildiği kültürün sınırlarını aşabilmek ve daha geniş kitlelere ulaşarak farklı coğrafyalara hitap edebilmektir. Bu ideali gerçekleştirebilmek ve kozmopolit bir platform oluşturabilmek için birden çok dili destekleyebilmek ve gelen ziyaretçilerin bölge ve kültürlerine uygun içerik sunabilecek uygulamalar geliştirmek gerekmektedir. Asp.NET Core mimarisi, bu ihtiyaca istinaden özünde zengin bir küreselleştirme(globalization) ve yerelleştirme(localization) desteği barındırmakta ve biz geliştiriciler açısından kolaylıkla çok dilli uygulamalar oluşturmamızı sağlayacak olan özellikler sunmaktadır. Bu içeriğimizde, web uygulamalarımızda ziyaretçilerin tercihlerine göre yerelleştirilmiş içerikler sunabilmek için kullanabileceğimiz yapıları sizlere tanıtacak ve çoklu dil desteğinin temelde hangi mantıkla sağlandığını pratiksel olarak inceliyor olacağız.

Başlarken

İçeriğimiz boyunca anlatılanlara tatbik amaçlı eşlik edebilmek istiyorsanız eğer makaleye başlamadan önce hali hazırda bir Asp.NET Core MVC ya da API uygulaması oluşturmanızı tavsiye ederim. Ben deniz bu içeriğimizdeki tüm kodları .NET 6 – Asp.NET Core mimarisi üzerinden örneklendireceğim ve çalışma neticesinde örnek projeyi makalenin sonunda paylaşıyor olacağım.

Asp.NET Core’da Localization(Yerelleştirme) Yapılandırması

Asp.NET Core mimarisinin en güçlü yanlarından biri doğuştan middleware yapılanmasına dayanmasıdır. Haliyle client’lardan gelen istekleri seri bir şekilde türlü kontroller ve iş akışlarından geçirebilmekte ve duruma göre an öncesi aksiyonlar alıp, gerekli öncül çalışmaları sağlayabilmekteyiz.

Bu mantıkla olayı değerlendirirsek eğer, bir client’ın bölgesine göre gerekli yerelleştirmeyi sağlayabilmek için yapılan isteğin handle edilme sürecinde kültürün otomatik olarak ayarlanması gerekmektedir. İşte bu sebepten dolayı UseRequestLocalization middleware’ini yapılandırmamız gerekmektedir. Bunun için öncelikle ilgili uygulamanın ‘Program.cs’ dosyasına gelerek(Asp.NET Core 6.0’dan önceki sürümlerde Startup.cs dosyasına) aşağıdaki konfigürasyonun sağlanması gerekmektedir.

.
.
.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews()
                .AddViewLocalization();

builder.Services.AddLocalization(options =>
{
    options.ResourcesPath = "Resources";
});
.
.
.

Yukarıdaki kod bloğuna göz atarsanız eğer; ilgili uygulamaya 9. satırda yerelleştirme ile ilgili AddLocalization servisinin eklendiğini görmekteyiz. Bu servis içerisindeki ‘ResourcesPath’ özelliği sayesinde, uygulamanın localization için nereden besleneceğine dair temel kaynak(resource) dosyalarının bulunduğu dizin bildirimini gerçekleştirmektedir.

Bunun dışında 6. satıra nazar eylersek eğer uygulamaya ‘AddControllersWithViews’ servisi akabinde AddViewLocalization servisi eklenmektedir. Bu servis ise MVC mimarisindeki V‘nin yani ‘View’ dosyalarının yerelleştirme için ihtiyacı olan hizmeti sağlamakta ve uygulamaya aktarmaktadır.

Uygulama Tarafından Desteklenecek Kültürlerin Yapılandırılması

Temel localization yapılandırmasından sonra sıra RequestLocalizationOptions konfigürasyonu üzerinden uygulama tarafından desteklenecek kültürlerin yapılandırmasına gelmiştir. Bunun için yine ‘Program.cs’ dosyasında(ya da Startup.cs) aşağıdaki gibi çalışılması yeterlidir;

.
.
.
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    options.DefaultRequestCulture = new("tr-TR");

    CultureInfo[] cultures = new CultureInfo[]
    {
        new("tr-TR"),
        new("en-US"),
        new("fr-FR")
    };

    options.SupportedCultures = cultures;
    options.SupportedUICultures = cultures;
});
.
.
.

Yukarıdaki kod bloğuna göz atarsanız eğer, uygulama sürecinde kullanılacak kültürlerin tanımlaması gerçekleştirilmiştir. Bizler örneklendirmemizde ‘Türkçe’, ‘İngilizce’ ve ‘Fransızca’ olmak üzere üç dil kullanacağız. Bu kültürler arasından ‘Türkçe’nin varsayılan kültür olarak belirlendiğine dikkatinizi çekerim.

Tüm bu işlemlerden sonra artık yapılması gereken tek hamle UseRequestLocalization middleware’inin çağrılarak, etkinleştirilmesidir.

.
.
.
app.UseRequestLocalization();
.
.
.

Kullanıcıların hangi kültürden olduğunu nereden anlayacağız?
Gelen isteklerin hangi kültürden olduğunu anlayabilmek için şu ana kadar konfigürasyonunu oluşturduğumuz UseRequestLocalization middleware’inin çalışma mantığını incelememiz yeterli olacaktır. UseRequestLocalization middleware’i tetiklendiği anda konfigüre edilmiş olan RequestLocalizationOptions türünden nesnenin ayarlarını uygulamaya yüklemekte ve gelen istekteki kültürü RequestCultureProvider şeklinde nitelendirilen bir sağlayıcı dizisiyle kontrol etmektedir. Bu sağlayıcı dizisi;

olmak üzere üç adettir. Kullanıcı yaptığı isteğin hangi kültürden olduğunu bu sağlayıcıların herhangi biriyle server’a bildirecektir. Haliyle bizler bu bildirime göre gerekli dil dönüşümünü sağlayacağız. Eğer ki kullanıcıdan herhangi bir provider’da kültür bilgisi söz konusu değilse o zaman ‘DefaultRequestCulture’ property’sine verilen kültür geçerli olacaktır.

Peki hoca! bu sağlayıcıları hafiften açıklayabilir misin?
Yazımızın devamında, çok dilliliği test edebilmek için bu provider’ları tek tek tam teferruatlı mevzu bahis ediyor olacağız. Amma velakin sizlerin meramını dindirebilmek ve hazır lafı geçmişken hafiften ön bilgi verebilmek için aşağıda sağlayıcılara dair kısaca açıklamalarda bulunalım.

Artık uygulama üzerinde temel localization konfigürasyonu tamamlanmış bulunmaktadır. Bundan sonra, farklı dillerde metin karşılığını barındıracak olan resource dosyalarının adlandırma ve konum kurallarının detaylarını konuşmaya geçebiliriz.

Resource Dosyalarını Adlandırma ve Konumlandırma Kuralları

Asp.NET Core uygulamalarında, klasik Asp.NET’te olduğu gibi simgeler, resimler, özelleştirilmiş dosyalar yahut sabit metinler için .resx uzantılı resource yapıları kullanılmaktadır. Bu yapılar sayesinde harici uzantıları ve sabit metinleri kaynak kodundan ayırabilmekte ve daha dinamik bir şekilde yönetebilmekteyiz. Bu mantıkla farklı kültür ve dillere dair gerekli verileri resource dosyalarında tutmamız ve o anki kültüre göre ihtiyaç doğrultusunda ilgili resource dosyasından verileri getirmemiz oldukça ideal bir yaklaşım olacaktır.

Peki resource dosyalarını nerede ve nasıl oluşturmalıyız?
Çoklu dil operasyonları için oluşturulacak resource dosyalarında isim standardı
[file-name].[language-code].resx şeklinde olacaktır. Buna örnek vermemiz gerekirse eğer; İngilizce dil verilerini depolayacağımız resource kaynağının adı Lang.en-US.resx olacakken, benzer mantıkla Fransızca için Lang.fr-FR.resx şeklinde olacaktır. Eee haliyle Türkçe dil kaynağının ise Lang.tr-TR.resx olacaktır. Tabi buradaki isim formatı her ne kadar bir standart gerektirse de kendi kararınıza göre değiştirilebilir.

Resource dosyalarının nerede tanımlanacağına gelirsek eğer bunun için yukarıda AddLocalization servisini tanımlarken ResourcesPath property’sine verdiğimiz ‘Resources’ değerini kullanacağız. Bu değer, projenin kök dizininde ‘Resources’ adında bir klasör olacağı ve içerisinde .resx uzantılı dil kaynaklarının barındırılacağı anlamına gelmektedir.
Yukarıdaki görselde görüldüğü üzere ‘Resources\Languages‘ dizini altında örneklendirmemiz gereği üç dile karşılık dil depoları oluşturulmuş bulunmaktadır. Burada zihinlerinizden geçen Neden Resources klasörü altına Languages klasörü eklendi? sorusunu duyar gibiyim… Bunun nedeni birazdan bu resource dosyalarını okumayı ele alırken anlaşılacaktır. O noktaya gelmeden şimdilik bu kaynak dosyalarını açalım ve içlerini aşağıdaki gibi olacak şekilde dolduralım.

Lang.tr-TR Lang.fr-FR Lang.en-US

Resource Dosyalarının Okunması

Ziyaretçilerin kültürlerine göre resource dosyalarının okunabilmesi için temelde

olmak üzere iki farklı yerel servis mevcuttur. Her bir servisin kendi şahsına münhasır davranış modelinin mevcut olmasının yanında tümünde ortak olan teknik bir nokta vardır. O da, tüm servisler de resource dosyalarının okunmasının pratikte aynı altyapıyı gerektirmesidir. Haliyle bu servisleri incelemeye gelmeden önce mevzu bahis altyapıyı oluşturup hali hazır duruma getirelim.

Bunun için tek yapılması gereken resource dosyalarıyla aynı isimde olan bir sınıfın oluşturulmasıdır. Bizlerin dil verilerini depoladığımız dosyalara tekrar göz atarsanız Lang.***.resx formatında oluşturulduğunu göreceksiniz. Haliyle bu resource dosyalarından okuma işlemini yapacak olan sınıfımızın adı da Lang.cs olacaktır. Burada isimlendirme formatının kritik arz ettiği aşikardır. Dosyaların formatı ne ise, okuma işleminden sorumlu sınıfın adı da bire bir o olmalıdır.

Peki bu sınıf nerede oluşturulacaktır?
İşte bu sorunun cevabı yukarıda oluşturulan ‘Resources\Languages‘de saklıdır. Evet, bu sınıf projenin kök dizininde bulunan ‘Languages’ isimli bir klasörde tanımlanmalıdır. Ya da bu sınıfın namespace’i namespace Project.Languages şeklinde olmalıdır. Aksi taktirde .NET Core’da ki yerel localization servisleri bu sınıfın dizinini, ilgili dil verilerinin bulunduğu depoların diziniyle eşleştiremeyecek ve böylece veri okuma kabileyeti söz konusu olmayacaktır. Burada genelleme yaparsak eğer resource dosyaları ‘a\b’ dizininde ise bu resource dosyalarındaki verileri okuyacak sınıf proje kök dizini içerisinde tanımlanmış ‘b’ dizinin de olmalıdır.

Şimdi localization açısından resource dosyalarını okumamızı sağlayacak servisleri sırasıyla incelemeye geçebiliriz.

RequestCultureProvider İle Kullanıcı Kültürünü Yakalama

Yazımızın önceki satırlarında, ziyaretçilerden gelen istekler üzerinden kültürlerini RequestCultureProvider ismi verilen sağlayıcılar sayesinde edinebileceğimizden ve bu sağlayıcıların QueryStringRequestCultureProvider, CookieRequestCultureProvider ve AcceptLanguageHeaderRequestCultureProvider olmak üzere üç adet olduğundan bahsetmiştik. Şimdi gelin, ziyaretçilerin kültürlerini öğrenebilmemizi sağlayacak olan bu sağlayıcıları tek tek teferruatlarıyla inceleyelim.

Web Uygulamasında Desteklenen Dillerin Bilgisini Almak

Localization operasyonlarında en önemli hususlardan biri uygulama sürecinde desteklenmekte olan dillerin toplu bilgisini alabilmektir. Bunun için türlü yöntemler uygulanabileceği gibi .NET Core localization servislerinin bizlere sağladığı aşağıdaki gibi pratik dokunuşlar da mevcuttur.

    public class LangController : Controller
    {
        readonly RequestLocalizationOptions _localizationOptions;

        public LangController(IOptions<RequestLocalizationOptions> localizationOptions)
            => _localizationOptions = localizationOptions.Value;

        public IActionResult AllLanguages()
        {
            IRequestCultureFeature requestCulture = HttpContext.Features.Get<IRequestCultureFeature>();
            var allCultures = _localizationOptions.SupportedCultures
                    .Select(culture => new
                    {
                        Name = culture.Name,
                        Text = culture.DisplayName
                    }).ToList();
            return Ok(allCultures);
        }
    }

Görüldüğü üzere IRequestCultureFeature arayüzü sayesinde uygulamanın ‘Program.cs'(Startup.cs) dosyasında konfigüre edilen RequestLocalizationOptions servisinin içerisinde tanımlanan tüm kültürler basit bir şekilde tarafımızca elde edilmiştir.

Nihai olarak;
Bu içeriğimizde, web geliştiricileri açısından korkutucu bir konu olan çoklu dil desteğinin özünde gayet basit ve sıradan bir mimariye sahip olduğunu gözlemlemiş olduk. Hele hele bu sıradanlığın Asp.NET Core mimarisiyle daha da basit bir hale getirildiğini görmüş ve hiç kompleks işleme gerek duyulmaksızın hızlıca nasıl entegre yapılabileceğini tecrübe etmiş olduk. Bu içeriğimiz boyunca, bir web uygulamasında yerelleştirme operasyonlarının nasıl yapılandırıldığına, çalışma zamanında ziyaretçilere uygun dile web uygulamasının nasıl eşlik edebileceğine ve belli başlı kültür sağlayıcılarının neler olduğuna teorik ve pratik olmak üzere temas etmeye çalıştık. Bol bol faydalamanız dileğiyle…

Sabredip okuduğunuz için teşekkür ederim.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Not : Örnek projeyi indirebilmek için buraya tıklayınız.

Mobil sürümden çık