AngularJS İle Çoklu Dil İçeren Uygulamalar Geliştirmek
İki hafta önce AngularJs Bootcamp de bu konu hakkında bir sunumum vardı. Sunumda sahnenin verdiği heyecandan dolayı atladığım şeyler olmuştur diye bu konuyu bir blog yazısında toplamanın iyi olabiliceğini düşündüm.
Konuya başlamadan önce bootcamp hakkındaki düşüncelerimi belirtmek istiyorum. Organizasyon sorunsuz ve sıfır aksama ile tamamlandı. Sunumlar arası oluşan aksamanın hiç olmadığı nadir etkinliklerden biri. 2 gün süren dolu dolu bir etkinlikti diyebilirim. Umarım böyle etkinliklerin sayısı gittikçe artar.
Gelelim konumuza;
internationalization (Uluslararasılaştırma) Nedir ? Tek sistemde birden fazla dil desteği vermeyi genel olarak Türkçe karşılığı uluslararasılaştırmak olan internationalization terimi ile adlandırıyorlar. Söylenmesi bile zor olan ve baya uzun olan bu kelimeyi i18n olarak kısaltmışlar. (i + 18 harf + n)
Localization (Yerelleştirme) Nedir ? Sorunumuz sadece bir sistemde dili çevirmekle bitmiyor. Tarih, zaman, para birimi, ısı birimleri ve bu tarz şeyleri de dille göre çevirilmesi gerekiyor. Bu işin kapsamı da Türkçe karşılığı yerelleştirmek olan Localization içinde yer alıyor. i18n gibi Localization kelimesini de l10n şeklinde kısaltmışlar.
Bu iki durumu da tamamladığımıza ürünümüz küreselleşmiş oluyor. Bu kavrama da Globalization diyorlar.
Dil Paketlerini isimlendirmek Sistemimizde birden fazla dil var. Her şey iyi güzel. Fakat dillerin birbirinden kolay ayırt edilebilmesi için bu isimlendirmenin belli bir standarta göre olması gerekiyor. Standarta uymanın artılarından biri ise herkes tarafından kabul görmüş ürünlerden dil bilgisi aldığınızda yine bu standart da olacağıdır. Örneğin kullanıcı ilk defa siteye girdiğinde kullandığı tarayıcının dilini varsayılan olarak gelmesini istiyorsunuz. navigator.language ile bu bilgiye eriştiniz. Kendi dilleriniz ile eşleştirmeniz gerekiyor. Eğer isimlendirmeniz bu standart da değil ise başlıyorsunuz ifleri yazmaya. Bu gibi sebeplerden dolayı kabul görmüş standartları kullanmak bence her zaman bir artısı vardır.
İsimlendirmeler Ülke + Bölge şeklinde yapılıyor. Örnek isimlendirmeler:
en_US (English US)
en_GB (English UK)
tr_TR (Türkiye)
Bu zamana kadar bu işleri nasıl yapıyorduk ? Herhangi bir Backend dilinin kullandığı template engine yardımı ile render ediyorduk. Örneğin gettext çok kullanılıyor. Bu şekilde herşey güllük gülistanlık gidiyorken neden client tarafında bu işlemi yapayım kı diyenleriniz oluyordur muhakkak. Burada da işin içine SPA lar (single page app.) giriyor. Sayfalar arası geçiş sayfa yüklenmeden yapılıyorken neden sadece dil değişimi için sayfayı yükleyelim ki. Sayfayı yenilediğimizde hem sayfanın tekrardan yüklenmesi hemde yüklendiği ara sizin client tarafında cachelediğiniz verinin gitmesi demek oluyor. Ekstra olarak bu tek sayfa uygulamayı geliştiriyorken bir framework kullanıyorsanız ve templatelerinizi farklı sayfalara parçaladıysanız o template leri render ederken de yine backend den render etmeniz gerekicek. Gittikçe karmaşık bir hale gidiyor.
Client tarafında çözüm üretmek ne kazandırıcak ? - SPA larda anlık dil değişimi. - Anlık oluşan olaylar kullanıcılar üzerinde bence güzel bir hissiyat bırakıyor. - Eğer işin fantezisine girerseniz şöyle bir şeyde yapabilirsiniz. Çeviri dosyalarınızı Firebase, Parse veya kendi yazdığınız bir WebSocket den serve ederseniz dil dosyasında herhangi bir değişim olduğu anda kullanıcı sayfayı yenilemeden onun ekranında bu işlemi yansıtabilirsiniz. - PO veya MO gibi uzantılar yerine JSON gibi temiz, okunaklı bir format da tutmuş oluyorsunuz. (Client de çözüm üretince illa bir JSON dosyasında tutacağınız anlamına da gelmiyor. Çeviri dosyalarını bir servisten de çekebilirsiniz. Bu gibi durumlar genelde sistem sahibinin kendi yönetim panelinden yeni dil ekleme çıkartma yapması durumlarında oluşuyor.)
AngularJS ile bir bakış. AngularJs ile Javascript kodundan çeviri işlemlerini oldukça başarılı bir şekilde ayırabiliyoruz. Aynı zamanda tarih zaman gibi şeylerin dille göre çevirimi çok pratik. Fakat AngularJS içinde dile göre içerik değişimi hakkında bir desteği yok. Ya bunu kendiniz yazmanız lazım yada bir paket tercih etmeniz lazım.
AngularJS tarih, zaman, para birimi gibi işlemleri dille göre çevirim yapan kısmı core kodundan ayrılmış durumda. Varsayılan olarak sadece ingilizce dile destek veriyor. Diğer diller için kullanmak istiyorsanız bower veya npm gibi bir paket yönetici ile bu paketi kurmanız gerekiyor.
$ bower install angular-i18n
Para Birimi, Tarih Çevirimleri
https://gist.github.com/bahattincinic/437e5b267ef2749d37d2
Kullanımı bu kadar basit. Sadece kullandığımız verinin tipini AngularJs e söylememiz yetiyor.
AngularJS deki Popüler İ18N Paketleri - github.com/rubenv/angular-gettext - github.com/angular-translate/angular-translate
i18n için bence en güzel AngularJS paketi diyebilirim. Çok esnek olması ve özelleştirilebilir olması ise en büyük artısı. Bu paketi geliştiren Pascal Precht yakın bir zamanda düzenlenen JSist konferansında sunum da yapmıştı.
Kurulum için:
$ bower install angular-translate
Basit bir örnek uygulama yapalım;
https://gist.github.com/bahattincinic/75ecfc470e136400dacf
Yukarıdaki örneği biraz inceleyelim. $translateProvider angular-translate paketi ile gelen özellikleri özelleştirebilmemizi sağlıyor. Örnekte çevirileri direk kod içerisinde olduğunu görüyoruz. key ve o keye karsılık gelen value lar şeklinde. angular-translate varsayılan olarak bu şekilde çalışıyor. Fakat size bunun dışında 3 tane daha yöntem sunuyor. Bunları da paketler haline getirmişler. Bu yöntemler şunlar;
- Çeviri içeriklerini apiden çekmek:
$ bower install angular-translate-loader-url
https://gist.github.com/bahattincinic/5105603a411f34c269f7
- Çeviri içeriklerini statik olarak dosyada tutmak:
$ bower install angular-translate-loader-static-files
https://gist.github.com/bahattincinic/6f075931584243c9fecc
- Çeviri içeriklerini parçalı tutmak (Her dili tek dosyada tutmak yerine mesela bunları modüllere göre parçalamak):
$ bower install angular-translate-loader-partial
https://gist.github.com/bahattincinic/dbae67fe0c5437676069
Sunduğu yöntemler bunlar. Bunların dışında kendinize özel bir şeyler yazmanıza da imkan tanıyor. Örneğin biz çeviri içeriklerimizi firebase de tutmak istiyoruz. Bununla ilgili küçük bir örnek yapalım.
https://gist.github.com/bahattincinic/077ea9e23557ae2bc57a
Örneğimizi incelemeye kaldığımız yerden devam edelim. preferredLanguage methodu eğer kullanıcı tarafından hiçbir dil seçilmemiş ise varsayılan dil atamada kullanılıyor.
Seçilen dili daha sonra hatırlanması için angular-translate bize 2 yöntem sunuyor.
Cookie de saklamak:
$ bower install angular-translate-storage-cookie
https://gist.github.com/bahattincinic/4b60c277ad08c2a5efea
localStorage da saklamak:
$ bower install bower install angular-translate-storage-local
https://gist.github.com/bahattincinic/176489785c1750601e8f
Bunların dışında özel olarak kendiniz de bir şeyler yazabiliyorsunuz. Örneği incelemeye kaldığımız yerden tekrardan devam :)
Javascript tarafında dil içeriklerimizi çektik dilin hatırlanmasını yaptık. Şimdi ise bu dil çevirisi içeriklerini template tarafında kullanalım
https://gist.github.com/bahattincinic/935440525c72e1a8531e
Her zaman template de çeviri yapmak sorunumuzu çözmeyebiliyor. Javascript tarafında kullanıcının yaptığı bir işlem sonrasında vermemiz gereken uyarıyı da çevirmemiz lazım.
https://gist.github.com/bahattincinic/d700cfb4ebe0feb738ee
Dil Değiştirmek:
Dil değiştirmek için $translate servisinin use fonksiyonunu kullanıyoruz. ($translate.use()) Bu fonksiyona parametre vermezsek aktif olan dili bize veriyor. Değer verirsek o değeri eskisi ile değiştiriyor.
https://gist.github.com/bahattincinic/da5891bea0ec8de37815
$locale değişimi
İçeriklerimizi çevirdik. Dil değiştirdiğimiz zaman tarih, zaman, para birimi gibi şeylerin değişmediğini gördük. Burada şöyle bir problem çıkıyor. AngularJS dinamik olarak $locale değişimine bir destek vermiyor. Dil değiştiği zaman o anda dile ait locale js dosyasını yükleyerek genelde çözüm buluyorlar. Bu işlemi yapan güzel bir paket var.
$bower install angular-dynamic-locale
https://gist.github.com/bahattincinic/da5891bea0ec8de37815
Örnek kod hakkında bahsedecek olursak, localeLocationPattern dil değiştiği anda ilgili dilin $locale içeriklerini nereden çekiceğinizi belirtmemize yarıyor. storeKey ise tuttuğu store daki (Cookie vs.) keyini temsil ediyor. useCookieStorage ise daha sonra hatırlaması için o anki dili cookie de tutmasını söylüyoruz. tmhDynamicLocale.set methodu ise locale değiştirmeye yarıyor.
Ek Not: - Buradaki para birimi çevirisi sadece sembolsel anlamda. Kur anlamında o paranın çevirilmesi sizin sorumluluğunuzda. - HTML5 form validation kullanıyorsanız verilen uyarılar tarayıcının diline göre veriliyor. Maalesef onu değiştiremiyorsunuz.
Sunumda yaptığım Demo: https://github.com/bahattincinic/angularjs-bootcamp
Sunum Linki: https://slides.com/bahattincinic/angularjs-ile-coklu-dil-destegi-veren-web-uygulamalari-gelistirmek/
İlgili Linkler
http://angular-translate.github.io/ https://github.com/lgalfaso/angular-dynamic-locale https://github.com/angular-translate/bower-angular-translate-storage-local https://github.com/angular-translate/bower-angular-translate-storage-cookie https://github.com/angular-translate/bower-angular-translate-loader-url https://github.com/angular-translate/bower-angular-translate-loader-static-files https://github.com/angular-translate/bower-angular-translate-loader-partial







