Next.js i18n SEO Rehberi

Next.js i18n routing ile çok dilli SEO yapılandırması

Next.js'in yerleşik i18n routing desteği, çok dilli siteler için ciddi bir kolaylık sunuyor. Tek bir konfigürasyon bloğuyla locale listesi tanımlanıyor, URL'ler otomatik olarak locale önekiyle üretiliyor ve varsayılan dil için özel bir davranış belirlenebiliyor. Bu altyapı doğru kurulduğunda tekrarlayan SEO sorunlarının büyük bölümünü baştan engelliyor; yanlış kurulduğunda ise canonical çatışmaları, hreflang eksiklikleri ve yönlendirme hataları sessizce birikmeye başlıyor. Projenin erken aşamalarında bu kararlar küçük teknik detaylar gibi görünüyor; bir yıl sonra altı dil ve yüz binlerce URL'le uğraşıldığında ise baştan tasarlanmamış her eksik ciddi bir yeniden yapılandırma gerektiriyor.

Sorun şu ki Next.js i18n routing bir SEO aracı olarak değil, bir geliştirici aracı olarak tasarlanmış. Locale URL'lerini oluşturmak ile bu URL'leri arama motorlarına doğru şekilde tanıtmak farklı sorumluluklar. Framework routing'i hallediyor, ama hreflang etiketlerini kim üretiyor, canonical URL'leri kim belirliyor, Googlebot'a hangi içeriğin hangi dil için olduğunu kim bildiriyor — bunların yanıtı konfigürasyon dosyasında değil, geliştirme kararlarında yatıyor.

Next.js ile çok dilli bir proje yürütüyorsanız ya da bu yapıya geçişi değerlendiriyorsanız, framework'ün sağladığı ile sizin sağlamanız gereken arasındaki sınırı net görmek önemli. Bu kararların bir kısmı mimari seçim, bir kısmı konfigürasyon, bir kısmı ise çalışma zamanında ne döndürüldüğünün kontrolü — ve hepsi farklı rollerin sorumluluğunu gerektiriyor.

next.config.js i18n bloğu: ne sağlıyor, ne sağlamıyor?

Next.js çok dilli projelerde sık tercih ediliyor çünkü hem SSG hem SSR hem de ISR'yi tek bir çatı altında sunuyor; React ekosistemiyle uyumu güçlü ve belgeleri kapsamlı. Bir geliştirici Next.js'e geçtiğinde i18n routing desteğini hazır buluyor. Ancak hazır routing ile tam anlamıyla çalışan çok dilli SEO arasındaki mesafeyi küçümsemek yaygın bir yanılgı. Framework'ün sağladığı altyapı URL üretimini çözüyor; ama meta etiketlerin doğruluğu, hreflang tutarlılığı ve Googlebot erişimi tamamen uygulamanın kendi katmanlarında belirleniyor.

Next.js'de i18n desteği next.config.js dosyasındaki i18n bloğuyla etkinleştiriliyor. Temel yapı üç elemandan oluşuyor: locales dizisi (desteklenen tüm locale'lerin listesi), defaultLocale (varsayılan dil) ve isteğe bağlı localeDetection bayrağı. Bu yapı tanımlandığında framework her sayfa için locale önekli URL'ler otomatik olarak üretiyor: /en/products, /fr/products, /de/products gibi.

Varsayılan locale'in davranışı önemli bir karar noktası. Varsayılan olarak, defaultLocale olarak belirlenen dil URL öneki almıyor: /products Türkçe ise, /tr/products gibi bir URL oluşturulmuyor. Bu tutarsızlık küçük görünüyor ama hreflang setini karmaşıklaştırıyor; canonical ilişkisi /products ile /tr/products arasında kurulmak zorunda kalıyor, iki URL ise aslında aynı içeriği temsil ediyor. Bunu çözmek için bazı projeler varsayılan locale'i de önekli yayımlamayı tercih ediyor; bu next.config.js'de doğrudan desteklenmiyor, middleware ile kurulması gerekiyor.

Locale listesinin büyümesi build süresi ve çalışma zamanı yükü üzerinde doğrusal bir etki bırakıyor. Üç locale için SSG build'de 2 dakika alıyorsa, dokuz locale için bu süre 6 dakikaya yaklaşıyor — ve bu hesap yalnızca sayfa sayısının locale sayısıyla çarpıldığı basit bir model için geçerli. Her locale'e özel sayfa mantığı, farklı içerik hacmi veya kısmi çeviriler varsa tahmin daha da güçleşiyor. Bu nedenle locale listesini ileriye dönük planlamak, geliştirme aşamasında küçük ama yayına geçildikten sonra değiştirmesi maliyetli bir karar.

