Modern web’de lokalizasyon, kelimeleri başka bir dile çevirmekten çok daha fazlasıdır. Bu, yüksek kaliteli bir kullanıcı deneyiminin (UX) temeli ve arama motoru optimizasyonu (SEO) için güçlü bir araçtır. Bir ziyaretçi oleant.dev adresine geldiğinde, kendi ana dilinde içerikle karşılaştığında sadece pratiklik değil; aynı zamanda kaynağın güvenilir olduğu ve yazarın izleyicilerinin konforunu önemsediği sinyali de alır. Lokalizasyon, sitenizi yerel bir projeden küresel bir kaynağa dönüştürür ve farklı ülkelerden yeni okuyucular ve müşterilere kapılar açar.
Ancak, diller arasında geçiş yapmanın görünürdeki basitliği ardında ciddi bir mimari zorluk barındırır. Arayüzdeki statik ifadeleri nasıl etkili bir şekilde yönetirsiniz? Dinamik blog içeriğini hangi yerde saklamalısınız ki farklı dillerde erişilebilirken düzenlenmesi kolay olsun? Arama motorlarının düzgün bir şekilde transkribe edilmiş URL’leri nasıl indekslemesini sağlarsınız?
Bu yazıda, Laravel projesinde tam ölçekli çok dilli desteği nasıl uygulayacağımızı güvenilir bir teknoloji yığını kullanarak analiz edeceğiz. Mevcut teknoloji setimize dayalı olacağız:
-
mcamara/laravel-localization— profesyonel yönlendirme, URL’lerde yerel ön eklerin yönetimi ve SEO dostu yönlendirmeler için kullanılır. -
spatie/laravel-translatable— yerelleştirilmiş içeriğin (makale başlıkları, kategoriler) esnek bir şekilde veritabanında JSON alanları kullanarak saklanması için kullanılır. -
Filament PHP— yönetim panelimiz için güçlü bir framework, burada sekmeler kullanarak uygun çeviri düzenleme işlemlerini gerçekleştiriyoruz.
Altyapı ve Yönlendirme
Altyapı ve Yönlendirme
Olağanüstü sıklıkla yerelleştirme için gerekli olan ayarları kullanarak oleant.dev üzerinde temiz URL’ler ve SEO uyumluluğu sağlıyoruz.
Desteklenen Dilleri Yapılandırma
Desteklenen Dilleri Yapılandırma
config/laravellocalization.php dosyasında aktiv dilleri tanımlıyoruz. SEO için önemli bir nokta, hideDefaultLocaleInURL ayarını true yapmak. Bu, içerik tekrarını önlemeye yardımcı olur: de ana yerel ise, sayfa /ueber-mich yerine /de/ueber-mich olarak erişilebilir.
// config/laravellocalization.php
'supportedLocales' => [
'de' => ['name' => 'German', 'script' => 'Latn', 'native' => 'Deutsch', 'regional' => 'de_DE'],
'en' => ['name' => , 'script' => , => , => ],
'ru' => ['name' => , => , => , => ],
'uk' => [=> , => , => , => ],
],
=> ,
=> true,
...
Yönlendirme ve Middleware
Yönlendirme ve Middleware
Asıl iş burada routes/web.php dosyasında gerçekleşiyor. Tüm yönlendirmeleri bir Route::group içerisinde sarıyoruz ve LaravelLocalization::setLocale() ile bir ön ek kullanıyoruz. Bu, paketin URL’den dili dinamik olarak belirlemesine olanak tanır.
Middleware sırasına dikkat etmelisiniz. SEO yönlendirmeleri için localizationRedirect kullanıyoruz (bu, yerel olmayan URL’leri yerel olan versiyona otomatik olarak düzeltir) ve çeviri dosyalarının doğru seçimi için localeViewPath kullanıyoruz.
// routes/web.php
Route::group([
=> LaravelLocalization::(),
=> 'localizationRedirect''localeViewPath''cacheResponse'
], () RoutegetLaravelLocalizationtransRouteBlogControllerclass, ->(
// ... kalan yollar
Otomatik Dil Tespiti
Dilin otomatik tespiti için useAcceptLanguageHeader true olarak ayarlanabilir. Ancak, birçok projede oturum veya açık kullanıcı seçimi ile kontrol sağlayabilmek tercih edilmektedir. Mevcut yapılandırmamızda useAcceptLanguageHeader => false ayarladık, böylece arama motoru botları için beklenmedik yönlendirmeleri önlemiş olduk ve bunun yanı sıra localizationRedirect yol yapılandırmasını doğru bir şekilde yönettiği için daha öngörülebilir bir durum sağlıyoruz.
Arayüz Lokalizasyonu (Statik)
Statik metinleri—menü öğeleri, düğmeler, sayfa başlıkları—yönetmek için klasik Laravel yaklaşımını kullanıyoruz: resources/lang dosya sistemi. Bu çözüm, performans ile geliştirme kolaylığı arasında mükemmel bir denge sağlar.
Dosya Organizasyonu
İçeriği amaçlarına göre ayırıyoruz. Örneğin, routes.php dosyası URL yapısından sorumlu iken, blog.php meta etiketleri ve blog metinlerini işler. Bu, gerekli anahtarın bulunmasını basitleştirir:
// resources/lang/en/blog.php
[
,
=> ,
// ...
Blade’de Uygulama
Arayüz geliştirirken, global yardımcı işlev __() kullanıyoruz. Eğer bir anahtar bulunamazsa, Laravel varsayılan olarak anahtarı kendisi döner, bu da geliştirme aşamasında kayıp çevirileri hemen fark etmeyi sağlar.
:title=
:description= class=
Neden Dosya Tabanlı Lokalizasyon “Altın Standart”?
resources/lang dosyalarını statik çevirileri saklamak için kullanmak, binlerce Laravel projesi tarafından doğrulanmış bir yaklaşımdır. Popülaritesi tesadüf değildir: performans ve bakım kolaylığı arasında neredeyse ideal bir denge sunar.
Metinleri PHP dizileri olarak sakladığınızda, maksimum performansa ulaşmış olursunuz. Laravel bu dosyaları verimli bir şekilde önbelleğe alır, bu nedenle çeviri dizesine erişmek neredeyse anında gerçekleşir; veritabanını gereksiz sorgularla yüklemez. Ayrıca, bu yöntem mükemmel sürüm kontrolü sağlar: tüm proje metinleri, Git ile kodla birlikte “seyahat eder.” Değişen her metni görmek için her zaman kimin neyi değiştirdiğini takip edebilir ve gerekirse tek bir komutla bir ay önceki çeviri sürümüne geri dönebilirsiniz.
Biz geliştiriciler için büyük bir artı, IDE ile çalışma kolaylığıdır. Modern editörлер, bu yöntemi “çok seviyor”: kullandıkları anahtarları otomatik olarak vurguluyor, çeviri dosyasına Ctrl+Tıklama ile anında gezinmenize izin veriyor ve eğer bir anahtar bir yerde çevrilmemişse uyarılar gösteriyor.
Yine de, “altın standart” olarak adlandırmanın bir ters tarafı vardır ve bu, projeye başlamadan önce bilinmesi gereken bir durumdur:
Öncelikle, dosya tabanlı yaklaşım, en küçük bir yazım hatasının bile tamamen bir döngüden (kod düzenleme, commit, push ve deploy) geçmesini gerektirir. Eğer siteniz haftalık olarak değişen birçok dinamik metne sahipse, bu süreç hızla yorucu hale gelebilir.
İkincisi, teknik uzmanlık gerektirir. Eğer siteniz bir içerik yöneticisi tarafından yönetiliyorsa, tamamen size bağımlıdır. Menü başlığını değiştirmek veya ana sayfadaki bir açıklamayı düzeltmek için her seferinde geliştiriciye bilet yazmak zorundadırlar. Eğer ekibinizde Git ve proje yapısını bilmeyen içerikten sorumlu kişiler varsa, dosya tabanlı çeviri sistemi onlarda bir “cam tavan” yaratacaktır, bu da ancak eğitim almakla veya daha esnek çözümlere geçmekle aşılabilir.
Ne Zaman Alternatiflere Bakmalıyız?
Eğer proje, üzerinde “anlık” düzenleme gerektiren binlerce metin satırına dönüşürse, veritabanları veya Lokalise veya Crowdin gibi özel hizmetleri düşünebilirsiniz. Bu, çevirileri kod dışına taşımanıza izin verir, ancak önbellek yapılandırması ve CI/CD süreçlerinde ek karmaşıklık ortaya çıkarır.
Dinamik İçerik ve Spatie Translatable
Bir proje birkaç statik sayfayı aştığında, dosya tabanlı lokalizasyon, veritabanıyla çalışma aşamasına geçer. Çok dilli makaleleri etkin bir şekilde yönetmek için Eloquent + Spatie Translatable kombinasyonunu kullanıyoruz.
Mimari: Veritabanı Ana Sistem Olarak
Tıpkı posts_translations gibi karmaşık tablolar oluşturmak yerine, tüm çevirileri ana posts tablosunda JSON türü alanlara kaydediyoruz. Bu, veri sorgulamasını olabildiğince hızlı hale getirir (gereksiz JOIN’ler olmadan) ve Laravel için doğaldır. Post modelinde, HasTranslations trait’ini dahil ediyoruz ve gerekli alanları işaretliyoruz:
use Spatie\Translatable\HasTranslations;
class Post extends Model
{
use HasTranslations;
public array $translatable = 'title''content''seo_title''slug_i18n'];
SEO-Dostu URL: Transliterasyonu Otomatikleştirme
Başlıca görevlerden biri, her yerel için temiz URL’ler oluşturmaktır (örneğin, /blog/kak-vyuchit-laravel Rusça için ve /blog/learning-laravel İngilizce için). Bunu, slug_i18n JSON alanı ve Filament’teki reaktif formlar aracılığıyla çözüyoruz. Yönetim panelinde başlığı doldurduğumuzda, slug’ı “anlık” olarak oluşturuyoruz:
// Filament Kaynağında
Forms\Components\TextInput::('title.' $localeName->('onBlur': true)
->('title' => function (, $set) use ($localeName) {
// Başlık girildiğinde otomatik slug oluşturma
('slug_i18n.' $localeName}),
Temiz HTML’nin Büyüsü
Ağır “standart” çözümleri terk ettiğimizde, çevirileri birçok yardımcı tabloda dağıtmak ya da hacimli paketler uygulamak gibi, yalnızca veritabanı yapısını basitleştirmekle kalmıyoruz; aynı zamanda içerik ile çalışma felsefesini de değiştiriyoruz.
Sistemimizin şeffaflığı, modelin öngörülebilir olmasından kaynaklanıyor. Standart bir Laravel uygulamasında, $post nesnesi üzerinde $post->title yöntemini çağırarak çalışıyorsunuz ve bu tanıdık sözdizimi yaklaşımınızda korunuyor. Laravel otomatik olarak geçerli yereli app()->getLocale() ile “alır”, böylece her kontrolcüde veya şablonda çeviri seçim mantığı yazma ihtiyacını ortadan kaldırırsınız. Birden fazla dili tek bir veritabanı alanında saklamayla ilişkili tüm karmaşık iş mantığı Spatie tarafından gizlenmiştir ve kodunuz gereksiz aramalardan arınmış bir şekilde kalır. Bu sadece karakter tasarrufu değil, teknik ayrıntılarla aşırı yüklenmiş iş mantığında hata olasılığını azaltır.
Çözümün esnekliği, Filament ile entegrasyonundan doğrudan kaynaklanmaktadır. Editörü farklı ekranlara atlamaya veya karmaşık formlarda kaybolmaya zorlamak yerine, sistem tüm dil versiyonlarını tek bir kayıtta toplar ve bunları kullanışlı sekmelere dağıtır. Bu sade görsel yaklaşım, yönetimi sezgisel bir hale getirir: farklı dillerdeki içeriği bir bütün olarak görebilir, sürüm karşılaştırması yapabilir, yapıyı koruyabilir ve tek bir başlık ya da metin parçasının unutulmadığından emin olabilirsiniz. Yönetim paneli sadece bir veri girişi formu olmaktan çıkıp, sitede yer alacak her metnin kalite standartlarını oluşturan profesyonel bir araca dönüşür.
Yönetim Paneli: Yönetim Kolaylığı
Artık mimari konkret bir biçimde gerçeği yansıtmaya başladığında, işte tam burada sizin yönetim panelinizin “kara kutu” olup olmayacağına karar verilir ya da etkili bir araç olup olmadığı belirlenir. Filament tabanlı yönetim paneli uygulamanızın yüzü ve ergonomisi projeyi rahatça geliştirip geliştiremeyeceğinizi belirler. Çok dilli destek konusunda ana görev, yönetim panelinde yereli değiştirmeyi teknik bir işlem değil, çalışma akışının doğal bir devamı haline getirmektir. Varsayılan yereli DE (Almanca) olarak ayarlamak, arayüzün önceliklerinizle hemen hizalanmasını sağlamak için ilk adımdır; gereksiz geçişlerin sayısını azaltır.
Formların içinde sekmelerin uygulanması, kullanıcı deneyimini köklü bir şekilde değiştiren ana bir mimari karardır. Uzun “diziler” oluşturarak çevirileri bir arada karıştırmak yerine, anahtarı belirli bir dil için alanları mantıksal bloklarda gruplandırırız. Bir editör kategori kartını açtığında, her bir dili yönetmek için ilgili sekmeleri yapısal olarak görür. Bu, görsel dağınıklığı ortadan kaldırır ve belirli bir yerellere odaklanmaya olanak tanırken aynı zamanda tüm varlığın bağlamını korur.
{
$form->schema([
Forms\Components\Hiddenmake('slug'), // Sistem slug
Tabsmake->('class' => 'full',)
{
= foreach$tabs[] = Tabmake(strtoupper($locale))
->([
Forms\Components\TextInputmake('name. $locale)
->->->->$set('slug_i18n.' . $locale)'=>Str::slug($state);}
if $set('slug', Str::slug($state));
}),
Forms\Components\TextInputmake('slug_i18n.' . $locale)
->->->Forms\Components\TextInputmake('title.' . $locale)
->Forms\Components\Textareamake('description.' . $locale)
->]);
->]);
Dil Değiştirici ve Kesintisiz Navigasyon
Çok dilli bir altyapı oluşturmanın son aşaması, kullanıcı dostu bir arayüze dönüştürmek için frontend bileşenidir. Dil değiştirici sadece bir açılır liste değil; kullanıcı, mevcut URL ve veritabanı arasındaki bağlantıdır. oleant.dev projesinde, bu bileşenin uygulanması mcamara/laravel-localization paketine dayanmakta, bu da URL’lerde yerel ön eklerin doğru yönetilmesini sağlamakta; böylece site yapısı ziyaretçiler ve arama motorları için öngörülebilir hale gelir.
Dil açılır liste bileşeninin özü, dinamik bağlantı oluşturma mantığıdır. LaravelLocalization::getLocalizedURL kullanımı, kullanıcının her zaman uygun sayfaya erişeceğinden emin olur, ister ana sayfa olsun ister yüksek özeliki bir makale. Burada “alternatif sluglar” ile ilgili özel bir dikkate alıyoruz:
@foreach @php
$routeName = Route$url = null;
// Eğer bu lokalize slugları olan bir sayfaysa
isset($alternateSlugs) && is_array($alternateSlugs) && isset($alternateSlugs[$localeCode])) {
$url = LaravelLocalization::getLocalizedURL($localeCode, route($routeName, ['slug' => $alternateSlugs[$localeCode]], false));
else $url = LaravelLocalization::getLocalizedURL($localeCode, null, [], false);
@endphp
@endforeach
Bu uygulama, çok dilli sitelerin yaygın bir sorununu çözüyor — belirli bir içeriği çevirileri ile bağlantılandırmayı. Kullanıcıyı, diller arasında geçiş yaptığında anasayfaya yönlendirmek yerine, bileşen “zekice” gerekli slug’ı (alternateSlugs) seçilen yerel için ekler. Bu, kesintisizlik hissini yaratır: bir makaleyi okurken, kullanıcı İngilizce veya Almanca versiyonuna bir tıklama ile geçiş yapabilir ve aynı içeriğin bağlamında kalır.
Bir çeviri mevcut değilse, sistem bu durumu route ve kontrolcü mimarisi düzeyinde yöneterek ele alır. Seçilen dil için alternateSlugs dizisinde veri yoksa, sistem kullanıcıyı “temiz” yerel URL’ye yönlendirir (varsayılan versiyona geri dönüş) veya uygun kontrolcü yapılandırması ile 404 döndürür ya da makalenin diğer dillerde mevcut olduğunu belirten bir mesaj gönderir. Bu, SEO için kritik bir durumdur: arama botları, hreflang meta etiketleri sayesinde bağlantı yapısını anlayarak, bu değiştirici ile birlikte sitenin dil versiyonları hakkında tam bir harita oluştururlar.
Sonuç olarak, dropdown listesinin durumunu yönetmek için Alpine.js kullanmak (x-data="{ open: false }"), arayüzü hafif ve hızlı tutar; frontend’in gereksiz isteklerle aşırı yüklenmesini engeller. Bu bileşen, tüm sistemin montaj noktasıdır: veritabanında biriken çevirileri alır, slug transliterasyon mantığını hesaba katar ve kullanıcıya basit ve güvenilir bir navigasyon sunar; tüm teknik karmaşıklığı gizleyerek.
Büyüme İçin Mimari
Bir çok dilli proje oluşturmak her zaman editör için esneklik ve son kullanıcı için performans arasında bir denge sağlamayı gerektirir. JSON alanları ile veritabanı yapısının tasarımından, Filament’te kullanışlı bir yönetim paneli oluşturmaya ve frontend’de zeki bir dil değişim sistemine kadar bir yolculuk yaptık. Ortaya çıkan mimari “hafif” oldu; gereksiz JOIN sorgularının ve yerel Laravel mekanizmalarının yokluğu, uygulamanın sayıca fazla dil versiyonlarıyla hızlı çalışmasını sağladı.
Performans ve Önbellekleme
Bununla birlikte, lokalizasyonu uygulanırken önbelleklemenin “tuzağını” göz önünde bulundurmak önemlidir. spatie/laravel-responsecache gibi paketler kullanıyorsanız, son derece dikkatli olmalısınız: varsayılan olarak önbellek, Accept-Language başlığını veya mevcut yerel ön ekini dikkate almayabilir. Bu, bir Almanya’daki kullanıcının sayfanın Rusça önbelleğe alınmış bir versiyonunu görmesi riski taşır.
Bu görevi çözmek için:
- Önbelleği İzole Etmek: Önbellek anahtarının (
cache_key) mevcut yerel tanımlayıcısını (app()->getLocale()) içermesini sağlamak. - Varnish/Nginx’i Doğru Yapılandırın: Sunucu düzeyinde ağır önbellekleme kullanıyorsanız,
Vary: Accept-Language başlığını geçirmeyi unutmayın; böylece önbellekleme proxy sunucuları sayfa içeriğinin seçilen dile bağlı olduğunu anlar. - Önbelleği Temizleme: Yönetim paneli üzerinden bir makalenin bir dil versiyonunu güncellerken, sistem yalnızca etkilenen URL’ler için önbelleği akıllıca temizlemelidir; “tüm site önbelleğini silmek” yerine.
Sonraki Adım: Vue ve Frontend
Artık arka uç altyapısı sorunsuz bir şekilde çalıştığına göre, Blade’de statik lokalizasyonun sadece işin yarısı olduğu aşikar hale geliyor. Modern Laravel uygulamalarında, arayüzün önemli bir kısmı Vue bileşenlerine düşmektedir. Nasıl lokalleştirilmiş modelden Inertia.js üzerinden veri geçirilir? Gereksiz çeviri dizelerini yüklememek ve JavaScript frontendinin sunucu ile aynı lokalizasyon yapısını “anlamasını” nasıl sağlarız?
Bir sonraki yazıda, çok dilli frontend’in “kutsal kâse”sini analiz edeceğiz: Vue bileşenlerine lokalizasyon entegre etme. Sadece gerekli çevirileri Inertia üzerinden nasıl geçireceğimizi ve sunucu (içerik sağlayan) ile istemci (etkileşimi yöneten) arasındaki sorumlulukların ayrımının gerçekten hızlı ve ölçeklenebilir uygulamalar oluşturmanın tek yolu olduğunu göstereceğiz.
Bu mimari, sadece başlangıçtır. Artık “herhangi bir görevi” “bağlamak” için güvenilir bir çerçeveniz var; temelin herhangi bir yükü kaldırabileceğinden eminsiniz.
Kaynak: Orijinal Makale


