İçeriğe geç →

ASP.NET Core MVC ve Elasticsearch’de Globalization

Merhaba arkadaşlar.

Biliyoruz ki günümüz teknoloji çağında firmalar, e-ticaret siteleri üzerinden hiç tanımadığı ve farklı şehirdeki insanlara ürünlerini satabilmektedirler. Bu satışlarını daha geniş bir alanda yapabilmek ve farklı ülkelere de satabilmek için ise, globalization konusu büyük bir önem taşımaktadır.

Bu makale içerisinde ise ASP.NET Core MVC ve Elasticsearch içerisinde, nasıl “globalization” desteğini sağlayabiliriz konusuna değinmeye çalışacağım.

1) ASP.NET Core MVC’de Globalization

Globalization konusunun önemiyle alakalı küçük bir giriş yapmanın ardından, ASP.NET Core MVC içerisinde globalization işlemlerini nasıl gerçekleştirebiliriz konusuna öncelikle bir bakalım. Globalization konusunda ASP.NET Core, bize farklı “dil” ve “culture” bilgileri için localization işlemlerini gerçekleştirebilmemiz adına middleware ve servisler sunmaktadır.

Localization işlemini gerçekleştirebilmek için uygulayacağımız üç temel nokta bulunmaktadır:

  1. Uygulamanın content’ini “localizable” bir hale getirmek
  2. İstenilen dil bilgileri için localize edilmiş “resource” dosyalarına sahip olmak
  3. Son olarak da configure edip bir “implementasyon stratejisi” belirlememiz gerekmektedir

Bu noktalardan yola çıkarak, uygulamanın content’ini nasıl localizable bir hale getirebiliriz konusuna bir bakalım.

NOT: Örnekler için “ASP.NET Core Web App (Model-View-Controller)” template’ini kullanacağım. “dotnet new mvc

1) Uygulamanın Content’ini Localizable Bir Hale Getirmek

Aslında, ASP.NET deki “.resx” resource dosyaları ile benzerlik göstermektedir. Hatırlarsak uygulama içerisinde desteklenen her bir culture bilgisi için bir “.resx” resource dosyası ekliyorduk. Ardından ise “Resources.Name” şeklinde erişim sağlayarak, culture bilgisine uygun olan localize edilmiş content’i kullanıyorduk.

ASP.NET Core içerisinde ise localize edilmiş content’i alabilmek için “Resources.Name” yaklaşımı yerine, “IStringLocalizer” ve “IStringLocalizer” tipinde iki adet abstraction bulunmaktadır.

Örneğin:

Localizer’ın kullanımına dikkat ettiniz mi? Localizer’ın içerisine bir key set etmek yerine, bir content set ettik. ASP.NET Core içerisindeki en büyük fark ise, istenilen culture bilgisi için localize bir content bulunamaz ise, içerisine set edilen content’i resource olarak kullanmaktadır. Bu sayede main culture bilgisi için bir resource dosyası oluşturmaya gerek kalmamaktadır. Tabi code review’lar sırasında magic string’lere takıntılı biri olarak, bu durumdan pek hoşlandığımı söyleyemem. 🙂

NOT: HTML içeren content’ler için ise, “IHtmlLocalizer” ve “IHtmlLocalizer” tipleri de bulunmaktadır.

Farklı tiplerdeki localizer’lara erişebilmek için ise birde “IStringLocalizerFactory” bulunmaktadır.

Örneğin shared resource’lara erişebilmek için, aşağıdaki gibi kullanabilmekteyiz.

Factory üzerinde bulunan “Create” method’u ile, istenilen resource için bir localizer oluşturabiliriz.

View localization kısmına baktığımızda ise, servis olarak kullanabileceğimiz “IViewLocalizer” bulunmaktadır. “ViewLocalizer” ise “IHtmlLocalizer” ı implemente etmektedir. Bu noktada Razor, parametreler hariç localize edilmiş string’i HTML encode yapmamaktadır.

Örnek bir view’a bakalım:

Yukarıdaki localize edilmiş string’in çıktısı, aşağıdaki gibi olacaktır.

Çıktıya baktığımızda ise Razor‘ın, parametre değerleri hariç localize edilmiş string’i HTML encode yapmadığını görmekteyiz.

View’ları en basit haliyle nasıl localize edebileceğimizi gördük. Şimdi birde Data Annotations‘ların localization konusuna bir bakalım. Bu noktada ise yine işimiz gayet basittir. ASP.NET Core içerisinde data annotations’lar hali hazırda “IStringLocalizer<” ile localize edilmiş durumdadır.