localeDetection: false ayarı SEO açısından kritik. Bu kapatılmadığında Next.js ziyaretçinin Accept-Language başlığını okuyarak locale tespiti yapıyor ve yönlendirme uyguluyor. Googlebot'un bu yönlendirmeyi tutarlı şekilde takip etmesi garanti değil; test ettiğinizde sorun görmeyebilirsiniz ama indeksleme davranışı beklenmedik biçimde farklılaşabiliyor. Accept-Language tabanlı yönlendirmenin SEO riskleri bu nedenle özellikle headless ve SSR projelerinde dikkatle değerlendirilmeli.

Hreflang Next.js'de kendiliğinden üretilmiyor

Next.js i18n routing'in en sık yanlış anlaşılan noktası bu: framework locale URL'lerini oluşturuyor ama hreflang etiketlerini üretmiyor. Bu etiketler her sayfanın <head> bölümüne elle ya da programatik olarak eklenmek zorunda. Vercel'in resmi örneklerinde bile bu konuda farklı yaklaşımlar görülüyor; standart bir çözüm sunulmamış, her projenin kendi kararını vermesi gerekiyor.

Uygulama yöntemi genellikle iki seçenekten birinde karar kılıyor. İlki, her sayfanın layout veya _app.tsx katmanında tüm locale'ler için hreflang etiketleri üretmek. Bu yaklaşımda locale listesi statik olarak biliniyor; her sayfa URL'si locale önekiyle birleştirilerek tag seti oluşturuluyor. Yapısal olarak temiz ama dikkat edilmesi gereken bir nokta var: belirli bir sayfanın bazı locale'lerde karşılığı yoksa, var olmayan URL'ye hreflang vermek hata yaratıyor. Google, hreflang etiketinde listelenen bir URL'yi taradığında 404 ya da farklı içerik bulursa o etiket kümesini geçersiz sayıyor.

İkinci yaklaşım, içerik kaynağından (CMS API, veritabanı) o içeriğin hangi locale'lerde mevcut olduğunu çekerek dinamik hreflang üretmek. Bu daha doğru bir yöntem; kısmi çeviriler söz konusu olduğunda gereksiz etiket üretilmiyor. Ancak her sayfa render'ında API çağrısı gerektiriyor; SSG'de build süresini, SSR'de istek başına yükü artırıyor. Hreflang karşılıklı referans kuralı burada da geçerli: Türkçe sayfa Fransızcaya işaret ediyorsa, Fransızca sayfa da Türkçeye işaret etmek zorunda. Bir locale'in hreflang setini eksik bırakmak tüm kümenin güvenilirliğini düşürüyor.

x-default değerinin de hreflang setine dahil edilmesi gerekiyor. Hangi URL'nin varsayılan dil sürümü olduğunu ya da hiçbir locale eşleşmediğinde nereye yönlendirileceğini belirtiyor. Next.js'de varsayılan locale öneksiz yayınlandığında bu URL x-default için doğal aday; ama bu ilişki manuel olarak tanımlanmak zorunda. Detaylar için x-default değerinin işlevi konusuna bakılabilir.

Pratikte sık karşılaşılan bir hata daha var: bileşen ya da layout seviyesinde hreflang üretilirken URL'nin base path'i yanlış hesaplanıyor. Özellikle subdirectory deployment'larda ya da CDN'nin farklı bir base URL sunduğu durumlarda, üretilen hreflang etiketi https://example.com/tr/products yerine https://example.com/products gibi locale öneki olmayan bir URL içerebiliyor. Bu sessiz bir hata — sayfa çalışıyor, hreflang görünüyor ama URL'ler yanlış. Üretilen hreflang değerlerini düzenli olarak kaynak koddan okuyup doğrulamak, bu tür drift'i erken yakalamak için yeterli.

getStaticPaths ve locale: SSG'de hangi sayfalar üretiliyor?

Pages Router kullanan Next.js projelerinde dinamik sayfalar getStaticPaths ile üretiliyor. Çok dilli yapıda bu fonksiyonun her locale için her sayfa yolunu döndürmesi gerekiyor; aksi hâlde bazı locale + sayfa kombinasyonları build sırasında üretilmiyor.

