YazılarVaka
İki dilli sitemizi nasıl kurduk: hreflang, yönlendirme ve dil algılama
Rues tarafından · Yayın: · Güncelleme:
Bu yazı, 7 Ekim 2026'da yayına giren kendi sitemiz ruesandora.com'da iki dili nasıl kurduğumuzun kaydı: ne yaptık, neden yaptık, kendi denetimlerimiz hangi hataları yakaladı ve neyi bilerek yapmadık. Trafik ya da sıralama verisi henüz yok, dolayısıyla burada sonuç da yok.
Nereden başladık, neyi değiştirdik?
Site önce Türkçe olarak kökte açıldı (/, /hizmetler/...); İngilizce sonradan /en/ altına eklendi. 7 Ekim 2026'da düzeni ters çevirdik: İngilizce kökte ana dil oldu, Türkçe /tr/ altına taşındı. İki dil eşit önemde. İngilizcenin kökte durmasının nedeni, sitenin Türkiye dışındaki aramalarda da bulunmasını istememiz.
Taşımayı İngilizce sayfaların eklendiği gün yaptık. Birkaç saatlik bir sitenin arama dizinlerinde az eski adresi, o adreslere verilmiş az bağlantısı olur; taşımanın bedeli en düşük o gündür. O sırada sitede 18 adres vardı: iki ana sayfa ve her dilde sekiz hizmet sayfası.
Taşımadan sonra adresler:
| Sayfa | İngilizce | Türkçe |
|---|---|---|
| Ana sayfa | / | /tr/ |
| Bir hizmet | /services/seo-geo-aeo | /tr/hizmetler/seo-geo-aeo |
Eski adresler nasıl yönleniyor?
Site Cloudflare Workers'ın statik dosya sunumuyla yayında ve bu sistem site klasöründeki _redirects dosyasını okuyor. Her satır kaynak hedef kod biçiminde. Bütün kurallarda 301 kullandık. Google'ın belgesine göre kalıcı yönlendirme (301 ya da 308), hedef adresin asıl adres sayılması için sinyal; geçici yönlendirme (302 ya da 307) bu sinyali vermiyor. Cloudflare bu dosyada kod yazılmazsa 302 kullanıyor, bu yüzden kodu her satıra yazmak gerekiyor.
/en/services/growth /services/growth 301
/hizmetler/buyume /tr/hizmetler/buyume 301
/en/services/* /services/:splat 301
/hizmetler/* /tr/hizmetler/:splat 301
/en/* /:splat 301
Cloudflare belgesindeki iki ayrıntı dosyanın biçimini belirledi. Sabit kurallar * içeren kurallardan önce gelmeli. Yönlendirmeler de istenen adreste dosya olsa bile uygulanıyor; yani dikkatsiz bir kural gerçek bir sayfayı görünmez yapabilir. Bir test dosyayı okuyup kaynağı hâlâ var olan bir dosya olan kural bulursa, ya da /, /tr/, robots.txt, sitemap.xml yönlenecek olursa hata veriyor.
Hangi hataları yakaladık?
Sitedeki büyük değişiklikler birleştirilmeden önce ayrı bir denetçi agent'a gidiyor; bu düzeni başka bir rehberde anlatıyoruz. Aşağıdakiler dil taşımasının git geçmişinden. İlk ikisini denetçi buldu.
Sonu bölülü eski adresler iki adımda gidiyordu. /hizmetler/seo-geo-aeo/ joker kurala uyup /tr/hizmetler/seo-geo-aeo/ adresine gidiyordu. Cloudflare'in varsayılan davranışı da /sayfa/ adresini 307 ile, yani geçici olarak /sayfa'ya gönderiyor. Denetçi tek adımlı yönlendirme istedi. Sonu bölülü her eski adrese ayrı bir 301 kuralı yazdık; çıplak klasör adresleri (/hizmetler, /en/services) de artık ana sayfanın hizmetler bölümüne gidiyor. Test şimdi bilinen her eski adresin kendi sabit kuralı olmasını ve tek adımda var olan bir sayfaya düşmesini şart koşuyor.
Kendi notumuz herkese açılacaktı. Site klasöründe kendimiz için tuttuğumuz bir README var. Cloudflare onu /README.md adresinde sunacaktı. Artık .assetsignore dosyası onu yayının dışında tutuyor.
İç bağlantılar her tıkta yönlendirmeye düşüyordu. Daha önceki bir araştırma, hizmet sayfalarının buyume.html gibi uzantılı adreslere bağlandığını bulmuştu. Cloudflare bu adreslere 307 ile temiz adresi gösteriyor. Bağlantıları düzelttik ve bir test ekledik: her iç bağlantıyı, site haritasındaki ve llms.txt'teki her adresi tarıyor, yönlendirmeye düşen olursa hata veriyor. Test kendini de sınıyor: .html uzantılı bağlantıyı, _redirects kaynağını ve sonu bölülü yazımı gerçekten yakaladığını doğruluyor.
Test araçları da yönlendiriliyordu. Dil betiğinin ilk sürümü, user agent'ında bot ya da crawl gibi kelimeler geçen ziyaretçiyi atlıyordu. Search Console'un URL denetimi Google-InspectionTool adıyla, Lighthouse da kendi adıyla geliyor; ikisinde de bu kelimeler yok. İkisi de öbür dile yönlenip yanlış sayfayı test edebilirdi. İlk sürümden yaklaşık 20 dakika sonra Inspection ve Lighthouse hariç tutulanlar listesine girdi.
hreflang nasıl kurulu?
Her sayfada üç link rel="alternate" etiketi var: en, tr ve İngilizceyi gösteren x-default. Aynı eşler sitemap.xml'de de yazılı. Google bu yöntemlerin kendisi için eşdeğer olduğunu ve her dil sürümünün kendini ve öbür sürümleri listelemesi gerektiğini söylüyor; iki sayfa birbirini göstermiyorsa etiketler yok sayılıyor. Bir test her eşin karşılıklı olduğunu ve her sayfanın canonical'ının kendi adresi olduğunu denetliyor.
Bazı sayfalar tek dilde kalacak. Türkiye'ye özgü bir konuyu anlatan Türkçe yazının İngilizce karşılığı olmayabilir. Böyle sayfalarda üretici yalnız sayfanın kendi hreflang satırını yazıyor, x-default koymuyor; dil düğmesi de öbür dilin ana sayfasına gidiyor. Yarım çevrilmiş sayfa yayınlamıyoruz. Google, yerelleştirilmiş sayfaları yalnız ana içerik çevrilmeden kalmışsa kopya sayıyor.
Ziyaretçiyi dile göre yönlendiriyor muyuz?
Kısmen, ve Google'ın genel önerisine rağmen. Google, kullanıcıyı sitenin bir dil sürümünden öbürüne otomatik yönlendirmemeyi öneriyor. Gerekçesi, bu yönlendirmelerin kullanıcıların ve arama motorlarının sitenin bütün sürümlerini görmesini engelleyebilmesi. Bizim sürümümüzde bu riskin düşük olduğunu düşünüyoruz: botlar hiç yönlendirilmiyor, Türkçe sayfalar yönlenmiyor ve her sayfanın iki adresi de site haritasında ve hreflang'de duruyor.
Biz dar bir sürümünü tuttuk. Sayfanın başındaki kısa betik ancak şunların hepsi doğruysa çalışıyor:
- Sayfa İngilizce. Türkçe sayfadan hiçbir yere yönlendirme yok.
- Ziyaretçi başka bir siteden geldi ya da adresi kendisi yazdı. Site içindeki tıklamalar yönlendirilmiyor.
- Tarayıcının dilleri arasında Türkçe var. Daha önce TR'ye basmış ziyaretçi, tarayıcı dili ne olursa olsun Türkçeye geçiriliyor.
- User agent bot ya da test aracına benzemiyor.
- Ziyaretçi daha önce İngilizceyi seçmemiş. TR ya da EN düğmesine basınca seçim tarayıcıda saklanıyor.
Her dilin kendi adresi olduğu için arama motorları iki sürümü ayrı sayfalar olarak görüyor. Bedeli açık: İngilizce okumak isteyen Türkçe konuşan biri bir kez EN'e basmak zorunda. Yönlendirme ilk başta ziyaretçinin geldiği kaynağı da kaybediyordu; çerezsiz ziyaret sayacımız kaynağı yanlış yazacaktı. Betik şimdi kaynağı bir sonraki sayfaya aktarıyor, orada bir kez okunup siliniyor.
Site haritası tarihleri neden git'ten geliyor?
Aynı araştırma, site haritasındaki 18 adresin hepsinde aynı lastmod tarihi olduğunu bulmuştu. Google lastmod değerini ancak tutarlı ve doğrulanabilir biçimde doğruysa kullandığını söylüyor. Üretici artık her sayfanın tarihini git'teki son commit'inden, Türkiye saatiyle alıyor. Sayfada kaydedilmemiş değişiklik varsa bugünün tarihini yazıyor. Site haritası ile git tutmazsa test hata veriyor; bu da sayfayı elle değiştirdikten sonra üreticiyi çalıştırmayı hatırlatıyor.
IndexNow burada ne yapıyor?
IndexNow, bir sitenin protokole katılan arama motorlarına "bu adres değişti" demesini sağlıyor. Site kökünde bir anahtar dosyası duruyor. Küçük bir betik, verilen commit'ten bu yana değişen sayfaları git diff ile bulup tek istekte gönderiyor. Protokol tek istekte en çok 10.000 adrese izin veriyor. 18 adreslik ilk gönderimimiz 202 aldı; IndexNow belgesinde bunun anlamı "adres alındı, anahtar doğrulaması bekliyor". Google, IndexNow'a katılan arama motorları listesinde yok; yani bu adım Google için bir şey yapmıyor.
Neyi yapmadık?
- IP adresine ya da çereze göre sunucu tarafında dil değiştirmedik. Her dilin kendi adresi var.
- Makine çevirisini olduğu gibi yayınlamadık.
- Gerçek karşılık olmayan sayfalar arasına hreflang koymadık.
Açık iş (7 Ekim 2026): http://'den https://'e ve www'den yalın alan adına yönlendirme henüz yok; çözümü Cloudflare panelinde bir ayar.
Sonuç ne oldu?
Henüz bilmiyoruz. Bu yazı yazıldığında Search Console ve Bing verisi gelmemişti; bir haftalık veri de zaten pek bir şey kanıtlamaz. Tarihli rakamlarımız olunca ölçüm tarihiyle bu sayfaya ekleyeceğiz.
Benzer bir taşıma planlıyorsanız notlarımızı karşılaştırmaktan memnun oluruz.
Kaynaklar
- Google, yerelleştirilmiş sürümler ve hreflang: developers.google.com
- Google, çok bölgeli ve çok dilli siteler: developers.google.com
- Google, yönlendirmeler ve Google Arama: developers.google.com
- Google, site haritası oluşturma (lastmod): developers.google.com
- Google, genel tarayıcılar (Google-InspectionTool): developers.google.com
- Cloudflare, Workers statik dosya yönlendirmeleri: developers.cloudflare.com
- Cloudflare, HTML işleme (.html ve sonu bölülü adres için 307): developers.cloudflare.com
- IndexNow, belge: indexnow.org/documentation
- IndexNow, katılan arama motorları: indexnow.org/searchengines.json