İçindekiler
Veritabanı Yerelleştirme: Çok Dilli Veri Depolama Stratejileri
Çok dilli bir uygulama geliştirirken, ilk mimari kararlardan biri çevrilmiş içeriğin veritabanında nasıl saklanacağıdır. Seçtiğiniz yaklaşım, sorgu performansını, şema karmaşıklığını ve yeni dil eklemenin ne kadar kolay olduğunu etkiler.
Tek bir "doğru" tasarım yoktur — en iyi seçim; dil sayısına, çevrilebilir içerik hacmine, sorgu kalıplarına ve ekip tercihlerine bağlıdır. Bu rehber, pratik örneklerle birlikte en yaygın üç stratejiyi ele almaktadır.
Sorun
Adlar, açıklamalar ve kategoriler içeren bir ürün kataloğunu ele alalım. Tek dilli bir uygulamada şema oldukça basittir:
CREATE TABLE products ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT, price DECIMAL(10,2), created_at TIMESTAMP DEFAULT NOW() );
Birden fazla dile ihtiyaç duyulduğunda, her metin sütunu bir yerelleştirme sorununa dönüşür. price ve created_at sütunları dilden bağımsızdır; ancak name ve description her yerel ayar için ayrı değerler gerektirir.
Strateji 1: Yerel Ayar Başına Sütun
En basit yaklaşım, her dil için bir sütun eklemektir:
CREATE TABLE products ( id SERIAL PRIMARY KEY, name_en VARCHAR(255) NOT NULL, name_de VARCHAR(255), name_fr VARCHAR(255), name_ja VARCHAR(255), description_en TEXT, description_de TEXT, description_fr TEXT, description_ja TEXT, price DECIMAL(10,2), created_at TIMESTAMP DEFAULT NOW() );
Sorgulama
-- Ürünü Almanca çevirilerle getir, İngilizce'ye geri dön SELECT id, COALESCE(name_de, name_en) AS name, COALESCE(description_de, description_en) AS description, price FROM products WHERE id = 42;
Avantajlar
- Basit sorgular — JOIN gerekmez. Tüm veriler tek bir satırda yer alır.
- Anlaşılması kolay — Şema kendini belgeler niteliktedir.
- İyi okuma performansı — JOIN olmaksızın tek tablo taraması.
Dezavantajlar
- Yeni diller için şema değişikliği — Dil eklemek, her çevrilebilir sütun için
ALTER TABLEgerektirir. 5 çevrilebilir alan ve 10 dil içeren bir tablo için bu 50 sütun anlamına gelir. - Seyrek veri — Çevirisi olmayan diller için satırların çoğu NULL değer içerir.
- Ölçeklemesi zor — Sorgu karmaşıklığı dil sayısıyla doğrusal olarak artar.
En uygun olduğu durumlar
Nadiren değişen küçük ve sabit bir dil setine (2-4 dil) sahip uygulamalar. Az sayıda çevrilebilir alan içeren basit içerik modelleri.
Strateji 2: Ayrı Çeviri Tablosu
Bu yaklaşım, çevirileri ayrı bir tabloya normalize eder:
CREATE TABLE products ( id SERIAL PRIMARY KEY, price DECIMAL(10,2), created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE product_translations ( id SERIAL PRIMARY KEY, product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE, locale VARCHAR(10) NOT NULL, name VARCHAR(255) NOT NULL, description TEXT, UNIQUE (product_id, locale) ); CREATE INDEX idx_product_translations_lookup ON product_translations (product_id, locale);
Sorgulama
-- Ürünü Fransızca çevirilerle getir, İngilizce'ye geri dön SELECT p.id, COALESCE(t.name, fallback.name) AS name, COALESCE(t.description, fallback.description) AS description, p.price FROM products p LEFT JOIN product_translations t ON t.product_id = p.id AND t.locale = 'fr' LEFT JOIN product_translations fallback ON fallback.product_id = p.id AND fallback.locale = 'en' WHERE p.id = 42;
Avantajlar
- Yeni diller için şema değişikliği gerekmez — Dil eklemek yalnızca yeni satır eklemekten ibarettir.
- Temiz ayrım — Dilden bağımsız veriler ana tabloda kalır.
- Esnek — Herhangi bir varlık için hangi dillerin mevcut olduğunu sorgulamak kolaydır.
- Standart kalıp — ORM'ler ve framework'ler tarafından iyi anlaşılmaktadır. Rails, Django ve Laravel'in hepsi bu kalıbı destekleyen kütüphanelere sahiptir.
Dezavantajlar
- JOIN gereklidir — Çevrilmiş veriler için her sorgu en az bir JOIN gerektirir.
- Geri dönüş karmaşıklığı — Dil geri dönüş zincirleri (örn.
pt-BR→pt→en) birden fazla JOIN veya uygulama mantığı gerektirir. - Daha fazla tablo — Her çevrilebilir varlık için ayrı bir çeviri tablosu gerekir.
En uygun olduğu durumlar
Çok sayıda dile sahip uygulamalar, zamanla büyüyen içerik modelleri ve temiz veritabanı normalizasyonuna değer veren ekipler.
Strateji 3: JSON Sütunu
Modern veritabanları (PostgreSQL 9.4+, MySQL 5.7+, SQLite 3.38+) JSON sütunlarını destekler:
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name JSONB NOT NULL DEFAULT '{}',
description JSONB DEFAULT '{}',
price DECIMAL(10,2),
created_at TIMESTAMP DEFAULT NOW()
);
-- Çevirilerle birlikte ekle
INSERT INTO products (name, description, price)
VALUES (
'{"en": "Wireless Headphones", "de": "Kabellose Kopfhörer", "fr": "Écouteurs sans fil"}',
'{"en": "Premium wireless headphones with noise cancellation.", "de": "Premium kabellose Kopfhörer mit Geräuschunterdrückung."}',
79.99
);
Sorgulama (PostgreSQL)
-- Ürünü Fransızca çevirilerle getir, İngilizce'ye geri dön SELECT id, COALESCE(name->>'fr', name->>'en') AS name, COALESCE(description->>'fr', description->>'en') AS description, price FROM products WHERE id = 42;
Avantajlar
- Şema değişikliği gerekmez — Yeni diller yalnızca yeni JSON anahtarlarıdır.
- JOIN yok — Tüm çeviriler aynı satırda yer alır.
- Esnek — Farklı satırlarda farklı dil setleri bulunabilir.
- GIN indeksleme — PostgreSQL JSONB sütunları, verimli sorgulama için GIN indekslerini destekler.
Dezavantajlar
- Daha zayıf tip güvenliği — Veritabanı, her JSON nesnesinin gerekli anahtarlara veya değer türlerine sahip olduğunu zorunlu kılamaz.
- Referans bütünlüğü yok — Çeviri tablosu yaklaşımının aksine, yerel ayar kodlarını doğrulayan bir foreign key bulunmaz.
- ORM desteği değişkendir — Bazı ORM'ler JSON sütunlarını yerel olarak işlemez; özel serileştirme gerektirir.
- Tam metin arama karmaşıklığı — Bir JSON sütunundaki tüm dillerde arama yapmak özel indeksleme gerektirir.
En uygun olduğu durumlar
PostgreSQL veya MySQL kullanan ve orta düzeyde çeviri hacmine sahip uygulamalar, minimal şema tercih eden ekipler ve diller arasında çeviri tamlığının önemli ölçüde farklılık gösterdiği projeler.
Karşılaştırma Özeti
| Ölçüt | Yerel Ayar Başına Sütun | Çeviri Tablosu | JSON Sütunu |
|---|---|---|---|
| Yeni dil maliyeti | Şema migrasyonu | Satır ekle | Değişiklik yok |
| Sorgu karmaşıklığı | Basit | JOIN gerekli | Orta |
| Okuma performansı | Mükemmel | İyi (indeksle) | İyi |
| Tip güvenliği | Tam | Tam | Kısmi |
| ORM desteği | Yerleşik | Geniş destek | Değişken |
| En uygun | Az ve sabit dil | Çok dil | Esnek modeller |
Yerel Ayar Geri Dönüşlerini Yönetmek
Hangi stratejiyi seçerseniz seçin, uygulamanızın eksik çeviriler için bir geri dönüş zincirine ihtiyacı vardır. Yaygın bir kalıp:
- Tam eşleşme (Kanada Fransızcası için
fr-CA) - Dil eşleşmesi (genel Fransızca için
fr) - Varsayılan dil (İngilizce için
en)
Bu geri dönüş mantığı, kullanıcı tercihleri ve yerel ayar müzakeresi içerebileceğinden genellikle SQL yerine uygulama katmanında uygulanır.
Sıkça Sorulan Sorular
Çevrilebilir ve çevrilemeyen sütunlar aynı tabloda mı olmalı?
Bu, sorgu kalıplarınıza bağlıdır. Çevrilmiş içeriği sık sık sorguluyorsanız, onu varlığa yakın tutmak JOIN sayısını azaltır. Yalnızca çevrilemeyen verileri (fiyatlandırma, envanter) sık sorguluyorsanız, çevirileri ayırmak gereksiz veri yüklenmesini engeller.
Diller genelinde tam metin aramayı nasıl ele alırım?
PostgreSQL, dile özgü tsvector yapılandırmalarını destekler. Her dil için ayrı metin arama indeksleri oluşturun ya da dilden bağımsız arama için simple yapılandırmasını kullanın. Çeviri tablosu yaklaşımıyla, her satır zaten bir dili temsil ettiğinden satır başına bir tsvector sütunu oluşturabilirsiniz.
NoSQL veritabanları ne olacak?
MongoDB gibi döküman veritabanları doğal olarak JSON yaklaşımını destekler — her döküman çevirileri iç içe nesneler olarak gömer. Bu, içerik ağırlıklı uygulamalar için iyi çalışır; ancak ilişkisel veritabanlarının referans bütünlüğü güvencelerinden yoksundur.
Bu, UI string yerelleştirmeyle nasıl ilişkilidir?
Veritabanı yerelleştirme, kullanıcı tarafından oluşturulan veya yönetici tarafından yönetilen içerikleri (ürün adları, sayfa başlıkları, CMS içeriği) ele alır. UI string yerelleştirme — buton etiketleri, hata mesajları, navigasyon metni — genellikle JSON veya diğer dosya tabanlı formatları kullanan i18n kütüphaneleriyle (react-intl, vue-i18n vb.) yönetilir. Çoğu uygulamanın her ikisine de ihtiyacı vardır.