MySQL türkçe desteği: Collation ve character set nedir?

MySQL 4.1’den itibaren türkçe karakter işi karıştı. “SET NAMES latin5” ile çözdük bu problemi gerçi ama yine de sistemin nasıl çalıştığını bilmezseniz sorun yaşayabilirsiniz. O yüzden MySQL’in karakter seti ve collation hakkında birşeyler yazayım istedim.

Tanımlar

Konuyu kavramak için önce tanımları ele alalım. Bilmemiz gereken iki tanım var: “Character Set” ve “Collation”

Character Set (Karakter Seti)
Adından anlayabileceğiniz gibi desteklenen karakterleri kast ediyoruz. Örneğin latin5 karakter setindeki “ı” harfinin ASCII kodu latin1 karakter setindeki ý harfi ile aynıdır. Dolayısıyla türkçe bir dokümanı latin1 olarak gösterirseniz tüm ‘ı’ harfleri ‘ý’ olarak gösterilecektir.

Farklı diller aynı karakter setlerine sahip olabilir, örneğin latin1 seti almanca, ispanyolca, ingilizce, isveçce gibi dilleri kapsar.

Notlar:
– Character set, charset, code page gibi isimleri de vardır.
– latin1: ISO-8859-1 ve windows-1252 ile aynıdır.
– latin5: ISO-8859-9 ve windows-1254 ile aynıdır.

Collation (koleyşın)
İki karakterin karşılaştırılmasında kullanılır. Pratikte bu tablo alanlarını sorgularken ve sıralamalarda işe yarar:

Yukarıdaki sorguların sonucu isim alanının collation’ına bağlıdır.

Örnekle açıklamak istersek, almancada da “ü” harfi var türkçede de, isveçcede de… Ama bu harflerin alfabedeki yeri dile göre değişiyor yani collation’ı latin1_swedish_ci seçip sıralama yaparsanız ü başka yerde, latin1_german1_ci seçerseniz başka yerde yer alır. Ya da latin1_german1_ci seçerseniz aramalarda ‘ä’ ile ‘a’ aynı kabul edilirken latin1_german2_ci seçerseniz farklı kabul edilir.

Collation altçizgiyle ( _ işareti) parçalara ayırırsak ilk kısım karakterin ait olduğu dil ailesini, ikinci kısım dili, üçüncü kısım ise karşılaştırma şeklini ifade eder.

Örneğin latin1_swedish_ci dediğimiz zaman “latin1” ailesinden “isveçce” dilini büyük-küçük harf ayrımı olmadan sırala demek istiyoruz.

