Türkçe "i" Harfi Yüzünden Kaybettiğim İki Gün
Uygulama benim telefonumda çalışıyor, test cihazında çalışıyor, ama bazı kullanıcılarda hiç açılmıyordu. Sebebi bulmam iki gün sürdü ve tek bir harften ibaretti.
Android tarafında yazdığım bir uygulamada tuhaf bir hata raporu almaya başladım. Uygulama bazı kullanıcılarda açılır açılmaz çöküyordu. Benim telefonumda sorun yoktu. Emülatörde sorun yoktu. Test ettiğim iki cihazda da sorun yoktu.
Çökme raporlarına baktığımda ortak bir nokta göremedim: farklı cihazlar, farklı Android sürümleri, farklı ekran boyutları. İki gün sonra fark ettim ki ortak nokta cihazda değildi — cihazın dil ayarındaydı.
Türkçenin dört "i" harfi
Çoğu dilde i harfinin büyüğü I'dır. Türkçede öyle değil.
Türkçede noktalı ve noktasız olmak üzere iki ayrı harf var ve her birinin kendi büyük
hâli var:
| Küçük | Büyük | Not |
|---|---|---|
i | İ | Noktalı i — büyüğünde de nokta var |
ı | I | Noktasız ı — büyüğünde nokta yok |
Bu, dil bilgisi açısından doğru. Sorun şu ki işletim sistemi bu kuralı cihazın dili Türkçe olduğunda her metne uyguluyor — kullanıcıya gösterilen metne de, kodun içindeki teknik dizgelere de.
Hatanın kendisi
Kodda şuna benzer bir karşılaştırma vardı:
String tip = sunucudanGelenDeger; // örneğin "IMAGE"
if (tip.toLowerCase().equals("image")) {
gorseliGoster();
}
Cihaz dili İngilizce olduğunda "IMAGE".toLowerCase() sonucu
"image" oluyor ve koşul sağlanıyor. Cihaz dili Türkçe olduğunda ise sonuç
"ımage" — noktasız ı ile. Koşul sağlanmıyor, akış beklenmedik
bir dala giriyor ve uygulama çöküyor.
Geliştirici genelde cihazını İngilizce kullanır. Yani hata sizin makinenizde hiçbir zaman çıkmaz. Sadece kullanıcılarda çıkar ve raporlarda tutarsız görünür.
Nerelerde patlıyor?
Bir kez farkına vardıktan sonra kodun her yerinde görmeye başladım. Risk taşıyan yerler:
- Dosya uzantısı kontrolü:
"FOTO.PNG".toLowerCase()→".pnğ"değil ama"IMG"içeren her kontrol bozulur. - Sunucudan gelen tip/durum alanları:
"ACTIVE","PENDING","INFO"gibi değerlerin karşılaştırılması. - HTTP başlıkları:
"Content-Type"gibi başlık adlarını küçültüp karşılaştırmak. - Arama ve filtreleme: kullanıcı "istanbul" yazınca "İstanbul" bulunmuyorsa sebebi budur.
- E-posta karşılaştırması: büyük harfli e-posta adreslerinin küçültülerek eşleştirilmesi.
- Renk ve stil adları:
"LIGHT","DARK"gibi değerlerin normalleştirilmesi.
Doğru çözüm: makine dizgesi mi, insan metni mi?
Çözüm "Türkçeyi devre dışı bırakmak" değil. Doğru soru şu: bu metin bir insana mı gösterilecek, yoksa makinenin karşılaştırması için mi kullanılacak?
- İnsana gösterilecekse — başlık, isim, şehir adı — cihazın dilini kullanın. Kullanıcı "İSTANBUL" görmeli, "ISTANBUL" değil.
- Makine karşılaştıracaksa — tip alanı, anahtar, uzantı, başlık adı — dilden bağımsız (invariant) dönüşüm kullanın.
Java ve Kotlin tarafında düzeltilmiş hâli:
// YANLIŞ — cihazın diline göre davranır
tip.toLowerCase()
// DOĞRU — dilden bağımsız, her cihazda aynı sonuç
tip.toLowerCase(Locale.ROOT)
// Kullanıcıya gösterilecek metinde ise cihazın dili doğru tercih
baslik.uppercase(Locale.getDefault())
Swift ve JavaScript tarafında durum biraz farklı ama tuzak aynı:
// Swift — lowercased() zaten dilden bağımsızdır, güvenli
tip.lowercased()
// Ama bu locale'e duyarlıdır, makine karşılaştırmasında kullanmayın
tip.lowercased(with: Locale.current)
// JavaScript — toLowerCase() dilden bağımsızdır, güvenli
tip.toLowerCase()
// Bu ise cihazın diline göre davranır
tip.toLocaleLowerCase()
Makine karşılaştırmalarını metin üzerinden yapmayı bırakıp enum
kullanmak bu sınıf hatayı tamamen ortadan kaldırıyor. Dizge karşılaştırması
kaçınılmazsa bile, dönüşümü tek bir yardımcı fonksiyonda toplayıp her yerde onu
çağırmak hem güvenli hem de denetlenebilir oluyor.
Nasıl test edilir?
Bu hatayı yakalamanın en hızlı yolu, test cihazının dilini Türkçe yapıp uygulamayı baştan sona bir kez gezmek. Otomatik testlerde ise dili zorlayarak çalıştırmak mümkün:
// Testte varsayılan dili geçici olarak Türkçe yap
Locale.setDefault(new Locale("tr", "TR"));
Çok dilli bir uygulama geliştiriyorsanız bu testi sürüm öncesi kontrol listesine eklemek, iki günlük bir hata avını on dakikalık bir kontrole indiriyor.
Özet
Bu hata bana iki şey öğretti. Birincisi teknik: büyük/küçük harf dönüşümü masum bir işlem değil, kullanıcının diline bağlı bir davranış. İkincisi daha genel: "benim makinemde çalışıyor" cümlesi bir teşhis değil, sadece bir gözlem.
Yalnızca belirli kullanıcılarda çıkan hataların ortak noktası genelde cihazda değil, o kullanıcıların ayarlarında oluyor. Dil, saat dilimi ve bölge — hata ararken ilk bakılacak üç yer.
- Locale
- Türkçe i
- Java
- Kotlin
- Hata ayıklama
- i18n
Demir Taşdemir
Mobil Uygulama & Web Geliştirici
2018'den beri yazılım geliştiriyorum. App Store ve Google Play'de 11 uygulama yayınladım; şu an 6 mobil uygulama, 1 e-ticaret platformu ve 1 masaüstü oyun üzerinde çalışıyorum.
Böyle hataları avlamayı seven biri
Yalnızca belirli kullanıcılarda çıkan hatalar en öğretici olanlar. Bu tarz problemleri kovalamayı seviyorum.