Pratikte bu şöyle görünüyor: getStaticPaths paths dizisi döndürürken her path nesnesi hem sayfa parametrelerini hem de locale değerini içermeli. fallback: false seçildiğinde listelenmemiş kombinasyonlar 404 döndürüyor — SEO açısından temiz bir davranış. fallback: 'blocking' seçildiğinde ise build'de üretilmeyen sayfalar ilk istekte SSR olarak üretiliyor ve önbelleğe alınıyor. Büyük kataloglarda bu ikinci seçenek build süresini kısaltıyor ama ilk Googlebot crawl'ında sayfanın hazır olup olmadığı belirsizleşiyor.

App Router'a geçiş yapan projelerde bu mantık generateStaticParams ile çalışıyor; locale parametresi de segment olarak modelleniyor. Yapısal fark şu: App Router'da locale genellikle URL segmenti olarak açıkça tanımlanıyor (/[locale]/products/[slug]), Pages Router'daki i18n bloğuna göre daha şeffaf bir mimari. Bu şeffaflık hata ayıklamayı kolaylaştırıyor; öte yandan her sayfanın locale'i nasıl aldığını açıkça belgelemek gerekiyor.

Kısmi çeviri senaryosunda bir ürün yalnızca Türkçe ve İngilizce mevcutsa, Almanca locale için getStaticPaths'e bu ürünün Almanca path'i eklenmemeli. Ancak bu kontrolü içerik API'sinden gelen veriye dayandırmak şart; statik bir liste tutulursa çeviri eklendikçe güncellemeyi unutmak kaçınılmaz oluyor. İdeal yaklaşım: build sırasında her içerik için hangi locale'lerde çevirisi olduğunu API'den sorgulamak ve yalnızca mevcut kombinasyonları path listesine eklemek. Bu ekstra build süresi maliyeti doğuruyor ama indeksleme tutarlılığı için çok daha güvenli bir zemin sağlıyor.

Revalidation stratejisi de bu noktada devreye giriyor. ISR kullandığınızda bir içeriğin Türkçe versiyonu yeniden üretilirken Almanca versiyonu eski içeriği sunuyor olabilir. Bu kısa süreli tutarsızlık çoğu durumda kabul edilebilir; Google sayfaları birkaç dakikalık farkla taramıyor. Ancak hreflang etiketleri locale varlığına göre dinamik değişiyorsa — yani yeni bir çeviri yayınlandığında hreflang setinin güncellenmesi gerekiyorsa — on-demand revalidation veya webhook tabanlı tetikleyici kurulması gerekiyor. Aksi hâlde hreflang Almanca sayfaya işaret etmeden önce Türkçe sayfa yenileniyor ve Google geçici olarak eksik bir locale seti görüyor.

Canonical URL ve locale: hangi sürüm tercih ediliyor?

Next.js i18n routing etkinleştirildiğinde /products ve /tr/products gibi iki farklı URL aynı içeriği sunuyor olabilir. Bu durum, canonical etiket doğru yönetilmezse çift içerik sorununa dönüşüyor. Tek bir sayfanın hangi URL'si canonical olarak belirlenmeli?

Yaygın ve temiz yaklaşım: varsayılan locale URL öneksiz yayınlandığında (/products), canonical da bu URL'yi göstermeli. Diğer locale'ler kendi önekli URL'lerini canonical olarak işaret etmeli (/fr/products, canonical = /fr/products). Middleware veya yönlendirme ile /tr/products varsa ve aynı içeriği sunuyorsa, bu URL'yi /products'a yönlendirmek daha temiz; iki URL'nin eşzamanlı yaşamasından kaçınmak gerekiyor.

Çok dilli sitelerde canonical etiket yönetimi, hreflang setiyle uyumlu olarak tasarlanmak zorunda. Canonical bir locale'i işaret ederken hreflang farklı bir URL kümesi bildiriyorsa Google hangisine güveneceğini bilemiyor. Bu çatışma özellikle ISR veya middleware katmanında yapılan yönlendirmeler devreye girdiğinde sık karşılaşılan bir sorun.

Next.js 13+ App Router'da generateMetadata fonksiyonu canonical URL'yi programatik olarak üretmeyi kolaylaştırıyor. Her sayfa kendi locale'ini ve route parametrelerini okuyarak doğru canonical'ı otomatik oluşturabiliyor. Pages Router'da ise bu değerin next/head içinde açıkça yazılması ya da merkezi bir yardımcı fonksiyonla üretilmesi gerekiyor.