ci: Case-Insensitive (Büyük – küçük harfe duyarsız)
cs: Case-Sensitive (Büyük – küçük harfe duyarlı. Sadece bazı diller için geçerli, türkçede kullanılmaz)
bin: Binary (Karakteri tanımlayan byte değerine göre karşılaştır demek. Pratikte büyük – küçük harfe duyarlılık istendiğinde kullanılır.

MySQL Dil Ayarı Nasıl Yapılır?

MySQL’le biraz uğraşırsanız dilin ayarlanabileceği tonla yer görürsünüz. Büyükten küçüğe doğru gidersek:

  1. Sunucu ayarı
  2. Veritabanı ayarı
  3. Tablo ayarı
  4. Tablodaki alanın ayarı

Not 1: Bu ayarlardan pratikte sizi tek ilgilendiren: tablodaki alanın karakter seti.
Not 2: Buradaki ayarlar ne olursa olsun son sözü yine bağlantıyı kurduktan sonra verdiğiniz “SET NAMES utf8” komutu söyler.

1. MySQL Sunucusu

MySQL derlenirken tanımlanabilir:

Ya da my.cnf / my.ini’de tanımlanabilir:

Bu tanımlar sadece yeni yaratılacak veritabanları ve tabloların varsayılan karakter setinin utf8, collation’ının ise utf8_general_ci olmasını sağlar. Yani bu tanımları yapsanız bile yine de “SET NAMES utf8” demeniz gerekecektir.

2. Veritabanı

Veritabanı yaratılırken tanımlanır. Eğer tanımlanmazsa MySQL sunucusundaki tanım neyse o kullanılır.

Bu yapılan tanım da sadece bu veritabanında oluşturulan tabloların varsayılan karakter seti ve collation’ının ne olacağını belirler.

3. Tablo

Tablo yaratılırken tanımlanır. Eğer tanımlanmazsa veritabanındaki tanım kullanılır:

Bu durumda tabloya eklenen satırların collation’ı verilmezse latin5_turkish_ci olur.

4. Tablo Alanı

Üstteki tabloya ispanyolca bir alan eklemek istediğimizi düşünelim:

Tablonun collation’ı türkçe olmasına rağmen alan ispanyolca oldu. Bu alana türkçe giriş yaparsanız ı,ş, ğ gibi karakterleri hatalı görürsünüz.

Çalışma Şekli

Bu kısımda SET NAMES ‘latin5’ ne demek onu detaylandıracağım…

Veritabanının çalışma şeklini düşünürsek, biz sunucuya bir sorgu gönderiyoruz, sunucu da bu sorguyu okuyup bize bir cevap gönderiyor.

  1. Gönderdiğimiz sorgu hangi karakter setinde? Biz örneğin ascii mi gönderiyoruz yoksa utf8 mi?
    Bunun için sunucu character_set_client parametresine bakar.
  2. Gönderilen sorguyu sunucu hangi karakter setine çevirmeli? Bunun için de character_set_connection ve collation_connection parametrelerine bakar. Yani sunucu gönderilen sorguyu character_set_client karakter setinden character_set_connection karakter setine çevirir.
    collation_connection parametresi sadece iki string birbiriyle kıyaslanacaksa kullanılır. Eğer bir tablo alanını bir string ile karşılaştırıyorsanız kullanılmaz çünkü bu durumda o tablo alanının collation’ı kullanılır.
  3. Sunucu sorguyu işleyip veritabanından sonucu çektikten sonra bize gönderir demiştik, bize göndermeden önce sonucun karakter setini değiştirebilir. Bunun için character_set_results parametresini kullanır.

Bütün bu ayarları sunucunun konfigürasyon dosyasından yapabileceğiniz gibi (my.cnf ya da my.ini) doğrudan bir sql cümlesi ile de yapabilirsiniz. Yani php için:

diyebilirsiniz örneğin.

Sadece türkçe kullanacaksanız eğer şu komuyu verebilirsiniz:

Her seferinde bu satırları aklımızda tutmamıza gerek olmasın diye MySQL bir kısa yol koymuş, o da:

SET NAMES satırından nasıl kurtulurum?

Programcıların SET NAMES satırını unutmaları ihtimaline karşı init_connect parametresini kullanmak pratik olabilir:

Tabii veritabanını başlatırken parametre olarak da verilebilir bu.

Performansı arttırmak için veritabanı ayarlarına skip-character-set-client-handshake parametresini ekleyebilirsiniz. Bu durumda tablo alanının collation’ı kullanılacaktır ancak SET NAMES ile dili değiştirmek bu durumda mümkün olmayacaktır. Bir de, bu parametre “super user” ile bağlandığınızda geçersizdir.

Türkçe Dil Desteği ile İlgili Diğer Etkenler

Farklı dillerin doğru desteklenmesi için sadece MySQL’in doğru ayarlanması tabii ki yeterli değil. Dinamik web sitelerini örnek aldığımızda, Apache – PHP – MySQL platformunu düşünürsek:

  1. Apache’nin dil ayarı
    Header göndererek apache’de dil tanımlanabilir:

    Bu tanımlanan dil hatalıysa PHP, HTML ve veritabanındaki ayarlarınız ne olursa olsun sorun yaşarsınız.

  2. PHP
    PHP’den de aynı apache’deki gibi header gönderilebilir:

    Bu gönderilen header, apache tarafından gönderilenlerin sonuna eklendiği için apache’nin ayarlarını değiştiremez ancak burada dil ile ilgili header yanlış gönderilirse HTML ve veritabanındaki ayarlarınız ne olursa olsun sorun yaşarsınız.

  3. HTML
    HTML meta komutu ile header bilgisi simüle edilebilir.

    Eğer PHP ya da apache’den header gönderildiyse bu değer işe yaramaz. Eğer buradaki bilgi hatalı olursa veritabanından doğru dilde çekseniz bile sorun yaşayabilirsiniz.

UTF-8 ile ilgili not

UTF8 sonradan çıkan bir tanım. İçerisinde hemen hemen tüm dilleri barındırdığı için aslında büyük kolaylık. Ancak ilk geliştirilen ASCII formatından farklı bir altyapısı var. Dolayısıyla eğer utf8 kullanmayı seçtiyseniz yukarıdakilere ek olarak bir de dosya formatı sorunu karşınıza çıkıyor.

Eğer PHP ya da HTML sayfalarınızı açtığınız editör UTF8 destekli değilse ya da yeni dosya yarattığınızda dosyayı utf8 olarak kaydetmediyseniz yine sorun yaşayabilirsiniz.

UTF8 başlı başına bir muamma olduğu için çok detaya girmeyeceğim…

Karakter sorunu çözmede izleyebileceğiniz yol

Eğer karakter sorunu yaşıyorsanız aşama aşama şunu yapabilirsiniz:

1. Öncelikle sayfada gösterilen karakterler bozuk mu yoksa yanlış karakter setinde göstermeye çalıştığı için mi öyle gözüküyor onu tespit edin. Bunun için firefox’ta View / Character Encoding kısmından encoding doğru mu gözüküyor bakın. Eğer doğruysa ve problem utf8’leyse hata ya ilgili dosyanın utf8 olmamasıdır ya da veritabanından hatalı geliyordur.

2. Eğer encoding hatalıysa header’larda sorun var mı ona bakın. Header’ları görmek için firefox’un firebug extension’ından ya da ie8’in developer tools’undan vs. yararlanabilirsiniz. Eğer header farklı ise header’ın nereden geldiğini bulun: Ya apache’den ya da PHP’den gelir.

3. Header doğruysa “View source” diyip meta elemanına bakın. Buna “view source” ile bakın zira özellikle dinamik sitelerde kazara birden fazla meta komutunun geldiğine çok şahit oldum.

1 Yorum

  1. ayrıca php.ini içerisinde

    ;default_charset = “iso-8859-1”

    direktifi var. bunu etkinleştirip

    default_charset = “utf-8”

    olarak değiştirirsek, header eklememize gerek kalmıyor.

    Reply
  2. Notepad++ kullanıyorsanız dosya formatını üstteki biçim menüsünden değiştirebilirsiniz.

    Dreamweaver 8 kullanıyorsanız ufak bir ayar yapmalısınız. Edit->Preferences->New Document dedikten sonra üstten HTML seçip altta “Default encoding” yazan yeri Unicode yaparsanız dw ile YENİ OLUŞTURULAN HER DOSYA utf-8 karakter setinde olur. ancak BAŞKA BİR DOSYA AÇARSANIZ işler biraz karışır:

    eğer açılan dosyanın formatı latin5 (Türkçe) ise ama meta etiketi kullanarak charset=iso-8859-9 yapmadıysanız, dw bu dosyayı utf-8 kabul edecek ve dosyayı düzenleyip kaydettiğiniz zaman karakter setini utf-8 yapacak ama Türkçe karakterleri düzeltmeyecek yani dosyayı Firefox ile filan açtığınızda karakterler bozuk çıkacaktır. bu yüzden eğer DW kullanıyorsanız meta etiketi kullanarak dosyanın karakter setini belirtmeyi unutmayın.

    Reply
  3. çok yararlı bir inceleme olmuş. teşekkürler. karakter meselesinin basit bir şey olmadığını anlamış oldum.

    Reply
  4. tebrikler. harikulade yazı. vakti zamanında çok aramıştım buna benzer açıklayıcı yazı, şimdi gözlerim doldu :)

    Reply
  5. Merhaba, bu yazıya karşılaştığım bir soruna çözüm ararken ulaştım ve faydalı bilgiler de edindim, teşekkür ederim. Şöyle bir sorunla karşılaşıyorum: Müşteri bilgilerini kaydettiğim bir database’im var, ad, soyad, yapılan işler, alınan ücretler vs.. Forma girilen bilgileri database’e kaydederken şu karakteri kullandığımda kayıt yapmıyor (‘ üst tek tırnak işareti)
    Örneğin bir iş yapılmış ve bu işe istinaden şöyle bir not girmem gerekiyor: “Bodrum’a yapılan iş” ya da “Ali’nin hesabına yatan” bu tip girişler yaptığımda üst tırnak işaretinin de dil kurallarına göre database’e yazılmasını istiyorum fakat kayıtta hata alıyorum, kayıt gerçekleşmiyor. Üst tırnak işaretlerini kaldırıp düz olarak giriş yaptığımda database’e kaydediyor. ayrıca phpMyAdmin’den manuel olarak giriş yapıp oradan kayıt yaptığımda da kaydediyor. Peki form vasıtası ile gelen üst tırnak (‘) işaretini neden kaydetmiyor? Merak ettiğim diğer bir konu ise phpMyAdmin’den manuel olarak “öçşiğüı” gibi karakterleri database’e kaydettiğimde alanlarda bu karakterler düzgün gözükürken form vasıtası ile aynı karakterleri kayıt ettirdiğimde phpMyAdmin’den kontrol ettiğimde giriş yapılan alanlar neden garip karakterlerle doluyor? Bu verileri ekrana bastırdığımda bir sorun yaşamıyorum karakterler düzgünce çıkıyorlar fakat içeride neden garip gözüküyorlar ? Elle manuel veri girişinde karakterler düzgün fakat veri formdan geldiyse anlaşılmaz karakterler beliriyor. Sizin bu konuda tecrübe sahibi olduğunuzu ve yardımcı olabileceğinizi düşünerek mail atıyorum, fikir verebilirseniz çok sevinirim. İyi çalışmalar, sevgi ve saygılar.

    Reply
    • Halil Bey: Tek tırnakları escape etmeniz gerekiyor, yani ‘ işaretini \’ ile değiştirmeniz gerek. Böyle bir hatayla karşılaşıyorsanız güvenlik açığı da var demektir, SQL injection kavramını da araştırmanızı tavsiye ederim.

      PHP manual’ından mysql_real_escape_string fonksiyonuna bakmanızı öneririm. Ayrıca PEAR::DB, PEAR::MDB2, AdoDb, Zend_Db gibi kütüphaneleri de inceleyin.

      Türkçe karakter sorunu ile ilgili olarak yazıyı tekrar okuyun yapmanız gereken, kontrol etmeniz gerekenler bunlardan ibaret :)

  6. @Halil,
    DB’de garip karakterlerin olmasının sebebi web sayfanızın encoding’inin UTF-8 dışında birşeyler olmasından kaynaklanıyor olabilir.

    Reply
  7. selam ustam bir türlü yapamadim dil dosyasi ve site header codlari ISO88599 windows-1254 tamam. fakat bende phpmyadminde. mysql characerset UTF-8 Unicode(utf8) ve mysql baglanti icin utf8_unicode_ci var yapilmis olan tabellelarda hep latin1_schwedish_ci var forumda türkce yumusak g konularda bosluk yapip gözükmüyor gerisi oluyor birisi yardim eylesin lütfen

    Reply
  8. @mehetaslan,
    yani tablolarını şöyle yaratacaksın.
    CREATE TABLE deneme (x INT) CHARACTER SET latin5 COLLATE latin5_turkish_ci

    Reply
  9. usta tesekür ederim yazdigin icin fakat phpmyadmin de characerset utf-8 degistiremiyorum bu kala bilirmi site su an kurulu ve phpmyadminde tablolari silmeden yapabilirmiyim yani hepsini degistirmem senin dedigini yapmistim datablase error geliyor illegal mix of latin5 turkis and swedish_1 tam olarak hatirlamiyorum yazani

    Reply
  10. ustam yaptim parmaklarim kirildi 80 tabella düzeltim fakat sonucu daha kötü tüm türkce özrl harfler ? böyle gözteriyor konuya cevap yazmak istersem datebas error geliyor

    Invalid SQL: SELECT postid FROM bb1_posts WHERE threadid=’162′ AND userid=’1′ AND username=’BOZOKLU’ AND posttopic=’Selam: hbkj’ AND posttime>=’1266359963′ AND message=’Adýndan anlayabilece iniz gibi desteklenen karakterleri kast ediyoruz. Örne in latin5 karakter setindeki “ý” harfinin ASCII kodu latin1 karakter setindeki ý harfi ile aynýdýr. Dolayýsýyla türkçe bir dokümaný latin1 olarak gösterirseniz tüm ‘ý’ harfleri ‘ý’ olarak gösterilecektir.’ LIMIT 0, 1
    mysql error: Illegal mix of collations (latin5_turkish_ci,IMPLICIT) and (latin1_swedish_ci,COERCIBLE) for operation ‘=’
    mysql error number: 1267
    mysql version: 4.1.22-standard
    php version: 5.2.11
    Date: 17.02.2010 @ 23:39
    Script: /wbb2/addreply.php

    Reply
  11. Merhaba Arkadaslar;
    coklu dil kullandigim bir projem var. charset=utf-8 kullanarak hersey problemsiz calsiyor. fakat personel kayit ederken upload yapilan resmin uzerini personelin ismini yazdirirken sorun yasiyorum. site rusca,turkce ve ingilizce calismaktadir. ornegin turkce kullanici ile oturum actim ve personel kayiti yapiyorum.
    Personel Adi: Атилли Атешоглу

    simdi burda yapmis oldugum kayiti personel listesinde gorebiliyorum. fakat databasye baktigimda enteresan karakterler gorunuyor. bu personel ismini aspjpeg bilesenini kullanarak fotografin uzerine kaydediyorum. dogal olarak databasede karakterler bozuk oldugu icin resim uzerindede bozuk oluyor. bu sorunu cozebilmem icin yani databaseye rusca karakterlerin duzgun gelebilmesi icin sayfalari charset=windows-1251 yapmam gerekiyor. hal boyle olunca turkce kullanici ile login olan birisi personel listesini duzgun goruyor,resim uzerinde rusca karakterler duzgun cikiyor fakat sayfa icerisindeki formlarda ve menulerde bulunan turkce karakterler sapitiyor…

    Bu konuda bana nasil yol gosterebilirsiniz?
    Tesekkurler

    Reply
  12. Merhaba,

    localde bir proje üzerinde çalışıyorum. Notepad++ kullanıyorum. UTF-8 BOM’suz kodlama sistemini kullanıyorum. Dosyalarımda, klasörlerimde, MySQL’deki tablolarda ve php dosyalarının hiçbirinde Türkçe karakter problemi yaşamıyorum. Fakat enteresan başka bir durum yaşıyorum:
    localhost’ta A isimli bir klasörüm var. İçinde mesela B, C, Ç isimlerinde klasörler var. B, C ve Ç klasörlerinde, isimleri sayılardan oluşan png dosyaları var. Ç klasörünün isminde Türkçe’ye özel ı, ş, ç gibi harfler var diye bu klasördeki resimleri okuyamıyor. Fakat, B ve C klasörlerinin isimlerinde Türkçe’ye özel harfler olmadığından, o klasörlerdeki resimleri okuyabiliyor.
    Sorunu nasıl çözebiliriz?

    Reply
    • Dosya ve klasör isimlerinde Türkçe karakter kullanmayarak çözebilirsiniz :)
      Bu her zaman başınızı ağrıtacaktır.
      İlla kullanmam lazım diyorsanız büyük ihtimalle dosya sisteminde dosya adları utf-8 değil. Dolayısıyla dosya adını iconv ya da mb_convert_encoding ile utf8’den iso-8859-9’a çevirip deneyebilirsiniz…

  13. Merhabalar,
    yazınız gerçekten çok faydalı emeğinize sağlık ancak ben sorunumu çözemedim:(
    php ile bir web sitesi hazırlıyorum.phpmyadmin de oluşturduğum tabloları utfs general ci yaptım.phpmyadmin de, veri kaydı yaptığımda herhangi bir türkçe karekter sıkıntısı yok yani tablo alanları türkçeye uyumlu. ancak veritabanından bilgi çektip bunu siteme yazdırdığımda türkçe karekterler görünmüyor.ı==>? oluyor ü ler değişik karekterlere bürünüyor.Lütfen acilen yardım edebilir misiniz?:(Şimdiden teşekkür ederim.:)

    Reply
  14. Hocam Merhaba php de bir program yaptım ancak tablo adını latin1_swedish_ci yaptığım için türkçe veri kaydedemiyorum bunu sonradan utf_8 e nasıl çevirebilirim yardımcı olabilirmisiniz acaba

    Reply
  15. slm düne kadar hic sorun yaşamadım ama host aldığım firma
    Hata aldıgınız sunucumuzda default iso charset iso-8859-9 şeklinde ayarlandıgı için olmuş şimdi karakter sorunu ckrdı fazla tecrübem yok bişeler yaptım olmadı nasıl cözerim tşk

    Reply
  16. Epey eski bir yazıymış ama yazayım istedim, belki bu değişiklikleri yapamayacak birinin işini görebilir.

    select SÜTUN from TABLO order by SÜTUN collate utf8_turkish_ci asc/desc

    şeklindeki bir sorguda sıralama yaparken collate utf8_turkish_ci ibaresini kullanırsanız, içindeki karakterlere ISO 8859 seti dahilinde Türkçe muamelesi yapar. Haliyle İ karakteriniz sorgu cevabının sonunda değil, I’dan sonra, Ö karakterinizde aynı şekilde O’dan sonra gelir. Yani alfabetik sıralama bozulmaz.

    Ayrıca her zaman UTF-8 kullanmanız sizin avantajınızadır. Scriptleriniz ENCODING özellikleri UTF-8 olursa veya PHP ile yazıyorsanız scriptinizin başına mb_internal_encoding(‘UTF-8’); parametresini eklerseniz sorununuz çözülür.

    Buna mukabil döküman kodlamanızda UTF-8 olmalıdır. Latin setinde olmayan karakterleride, Türkçe karakterleride bu şekilde sorunsuz görüntülersiniz.

    Ayrıca her veritabanı sunucusu için farklı ORDER karakter setleri mevcuttur. Dökümanlarını incelemeniz yeterli.

    Reply

Leave a Comment.