ASP.NET Core ile Çoklu Dil Destekli Uygulamalar Geliştirmek

Sinan BOZKUŞ
BilgeAdam Teknoloji
8 min readJul 17, 2019

Web sitelerimizi daha fazla kitleye ulaştırabilmek veya daha fazla ziyaret alabilmek gibi farklı sebeplerle çoklu dil desteği gerektiren yapılara ihtiyaç duyabilmekteyiz. Bu makale de günümüzde neredeyse standart hale gelen çoklu dil destekli web sitelerinin ASP.NET Core ile birlikte nasıl yapıldığını öğreneceğiz.

Örnekler ve uygulamalar ASP.NET Core 2.x sürümlerini destekleyecek şekilde olacaktır.

ASP.NET Core içerisinde Localization desteğini kullanabilmek için “Microsoft.AspNetCore.Mvc.Localization” sınıfına kütüphanesine ihtiyaç duymaktayız. İlgili kütüphane “Microsoft.AspNetCore.Mvc” içerisinde yer aldığından ayrıca referans vermemize gerek yoktur.

İlk iş olarak “Startup.cs” dosyası altında bulunan “ConfigureServices”metodu içerisinde gerekli sınıflarımızı kayıt etmemiz gerekiyor. Uygulamamız çalışmaya başladığında ilgili sınıflar burada belirtilen şekilde oluşturulacaktır.

Not: Anlatılan konudan bağımsız olduğu için ConfigureServices detaylarına bu makalede girmeyeceğiz, merak edenler ASP.NET Core Dependency Injectionolarak araştırabilirler.

// Kayıt işlemimizi gerçekleştiriyoruz, AddMvc() den önce eklediğinizden emin olunuz.services.AddLocalization(options =>
{
// Resource (kaynak) dosyalarımızı ana dizin altında "Resources" klasorü içerisinde tutacağımızı belirtiyoruz. options.ResourcesPath = "Resources";
});
services.AddMvc();

Kayıt işlemimizi tamamlandıktan sonra yine “Startup.cs” altında bulunan “Configure” metodu içerisinde dil desteğimizin ne şekilde çalışacağını ve hangi dillere destek vereceğimizi tanımlıyoruz. Bu metod çalışma zamanında (runtime) çalışır ve gelen HTTP istekleriyle ilgili yapılandırmalara izin verir.

Dil tanımlamalarımızı yaparken Türkçe ve İngilizce dillerine destek vereceğiz. Varsayılan dil tanımlamamız ise Türkçe olacaktır.

// Bu bölüm UseMvc()' den önce eklenecektir.
// Uygulamamız içerisinde destek vermemizi istediğimiz dilleri tutan bir liste oluşturuyoruz.
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("tr-TR"),
new CultureInfo("en-US"),
};
// SupportedCultures ve SupportedUICultures'a yukarıda oluşturduğumuz dil listesini tanımlıyoruz.
// DefaultRequestCulture'a varsayılan olarak uygulamamızın hangi dil ile çalışması gerektiğini tanımlıyoruz.
app.UseRequestLocalization(new RequestLocalizationOptions
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
DefaultRequestCulture = new RequestCulture("tr-TR")
});

CultureInfo ve RequestCulture sınıfları için aşağıdaki namespace lerin ekli olduğundan emin olalım.

using System.Globalization;
using Microsoft.AspNetCore.Localization;

Not: Uygulamalarınızda İngilizce diline destek verecekseniz “en-US” kullanımının Amerikan İngilizcesini, “en-GB” kullanımının ise İngiliz İngilizcesini belirttiğini unutmayınız.

Dil desteğiyle ilgili tanımlamalarımızı yaptıktan sonra şimdi ilgili metinleri tutacağımız dil dosyalarımızı oluşturabiliriz. Bunun için uygulamamızın ana dizininde “Resources” adında yeni bir klasör oluşturuyoruz. Bu klasör adını “Startup.cs” dosyasındaki “ConfigureServices” altında belirtmiştik. İsimlendirme konusunda özgürüz.

Uygulama içerisinde ek bir Controller veya View oluşturmayacağız, yeni bir ASP.NET Core 2.x projesi oluşturduğumuzda içerisinde varsayılan olarak gelen “HomeController.cs” ve bunun View dosyalarından faydalanacağız.

