Yerelleştirme Çeviri Değildir: Türkçe Arayüzde Yapılan Hatalar
Yerelleştirme çeviriden fazlası: tarih, para, sıralama, çoğul ve harf kuralları da hedef dile göre değişiyor. Türkçe arayüzde en kolay bozulan yerleri sırayla anlattım.
Çeviri Değil, Uyarlama
W3C iki kavramı ayırıyor. Internationalization (i18n), ürünü farklı dil ve kültürlere kolayca uyarlanabilecek şekilde tasarlamak. Localization (l10n) ise bu uyarlamanın kendisi. Çeviri bunun yalnızca bir parçası: tarih ve sayı biçimleri, para birimi, sıralama, isim sırası ve adres biçimi de yerele (locale) göre değişiyor.
i18n koda ait iş: UI metnini koddan ayırmak, Unicode kullanmak, biçimlendirmeyi yerele bırakmak. W3C bunu sonradan eklemenin pahalı olduğunu söylüyor ve Y2K'yı örnek veriyor.
Metni Bölme, Eki Tahmin Etme
Cümleyi parçalara bölüp birleştirirsen çevirmen sırayı değiştiremez. Her cümle tek bir mesaj olmalı, değişkenler yer tutucu olarak içinde durmalı. (FormatJS de aynısını öneriyor.)
// Kaçın
const greeting = t("Hoş geldin")
return `${greeting}, ${name}!`
// Tercih et
return t("Hoş geldin, {name}!", { name })Türkçe'de asıl sorun değişkenden sonra gelen ek. Ek, kelimenin son harfine ve son ünlüsüne göre değişiyor:
| Değişken | Yönelme | Bulunma | İlgi |
|---|---|---|---|
| Ahmet | Ahmet'e | Ahmet'te | Ahmet'in |
| Ayşe | Ayşe'ye | Ayşe'de | Ayşe'nin |
| Kars | Kars'a | Kars'ta | Kars'ın |
{name}'e yazarsan Ayşe'e ve Kars'e çıkar. Logrus IT aynı sorunu oyun yerelleştirmesinde "Rodrigo'ya" ve "Elly'ye" örneğiyle anlatıyor.
Pratik çözüm, cümleyi değişkenden sonra ek gerekmeyecek şekilde kurmak:
// Ek gerekir
t("{name}'e mesaj gönderildi", { name })
// Ek gerekmez
t("Alıcı: {name}", { name })Cümle biraz daha kuru kalıyor ama hiçbir isimde bozulmuyor.
İ ve ı
Türkçe'de iki ayrı i var: i/İ ve ı/I. JavaScript'in varsayılan büyük-küçük harf dönüşümü bunu bilmiyor (MDN):
"izmir".toUpperCase() // "IZMIR"
"izmir".toLocaleUpperCase("tr") // "İZMİR"
"ISPARTA".toLowerCase() // "isparta"
"ISPARTA".toLocaleLowerCase("tr") // "ısparta"Ama her yere "tr" vermek de hata. Kullanıcıya gösterilen metinde doğru, fakat HTML etiketi, dosya uzantısı ve enum gibi makine değerlerinde yanlış sonuç verir: "TITLE".toLocaleLowerCase("tr") sonucu "tıtle" olur.
Çoğul ve Biçimler
Türkçe'de sayıdan sonra isim tekil kalır: 5 kedi, 5 kediler değil. Intl.PluralRules("tr") de yalnızca one ve other döndürüyor, yani iki dal aynı yazılıyor. Çoğul sözdizimi yine de sıfır durumu ve ileride eklenecek diller için yer açıyor:
t("{count, plural, =0 {Yorum yok} other {# yorum}}", { count })Rusça'da one/few/many/other, Arapça'da altı kategori var (MDN).
Tarih, sayı ve para birimi de yerele göre değişiyor:
| tr-TR | en-US | |
|---|---|---|
| Uzun tarih | 31 Ekim 2026 | October 31, 2026 |
| Kısa tarih | 31.10.2026 | 10/31/26 |
| Para (TRY) | ₺1.234,50 | TRY 1,234.50 |
| Sayı | 1.234.567,89 | 1,234,567.89 |
Yüzde işareti bile yer değiştiriyor: Türkçe'de %25, İngilizce'de 25%. Intl.DateTimeFormat ve Intl.NumberFormat bu farkları kendileri uyguluyor.
Bu hataların hepsi aynı varsayımdan çıkıyor: İngilizce'nin kuralları her dilde geçerli sanılıyor. Metin kusursuz çevrilse bile ek, harf, çoğul ve biçimler kodda İngilizce'ye göre yazıldıysa arayüz bozuk kalıyor.