Örneğin:

Yukarıdaki gibi bir kullanımda “This field is required.” ve “Name” string’leri, istenilen culture’da bir resource dosyası varsa bu değerler localize edilecektir.

2) Localize Edilmiş Resource Dosyası Oluşturmak

Aslında makalenin girişinde de bahsettiğim gibi localize edilmiş resource dosyaları, ASP.NET‘deki “.resx” resource dosyaları ile aynıdır. Visual Studio üzerinden, aşağıdaki gibi ekleyebilmekteyiz.

NOT: Henüz Visual Studio Code ve Visual Studio for MAC üzerinden eklenememektedir. 🙁

Bu noktada dikkat etmemiz gereken önemli unsurlardan birisi, resource dosyalarının isimlendirilmesidir.

Hem class’ların hem de view’ların içerisinde kullanmış olduğumuz localizer’lar, iki farklı yöntemle localize edilmiş resource’lara erişim sağlamaktadır.

  1. Fully-qualified class name’inden erişebilmektedir. Örneğin: “Controllers.TodoController.en-US.resx” – “Views.Todo.Index-en-US.resx
  2. Folder structure’ı üzerinden de erişebilmektedir. Örneğin: “Resources/Controllers/TodoController.en-US.resx” – “Resources/Views/Todo/Index.en-US.resx”

Buradaki kullanım tercihi tamamen bize kalmış durumdadır.

3) Configurasyon ve Implementasyon Stratejisi

Bu noktaya kadar content’leri nasıl localize bir hale getirebiliriz konularına baktık. Şimdi ise localization servislerini, uygulama içerisine nasıl ekleyebiliriz ve configure edebiliriz kısımlarına değineceğiz.

İlk olarak resource dosyaları için olan path’i, Startup class’ı içerisinde aşağıdaki gibi belirleyebilmek mümkündür.

NOT: Eğer herhangi bir path set etmez isek, localization servisi culture resource’larını uygulamanın root folder’ı altında arayacaktır.

View’lar için localization’ı etkinleştirebilmek ise localization servisini aşağıdaki gibi service collection’ına ekleyebiliriz.

Eğer resource dosyaları oluşturmak yerine culture’lara göre farklı view’lar oluşturmak istersek, “LanguageViewLocationExpanderFormat” parametresi ile mümkündür.

LanguageViewLocationExpanderFormat” enum’ı, “SubFolder” ve “Suffix” değerlerine sahiptir.

  • Suffix: “Todo.en-US.cshtml
  • SubFolder: “en-US/Todo.cshtml

Data annotation’lar için ise localization’ı, “AddDataAnnotationsLocalization” method’u sağlayabiliriz:

Data annotation’lar için shared bir resource kullanmak istiyorsak eğer, aşağıdaki gibi configure etmemiz yeterli olacaktır.

Configure” method’u içerisinde eklememiz gereken bir diğer önemli middleware ise, Request Localization middleware’idir.

Burada uygulamanın destekleyeceği culture bilgilerini ve default culture bilgisini belirleyebiliriz.

Localization’ın son kısmı olarak implementasyon stratejilerine bakacağız. Request localization middleware’i, current culture’ı seçebilmek için default olarak üç farklı opsiyona sahiptir.

  1. QueryStringRequestCultureProvider
    QueryStringRequestCultureProvider”, “RequestCultureProvider” içerisinde ilk provider olarak kayıtlıdır. Query string üzerinden localization işlemini gerçekleştirmektedir. Örneğin: “http://localhost:5000/?culture=en-US
  2. CookieRequestCultureProvider
    “CookieRequestCultureProvider” ise kullanıcının seçmiş olduğu culture’ı, cookie bilgisini kullanarak track edebilmeyi sağlamaktadır.
  3. AcceptLanguageHeaderRequestCultureProvider
    AcceptLanguageHeaderRequestCultureProvider” ise kullanıcının browser’ında ön tanımlı olarak gelen culture bilgisine göre, uygulamanın localize edilmesini sağlamaktadır.

Opsiyonel olarak kullanabileceğimiz birde, “RouteDataRequestCultureProvider” vardır. Bununla birlikte route üzerinden localize edebilmek mümkündür. Örneğin: “http://localhost:5000/en-US/home

Bunun için:

Yukarıdaki kod bloğuna baktığımızda “MapMiddlewareRoute” method’u ile culture bilgisini, localization middleware’i çalışmadan önce yakalıyoruz. Bunu yapmamızın sebebi ise, localization middleware’inin MVC router’ından önce çalışıyor olmasıdır. Ardından “RouteDataRequestCultureProvider” ı ilk request culture provider’ı olarak insert ederek, request localization middleware’ini configure ettik. Son olarak route template’i içerisine ise birde culture bilgisini ekledik. Bu configuration ile, localization işlemi route üzerinden gelen culture bilgisi ile gerçekleştirilecektir.

NOT: Bu implementasyonları içeren bir örneği, makalenin sonunda paylaşıyor olacağım.

2) Elasticsearch İçerisinde Globalization

Elasticsearch kısmında ise her bir culture bilgisi için, farklı index’ler oluşturarak multi-language desteğini sağlayacağız. Burada tercih edebileceğiniz farklı yöntemlerde mevcut. Örneğin tek bir index üzerinde multi-field’lar ile multi-language desteğini sağlayabilmek gibi. Farklı index’ler oluşturarak multi-language desteği sağlamak, ileride farklı culture bilgilerinin de kolaylıkla entegre olabilmesi açısından kolaylık sağlayacaktır. Buna ek olarak her bir dil bilgisi için analyzer’lar ayarlayabilmek de kolay olacaktır. (Ben daha önce bu şekilde kullandım)

Örnek içerisinde ASP.NET Core MVC içerisinde NEST kütüphanesinin implementasyonunu gerçekleştireceğiz. Öncelikle “IProductService” isminde bir interface tanımlayalım.

CreateIndexAsync” method’u ile bir index oluşturup, “IndexAsync” method’u ile de product’ları indexleyeceğiz. Ardından kullanıcının istediği culture bilgisine göre ise search işlemini “SearchAsync” method’u ile gerçekleştireceğiz.

Şimdi “ProductService” isminde bir class oluşturalım ve aşağıdaki gibi implemente edelim.

IndexAsync” method’u ile “products_{langCode}” şeklinde bir index name oluşturduk ve product’ları bu index name ile indexleyeceğiz. “SearchAsync” method’u ile ise, istenilen dil bilgisine göre “Name” ve “Description” field’ları üzerinde full text search işlemini gerçekleştireceğiz.

Örnek olarak aşağıdaki gibi bir controller oluşturalım.

Controller’ın “Index” method’u içerisinde Türkçe ve İngilizce culture’ları için bir index oluşturacağız ve product’ları oluşturulan bu index’e feed edeceğiz. “Products” action içerisinde ise query string üzerinden alacak olduğumuz “keyword” ile, culture’a uygun product index’i üzerinden search işlemini gerçekleştireceğiz.

Products” action için ise aşağıdaki gibi bir view oluşturalım.

Docker üzerinde test işlemleri için single-node olarak bir Elasticsearch çalıştıralım.

Son olarak “Startup” class’ı içerisinde ise, “ProductService” ve “ConnectionSettings” class’larının service collection’ına injection işlemini aşağıdaki gibi gerçekleştirelim.

İşte bu kadar.

Şimdi projeyi “dotnet run” komut satırı ile çalıştıralım. Ardından “http://localhost:5000/tr-TR/home” URL’ine girelim. Böylece product index’i oluşturulacak ve product dokümanları indexlenecektir.

Şimdi search işlemi için “http://localhost:5000/tr-TR/home/products?keyword=Cep Telefonu” URL’ine browser üzerinden girelim ve sonucuna bakalım.

Eğer response’a bakarsak, resource’ların culture’a göre localize edildiğini, ayrıca product result’larının da “products_tr” index’i içerisinde arandığını görebiliriz.

Culture bilgisini birde “en-US“, keyword’ü ise “Cell Phone” olarak değiştirelim ve response’a tekrar bir bakalım. URL ise: “http://localhost:5000/en-US/home/products?keyword=Cell Phone“.

Şimdi ise resource’lar “en-US” culture’ı için localize olmuş durumda ve product result’ları ise “products_en” index’i içerisinde aranmıştır.

Örnek projeye ise buradan ulaşabilirsiniz: https://github.com/GokGokalp/Globalization-In-ASP.NET-Core-MVC-And-Elasticsearch

Referanslar

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization

https://joonasw.net/view/aspnet-core-localization-deep-dive

Bu makale toplam (2421) kez okunmuştur.

11
0



Kategori: ASP.NET Core Search Engine

2 Yorum

  1. Emre Emre

    Böyle kaliteli Türkçe kaynaklar için çok teşekkür ederim

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.