Not: VS 2019 Update’i ile birlikte Contact action’ı varsayılan olarak gelmemektedir. HomeController.cs içerisinde Contact adında bir action ve buna karşılık bir View oluşturabilirsiniz.

Controller İçerisinden Dil Dosyalarının Kullanımı

İlk olarak “HomeController.cs” içerisinde bulunan “Contact” isimli Action’dan başlayacağız. Buradaki ViewData[“Message”]‘ı kullanıcının seçmiş olduğu dil değerine göre getireceğiz. Bunun için az önce oluşturduğumuz “Resources” klasörü altına “Controllers.HomeController.tr-TR.resx” ve “Controllers.HomeController.en-US.resx” şeklinde iki tane resource dosyası oluşturuyorum. (“Resources” klasörüne sağ tıklayıp “Add New Item” dedikten sonra “Resources File” ı seçmeniz gerekmektedir.)

Dosya adlarını isteğimize bağlı olarak iki farklı şekilde oluşturabiliriz. Biz örneğimizde noktalı şekilde kullanacağız.

Oluşturmuş olduğumuz her iki resource dosyası içerisine de “Message” adında bir anahtar ekliyoruz ve değerlerine Türkçe için “İletişim sayfanız.”, İngilizce için ise “Your contact page.” yazıyoruz.

Controllers.HomeController.tr-TR.resx

Controllers.HomeController.en-US.resx

Resource dosyalarıyla ilgili işlemlerimiz tamamlandı. Şimdi oluşturduğumuz bu resource dosyalarımızı uygulamamızda kullanalım.

“HomeController.cs” dosyasına geliyoruz ve burada bir localizer oluşturacağız. Oluşturacağımız bu localizer IStringLocalizer tipinde olacak ve kullanmak istediğimiz Controller’ı içine gönderip bize ilgili resource dosyasındaki değerlerin gelmesini sağlayacağız. Bu localizer’ı oluştururken dependency injection’dan yararlanıp constructor injection yapacağız.

Aşağıdaki kodları Controller’ımızın hemen üst kısmına ekliyoruz.

private readonly IStringLocalizer<HomeController> _localizer;public HomeController(IStringLocalizer<HomeController> localizer)
{
_localizer = localizer;
}

Kodların aşağıdaki şekilde görünmesi gerekiyor.

_localizer nesnemizi ürettiğimize ve bunu dependency injection yöntemi ile dolduruğumuza göre artık bu nesneyi kullanabiliriz.

Contact isimli Action’ımıza geliyoruz ve buradaki ViewData[“Message”]mesajının içeriğini dil dosyamızdan (resource) çekiyoruz. Bunun için ister “_localizer[“Message”]” şeklinde isterseniz “_localizer.GetString(“Message”)” şeklinde kullanabiliriz. Localizer içerisinde tanımladığımız “Message” anahtarı Resources dosyamızdaki Message anahtarına karşılık geliyor. ViewData içerisinde kullandığımız Message ile bir bağlantısı bulunmamakta.

Contact Action’ımızı aşağıdaki şekilde düzenliyoruz.

public IActionResult Contact()
{
//ViewData["Message"] = _localizer["Message"];
ViewData["Message"] = _localizer.GetString("Message");
return View();
}

Uygulamamızı çalıştırıyoruz ve menüden Contact’a tıklayarak ilgili sayfaya gidiyoruz. Karşımıza bilgisayarınızın diline göre bir karşılama mesajı gelecektir.

Şimdi dil desteğimizin çalıştığından emin olalım ve sayfamızı iki farklı şekilde çağıralım.

http://localhost:53218/Home/Contact?culture=tr-TR

http://localhost:53218/Home/Contact?culture=en-US

* 53218 yerine uygulamanızın çalıştığı port değerini yazmanız gerekmektedir.

Uygulamanızı bu şekilde çağırdığınızda QueryString ile belirtmiş olduğunuz culture değerine göre mesajın değiştiğini göreceksiniz.

Aşağıdaki QueryString tanımlamalarını da kullanabilirsiniz.

?culture=tr-TR
?ui-culture=tr-TR
?culture=tr-TR&ui-culture=tr-TR

