Uzun zamandır bloga birşeyler yazmıyorum, ha şu gün yazayım ha bu gün yazayım derken bir türlü üşengeçliği kıramamıştım. Bir de büyük bir alışveriş sitesinde bile Türkçe karakter problemi olduğu görünce artık yazayım dedim…
PHP’nin herşeyi (herşeyi dediysem tüm herşeyi demiyorum tabîi) güzel fakat Türkçe karakterler (sadece Türkçe değil) ile arası pek yok. Bu yazımda detaylı bir şekilde inceleyip, hem Türkçe karakter hatalarının nedenlerini, hem de nasıl düzeltebileceğinizi anlatmaya çalıştım…
Teoriyi boşver, konuya gel için tık.
Substring yaparken Türkçe karakterleri (ç, ğ, ı, ö, ş, ü, Ç, Ğ, İ, Ö, Ş, Ü) 2 karakter olarak sayar.
1 2 3 |
$metin = "fıstık"; echo(substr($metin, 0 , 5)); // cikti: 'fıst' |
Yukarıda çıktının “fıstı” olması gerekirken ekrana sadece “fıst” gelir. Bunun nedeni yukarıda da belirttiğim gibi Türkçe karakterler 2 karakterden sayılıyor. Nedeni ise bu Türkçe karakterlerin ASCII tablosunda direk olmayıp, UTF-8 ile desteklenebilirliğinin getirilerek 2 karakterin birleşimini 1 karakter gibi okumasıdır.
Öncelikle UTF-8‘i tanıyalım ve anlayalım;
Wikipedia Türkçe sayfasında:
Unicode karakterlerini değişken sayıda 8 bitten oluşan bayt (kod birimi) gruplarıyla kodlamakta kullanılır.
Gayet basit ve anlaşılır aslında. İngilizcesinde ilk cümlenin çevirisini de ekleselermiş Türkçe karşılığına, daha iyi olurmuş… O da;
UTF-8; Unicode içerisindeki tüm karakter veya kod noktalarını dönüştürebilecek bir karakter kodlamasıdır.
gibi bir çevirisi yapılabilir sanırım. Yani; ASCII karakter tablosunda olmayan karakterleri gruplayarak (kodlayarak) yeni karakter oluşturur.
Buraya kadar herşey tamam. Şimdi teoiriyi bırakıp pratiğe geçelim; aşağıdaki örneği kendiniz de deneyip, metni değiştirerek kendiniz de kurcalayın.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$metin = "aç"; $genislik = strlen($metin); echo ($genislik); // cikti: '3' (hatali) for($i = 0; $i < $genislik; $i++) { $c = substr($metin, $i, 1); echo('<br />' . $c . ' : ' . ord($c)); } /* cikti: a : 97 Ã : 195 § : 167 */ |
NOT: yukarıda daha önce görmemiş olabileceğiniz bir function olan “ord” u kullandım. ord; bir karakterin ASCII değerini verir: php.net/ord.
“aç” kelimesinde; küçük a harfinin ASCII değeri 97, ç ise 2 karaktere bölünmüş ve ASCII değerleri 195 ve 167 olan 2 farklı karakter olarak görünüyor.
Japoncadaki “日” (ni) karaktarine bakacak olursak;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$metin = "日"; $genislik = strlen($metin); echo ($genislik); // cikti: '3' for($i = 0; $i < $genislik; $i++) { $c = substr($metin, $i, 1); echo('<br />' . $c . ' : ' . ord($c)); } /* cikti: æ : 230 — : 151 ¥ : 165 */ |
yine string genişliği 3 karakter çıkıyor ve 230, 151 ve 165 ASCIIlerine karşılık geliyor.
Daha fazla örneğe ya da teoriye ihtiyaç yoktur sanırım.
ve çözüm…
Tek yapmanız gereken UTF-8 destekleyen string functionlarını kullanmak. Bunlar da;
strlen yerine mb_strlen
substr yerine mb_substr
strtoupper yerine mb_strtoupper
gibi functionları kullanmak. Tam liste için buraya bakabilirsiniz.
Biraz örnekleyelim;
1 2 3 4 5 6 |
$metin = "fıstık"; echo(substr($metin, 0 , 5)); // cikti : 'fıst' echo(mb_substr($metin, 0, 5, 'UTF-8')); // cikti : 'fıstı' |
1 2 3 4 5 6 |
$metin = "fıstık"; echo(strlen($metin)); // cikti : '8' echo(mb_strlen($metin, 'UTF-8')); // cikti : '6' |
strtoupper yerine mb_strtoupper
1 2 3 4 5 6 |
$metin = "fıstık"; echo(strtoupper($metin)); // cikti : 'FıSTıK' echo(mb_strtoupper($metin, 'UTF-8')); // cikti : 'FISTIK' |
gibi diğer functionları da kullanabilirsiniz elbette.
Sürekli ‘UTF-8’ diye belirtmeye gerek var mı sorusunun cevabı ise hem evet hem hayır. Belirtmenize gerek olmama durumu var fakat sunucunuzun güvenlik ayarları buna izin vermiyorsa diye belirtmekte fayda var. Genel kodlamayı değiştirmeyi UTF-8 olarak yapmanın ise bir kaç yöntemi var;
mb_internal_encoding functionı;
mixed mb_internal_encoding ([ string
$kodlama
= mb_internal_encoding() ] )
$kodlama argümanı opsiyoneldir. Eğer $kodlama’yi gonderirseniz size kodlamayı aktif etme başarısını true/false döndürür. Eğer $kodlama belirtilmemişse aktif karakter kodlamasını string olarak geri döndürür.
1 2 3 4 5 6 7 8 |
echo mb_internal_encoding(); // cikti : 'ISO-8859-1' var_dump(mb_internal_encoding("UTF-8")); // cikti : bool(true) echo mb_internal_encoding(); // cikti : 'UTF-8' |
Localde çalıştırdığımda başarılı oldu ve true döndürdü ve artık her seferinde ‘UTF-8’ diye belirtmeme gerek yok.
Alternatif olarak PHP Maksimum Upload Limitini Değiştirmek yazımı incleyerek bu 3 yöntemden biri veya bir çoğu ile
1 |
mbstring.internal_encoding |
ayarına, UTF-8 değerini atayabilirsiniz.
Şimdilik bu kadar. Eğer anlaşılmayan veya atlamış olduğum birşey var ise belirtin ki yazılımdaki Türkçe kaynak sorunsalını gidermekte katkımız olsun.
Kaynaklar: