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.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir