RSS

PHP çoklu dil desteği

29 24 Aralık 2011 ~ Musa Avcı — Orta

İşin aslı henüz  çoklu dil destekleyen bir site çalışmam olmadı. Fakat bu konularda bilgi edinmek amacıyla “gettext” ile karşılaştığımda yöntemin pratikliği ve fonksiyonları karşısında tüm çalışmalarıma çoklu dil desteği ekleyesim geldi.

Genelde çalışmamızın sonlarına geldiğimizde ya da önce Türkçe halini yaptıktan sonra çoklu dil olaylarına bakarız diye düşünürdüm. Ama bu düşünceyle gettext’in bize sağladığı en büyük kolaylığı aksine bir zorluğa çevirmiş oluyoruz. İşte bu yüzden henüz sizinde ihtiyacınız olmasa bile bu yöntemle tanışmanız için paylaşma gereksinimi duydum.

Gettext fonksiyonu

Bu yöntemin adı ve kullanım fonksiyonu gettext diye geçse de “_()” bu şekilde alt tire fonksiyonu haliyle de kullanabiliyoruz, ki zaten tercih edilen kullanımı da budur. Çalışmamızda kullanacağımız her dinamik olmayan cümle ve terimleri bu fonksiyondan geçirmemiz gerekiyor. Dinamik olmayandan kastım ise örneğin bir yazı sayfasında o yazının içeriği ya da başlığı gibi şeyler bu çeviri olayına dahil olmamalı.

Sadece kullanıma bir örnek göstermemiz gerekirse gayet basit ve işlere pek engel olmayacak bir tarzı olduğunu görebiliriz:

<h2><?php echo _('Hoşgeldiniz') ?></h2>
<p><?php echo _('Selam Dünya') ?></p>

Bu fonksiyonu kullanırken her seferinde echo demekten de kurtulmak içinde dilerseniz WordPress’in yaptığı gibi sizde bir “_e()” fonksiyonu oluşturabilirsiniz.

function _e($mesaj) {
   echo _($mesaj);
}

Eğer bunları şimdiden denemeye başladıysanız ve böyle fonksiyonların olmadığına dair hatalar alıyorsanız, büyük ihtimalle denediğiniz sunucuda gettext eklentisi kurulu değildir. Bu konularda bilgiliyseniz şuradan kütüphaneye ulaşıp sunucunuza kurabilirsiniz, eğer bilginiz yoksa sunucu sağlayıcınızla irtibata geçerek bu eklentinin kurulmasını isteyebilirsiniz.

Çevirinin yapılması ve dil dosyalarının hazırlanması

Başta bahsettiğim bu olayının en sevdiğim ve kullanmadan önce bilgi sahibi olunması gereken kısmı; projemizi hazırlarken yapmamız gereken tüm çevrilecek sabit yazıları “_()” ya da “_e()” fonksiyonuyla yazdırmamız. Varsayılan halinde çalışmaya hiç bir engeli olmadığı için bunu uygularken size herhangi bir rahatsızlık vermez.

Şimdi ben misalen bir blogun anasayfasını göstereyim.

<h2><?php echo $baslik ?></h2>
<p>
   <?php echo _('Yazar') . ': ' . $yazar ?> -
   <?php echo _('Tarih') . ': ' . date('d.m.Y') ?>
</p>
<p><?php echo $icerik ?></p>
<?php echo _('Yorumlar') ?>
...

Biz projemizi bu tarzda hazırladıktan sonra bunların bir dil dosyası oluşturulması için son aşamalara doğru Poedit adlı programı yükleyip açıyoruz.

Poedit’te “Dosya > Yeni kataloğu oluştur“a basıyoruz. Burada çıkan forma genel olarak yaptığınız projenin adını, e-mail adresinizi ve hazırlayacağınız çevirinin hangi dile ait olacağını belirtiyorsunuz. Varsayılan olarak projemizi Türkçe yazdığımız için ben bu örnekte çalışmamızın İngilizce çevirisini yapacağım.