Bir diğer karar noktası: dil seçici bileşeni sayfa URL'sini değiştirirken canonical'ı da güncelliyor mu? Kullanıcı dil seçiciden Fransızcayı seçtiğinde tarayıcı /fr/products'a yönlendiriliyor; bu sayfa kendi canonical'ını doğru göstermeli. JavaScript ile dil değişikliği yapıp URL'yi güncellemeden içeriği değiştiren uygulamalar ise gerçek bir SEO riski yaratıyor: canonical sabit kalırken görüntülenen içerik locale'e göre farklılaşıyor. Bu, Google'ın hangi dil sürümünü indekslediğini belirsizleştiren bir durum.

Sitemap üretimi ve GSC doğrulaması

Çok dilli Next.js sitelerinde sitemap üretimi iki yöntemle çözülüyor. İlki, build sürecinde tüm locale ve sayfa kombinasyonlarını içeren tek bir sitemap dosyası üretmek. Her URL kendi xhtml:link elementleriyle diğer dil varyantlarına işaret ediyor. İkincisi, her locale için ayrı sitemap üretip bunları ana sitemap index dosyasında listelemek.

İlk yöntem küçük ve orta ölçekli projeler için yeterli; tek dosya yönetimi kolay, Googlebot'a tüm dil varyantlarını tek seferde sunuyor. İkinci yöntem büyük kataloglarda tercih ediliyor çünkü sitemap dosya başına 50.000 URL sınırı var; dil sayısı ve içerik hacmi çarpıldığında tek dosyada kalmak güçleşiyor. Çok dilli sitemap planlaması bu ölçek kararını doğrudan etkiliyor. Örneğin beş dil ve 15.000 ürün sayfası olan bir katalog, ürün URL'leri için tek başına 75.000 URL oluşturuyor; bu durumda locale başına ayrı sitemap dosyası hem dosya sınırını aşmamayı hem de hata ayıklamayı kolaylaştırıyor.

Google Search Console'da uluslararası hedefleme raporunu kontrol etmek, hreflang kurulumunun işe yarayıp yaramadığını görmenin en pratik yolu. Ancak bu rapor her hatayı göstermiyor; bazı hreflang çatışmaları GSC'de hata olarak görünmeden sessizce yanlış sürümü sıralamaya sokmaya devam edebiliyor. Yeni locale eklendikten birkaç hafta sonra hangi dil sürümlerinin hangi bölgelerde göründüğünü takip etmek, yapısal sorunları erkenden yakalamayı sağlıyor.

Dinamik sitemap üretimi için Next.js'in app/sitemap.ts (App Router) veya özel API route (Pages Router) yaklaşımı kullanılıyor. İçerik kaynağından tüm sayfaları ve her sayfanın mevcut locale'lerini çeken bir fonksiyon yazıldığında, sitemap her build'de güncel kalıyor. Burada dikkat edilmesi gereken nokta: sitemap'e dahil edilen URL'lerin gerçekten erişilebilir ve indexlenebilir olması. noindex etiket taşıyan sayfaları sitemap'e eklemek, Google'a çelişkili sinyal gönderiyor. Kısmi çevirilerde hangi locale URL'lerinin sitemap'e gireceği bu nedenle içerik API'sinden gelen veriye dayanmalı, statik locale listesine değil.

Middleware katmanı: kullanıcı deneyimi ile bot erişimi arasındaki denge

Next.js middleware, istekler sunucuya ulaşmadan önce çalışıyor ve locale tespiti, yönlendirme, A/B testi gibi işlemler için kullanılıyor. Çok dilli SEO açısından en büyük risk: middleware'ın Googlebot'a kullanıcıdan farklı içerik sunması ya da Googlebot'u beklenmedik bir URL'ye yönlendirmesi.

Özellikle şu senaryo dikkat gerektiriyor: middleware ziyaretçinin IP'sine ya da Accept-Language başlığına göre locale tespiti yapıyor ve yönlendiriyor. Googlebot genellikle ABD'den tarama yapıyor; bu durumda İngilizce sayfaları tercih edebiliyor. Türkçe ve diğer locale içerikleri düzenli olarak taranmıyorsa indeksleme kapsamı daralıyor. next.config.js'de localeDetection: false ayarlamak ve locale tercihini URL yapısına bırakmak bu riski azaltıyor.

