İçindekiler
Astro + i18n: Çok Dilli Statik ve Dinamik Siteler Oluşturma
Bir Astro sitesi yayınladıktan sonra "Fransızca ve Almanca ekleyelim" talebini alan herkes o hissi bilir: Kısa bir umut anının ardından, bulabildiğiniz belgelerin, topluluk kütüphanelerinin ve blog yazılarının birbiriyle hafifçe çeliştiğini fark edersiniz. Kimisi dosya tabanlı routing kullanır. Kimisi başka bir şeyi sarmalayan bir kütüphane kullanır. Bir kısmı artık 4.x sürümündesiniz diye Astro 2.x için yazılmıştır.
Bu yazı, tam da bu sorunu çözme girişimidir. Astro'nun yerleşik i18n routing yapısını, bir topluluk kütüphanesine ne zaman başvurmanız gerektiğini, harici bir çeviri sisteminin nasıl entegre edileceğini ve nelerin yanlış gidebileceğini ele alacağız. Amaç, Astro üzerinde çok dilli siteler geliştiren geliştiriciler için tek, dürüst bir referans kaynağı sunmaktır — ister statik site üretimi, ister sunucu taraflı rendering, ister ikisinin karışımını kullanıyor olun.
Çok Dilli Siteler İçin Neden Astro
Astro'nun mimarisi, yerelleştirilmiş içerik için doğal bir uyum sağlar. Framework varsayılan olarak sıfır istemci taraflı JavaScript kullanır; bu da çevrilmiş içeriğinizin düz HTML olarak sunulduğu anlamına gelir — hızlı yüklenir, kolayca dizine eklenir, Tokyo'daki ya da Berlin'deki kullanıcılar için hydration gecikmesi olmaz.
Dosya tabanlı routing modeli, /fr/about veya /de/pricing gibi locale önekli URL'lerle düzgünce eşleşir. Astro aynı proje içinde hem statik üretimi (SSG) hem de sunucu taraflı rendering'i (SSR) desteklediğinden, kararlı çevirilerle sayfaları önceden render edebilir ve yine de sunucu endpoint'leri aracılığıyla dinamik içerikleri yönetebilirsiniz.
Bununla birlikte, Astro'nun i18n hikayesi birkaç ana sürüm boyunca olgunlaştı; eski yazılarda bulduğunuz rehberlik, bugün üzerinde çalıştığınız sürüme uygulanamayabilir.
Yerleşik i18n Routing (Astro 3.x+)
Astro, 3.0 sürümünde birinci sınıf i18n routing desteğini tanıttı. Bundan önce, her ekip kendi locale tespitini ve URL yönetimini kendisi kuruyordu. Yerleşik sistem standart kodları ele alır ve SSR için Astro'nun adapter katmanıyla entegre olur.
astro.config.mjs Kurulumu
Giriş noktası Astro yapılandırma dosyanızdır:
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr', 'de', 'ja'],
routing: {
prefixDefaultLocale: false,
},
},
});
Burada anlamaya değer birkaç nokta var:
defaultLocale, sitenizin geri dönüş yaptığı dildir.prefixDefaultLocaledeğerifalseise URL'de görünmesi gerekmez — yani/aboutİngilizce sunarken/fr/aboutFransızca sunar.locales, BCP 47 dil etiketlerinden oluşan bir dizidir. Özel yol segmentlerine ihtiyaç duyarsanız nesneler de geçirebilirsiniz (örneğinpt-BR'yi/br/ile eşleştirmek).prefixDefaultLocale: falseen yaygın üretim kurulumudur.trueyapılması/aboutyerine/en/aboutşeklinde URL verir; bu SEO netliği açısından işe yarasa da daha uzun URL'ler oluşturur.
Dosya Yapısı
i18n routing etkinleştirildiğinde Astro, yerelleştirilmiş sayfalarınızı src/pages/ altında bekler:
src/
pages/
index.astro ← Varsayılan locale (İngilizce)
about.astro
fr/
index.astro ← Fransızca
about.astro
de/
index.astro ← Almanca
about.astro
ja/
index.astro ← Japonca
about.astro
Bu mümkün olan en basit kurulumudur. Her dosya kendi içeriğine sahip bağımsız bir sayfadır. Paylaşılan bileşenler zorunlu değildir — pratikte onları isteyeceksiniz.
getRelativeLocaleUrl ile Yerelleştirilmiş Dahili Bağlantılar
Astro'nun ortaya koyduğu en kullanışlı yardımcılardan biri getRelativeLocaleUrl'dir. Bir yol ve locale alır; varsayılan locale'nin öneki olup olmadığını hesaba katarak doğru önekli URL'yi döndürür.
---
// src/pages/fr/index.astro
import { getRelativeLocaleUrl } from 'astro:i18n';
const aboutUrl = getRelativeLocaleUrl('fr', '/about');
// Döndürür: /fr/about
---
<a href={aboutUrl}>À propos</a>
Sitemap veya OG etiketleri için tam URL'lere ihtiyaç duyarsanız getAbsoluteLocaleUrl'i de kullanabilirsiniz:
---
import { getAbsoluteLocaleUrl } from 'astro:i18n';
const canonicalUrl = getAbsoluteLocaleUrl('fr', '/about');
// Döndürür: https://yoursite.com/fr/about
---
Sabit kodlanmış dizeler yerine bu yardımcıları kullanmak, bağlantı yapınızın yapılandırma değişikliklerinden — prefixDefaultLocale'i false'tan true'ya geçirmek gibi — tüm kod tabanınızda bul-değiştir yapmadan sağ çıkmasını sağlar.
Yedek İçerik
Her sayfa ilk günden itibaren her dile çevrilmeyebilir. Astro bunu fallback yapılandırmasıyla yönetir:
// astro.config.mjs
export default defineConfig({
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr', 'de', 'ja'],
fallback: {
fr: 'en',
de: 'en',
ja: 'en',
},
routing: {
prefixDefaultLocale: false,
fallbackType: 'rewrite',
},
},
});
fallbackType: 'rewrite' ile Astro, src/pages/fr/about.astro mevcut değilse kullanıcıyı yönlendirmeden /fr/about URL'sinde İngilizce içeriği sunar. URL /fr/about olarak kalır ancak içerik İngilizce'den gelir. Bu, çevirileri aşamalı olarak yayınladığınız yumuşak lansmanlar için kullanışlıdır.
Alternatif olarak fallbackType: 'redirect', Fransızca sürüm eksikse kullanıcıyı /about'a yönlendirir; bu daha şeffaftır ancak rahatsız edici bir deneyim yaratır.
Özel Domain Eşleştirme
Locale'leriniz URL önekleri yerine ayrı domain'lerde yer alıyorsa — yaygın bir kurumsal örüntü — Astro bunu i18n.domains aracılığıyla destekler:
// astro.config.mjs
export default defineConfig({
site: 'https://example.com',
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr', 'de'],
domains: {
fr: 'https://fr.example.com',
de: 'https://de.example.com',
},
},
output: 'server',
adapter: node({ mode: 'standalone' }),
});
output: 'server' gereksinimini not edin. Domain eşleştirme yalnızca SSR ile çalışır çünkü Astro'nun hangi locale'nin sunulacağını belirlemek için çalışma zamanında gelen istek hostname'ini incelemesi gerekir.
getAbsoluteLocaleUrl yardımcısı domain eşleştirmeye saygı gösterir:
---
import { getAbsoluteLocaleUrl } from 'astro:i18n';
const frenchHome = getAbsoluteLocaleUrl('fr', '/');
// Döndürür: https://fr.example.com/
---
Çeviri Dizilerini Yönetme
Yerleşik routing, URL'leri ve sayfa çözümlemeyi ele alır. UI bileşenlerinizdeki gerçek çevrilmiş metin olan çeviri dizilerini ele almaz. Bunun için birkaç seçeneğiniz var.
Seçenek 1: Basit JSON Dosyaları (Kütüphane Olmadan)
Küçük siteler için düz JSON yaklaşımı gayet iyi çalışır:
src/
i18n/
en.json
fr.json
de.json
// src/i18n/en.json
{
"nav.home": "Home",
"nav.about": "About",
"hero.title": "Build faster websites",
"hero.subtitle": "The web framework for content-driven sites"
}
---
// src/pages/fr/index.astro
const locale = 'fr';
const t = (await import(`../../i18n/${locale}.json`)).default;
---
<h1>{t['hero.title']}</h1>
<p>{t['hero.subtitle']}</p>
Bu çalışır, ancak site büyüdükçe acısını hissedersiniz: tür güvenliği yok, eksik anahtar uyarıları yok, her yerde manuel import'lar.
Seçenek 2: Yardımcı Bir Fonksiyon
Küçük bir sarmalayıcı işleri daha yönetilebilir kılar:
// src/i18n/utils.ts
import en from './en.json';
import fr from './fr.json';
import de from './de.json';
const translations = { en, fr, de } as const;
type Locale = keyof typeof translations;
type TranslationKey = keyof typeof en;
export function useTranslations(locale: Locale) {
return function t(key: TranslationKey): string {
return translations[locale][key] ?? translations['en'][key] ?? key;
};
}
---
// src/pages/fr/index.astro
import { useTranslations } from '../../i18n/utils';
const t = useTranslations('fr');
---
<h1>{t('hero.title')}</h1>
Artık çeviri anahtarlarında TypeScript otomatik tamamlama ve eksik dizeler için İngilizce'ye geri dönüş mekanizmanız var. Bu, Astro'nun resmi i18n tarifinin önerdiği örüntüdür ve bağımlılık eklemeden pek çok durumu kapsar.
Topluluk Kütüphaneleri
Yerleşik yaklaşım yeterli olmadığında, ekosistem bilmeye değer birkaç seçenek sunar:
astro-i18next
i18next üzerine kurulu, en özellik zengini seçenektir:
npm install astro-i18next i18next
// astro.config.mjs
import { defineConfig } from 'astro/config';
import astroI18next from 'astro-i18next';
export default defineConfig({
integrations: [astroI18next()],
});
// astro-i18next.config.mjs export const defaultLocale = 'en'; export const locales = ['en', 'fr', 'de'];
Çoğullama, interpolasyon, namespace desteği ve tam i18next ekosistemine sahip olursunuz. Takas-off, karmaşıklıktır — i18next'in geniş bir yüzey alanı vardır ve SSR edge case'lerini hata ayıklamak sinir bozucu olabilir.
Paraglide JS (Inlang)
Paraglide farklı bir yaklaşım benimser: çalışma zamanında dizileri çözmek yerine derleme zamanında tür güvenli mesaj fonksiyonları üretir. Bu, çeviriler için sıfır çalışma zamanı maliyeti ve tam TypeScript güvenliği anlamına gelir.
npm install @inlang/paraglide-astro
// astro.config.mjs
import { defineConfig } from 'astro/config';
import paraglide from '@inlang/paraglide-astro';
export default defineConfig({
integrations: [
paraglide({
project: './project.inlang',
outdir: './src/paraglide',
}),
],
});
// Derleme zamanında üretildi — tam tipli import * as m from '../paraglide/messages'; // Otomatik tamamlama çalışır, yazım hataları derleme hatasıdır const title = m.hero_title();
TypeScript doğruluğu ekibiniz için önemliyse Paraglide ciddi bir değerlendirmeye değer. Inlang ekosistemi büyüyor ve araçlar aktif olarak bakımı yapılıyor.
intlayer
Intlayer çevirileri ayrı locale dosyaları yerine bileşenlerle birlikte konumlandırır:
// src/components/Hero.content.ts
import { t, type DeclarationContent } from 'intlayer';
const heroContent = {
id: 'hero',
title: t({
en: 'Build faster websites',
fr: 'Construisez des sites plus rapides',
de: 'Bauen Sie schnellere Websites',
}),
} satisfies DeclarationContent;
export default heroContent;
Bu birlikte konumlandırma modeli, bileşen mantığını çeviri anahtarlarıyla eşleştirmenin bilişsel yükünü azaltır. İçerik ve düzenin birlikte geliştiği bileşen yoğun projeler için iyi bir uyumdur.
Astro Build'leriyle Harici TMS Entegrasyonu
Üretim yerelleştirme iş akışlarının büyük çoğunluğu, çevirmenlerin, gözden geçirenlerin ve proje yöneticilerinin iş birliği yaptığı bir çeviri yönetim sistemi (TMS) içerir. Astro'nun build adımı doğal entegrasyon noktasıdır.
Tipik örüntü:
- Kod tabanınızdan kaynak dizileri TMS'e aktarın.
- Çevirmenler TMS arayüzünde çalışır (veya orada yapay zeka çevirisi çalıştırılır).
- Çevrilen diziler repo'nuza geri çekilir veya build zamanında alınır.
- Astro siteyi mevcut çevirilerle oluşturur.
CI/CD için bu, build pipeline'ınızın şöyle görünebileceği anlamına gelir:
# .github/workflows/deploy.yml
jobs:
build:
steps:
- uses: actions/checkout@v4
- name: Çevirileri çek
run: npx better-i18n pull --all-locales
env:
BETTER_I18N_API_KEY: ${{ secrets.BETTER_I18N_API_KEY }}
- name: Siteyi oluştur
run: npm run build
- name: Deploy et
run: npm run deploy
pull adımı, build çalışmadan önce en son onaylanmış çevirileri getirir. Better i18n gibi platformlar bu örüntüyü CDN dağıtımı aracılığıyla destekler — deployment modelinize bağlı olarak SSG için build zamanında locale dosyalarını çekebilir ya da SSR için edge'den çalışma zamanında alabilirsiniz.
Astro i18n'in Karmaşıklaştığı Yerler
Ekiplerin sürekli sorunlarla karşılaştığı birkaç alan:
Çeviri Anahtarları İçin Tür Güvenliği
Dosya tabanlı JSON çevirilerinin derleme zamanı garantisi yoktur. Anahtardaki bir yazım hatası sessizce undefined döndürür ya da anahtar dizisine düşer. TypeScript ile yardımcı fonksiyon yaklaşımı yardımcı olur, ancak anahtar kümesi büyüdükçe disiplin gerektirir.
Burada Better i18n gibi araçlar değer katar — platformun SDK'ları çeviri şemanızdan TypeScript türleri üretir, böylece eksik veya yanlış yazılan anahtarlar üretimde sessizce bozulmak yerine derleme zamanında başarısız olur.
Her Locale İçin SEO Meta Verileri
Her locale'in kendi <title>'ı, <meta name="description">'ı ve canonical URL'si olması gerekir. Bunu unutmak kolay, manuel olarak denetlemek zordur. Bunları doğru yapmak, herhangi bir uluslararası SEO kontrol listesinin temel maddelerinden biridir — özellikle arama motorlarına locale'lerinizin birbirleriyle nasıl ilişkili olduğunu anlatan canonical ve hreflang etiketleri.
Paylaşılan bir layout bileşeni yardımcı olur:
---
// src/layouts/Base.astro
import { getAbsoluteLocaleUrl } from 'astro:i18n';
interface Props {
locale: string;
title: string;
description: string;
}
const { locale, title, description } = Astro.props;
const canonical = getAbsoluteLocaleUrl(locale, Astro.url.pathname);
---
<html lang={locale}>
<head>
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonical} />
<link rel="alternate" hreflang="x-default" href={getAbsoluteLocaleUrl('en', Astro.url.pathname)} />
<link rel="alternate" hreflang="fr" href={getAbsoluteLocaleUrl('fr', Astro.url.pathname)} />
<link rel="alternate" hreflang="de" href={getAbsoluteLocaleUrl('de', Astro.url.pathname)} />
</head>
<body>
<slot />
</body>
</html>
Hreflang etiketleri, Google'ın locale yapınızı anlaması için özellikle önemlidir. Eksik veya yanlış hreflang, çok dilli sitelerde en yaygın SEO hatalarından biridir. Siteniz birden fazla bölgedeki İspanyolca konuşan kitleleri hedefliyorsa, İspanyolca pazarlar için hreflang kılavuzu tam bölgesel varyant kodlarını ve karşılıklı bağlantı gereksinimlerini ele alır.
Sitemap Oluşturma
Astro'nun @astrojs/sitemap entegrasyonu i18n'i destekler, ancak bunu açıkça yapılandırmanız gerekir:
// astro.config.mjs
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://yoursite.com',
integrations: [
sitemap({
i18n: {
defaultLocale: 'en',
locales: {
en: 'en-US',
fr: 'fr-FR',
de: 'de-DE',
},
},
}),
],
});
Bu, her URL için <xhtml:link> hreflang alternatifleriyle bir sitemap üretir — arama motorlarının yerelleştirilmiş içeriğinizi doğru şekilde dizine eklemesi için tam olarak ihtiyaç duyduğu şey.
Content Collection'larla Yerelleştirme
Blog yazıları veya dokümantasyon için Astro'nun Content Collection'larını kullanıyorsanız, yerelleştirme başka bir boyut ekler. Yaygın bir örüntü, collection dizininde içeriği locale'e göre ayırmaktır:
src/
content/
blog/
en/
getting-started.md
advanced-config.md
fr/
getting-started.md
de/
getting-started.md
Sonra sorgulamada locale'e göre filtreleyin:
---
import { getCollection } from 'astro:content';
const locale = 'fr';
const posts = await getCollection('blog', ({ id }) => {
return id.startsWith(`${locale}/`);
});
---
Çevrilmemiş yazılar için İngilizce sürüme düşüş yapabilirsiniz:
---
import { getCollection } from 'astro:content';
const locale = 'fr';
const allPosts = await getCollection('blog');
const localizedPosts = allPosts
.filter(post => post.id.startsWith(`${locale}/`))
.map(post => post.slug.replace(`${locale}/`, ''));
const englishPosts = allPosts
.filter(post => post.id.startsWith('en/'))
.filter(post => !localizedPosts.includes(post.slug.replace('en/', '')));
const posts = [
...allPosts.filter(post => post.id.startsWith(`${locale}/`)),
...englishPosts,
];
---
Bu, Fransız kullanıcılara tüm çevrilmiş Fransızca yazıları artı yalnızca İngilizce olan yazıları birlikte sıralanmış olarak sunar. Mükemmel bir kullanıcı deneyimi değil — kullanıcılar karışık dil içeriği görür — ancak İngilizce'de elli yazı varken beş yazılık bir sayfa sunmaktan iyidir.
SSR'da Dinamik Locale Tespiti
Astro'yu SSR modunda çalıştırıyorsanız, yalnızca URL yapısına güvenmek yerine Accept-Language başlığından kullanıcının tercih ettiği locale'i tespit edip buna göre yönlendirmek isteyebilirsiniz.
Astro, istek başlıklarını Astro.request aracılığıyla sunar:
---
// src/pages/index.astro
// Yalnızca prefixDefaultLocale true olduğunda ve otomatik tespit istediğinizde kullanılır
const acceptLanguage = Astro.request.headers.get('accept-language') ?? '';
function parsePreferredLocale(header: string, supported: string[]): string {
const requested = header
.split(',')
.map(part => {
const [locale, q = '1'] = part.trim().split(';q=');
return { locale: locale.trim().split('-')[0], q: parseFloat(q) };
})
.sort((a, b) => b.q - a.q)
.map(item => item.locale);
return requested.find(locale => supported.includes(locale)) ?? 'en';
}
const supported = ['en', 'fr', 'de', 'ja'];
const preferred = parsePreferredLocale(acceptLanguage, supported);
if (preferred !== 'en') {
return Astro.redirect(`/${preferred}/`);
}
---
Burada birkaç uyarı var. Birincisi, bu tespit yalnızca sunucu taraflı çalışır — SSG'de sayfa bir kez oluşturulur ve herkese sunulur, bu yüzden kişiye göre uyarlayamazsınız. İkincisi, sunucu taraflı locale tespiti yaparken her zaman Vary: Accept-Language başlığını ayarlayın; böylece CDN'ler her dil tercihi için doğru sürümü önbelleğe alır. Üçüncüsü, başlık tespitinden ziyade her zaman açık URL tercihlerine saygı gösterin — bir kullanıcı doğrudan /fr/ adresine giderse, tarayıcı ayarlarına göre yönlendirmeyin.
Üretim sitelerinin büyük çoğunluğu için kullanıcıların locale değiştirici aracılığıyla dillerini açıkça seçmesine izin vermek daha güvenilirdir ve kullanıcı niyetine saygı gösterir.
Ölçeklenebilir Bir İş Akışı Seçme
Küçük siteler için (20 sayfanın altında, 2-3 locale), yerleşik Astro i18n routing ve tiplendirilmiş bir JSON yardımcı fonksiyonu yeterlidir. Bir kütüphanenin çözdüğü spesifik acıyı hissedene kadar kütüphane eklemeyin.
Daha büyük siteler veya özel çevirmenlere sahip ekipler için bir TMS'e ihtiyaç duyarsınız. Temel sorular şunlardır:
- Çevirmenler dizelere nasıl erişir? Git tabanlı iş akışları teknik ekipler için iyi çalışır. Arayüz tabanlı bir platform teknik olmayan çevirmenler için daha iyidir.
- Çeviriler build'e nasıl girer? Build zamanında çekme (daha basit, olası bayatlık) veya çalışma zamanı CDN getirme (her zaman taze, SSR veya istemci taraflı yükleme gerektirir).
- Çeviri kalitesini nasıl yönetirsiniz? Yapay zeka çevirisi hızlıdır, ancak sözlük uygulaması ve gözden geçirme iş akışları marka tutarlılığı için önemlidir.
Better i18n, çevirmenleri Git kullanmaya zorlamadan geliştirici düzeyinde araçlar (tür güvenli SDK'lar, Git entegrasyonu, CI/CD kancaları) istediğiniz senaryolar için tasarlanmıştır. Çeviriler yönetilen bir platformda yaşar, SDK build zamanında şemanızdan türler üretir ve CDN en son onaylanmış dizeleri sunar. Bu yaklaşımın alternatiflere göre temel boyutlarda nasıl karşılaştırıldığını görmek için özellikler sayfasını inceleyebilirsiniz.
Pratik Özet
İşte öğrenmeniz gerekenler:
Astro'nun yerleşik i18n routing'ini kullanın URL yapısı ve locale tespiti için. Sağlam, iyi entegre ve eskiden özel middleware gerektiren durumları ele alıyor.
Tiplendirilmiş bir çeviri yardımcısı yazın küçük-orta ölçekli siteler için. 20 satırdan az TypeScript ve anlamlı derleme zamanı garantileri sağlar.
Bir topluluk kütüphanesi ekleyin (astro-i18next, Paraglide, intlayer) çoğullama, karmaşık namespace yönetimi veya büyük anahtar kümesinde derleme zamanı tür üretimine ihtiyaç duyduğunuzda.
Bir TMS bağlayın çeviri iş akışınız teknik olmayan katkıcıları, birden fazla gözden geçirme aşamasını veya birkaçtan fazla locale'i içerdiğinde. SSG için build zamanında çevirileri çekin, SSR için çalışma zamanı CDN dağıtımını kullanın.
SEO temellerini atlamayın: hreflang etiketleri, canonical URL'ler, locale'e özel meta veriler. Bunları gözden kaçırmak kolay ve sonradan denetlemek zordur. Yerelleştirilmiş sayfalarınızın doğru kitleye dizine eklenip sunulup sunulmadığını izlemek için analitiğinizin yanında Search Console'u kullanın.
Astro i18n hikayesi eskiye kıyasla çok daha tutarlı. Yerleşik primitifler yaygın durumları karşılıyor ve ekosistem boşlukları dolduruyor. Önemli olan, sitenizin gerçek karmaşıklığına uyan bir yaklaşım seçmek — ve gerçek bir sorununuz olmadan mühendisliği aşırıya kaçırma cazibesine direnmek.
Better i18n, modern frontend ekipleri için tasarlanmış, geliştirici öncelikli bir yerelleştirme platformudur. Repo'nuzda locale dosyaları olmadan tür güvenli SDK'lar, Git tabanlı iş akışları, CDN dağıtımı ve sözlük uygulamasıyla yapay zeka çevirisi.