Kullanmayacak olsak bile öğrenmemizi önerdiğim kısım

İşte ilk açıklamamda kullanmadan önce bilmemizi önerdiğim kısım burasıydı. Eğer bu olayı genel olarak projeyi tamamladıktan sonra araştırıp keşfetsek bu güzel özellik için geç kalmış oluyoruz ve tüm projeyi baştan kendimiz tarayıp tüm metinlere “_()” fonksiyonu eklemek durumunda kalıyoruz.

Bu formu doldurduktan sonra “Veri yolları” tabına gelip projemizin ana dizinini ekliyoruz. Benim örnek çalışma dosyam “/var/www/ornek” klasörü altında olduğu için burada bunu belirtiyorum. Bu sayede Poedit tüm projeyi tarıyarak çevrilecek metinleri bulup getiriyor ve dil dosyasını oluşturuyor.

Daha sonra “Anahtar kelimeler” tabından hangi fonksiyonları tarıyacağını belirtiyoruz. Varsayılan olarak “_” ve “gettext” eklidir, bunun dışında yukarıda belirttiğim gibi kendiniz bir “_e()” fonksiyonuda kullandıysanız buraya “_e” eklemeniz gerekecek.

Ve tamam dediğinizde .po dosyasının kaydedileceği yeri soruyor. Burada dosyayı kaydetmemiz gereken dizin önemli, temel olarak olması gereken yer şöyledir: “/diller/en_US/LC_MESSAGES/projemiz.po

Bu örnekte ben İngilizce’ye çevireceğim için en_US klasörüne atıyorum. Eğer yapacağım çeviri Türkçe olsaydı tr_TR klasöründe olurdu. Diğer dillerin kodlarını merak ediyorsan şuradan bakabilirsin.

Kaydedilecek yeri de belirttikten sonra karşımıza “Güncelleme özeti” başlığı altında projemizde kullandığımız ve kataloğuna ekleyeceği metinleri gösterir. Burada da onay verdikten sonra artık çevirmeye başlayıp dil dosyamızı hazırlayabiliriz.

Dilin belirlenmesi

Projemizi gettext ile hazırladık, sonrasında Poedit ile kataloğumuzu oluşturup çevirisini yaptık ve gerekli dizine yerleştirdik. Şimdi geldi projemize dili belirtmeye. Bunu ifade etmek için yaklaşık 6 satırlık bir ifadede bulunuyoruz:

<?php
// dili belirtiyoruz
$dil = 'en_US';
putenv('LC_ALL=' . $dil);
setlocale(LC_ALL, $dil);

// burada hangi kataloğumuzu kullanacağımızı
// ve dil dosyaların hangi dizinde olduğunu
// yani: /diller/en_US/LC_MESSAGES/projemiz.po
$katalog = 'projemiz';
bindtextdomain($katalog, "./diller");

// burada da kataloğumuzun adını belirtiyoruz.
textdomain($katalog);

$baslik = 'Deneme başlık';
$yazar = 'Musa Avcı';
$icerik = 'Selam Dünya';

// ve devamında örnek sayfamız ...
?>
<h2><?php echo $baslik ?></h2>
<p>
   <?php echo _('Yazar') . ': ' . $yazar ?> -
   <?php echo _('Tarih') . ': ' . date('d.m.Y') ?>
</p>
<p><?php echo $icerik ?></p>
<?php echo _('Yorumlar') ?>

Bu ifadeden sonra örnek çalışmamıza baktığımızda projenin İngilizce’ye çevrilmiş halini görmüş oluruz.

Eğer herhangi bir hata almadığınız halde halen Türkçe görüyorsanız bunun sebebi “setlocale” ve “putenv” fonksiyonlarda belirttiğimiz dil kodunun sisteminize kurulu dillerin adının böyle olmadığından kaynaklanabilir. Özetle sisteminizde İngilizce dili “en_US.utf8” ya da “en” adında tanımlanmış olabilir. Bu nedenle dizin adlarını değiştirmeden kodlardan bu dil değerini “en_US.utf8, en_US, en” gibi şekillerde değiştirerek tekrar deneyin.

