~/blog / Next.js
Next.js App Router ile çift dilli SEO: tek domain, iki dil, sıfır kanibalizasyon
Çok dilli bir siteyi Google'a doğru anlatmak, çeviri yapmaktan ibaret değil. Asıl iş, arama motoruna "bu iki sayfa aynı içeriğin iki dili, birbirinin kopyası değil" demeyi makine tarafından okunur şekilde yapmakta. Yanlış kurulunca iki dil birbirini yer (kanibalizasyon), doğru kurulunca her dil kendi sorgusunda yarışır.
Bu yazıda kendi sitemizi Next.js App Router ile kurarken aldığımız kararları ve neden öyle yaptığımızı anlatıyoruz.
Karar 1: Türkçe prefixsiz, İngilizce /en altında
İlk karar URL şemasıydı. İki yaygın yol var:
/tr/...ve/en/...— her iki dil de prefixli/...(varsayılan dil) ve/en/...— varsayılan dil kökte
Birincil pazarımız Türkiye olduğu için Türkçeyi prefixsiz köke koyduk:
/ → Türkçe ana sayfa
/en → İngilizce ana sayfa
/diyarbakir-yazilim → Türkçe şehir sayfası
/en/diyarbakir-software → İngilizce şehir sayfası
Bunun SEO faydası şu: en çok trafik beklediğiniz dil, en kısa ve en "otoriter" görünen URL'yi alır. Kök alan adına yakın URL'ler kullanıcı gözünde de arama motoru gözünde de bir tık daha güçlüdür.
Karar 2: Dil başına slug — çeviri sadece metinde değil, URL'de de
Dikkat edilmesi gereken nokta yukarıdaki örnekte gizli: şehir sayfasının slug'ı dile göre değişiyor. Türkçede diyarbakir-yazilim, İngilizcede diyarbakir-software.
Çünkü SEO'nun amacı, kullanıcının gerçekten arattığı kelimeyi URL'de yakalamak. Türk kullanıcı "diyarbakır yazılım" arar, İngiliz kullanıcı "software" arar. URL'yi tek dile sabitlerseniz, diğer dilin anahtar kelimesini URL katmanında kaybedersiniz.
Bunun bedeli, dil değiştirme (language switcher) mantığının biraz daha zorlaşması: kullanıcı Türkçe şehir sayfasındayken İngilizceye geçince, /en + aynı slug değil, o şehrin İngilizce slug'ına gitmeli. Yani basit bir prefix takası yetmez; slug eşlemesi gerekir.
Karar 3: hreflang'i her sayfada, her iki yönü de kapsayacak şekilde ver
En sık yapılan hata: hreflang'i tek yönlü vermek ya da hiç vermemek. Google'ın kuralı net — hreflang karşılıklı olmalı. A sayfası B'yi gösteriyorsa, B de A'yı göstermeli.
App Router'da bunu sayfa metadata'sında alternates.languages ile veriyoruz:
alternates: {
canonical: `/services/${slug}`,
languages: {
tr: `/services/${slug}`,
en: `/en/services/${slug}`,
'x-default': `/services/${slug}`,
},
}
x-default, dil/bölge eşleşmesi olmayan kullanıcı için hangi sürümün gösterileceğini söyler — bizde her zaman Türkçe sürüm, çünkü birincil pazar orası.
Karar 4: canonical'ı her sayfaya elle koy, kökten miras alma
İnce ama pahalı bir tuzak: root layout'ta bir canonical bırakırsanız, kendi canonical'ını belirtmeyen her sayfa sessizce /'yi canonical olarak devralır. Sonuç: onlarca sayfa "aslında ana sayfayım" der ve indeksten düşer.
Biz bu yüzden root'ta canonical koymadık; her sayfa kendi canonical'ını açıkça veriyor. Küçük bir disiplin, büyük bir hata sınıfını kapatıyor.
Karar 5: Her şeyi statik üret (SSG)
Diller arası içerik veriden geldiği ve istek anında değişmediği için tüm ağacı statik HTML'e prerender ediyoruz. generateStaticParams ile her slug build sırasında üretiliyor, dynamicParams = false ile bilinmeyen slug'lar 404 dönüyor.
Bunun SEO tarafındaki karşılığı: Google'ın botu sayfayı ziyaret ettiğinde beklemesi, JS çalıştırması gereken bir şey yok — içerik ilk byte'ta hazır. Hız, artık bir sıralama sinyali; statik üretim bu sinyali bedavaya veriyor.
Özet
Çok dilli SEO'da işi yapan şey çeviri değil, arama motoruna verdiğiniz yapısal sinyallerin tutarlılığı:
- Birincil dili köke koyun, ikincil dili prefixe.
- Slug'ı da çevirin — anahtar kelime URL'de olsun.
- hreflang'i karşılıklı ve
x-default'lu verin. - canonical'ı her sayfada açıkça belirtin, kökten miras aldırmayın.
- Statik üretin; hızı bedavaya alın.
Bunları bir kere doğru kurduğunuzda, yeni sayfa eklemek sadece veri eklemek oluyor — SEO altyapısı kendini tekrar ediyor. Asıl kazanç da bu: ölçeklenen, tutarlı bir temel.