Middleware'da Googlebot tespiti yaparak bota yönlendirme uygulamaktan kaçınmak gerekiyor. User-agent'a dayalı farklı davranış, cloaking olarak değerlendirilebilir. Tercih edilen yaklaşım: kullanıcıya da bota da aynı URL mantığını uygulamak, dil tercihini cookie veya URL üzerinden yönetmek.

Middleware performansı da göz ardı edilmiyor. Edge runtime'da çalışan middleware her istek için çalışıyor; locale tespiti için harici bir API çağrısı yapılıyorsa gecikme kullanıcı deneyimini ve Core Web Vitals skorlarını doğrudan etkiliyor. Locale tespiti mümkün olduğunca statik kurallara — URL path analizi, cookie okuma — dayandırılmalı; dinamik sorgular için ise yalnızca gerektiğinde çalışan koşullu mantık kurulmalı.

Middleware'ın hangi path'lerde çalışacağını belirleyen matcher yapılandırması da SEO açısından önem taşıyor. Varsayılan olarak middleware tüm istekleri kapsıyor; bu statik varlıklar, API route'ları ve _next istekleri dahil. Gereksiz yük oluşturmamak için matcher'ı yalnızca sayfa route'larıyla sınırlamak hem performansı iyileştiriyor hem de beklenmedik locale yönlendirmelerinin bot tarama sürecine sızmasını engelliyor. Özellikle sitemap ve robots.txt isteklerinin middleware'dan geçmemesini sağlamak, bu dosyaların her zaman doğru içeriği döndürmesi için kritik.

Next.js, çok dilli SEO için iyi bir başlangıç noktası sunuyor: locale URL'leri yapısal olarak yerleşik, render seçenekleri esnek, meta üretim kontrolü geliştiricinin elinde. Ama bu başlangıç noktasından SEO açısından sağlam bir çok dilli yapıya gitmek için birkaç katmanı daha doğru kurmak gerekiyor — hreflang'ı içerik verisiyle beslemek, canonical'ı locale mantığıyla hizalamak, sitemap'i dil ve sayfa ölçeğine göre planlamak ve middleware'ı bot erişimini engellemeyecek şekilde tasarlamak. Bu çalışmanın büyük bölümü geliştirici sorgularında değil, teknik SEO denetiminde görünür hale geliyor. Özellikle yeni locale eklenirken — projenin başlangıcında değil, büyüdükçe — mevcut altyapının yeni locale'i doğru kapsayıp kapsamadığını doğrulamak önemli. Hreflang üretim mantığı, sitemap şablonu ve middleware kuralları her yeni locale'le birlikte test edilmeli.

Pages Router'dan App Router'a geçiş yapan projelerde bu dönüşüm ekstra bir dikkat gerektiriyor. App Router'da i18n bloğu artık next.config.js'de değil, URL segmentleri üzerinden yönetiliyor. Geçiş sırasında locale URL'lerinin yapısı değişirse mevcut indekslenmiş sayfalar için 301 yönlendirme planı hazırlanmalı. Hreflang etiketlerinin de yeni URL yapısını yansıtacak şekilde güncellenmesi gerekiyor; bu adım atlanırsa Google eski ve yeni URL'leri farklı içerikler olarak değerlendirerek ikisini de sıralamaya sokmaya çalışıyor — bu da otorite bölünmesine yol açıyor. Geçiş sürecinde önce yeni URL yapısını yayına almak, ardından 301 yönlendirmeleri etkinleştirmek ve son olarak hreflang'ı güncellemek, işlemi aşamalı ve izlenebilir kılıyor.

Bu katmanlar birbirinden bağımsız değil. Hreflang doğru kurulmuş ama canonical çatışmalıysa, ya da sitemap tam ama middleware belirli locale'lerin taranmasını engelliyorsa, sorun parçalı kalıyor ve çoğu zaman Search Console'da görünmeden devam ediyor. Dil sayısı arttıkça bu birlikteliğin önemi de artıyor; iki locale için kabul edilebilir olan ihmal, altı locale için yapısal bir soruna dönüşebiliyor. Kurulumu tamamlandıktan sonra hreflang testinin sistematik olarak yapılması, sessiz hataları yayına girmeden yakalamanın en güvenilir yolu.