Eğer Linux kullanıyorsanız “locale -a” komutu ile sisteminizde tanımlanmış dilleri görebilirsiniz.

Çevirilerin arasına değişken veriler eklemek

Bu durumuda yine basit bir örnekle izah etmek gerekirse, “Bu yazıya 12 adet yorum gönderilmiş” gibi bir cümlenin çevirisini yapmak için:

echo _('Bu yazıya ') . $yorum_sayisi . _(' adet yorum gönderilmiş.');

Tarzında bir yöntem uygulamak pek kullanışlı değil ve hatta diğer dillere çevrilirken gramer farklarından dolayı hatalı olabilir.

Bu olay içinde çok güzel bir yöntem mevcut, “printf” fonksiyonu sayesinde bir metnin içinde “%” işareti ile belirlediğimiz yerlere diğer parametrelerinde ifade ettiğimiz değerleri getirebiliyoruz.

$yorum_sayisi = 12;
printf('Bu yazıya %d adet yorum gönderilmiş', $yorum_sayisi)

Ekranda “Bu yazıya 12 adet yorum gönderilmiş” yazar. Eğer ekrana yazdırmak yerine sonucu döndürmesini istiyorsanız “sprintf” fonksiyonunu kullanabilirsiniz.

Bu fonksiyon sayesinde çeviride yapmak istediğimiz araya değişken ekleme olayını rahatlıkla yapabiliriz.

printf(_('Bu yazıya %d adet yorum gönderilmiş'), $yorum_sayisi)

Bu cümlenin İngilizce çevirisinde şöyle diyerek yorum sayısını rahatlıkla istediğimiz yerde kullanabiliriz: “This post have %d comments”

“%” işaretinden sonra kullandığımız “d” harfi yerine gelecek değerin yazı, tamsayı, işaretli sayı vs. gibi olduğunu ifade etmeye yarıyor. Bu harfleri şuarada biçem parametresinin 6. maddesinde inceleyebilirsiniz.

Bu olay yukarıda gösterdiğim gibi yapılsa da bu şekilde kullanılması pek kullanışlı değil, yani bu olay içinde bir fonksiyona ihtiyacımız var. Bunu da “_e” fonksiyonu üzerinden yapıp göstereyim:

function _e() {
   // fonksiyona gönderilen parametreleri esnek bir yöntem ile alıyoruz
   $parametreler = func_get_args();
   // ilk parametre çevrilecek metin olduğu için
   // _ fonksiyonu ile çevrilmiş hali ile değiştiriyoruz
   $parametreler[0] = _($parametreler[0]);
   // daha sonra printf fonksiyonunu manuel şekilde çağırıyoruz
   call_user_func_array('printf', $parametreler);
}

Bu fonksiyon ile “_e(‘Bu yazıya %d adet yorum yazılmış’, $yorum_sayisi)” dediğimizde sonuç olarak yapılacak işlem “printf(_(‘Bu yazıya %d adet yorum yazılmış’), $yorum_sayisi)” olacak. Ve eğer daha fazla parametre girmeye devam edersekte aynı şekilde onlarıda printf fonksiyonuna iletecektir.

Gettext ile plural (çoğul) ifadeler

Yukarıdaki örnekte diğer dillerde metin içerisine rahatlıkla yorum sayısı gibi değişken değerleri nasıl gösterileceğini “Bu yazıya %d adet yorum yazılmış” örneği ile verdim. Fakat İngilizce başta olmak üzere bazı dillerde tekil ya da çoğul şeyler farklı kelimelerle ifade ediliyor. Türkçe’de “1 Yorum” ile “10 Yorum” doğru olsa da İngilizce’de çoğul olduğunda “s” takısı alması gerekiyor, “1 Comment” ve “10 Comments” şeklinde.