Buraya kadar öğrendiklerimiz her bir Controller dosyası için bir dil dosyası (resource) oluşturmak şeklindeydi. “Kaydet”, “İptal” gibi uygulamamızın her alanında kullandığımız genel tanımlamalar için ise paylaşılan bir dil dosyasına (resource) ihtiyacımız olacak. Bunun için Models klasörü altına “SharedResources.cs” şeklinde dummy (yapmacık) bir class oluşturuyoruz. Uygulamanızın yapısına göre farklı bir klasör altında da oluşturabiliriz. Bu class’a karşılık yine Resources klasörü altında bir dil dosyası (resource) oluşturuyoruz. Class’ımızı “Models” klasörü altına koyduğumuzdan dil dosyamızın adı “Models.SharedResource.tr-TR.resx”şeklinde olacak.

Bundan sonrasında dil dosyalarımıza ulaşmak için HomeController.cs içerisinde IStringLocalizer<HomeController>‘ı nasıl kullandıysak aynı şekilde IStringLocalizer<SharedResources>‘ ı kullanabiliriz.

Dil dosyaları içerisinde tutmuş olduğunuz metinler HTML formatında ise IStringLocalizer yerine IHtmlLocalizer tercih etmemiz gerekmektedir.

Viewler (Arayüzler) İçerisinden Dil Dosyalarının Kullanımı

Aynı Controllerlarda olduğu gibi Viewlerimiz için de dil dosyaları (resourcelar) kullanarak birden fazla dil destekleyen arayüzler hazırlayabiliriz.

“Startup.cs” dosyamıza gelerek “ConfigurationServices” metodu altında AddMvc()‘yi bulup sonuna gerekli kodları ekliyoruz.

// ViewLocalization desteğinin eklenmesini ve bunun da format olarak sonuna eklenecek yapıda olacağını belirtiyoruz.services.AddMvc().AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)

Viewler içerisinde localizer kullanabilmek için “Views” klasörü altındaki “_ViewImports.cshtml” dosyasına aşağıdaki “Microsoft.AspNetCore.Mvc.Localization” namespace’ini ekleyip inject (dependency injection) ile bir localizer nesnesi oluşturmamız gerekiyor.

@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer

Viewlerimiz için dil dosyalarımızı (resource) aynı Controllerlarda olduğu gibi iki farklı şekilde oluşturabiliyoruz. İster noktalı istersek dizin yapısını tercih edebiliyoruz.

Resources klasörü altında “Views.Home.Contact.tr-TR.resx” ve “Views.Home.Contact.en-US.resx”isimlerinde iki tane dil dosyası (resource) oluşturuyoruz.

Dil dosyalarımızın içerisine “Merhaba” anahtarıyla aşağıdaki metinleri ekliyoruz.

Views.Home.Contact.tr-TR.resx

Views.Home.Contact.en-US.resx

Contact.cshtml view dosyamıza geliyoruz ve içerisine “@Localizer[“Merhaba”]” şeklinde metni çağırıyoruz. Mevcut culture değerimize göre ilgili metin dil dosyalarından okunacaktır.

@Localizer[“Merhaba”]

Dil Seçeneklerine Göre Farklı Viewler (Arayüzler) Oluşturmak

Bir diğer alternatif olarak resource dosyaları ile uğraşmak istemiyorsak veya proje yapımız buna uygun değilse her dil için ayrı viewler de oluşturabiliriz. Bunun için View dizin yapımızın aşağıdaki şekilde olması gerekmektedir.

Dillere Göre Data Annotations Yapısını Kullanmak

Data Annotationslar ile birlikte dil desteği kullanabilmek için “Startup.cs”içerisindeki “ConfigureServices”metodunda bulunan AddMvc()’ye .”AddDataAnnotationsLocalization()” eklememiz gerekmektedir. İlgili kodu ekledikten sonra aşağıdaki gibi görünmesi gerekmektedir.

services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();

“HomeController.cs” içerisinde bulunan “Contact” sayfamız için örnek bir iletişim formu hazırlayacağız.

“Models” klasorü içerisinde “ContactFormViewModel.cs” adında yeni bir class oluşturuyoruz. İçeriği aşağıdaki şekilde olacaktır.

