Devamı

Next.js 16 ve next-intl ile bilingual SEO

blog.posts.bilingualSeo.minRead dk okuma

Lumina'nın kurumsal sitesini Türkçe ve İngilizce iki dilde yayına almaya karar verdiğimizde, "sadece çeviri yeter" sandık. Pratikte iki dilli bir Next.js 16 sitesinin SEO sağlığını korumak; routing, metadata, sitemap, canonical ve UX kararları zinciri demekti. Bu yazıda kurduğumuz sistemi özetliyoruz.

1. Routing: as-needed prefix

next-intl'de üç prefix modu var: always,as-needed ve never. Türkçe'yi varsayılan dilimiz seçtik (ana hedef pazar) ve as-needed moduna geçtik. Sonuç:

  • / ve /hakkimizda doğrudan Türkçe
  • /en ve /en/hakkimizda İngilizce
  • Slug'lar her iki dilde aynı (/hakkimizda)

Slug'ları İngilizce'ye lokalize etmedik (/en/about gibi). next-intl'in pathnames özelliği bunu destekliyor ama bizim için ek karmaşa: aynı içerik için iki ayrı kanonik path olmak zorunda kalıyor. İlk sürümde basit tutmayı seçtik; ileride bunu yeniden değerlendirebiliriz.

2. Hreflang: arama motorlarına dil ailelerini söylemek

Her sayfada her iki dil için hreflangtag'leri olmalı:

<link rel="alternate" hreflang="tr" href="/hakkimizda" />
<link rel="alternate" hreflang="en" href="/en/hakkimizda" />
<link rel="alternate" hreflang="x-default" href="/hakkimizda" />

Next.js 16'ın metadata API'sı bunu kolaylaştırıyor: alternates.languagesfield'ı. Biz bunu bir helper'la sarmalayıp tüm sayfalardan tutarlı şekilde kullanıyoruz.

3. Sitemap: dinamik ve dil farkındalı

app/sitemap.tsdosyası tüm rotaları üretirken her rota için iki entry oluşturmalı (TR ve EN). Aksi halde Google İngilizce sayfaları "görünmez" sanır:

for (const route of ROUTES) {
  for (const locale of locales) {
    entries.push({
      url: `${siteUrl}${getPath(route, locale)}`,
      lastModified: new Date(),
      alternates: {
        languages: { tr: `...`, en: `...` }
      }
    });
  }
}

4. Locale switcher UX

Kullanıcı dil değiştirdiğinde aynı içerik sayfasının diğer dilini görmeli — anasayfaya değil. usePathnameile mevcut path'i alıp router.replace(pathname, {locale})ile değiştiriyoruz. next-intl'in typed navigation helper'ları bu işlemi tip güvenli yapıyor.

5. Apple-grade içerik standardı

SEO sadece teknik bir disiplin değil. Apple App Store Developer Program enrollment red gerekçemiz "minimal content"tı — bu, sayfa sayısı kadar her sayfanın doluluğu da önemli demek. Lumina sitesinde 21 unique route × 2 dil = 42 sayfa hedefliyoruz. Her sayfa, varolan bir şirketten beklenen seviyede içerik taşıyor:

  • Şirket künyesi (sicil, vergi, adres) footer'da görünür
  • Engineering blog: gerçek teknik içerik
  • Yasal sayfalar: gerçek hukuki metinler, jenerik şablon değil
  • İletişim formu: çalışıyor, test ediliyor (Apple test eder!)

6. Lighthouse'ı build pipeline'a bağlamak

Build sonrası her route için Lighthouse koşmak yerine; kritik sayfaları (anasayfa, hakkımızda, ürünler) belirleyip Lighthouse CI ile her PR'da kontrol ediyoruz. Performans bütçesi:

  • LCP < 2.0s on mobile 4G
  • CLS < 0.05
  • INP < 150ms
  • Initial JS < 200KB

Sonuç

Çok dilli SEO; Next.js 16, next-intl ve Vercel'in ortak sistemiyle çözülmüş bir problem. Kritik nokta, dilsel UX'in ve içerik kalitesinin teknik altyapı kadar önemli olduğu. Apple'ın "minimal content" reddi bize bunu net bir şekilde hatırlattı: arama motorları kadar insan da, sitenin dolu olduğunu görmek istiyor.