Bu yöntem için “ngettext” fonksiyonunu kullanmamız gerekiyor, fakat istersen yukarıda hazırladığımız “_e” fonksiyonunu geliştirerek de bu yöntemi ekleyebilirsin. Espirisi gayet basit, birinci parametreye tekil, ikinci parametreye ise çoğul ifadede kullanılacak olan metin girilir ve son parametresine de metin içerisinde kullanılacak sayı değeri girilir. Eğer sayı 1 ise ilk ifade, birden büyük ise ikinci ifade kullanılır. Fakat yine içerisinde gerekli yere sayının yazılması için printf fonksiyonundan faydalanır.

printf(ngettext("%d comment", "%d comments", 1), 1); // 1 comment
printf(ngettext("%d comment", "%d comments", 2), 2); // 2 comments

Yalnız benim yukarıda hazırladığım örnekte varsayılanı Türkçe olan içeriğe İngilizce çeviri yaptık. Bu yöntemi doğru kullanabilmemiz için varsayılan dilimizin İngilizce olması gerekir. Çünkü Türkçe’de her iki tarafta aynı olacağı için İngilizce çevirisinde ayırt edemeyiz. Ama İngilizce’den Türkçe’ye çevirirken “%d comments” metnini de “%d yorum” şeklinde çevirebiliriz.

Sadece Türkçe ve Türkiye ile kısıtlı kalmayıp nice diller destekleyen nice başarılı projeleriniz olması dileğiyle!

Bir başka PHP serüveninde görüşmek üzere _(‘goodbye’)!

Etiketler:

Yazılanlar ilgini çektiyse, yenilerinden haberdar olmak için e-mail bültenine abone olabilirsin.