using System.ComponentModel.DataAnnotations;namespace ASPNETCore_Localization.Models
{
public class ContactFormViewModel
{
[Display(Name = "Ad Soyad")]
[Required(ErrorMessage = "{0} gereklidir.")]
public string AdSoyad { get; set; }
[Display(Name = "Mesaj")]
[Required(ErrorMessage = "{0} gereklidir.")]
public string Mesaj { get; set; }
}
}

Burada Name ve Message alanlarının ekranda görünen label değerleri için “Display” attribute’ünden, boş geçilememesi için ise “Required”attribute’ünden faydalandık. Hata mesajlarını ise açık bir şekilde Türkçe olarak yazdık.

Dil dosyalarımızı oluşturmak için yine “Resources” klasörümüze geliyoruz. “Models.ContactFormViewModel.en-US.resx”şeklinde yeni bir dil dosyası oluşturuyoruz ve içerisine ilgili metinlerin İngilizce karşılıklarını yazıyoruz.

“Views” klasörümüzün altına geliyoruz ve “Contact.cshtml” e aşağıdaki HTML kodlarını ekliyoruz.

@model ContactFormViewModel<form class="form-horizontal" method="post" action="Kaydet">
<div class="form-group">
<label class="col-sm-2 control-label" asp-for="AdSoyad">
</label>
<div class="col-sm-10">
<input type="text" class="form-control" asp-for="AdSoyad">
<span asp-validation-for="AdSoyad"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" asp-for="Mesaj">
</label>
<div class="col-sm-10">
<input type="text" class="form-control" asp-for="Mesaj">
<span asp-validation-for="Mesaj"></span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Kaydet</button>
</div>
</div>
</form>

Şimdi formuzun gönderileceği Action’ı hazırlamamız gerekiyor. Bunun için “HomeController.cs” dosyasına aşağıdaki kodları ekliyoruz.

[HttpPost]
public IActionResult Kaydet(ContactFormViewModel viewModel)
{
return View("Contact", viewModel);
}

Uygulamamızı çalıştırdığımızda eğer culture değerimiz en-US ise İngilizce mesajların değilse Türkçe mesajların geldiğini göreceğiz. Test etmek için tarayıcıdan QueryString ile ?culture-en-US gönderebiliriz.

Routing Mekanizması ile Dil Desteğinin Sağlanması

Uygulamamızda dil desteğini direkt Url/Uri üzerinden vermek isteyebiliriz. Gelen culture değerine göre uygulamamızın dilinin ayarlanmasını sağlayabiliriz.

bilgeadam.com/tr-TR/Home
bilgeadam.com/en-US/Home

gibi…

Bunun için “Startup.cs” dosyamıza geliyoruz ve aşağıdaki kodları “Configure” metodu içerisine ekliyoruz. Kodları eklemeden öne metod içerisinde bulunan UseMvc() ve diğer dil tanımlama ayarlarımızı kaldırıyoruz.

// Uygulamamızın desteklediği dilleri tanımlıyoruz.
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("tr-TR"),
new CultureInfo("en-US"),
};
// Dil ayarlarını ve varsayılan dil seçimini tanımlıyoruz.
var localizationOptions = new RequestLocalizationOptions
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
DefaultRequestCulture = new RequestCulture("tr-TR"),
};
var requestProvider = new RouteDataRequestCultureProvider();
localizationOptions.RequestCultureProviders.Insert(0, requestProvider);
// Mevcut MVC routing yapısı yerine özelleştirilmiş yeni routing yapısını tanımlıyoruz.
app.UseRouter(routes =>
{
routes.MapMiddlewareRoute("{culture=tr-TR}/{*mvcRoute}", subApp =>
{
subApp.UseRequestLocalization(localizationOptions);
subApp.UseMvc(mvcRoutes =>
{
mvcRoutes.MapRoute(
name: "default",
template: "{culture=tr-TR}/{controller=Home}/{action=Index}/{id?}");
});
});
});

Github üzerinden uygulamaya ait kodları indirebilirsiniz.
https://github.com/sinanbozkus/ASPNETCore_Localization

Kaynaklar:
http://www.sinanbozkus.com/asp-net-core-ile-coklu-dil-destegi-olan-uygulamalar-gelistirmek/
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization
https://joonasw.net/view/aspnet-core-localization-deep-dive

--

--