OpenAI ile Çok Dilli Uygulama Geliştirme
Çok dilli uygulama geliştirme, özellikle Türkiye gibi hem yerel hem de global pazara hitap eden ürünler üretenler için her zaman baş ağrısı olmuştur. Klasik yaklaşımlarda gettext, .po dosyaları, elle çeviri süreçleri ve sonsuz review döngüleri derken projenin yarısı lokalizasyona gidiyordu. OpenAI API’yi bu sürece dahil ettiğimde işler ciddi anlamda değişti. Hem çeviri kalitesi hem de dinamik içerik yönetimi açısından bambaşka bir dünyaya adım attım.
Bu yazıda OpenAI API kullanarak gerçekten production-ready çok dilli uygulama nasıl inşa edilir, hangi tuzaklara düşülür ve nasıl çıkılır bunları paylaşacağım. Saf çeviri entegrasyonundan tutun bağlam farkında lokalizasyona kadar geniş bir yelpazede konuşacağız.
Neden Klasik Çeviri Yaklaşımları Yetersiz Kalıyor?
Bir e-ticaret platformunda çalışırken şunu fark ettim: ürün açıklamalarını Türkçe, İngilizce, Almanca ve Arapça yönetmemiz gerekiyordu. Geleneksel yöntemde her içerik değiştiğinde dört ayrı çeviri süreci başlıyordu. Üstelik bağlam kayıpları had safhaya ulaşmıştı. “Sepete ekle” düğmesi Almancada “In den Warenkorb” olabiliyordu ama uygulamanın tonuna, hedef kitleye göre bu değişebiliyordu. Statik çeviri dosyaları bunu hiçbir zaman çözemedi.
OpenAI API’nin buradaki gücü sadece çeviri değil, bağlam farkındalığı. Modele “Bu bir B2B yazılım şirketinin resmi destek sayfası içeriği, formal ama anlaşılır bir Almanca kullan” diyebiliyorsunuz. Bu tek cümle, yıllardır çözemediğimiz bir problemi halletti.
Ortam Kurulumu ve Temel Yapı
Önce sağlam bir temel atalım. Python 3.10+ ortamında çalışıyorum, aynı mantığı Node.js ile de uygulayabilirsiniz.
# Virtual environment oluştur
python3 -m venv multilang_env
source multilang_env/bin/activate
# Gerekli kütüphaneleri kur
pip install openai python-dotenv langdetect fastapi uvicorn redis
# .env dosyası oluştur
cat > .env << 'EOF'
OPENAI_API_KEY=sk-your-key-here
OPENAI_MODEL=gpt-4o-mini
REDIS_URL=redis://localhost:6379
MAX_TOKENS=2000
TEMPERATURE=0.3
EOF
Dikkat edin, çeviri işlemleri için temperature değerini düşük tutuyorum (0.3). Yaratıcı içerik üretmiyoruz, tutarlılık istiyoruz. Bu ince ayar production’da çok kritik.
Temel Çeviri Servisi
Basit ama genişletilebilir bir çeviri servisi yazalım:
cat > translation_service.py << 'EOF'
import os
import json
import hashlib
import redis
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
cache = redis.from_url(os.getenv("REDIS_URL"))
SUPPORTED_LANGUAGES = {
"tr": "Türkçe",
"en": "İngilizce",
"de": "Almanca",
"ar": "Arapça",
"fr": "Fransızca",
"es": "İspanyolca"
}
def get_cache_key(text: str, target_lang: str, context: str) -> str:
content = f"{text}:{target_lang}:{context}"
return f"trans:{hashlib.md5(content.encode()).hexdigest()}"
def translate_with_context(
text: str,
target_lang: str,
source_lang: str = "auto",
context: str = "",
tone: str = "formal"
) -> dict:
cache_key = get_cache_key(text, target_lang, context)
cached = cache.get(cache_key)
if cached:
return json.loads(cached)
lang_name = SUPPORTED_LANGUAGES.get(target_lang, target_lang)
system_prompt = f"""Sen profesyonel bir lokalizasyon uzmanısın.
Metni {lang_name} diline çevir.
Ton: {tone}
Önemli kurallar:
- Kültürel uyumu koru
- Teknik terimleri hedef dildeki standart karşılıklarıyla kullan
- Orijinal metnin anlamını ve nüansını koru
- Sadece çeviriyi döndür, açıklama ekleme"""
user_prompt = text
if context:
user_prompt = f"Bağlam: {context}nnÇevirilecek metin: {text}"
if source_lang != "auto":
user_prompt = f"Kaynak dil: {SUPPORTED_LANGUAGES.get(source_lang, source_lang)}n{user_prompt}"
response = client.chat.completions.create(
model=os.getenv("OPENAI_MODEL"),
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
temperature=float(os.getenv("TEMPERATURE", 0.3)),
max_tokens=int(os.getenv("MAX_TOKENS", 2000))
)
result = {
"original": text,
"translated": response.choices[0].message.content.strip(),
"target_language": target_lang,
"tokens_used": response.usage.total_tokens
}
cache.setex(cache_key, 86400, json.dumps(result, ensure_ascii=False))
return result
EOF
echo "Çeviri servisi hazır"
Burada cache mekanizması kritik. Aynı içeriği her seferinde API’ye göndermek hem maliyetli hem yavaş. Redis ile 24 saatlik cache ile production’da token maliyetimi yüzde 60-70 düşürdüm.
Toplu Çeviri ve JSON Lokalizasyon Dosyası Yönetimi
Gerçek dünyada genellikle tek tek string değil, lokalizasyon dosyalarının tamamını işlemeniz gerekir. Şöyle bir senaryo düşünün: i18n klasörünüzdeki en.json dosyasını otomatik olarak diğer dillere çevirmek istiyorsunuz.
cat > bulk_localization.py << 'EOF'
import json
import time
import os
from translation_service import translate_with_context
def translate_json_file(
source_file: str,
target_lang: str,
app_context: str,
output_dir: str = "./locales"
) -> str:
with open(source_file, 'r', encoding='utf-8') as f:
source_data = json.load(f)
os.makedirs(output_dir, exist_ok=True)
translated_data = {}
def translate_recursive(obj, path=""):
if isinstance(obj, str):
if len(obj.strip()) == 0:
return obj
result = translate_with_context(
text=obj,
target_lang=target_lang,
context=f"Uygulama: {app_context}, Anahtar yolu: {path}",
tone="formal"
)
time.sleep(0.1)
return result["translated"]
elif isinstance(obj, dict):
return {k: translate_recursive(v, f"{path}.{k}") for k, v in obj.items()}
elif isinstance(obj, list):
return [translate_recursive(item, f"{path}[{i}]") for i, item in enumerate(obj)]
return obj
translated_data = translate_recursive(source_data)
output_file = os.path.join(output_dir, f"{target_lang}.json")
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(translated_data, f, ensure_ascii=False, indent=2)
print(f"Çeviri tamamlandı: {output_file}")
return output_file
if __name__ == "__main__":
for lang in ["de", "fr", "ar"]:
translate_json_file(
source_file="./locales/tr.json",
target_lang=lang,
app_context="Kurumsal İK yönetim sistemi"
)
EOF
time.sleep(0.1) satırı küçük ama önemli. Rate limit’e çarpmamak için her istek arasında minimal bir bekleme ekliyorum. Rate limiting stratejisi olmadan büyük dosyalarda mutlaka hata alırsınız.
Dil Tespiti ve Otomatik Yönlendirme
Kullanıcının hangi dilde yazdığını tespit edip otomatik cevap vermek, destek sistemleri için çok değerli bir özellik. Bunu hem langdetect kütüphanesiyle hem de OpenAI ile hibrit yapabiliyoruz:
cat > language_detector.py << 'EOF'
from langdetect import detect, detect_langs
from openai import OpenAI
import os
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def detect_language_smart(text: str) -> dict:
"""
Kısa metinlerde langdetect güvenilir değil,
o yüzden hibrit yaklaşım kullanıyoruz
"""
if len(text.split()) > 10:
try:
detected = detect(text)
confidence = max([l.prob for l in detect_langs(text)])
if confidence > 0.85:
return {
"language": detected,
"confidence": confidence,
"method": "langdetect"
}
except Exception:
pass
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "system",
"content": """Verilen metnin dilini tespit et.
Sadece ISO 639-1 dil kodunu döndür (örn: tr, en, de, ar, fr).
Hiçbir açıklama ekleme."""
},
{"role": "user", "content": text}
],
temperature=0,
max_tokens=5
)
lang_code = response.choices[0].message.content.strip().lower()
return {
"language": lang_code,
"confidence": 0.95,
"method": "openai"
}
def auto_respond_multilingual(
user_message: str,
response_content: str
) -> str:
"""
Kullanıcının dilini tespit et ve o dilde yanıt ver
"""
detected = detect_language_smart(user_message)
user_lang = detected["language"]
if user_lang == "tr":
return response_content
from translation_service import translate_with_context
result = translate_with_context(
text=response_content,
target_lang=user_lang,
context="Müşteri destek sistemi otomatik yanıtı"
)
return result["translated"]
EOF
FastAPI ile Çok Dilli API Servisi
Bunu bir REST API olarak servis etmek için FastAPI idealdir. Hem async desteği hem de otomatik dokümantasyonu ile production’a çıkmak çok hızlı oluyor:
cat > main.py << 'EOF'
from fastapi import FastAPI, HTTPException, BackgroundTasks
from pydantic import BaseModel
from typing import Optional, List
import asyncio
import os
from openai import AsyncOpenAI
app = FastAPI(title="Çok Dilli İçerik Servisi")
async_client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY"))
class TranslationRequest(BaseModel):
text: str
target_languages: List[str]
context: Optional[str] = ""
tone: Optional[str] = "formal"
class BatchTranslationResponse(BaseModel):
original: str
translations: dict
total_tokens: int
@app.post("/translate/batch", response_model=BatchTranslationResponse)
async def batch_translate(request: TranslationRequest):
if not request.text.strip():
raise HTTPException(status_code=400, detail="Metin boş olamaz")
if len(request.text) > 5000:
raise HTTPException(status_code=400, detail="Metin 5000 karakteri geçemez")
async def translate_single(lang: str) -> tuple:
system_msg = f"""Profesyonel lokalizasyon uzmanısın.
Metni {lang} diline çevir. Ton: {request.tone}.
Sadece çeviriyi döndür."""
user_msg = request.text
if request.context:
user_msg = f"Bağlam: {request.context}nMetin: {request.text}"
response = await async_client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": system_msg},
{"role": "user", "content": user_msg}
],
temperature=0.3
)
return lang, response.choices[0].message.content.strip(), response.usage.total_tokens
tasks = [translate_single(lang) for lang in request.target_languages]
results = await asyncio.gather(*tasks, return_exceptions=True)
translations = {}
total_tokens = 0
for result in results:
if isinstance(result, Exception):
continue
lang, translation, tokens = result
translations[lang] = translation
total_tokens += tokens
return BatchTranslationResponse(
original=request.text,
translations=translations,
total_tokens=total_tokens
)
@app.get("/health")
async def health_check():
return {"status": "ok", "service": "multilang-api"}
EOF
# Servisi başlat
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
Async yapının güzelliği burada: 6 dile aynı anda paralel istek atabiliyorsunuz. Sıralı yaklaşımda 6 saniye süren işlem async ile 1-1.5 saniyeye düşüyor.
Kültürel Lokalizasyon: Sadece Çeviri Yeterli Değil
Bu kısım çoğu geliştirici tarafından göz ardı edilir ama production’da en çok şikayeti buradan alırsınız. Tarih formatları, para birimi gösterimi, sağdan sola dil desteği bunların hepsi “lokalizasyon” kapsamında ama çeviri değil.
cat > cultural_localizer.py << 'EOF'
from openai import OpenAI
import os
import json
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
CULTURAL_RULES = {
"ar": {
"rtl": True,
"date_format": "DD/MM/YYYY",
"currency_position": "after",
"formal_greeting": "السلام عليكم"
},
"de": {
"rtl": False,
"date_format": "DD.MM.YYYY",
"currency_position": "after",
"decimal_separator": ","
},
"tr": {
"rtl": False,
"date_format": "DD.MM.YYYY",
"currency_position": "after",
"decimal_separator": ","
},
"en": {
"rtl": False,
"date_format": "MM/DD/YYYY",
"currency_position": "before",
"decimal_separator": "."
}
}
def culturally_adapt_content(
content: str,
target_lang: str,
content_type: str = "ui_text"
) -> dict:
"""
Sadece çevirmekle kalmayıp kültürel adaptasyon da yap
"""
cultural_context = CULTURAL_RULES.get(target_lang, {})
system_prompt = f"""Sen bir lokalizasyon ve kültürel adaptasyon uzmanısın.
Verilen içeriği {target_lang} dili ve kültürüne uyarla.
Kültürel bilgi:
{json.dumps(cultural_context, ensure_ascii=False)}
İçerik türü: {content_type}
Görevler:
1. Metni doğal ve akıcı şekilde çevir
2. Kültürel referansları hedef kültüre uyarla
3. Yerel deyim ve ifadeleri kullan
4. Resmiyet seviyesini kültüre göre ayarla
Yanıtı JSON formatında döndür:
{{
"translated_text": "...",
"cultural_notes": ["...", "..."],
"rtl_required": true/false
}}"""
response = client.chat.completions.create(
model=os.getenv("OPENAI_MODEL", "gpt-4o-mini"),
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": content}
],
temperature=0.4,
response_format={"type": "json_object"}
)
result = json.loads(response.choices[0].message.content)
result["language"] = target_lang
result["cultural_rules"] = cultural_context
return result
if __name__ == "__main__":
test_content = "Sipariş başarıyla tamamlandı! Teşekkür ederiz."
for lang in ["en", "de", "ar"]:
result = culturally_adapt_content(test_content, lang, "success_message")
print(f"n{lang.upper()}:")
print(f"Çeviri: {result.get('translated_text')}")
print(f"Notlar: {result.get('cultural_notes')}")
EOF
response_format={"type": "json_object"} parametresi önemli. Yapılandırılmış çıktı için bu özelliği mutlaka kullanın, JSON parse hatalarından kurtarır.
Maliyet Optimizasyonu ve Token Yönetimi
Canlıya aldıktan sonra aylık maliyetiniz patlamadan önce şu önlemleri alın:
cat > cost_optimizer.py << 'EOF'
import tiktoken
import os
def estimate_translation_cost(
texts: list,
target_languages: list,
model: str = "gpt-4o-mini"
) -> dict:
"""
Çeviri yapmadan önce tahmini maliyet hesapla
"""
encoding = tiktoken.encoding_for_model("gpt-4o-mini")
# gpt-4o-mini fiyatları (1K token başına USD)
INPUT_COST_PER_1K = 0.00015
OUTPUT_COST_PER_1K = 0.00060
total_input_tokens = 0
total_output_tokens = 0
system_prompt_base = "Sen profesyonel bir lokalizasyon uzmanısın. Metni hedef dile çevir."
system_tokens = len(encoding.encode(system_prompt_base))
for text in texts:
text_tokens = len(encoding.encode(text))
output_tokens = int(text_tokens * 1.2)
for lang in target_languages:
total_input_tokens += system_tokens + text_tokens
total_output_tokens += output_tokens
input_cost = (total_input_tokens / 1000) * INPUT_COST_PER_1K
output_cost = (total_output_tokens / 1000) * OUTPUT_COST_PER_1K
total_cost = input_cost + output_cost
return {
"estimated_input_tokens": total_input_tokens,
"estimated_output_tokens": total_output_tokens,
"estimated_cost_usd": round(total_cost, 4),
"estimated_cost_try": round(total_cost * 32, 2),
"texts_count": len(texts),
"languages_count": len(target_languages)
}
def chunk_large_text(text: str, max_tokens: int = 1500) -> list:
"""
Büyük metinleri token sınırına göre parçala
"""
encoding = tiktoken.encoding_for_model("gpt-4o-mini")
tokens = encoding.encode(text)
if len(tokens) <= max_tokens:
return [text]
chunks = []
sentences = text.split('. ')
current_chunk = []
current_tokens = 0
for sentence in sentences:
sentence_tokens = len(encoding.encode(sentence))
if current_tokens + sentence_tokens > max_tokens:
chunks.append('. '.join(current_chunk) + '.')
current_chunk = [sentence]
current_tokens = sentence_tokens
else:
current_chunk.append(sentence)
current_tokens += sentence_tokens
if current_chunk:
chunks.append('. '.join(current_chunk))
return chunks
if __name__ == "__main__":
sample_texts = [
"Ürününüz başarıyla sepete eklendi.",
"Siparişiniz kargoya verilmiştir, takip numaranız e-posta ile gönderildi.",
"Hesabınızda güvenlik nedeniyle değişiklik yapıldı."
]
estimate = estimate_translation_cost(
texts=sample_texts,
target_languages=["en", "de", "ar", "fr"]
)
print(f"Tahmini maliyet: ${estimate['estimated_cost_usd']} / {estimate['estimated_cost_try']} TL")
print(f"Toplam token: {estimate['estimated_input_tokens'] + estimate['estimated_output_tokens']}")
EOF
Bu maliyet hesaplayıcıyı CI/CD pipeline’ınıza entegre edin. Yeni lokalizasyon dosyası eklemeden önce otomatik maliyet tahmini üretilsin, eşiği aşarsa alert atsın.
Production’da Dikkat Edilmesi Gereken Noktalar
Birkaç ay production’da koşturduktan sonra öğrendiklerimi özetleyeyim:
- Rate limiting: Tier’ınıza göre RPM (requests per minute) sınırını aşmamak için exponential backoff uygulayın
- Fallback mekanizması: API cevap vermediğinde statik çeviri dosyalarına düşün, kullanıcı boş sayfa görmesin
- Cache geçersizleştirme: Kaynak metin değiştiğinde ilgili çevirilerin cache’ini temizleyin, yoksa eski çeviriyi sunmaya devam edersiniz
- Prompt injection koruması: Kullanıcı girdisi içeren çevirilerde input sanitization yapın
- Uzun metin stratejisi: 2000 tokenü geçen metinleri parçalayın, parçaları ayrı ayrı çevirip birleştirin
- A/B test desteği: Farklı çeviri yaklaşımlarını aynı anda test edebilmek için feature flag entegre edin
- Log ve monitoring: Hangi dil çiftleri en çok kullanılıyor, ortalama gecikme ne, bunları Prometheus/Grafana ile izleyin
Gerçek Dünya Sonuçları
Bir İK yazılımı projesinde bu altyapıyı kurduktan sonra somut rakamlara baktım. Dört dil desteğini ayaklandırmak eskiden 3-4 hafta sürüyordu, şimdi yeni bir dil eklemek 2-3 saate düştü. Çeviri tutarsızlık şikayetleri yüzde 80 azaldı çünkü aynı model, aynı bağlam ile tutarlı sonuç üretiyor. Maliyet tarafında ise profesyonel çeviri bürosu maliyetinin onda biri ile aynı kaliteyi yakaladık.
Elbette her şey mükemmel değil. Hukuki veya medikal içeriklerde hala insan gözden geçirmesi şart. Modelin “emin” olmadığı ama yanlış çevirdiği durumlar oluyor, özellikle deyimler ve kültürel özgün ifadelerde. Bunun için kritik içerik kategorilerinde çeviri confidence skoru üretip düşük skorlarda insan review’a yönlendirme mekanizması ekledim.
Sonuç
OpenAI API ile çok dilli uygulama geliştirmek, doğru mimaride kurulduğunda ciddi bir rekabet avantajı sağlıyor. Temel prensipler şunlar: bağlam her zaman çeviriden daha değerlidir, cache olmadan ölçeklenemezsiniz, kültürel adaptasyon çeviriden ayrı bir katman olarak tasarlanmalı ve maliyet her zaman izlenmelidir.
Eğer sıfırdan başlıyorsanız önce basit bir çeviri servisi kurun, cache ekleyin, sonra yavaş yavaş kültürel adaptasyon ve dil tespiti katmanlarını ekleyin. Big bang yaklaşımı yerine incremental geliştirme bu alanda çok daha sağlıklı sonuçlar veriyor. Sorularınız olursa yorumlarda buluşalım.