"PHP çoklu dil desteği" yazısı için 29 yorum yapılmış.

  1. Ersan YAKIT dedi ki:

    merhaba,

    google da ararken turkiyede buldum.. guzel bir paylasim olmus..tesekkurler…

    saygilar..

    Ersan YAKIT
    Application Developer

  2. Phpci dedi ki:

    Veri yolları demişsiniz de üstte adres çubuğu gibi “temel veri yolu” yazan kısım var bide altta “veri yolları” yazan kısım var hangisi bunlardan? Daha doğrusu hangisine ne yazmamız gerekli üstü kapalı geçmişsiniz orayı.

    C:xampphtdocsdeneme içerisinde de yukarıda verdiğiniz örnek yorum sayfasını index.php adıyla kayıt ettim.

    default.po dosyasını C:xampphtdocsdenemedilleren_USLC_MESSAGES dizinine kaydederken aşağıdaki hataları veriyor

    14:25:44: Poedit taranan dizinlerde dosya bulamadı.
    14:25:44: Kataloğdaki öğeler muhtemelen hatalı.
    14:25:44: Katalog güncellemesi başarısız oldu. Ayrıntılar için ‘Detaylar >>’ düğmesini tıklayınız.

    Hata da yok ne yazıyorsa onu yaptım

    • Musa Avcı dedi ki:

      “Veri yolları” tabına geçtikten sonra aşağıda “Veri yolları” diye boş bir liste var. O listede yeni öğe ekle butonuna basarak listeye veri yolları ekleyebiliyorsun. eklemeniz gereken veri yolu da dosyalarınız olduğu klasör.

      Örneğin tüm çalışmalarınız deneme klasöründeyse “C:xampphtdocsdeneme” yazarsınız ve Poedit tüm deneme klasöründeki dosyaları tarar.

  3. Serkan dedi ki:

    Budur. Tam gettext kullanmaktan vazgeçmek üzereydim ki yazına denk geldim. Teşekkür ederim.

  4. mehmet yılmaz dedi ki:

    Modüler bir yapı üzerinde çalışıyorum ve her modülün dil dosyası kendisi içerisinde olmalı. bu sistemi kullandığımızda aynı verimi sağlayabilir miyiz sizce.

  5. kuaza dedi ki:

    Merhaba guzel bir makale olmus ama ilginc sorunlar yasiyorum 😀

    oncelikle dil dosyasi klasorunu degistirmeme, silmeme ragmen hala turkce ceviri gorunuyor. Ben dil dosyasini sildim, Neden hala ceviri devam ediyor ?

    Dil dosyasini guncelliyorum, ama hala eski bilgiler var, Turkce karakter sorunuda var tabi.

    php dosyasinda printf ve ngettext kullanidigimda dosya hata veriyor kaydetmiyor, sorun var diyor.

    Guzel bir sey, yeni basladim ancak basit bir kac sorun yuzunden de vazgecmek istemiyorum 🙂

    • Musa Avcı dedi ki:

      eğer dil dosyası yoksa normal hali yazar. örneğin echo __('selam') dediğinizde eğer dil dosyası yoksa ya da dil dosyasında bunun çevirisi yoksa “selam” yazar. fakat siz ben ingilizce çeviri dosyasını sildim, halen “hello” yazıyor diyorsanız bu gerçekten garip. lokalde çalışıyorsanız sunucu programınızı kapatıp açmayı deneyin. işe yaramazsa tekrar bir yazın, tekrar bir düşünelim.

      • kuaza dedi ki:

        Yardimci olmaya calistigin icin tesekkurler. Oncelikle host uzerinde calisiyorum ve evet gercekten garip, dil dosyasini hatda klasorunu komple siliyorum hala ayarlarda belirttigim sekilde dil dosyasini goruyor ve kelimeleri cevriyor. Sunucu tarafli yada browser tarafli bir cache olayi olabilir.

        Arada bir sorun oluyor ama ustesinden geliyorum, suanki halini alana kadar 5 kere klasor ismi degistim 😀

        turkce problemine gelince asagidaki kodu textdomain($katalog); dan once eklediginizde sorun ortadan kalkiyor, ayni sorunu yasayanlara yardimci olur umarim:
        bind_textdomain_codeset($katalog, ‘UTF-8’);

        Guzel anlatim, guzel site, devam etmeni ve gelistirmeni temenni ediyorum, kolay gelsin.

        • kuaza dedi ki:

          Kataloğu güncellediğinizde yada çeviri yaptığınızda apache ye restart atarsanız yeni değişiklikleri görecektir. Daha önceki sorunumun cevabı budur arkadaşlar. Kolay gelsin..

  6. Mehmet dedi ki:

    İlerde kullanmak için güzel bilgiler.
    Teşekkürler.

  7. Onat dedi ki:

    Merhaba, yazı için teşekkürler . Ben de ilginç bir sorunla karşı karşıyayım, benim sitemin orjinali İngilizce fakat Türkçe’ye çevirmek için gettext kullanmak istedim. Şu an php dosyasıyla ne yazdırırsam yazdırayım Türkçe karşılığı geliyor.
    Dosyanın içeriği şu şekilde:

    • Musa Avcı dedi ki:

      merhabalar. paylaştığınız kod gelmemiş, pastebin ile göndermeyi deneyin bir bakalım

      • Onat dedi ki:

        Sorunu bir kaç saat boyunca araştırdıktan sonra kodda bir problem olmadığını öğrendim. Esas sorun Windows işletim sistemi ve doğal olarak wamp server (2.2) kullanıyor olmam. Windows’da locale değiştirmek Unix tabanlı sistemlerdeki gibi çalışmıyormuş. Sanırım gettext kullanmaktan vazgeçmek durumundayım..

        • Onat dedi ki:

          PHP’nin Gettext fonksiyonu 64 bit sistemlerde sorun yaratıyor ve henüz çözülebilmiş değil. Sorunun nedenini merak edenler “windows multi-threading locale” diye aratabilir. Benim gibi 64 bit Windows işletim sistemi kullananların sorununu gidermek için şöyle bir yöntem var:

          https://launchpad.net/php-gettext/

          Burada PHP’nin kendi gettext uzantısı yerine kullanabileceğiniz bir kod bulabilirsiniz.

  8. Olcay dedi ki:

    Tema sistemlerinde kullanımın nasıl olacağına dair bir fikri olan var mı? Tema motoru olarak RainTpl kullanıyorum. Dolayısıyla tema ve php dosyaları birbirinden ayrı durumda. Aklıma HTML içindeki sabit yazılarıda değiştirmek geliyor ama bu durumda da php tarafında çok ciddikod birikecek gözüküyor.

  9. Eray AYDIN dedi ki:

    Yararlı bir makale olmuş, çok işime yaradı, eskiden en.php ru.php gibi dosyalar oluştururdum 😀

  10. Samet ATABAŞ dedi ki:

    bende locale -a çıktısı şu şekilde :
    C
    C.UTF-8
    en_AG
    en_AG.utf8
    en_AU.utf8
    en_BW.utf8
    en_CA.utf8
    en_DK.utf8
    en_GB.utf8
    en_HK.utf8
    en_IE.utf8
    en_IN
    en_IN.utf8
    en_NG
    en_NG.utf8
    en_NZ.utf8
    en_PH.utf8
    en_SG.utf8
    en_US.utf8
    en_ZA.utf8
    en_ZM
    en_ZM.utf8
    en_ZW.utf8
    POSIX

    buraya yeni bir seçenek nasıl ekleyebilirim yada bu konunun araştırmasını nasıl yapabilirim ?

    Birde dil dosyalarını public_html/lang/en_IN/LC_MESSAGES/ yerine public_html/lang/en_IN.po ,public_html/lang/tr.TR.po şeklinde nasıl çalıştırabilirim ?

    • Musa Avcı dedi ki:

      Bunun için kullandığın işletim sistemine istediğin dili eklemen gerekiyor. Örneğin Ubuntu kullanıyorsan şuradaki gibi yeni dil paketini yüklüyorsun, sonrasında gelmiş olması gerekiyor.

  11. Azeri dedi ki:

    Selam . bende ç,ş,ı,ö,ğ harfleri açmıyor. poeditde utf-8 seçiliyor.nasıl yapa bilirim bu harflar işlesin?

  12. ferit dedi ki:

    Abi süper bir anlatım ve kodlar üzerinde yaptığın açıklamalar on numara olmuş başarılar ve devamını bekleriz.

  13. yunus ç dedi ki:

    peki ziyaretçi dili nasıl değiştirebilir?

    • Musa Avcı dedi ki:

      Dili belirlediğimiz şu kısımda $dil değişkenini istediğiniz gibi programlayıp değiştirdiğiniz takdirde dil değişecektir.

      <?php
      // dili belirtiyoruz
      $dil = 'en_US';
      putenv('LC_ALL=' . $dil);
      setlocale(LC_ALL, $dil);
      
  14. yunus ç dedi ki:

    hocam bende dili çekmiyor. sadece kendi değerini ekrana basıyor. kullanımım şu şekilde

    diller
    lang/tr/LC_MESSAGES/tr_tr.po ve diğer dil dosyası

    bu şekilde de denedim
    lang/tr_TR/LC_MESSAGES/tr_tr.po ve diğer dil dosyası

    sayfa içindeki gettex kullanımım

    dediğim gibi sadece gettex in kendisini basıyor dil dosyasına ulaşmıyor hiç

  15. yunus ç dedi ki:

    $dil = ‘tr_TR’;
    putenv(‘LC_ALL=’ . $dil);
    setlocale(LC_ALL, $dil);
    $katalog = ‘ahmetkaragoz’;
    bindtextdomain($katalog, “./lang”);
    textdomain($katalog);

    sayfa içinde kullanım

    echo _(‘Paylas’)

  16. yunus ç dedi ki:

    reis bi yardımcı olabilir misin 4 saattir pc başında bekliyorum ve olmuyor nedense =)

  17. yunus ç dedi ki:

    hallettim üstad saygılar

  18. sinem mai dedi ki:

    Merhaba. Yazı için teşekkürler. Lakin bir sorum olacaktı. Gettext ile link yapılanmamız değişmediğinden dolayı google açısından hreflang ları nasıl tanıtacağız arama motoru için bir kargaşaya sebep olacaktır.

Bir cevap yazın

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