İçindekiler
CI/CD Pipeline'ınızda i18n'i Otomatikleştirme: Git Push'tan Canlı Çevirilere
"12 dili destekliyoruz." Harika. Ancak bir geliştirici yeni bir özellik yayımladığında, bu 12 dildeki kullanıcılar çevrilmiş içeriği ne zaman görür?
Çoğu ekip için cevap, uzun bir beklemeyi kapsar. Bir geliştirici string ekler, bunu Slack'te belirtir, bir PM bir elektronik tablo açar, çeviri firmasına e-posta gönderilir, firma dosyaları bir hafta sonra teslim eder, bir geliştirici JSON'ı kopyalar, PR açar, incelenir, birleştirilir, dağıtılır. O noktada özellik, İngilizce kullanıcılarına iki hafta önce gönderilmiştir.
Bu yazı, o beklemeyi tamamen ortadan kaldırmakla ilgili. Tek manuel adımın bir geliştiricinin kod yazması olduğu — ve isteğe bağlı olarak bir insan çevirmenden AI çıktısını gözden geçirmesinin olduğu — uçtan uca otomatik bir i18n pipeline'ı inşa edeceğiz. Diğer her şey otomatik olarak gerçekleşir.
Manuel i18n İş Akışı (Ve Neden Bozulur)
Tipik akış şu şekildedir:
- Geliştirici kaynak koda
t('checkout.shipping.title', 'Shipping Address')ekler - Geliştirici PM'e "yeni bir string var" der
- PM çeviri anahtarlarını bir elektronik tabloya aktarır
- PM elektronik tabloyu çeviri ajansına e-posta ile gönderir
- Ajans çevirir, elektronik tabloyu iade eder
- Geliştirici (veya PM) çevirileri manuel olarak JSON locale dosyalarına kopyalar
- Geliştirici locale dosyalarıyla bir PR açar
- PR incelenir, birleştirilir
- Uygulama yeni çevirilerle yeniden dağıtılır
Her el değiştirme gecikme yaratır. Dahası, her el değiştirme potansiyel bir başarısızlık noktasıdır: anahtarlar gözden kaçar, elektronik tablolardan kopyala-yapıştır sırasında formatlar bozulur, JSON sözdizimi hataları sızar ve hataları yakalamak için geri bildirim döngüsü dakikalar değil günler olarak ölçülür.
Otomasyonun amacı: "geliştirici t('key') yazar" ile "kullanıcı çevrilmiş içeriği görür" arasında sıfır insan adımı.
Otomatik Pipeline
İnşa ettiğimiz mimari şu şekildedir:
git push
└─> CI: yeni/değişmiş/silinen anahtarları çıkar
└─> değişiklik tespit edilirse: çeviri API'sini tetikle
└─> AI ilk geçiş çevirisi
└─> (isteğe bağlı) insan inceleme kuyruğu
└─> kalite kapıları: sözlük, yer tutucular, uzunluk
└─> CDN'e yayımla
└─> tüm kullanıcılar için canlı (yeniden dağıtım gerekmez)
Dosya kopyalama yok. Locale dosyaları için PR yok. Çeviri güncellemeleri için yeniden dağıtım yok. Her adımı inşa edelim.
Adım 1: Otomatik Anahtar Çıkarma
İlk CI işinin, commit'ler arasında hangi çeviri anahtarlarının değiştiğini belirlemesi gerekir.
AST tabanlı çıkarma kullanımı
i18next-parser ve @formatjs/cli gibi araçlar AST tabanlı çıkarma yapar — kaynak kodunuzu ayrıştırır ve dinamik import'lar ile karmaşık bileşen ağaçlarında bile çeviri fonksiyonunuza yapılan her çağrıyı çıkarır.
# Kur npm install --save-dev i18next-parser # Kaynaktan tüm anahtarları çıkar npx i18next-parser --config i18next-parser.config.js
Temel bir i18next-parser.config.js:
module.exports = {
input: ['src/**/*.{ts,tsx,js,jsx,vue,svelte}'],
output: '.i18n-extracted/$NAMESPACE.json',
locales: ['en'],
defaultNamespace: 'common',
keySeparator: '.',
namespaceSeparator: ':',
};
CI'da değişiklikleri tespit etme
Temel içgörü: her push'ta her şeyi yeniden çevirmek istemezsiniz. Son çıkarmadan bu yana yeni, değişmiş ve silinmiş anahtarları tespit etmek istersiniz.
Aşağıda, mevcut commit ile son bilinen çıkarma anlık görüntüsü arasındaki çıkarılmış anahtarları karşılaştıran bir shell script'i bulunmaktadır:
#!/bin/bash # scripts/detect-i18n-changes.sh set -euo pipefail SNAPSHOT_FILE=".i18n-snapshot/en.json" EXTRACTED_FILE=".i18n-extracted/common.json" if [ ! -f "$SNAPSHOT_FILE" ]; then echo "Anlık görüntü bulunamadı — tüm anahtarlar yeni kabul ediliyor" echo "new_keys=$(jq -r 'keys | length' "$EXTRACTED_FILE")" cp "$EXTRACTED_FILE" "$SNAPSHOT_FILE" exit 0 fi # Çıkarılmış ama anlık görüntüde olmayan anahtarları bul (yeni anahtarlar) NEW_KEYS=$(jq -n \ --slurpfile current "$EXTRACTED_FILE" \ --slurpfile snapshot "$SNAPSHOT_FILE" \ '[$current[0] | keys[]] - [$snapshot[0] | keys[]]' ) # Anlık görüntüde olan ama çıkarılmamış anahtarları bul (silinen anahtarlar) DELETED_KEYS=$(jq -n \ --slurpfile current "$EXTRACTED_FILE" \ --slurpfile snapshot "$SNAPSHOT_FILE" \ '[$snapshot[0] | keys[]] - [$current[0] | keys[]]' ) NEW_COUNT=$(echo "$NEW_KEYS" | jq 'length') DELETED_COUNT=$(echo "$DELETED_KEYS" | jq 'length') echo "Yeni anahtarlar: $NEW_COUNT" echo "Silinen anahtarlar: $DELETED_COUNT" # GitHub Actions için dışa aktar echo "new_keys=$NEW_COUNT" >> "$GITHUB_OUTPUT" echo "deleted_keys=$DELETED_COUNT" >> "$GITHUB_OUTPUT" echo "has_changes=$([ "$NEW_COUNT" -gt 0 ] || [ "$DELETED_COUNT" -gt 0 ] && echo true || echo false)" >> "$GITHUB_OUTPUT"
has_changes true ise, çeviriyi tetiklemeye devam ediyoruz.
Adım 2: Çeviri Tetikleme
Yeni anahtarlar tespit edildiğinde, bir Çeviri Yönetim Sistemi'ne (TMS) veya bir çeviri API'sine bildirim göndermemiz gerekir. Bu bir webhook çağrısıdır.
Çeviri tetiklemesi için GitHub Action
# .github/workflows/i18n-sync.yml (kısmi)
- name: Yeni anahtarlar için çeviriyi tetikle
if: steps.detect-changes.outputs.has_changes == 'true'
run: |
curl -X POST "${{ secrets.TMS_WEBHOOK_URL }}" \
-H "Authorization: Bearer ${{ secrets.TMS_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{
"project_id": "${{ vars.I18N_PROJECT_ID }}",
"source_locale": "en",
"target_locales": ["de", "fr", "es", "ja", "pt-BR"],
"keys_file_url": "${{ steps.upload-keys.outputs.url }}"
}'
Better i18n kullanan ekipler için platform bu adımı natively yönetir — bağlı deponuza yapılan bir push, anahtar değişikliklerini otomatik olarak tespit eder ve ayrı bir webhook kurulumuna gerek kalmadan bunları çeviri için kuyruğa alır.
Adım 3: AI Çevirisi + İnsan İncelemesi
AI çevirisi, çoğu içerik için uygulanabilir bir ilk geçiş haline gelecek kadar iyi hale geldi. İş akışı:
- AI anında çevirir (sıfır bekleme)
- Çeviriler kullanıcılara hemen AI kalitesiyle sunulur
- İnsan çevirmenler eş zamansız olarak inceler ve onaylar/düzenler
- Onaylanan çeviriler AI sürümlerinin yerini CDN güncellemesiyle alır
AI çıktısında kalite kapıları
AI çevirileri kabul edilmeden önce otomatik kontroller çalıştırın:
// scripts/validate-translations.ts
import { readFileSync } from 'fs';
interface TranslationFile {
[key: string]: string;
}
interface ValidationResult {
key: string;
locale: string;
error: string;
}
function extractPlaceholders(str: string): string[] {
return [...str.matchAll(/\{(\w+)\}/g)].map(m => m[1]);
}
function validateTranslations(
source: TranslationFile,
target: TranslationFile,
locale: string
): ValidationResult[] {
const errors: ValidationResult[] = [];
for (const [key, sourceValue] of Object.entries(source)) {
const targetValue = target[key];
// Eksik anahtar
if (!targetValue) {
errors.push({ key, locale, error: 'Eksik çeviri' });
continue;
}
// Yer tutucu uyuşmazlığı
const sourcePlaceholders = extractPlaceholders(sourceValue);
const targetPlaceholders = extractPlaceholders(targetValue);
const missingPlaceholders = sourcePlaceholders.filter(
p => !targetPlaceholders.includes(p)
);
if (missingPlaceholders.length > 0) {
errors.push({
key,
locale,
error: `Eksik yer tutucular: ${missingPlaceholders.join(', ')}`,
});
}
// String uzunluk kontrolü (çeviri kaynaktan 3 kat uzunsa uyar)
if (targetValue.length > sourceValue.length * 3) {
errors.push({
key,
locale,
error: `Şüpheli derecede uzun çeviri (${targetValue.length} ve ${sourceValue.length} karakter)`,
});
}
}
return errors;
}
// Doğrulamayı çalıştır
const locales = ['de', 'fr', 'es', 'ja', 'pt-BR'];
const source: TranslationFile = JSON.parse(
readFileSync('.i18n-extracted/common.json', 'utf-8')
);
let hasErrors = false;
for (const locale of locales) {
const target: TranslationFile = JSON.parse(
readFileSync(`.i18n-translations/${locale}/common.json`, 'utf-8')
);
const errors = validateTranslations(source, target, locale);
if (errors.length > 0) {
console.error(`\n${locale} için doğrulama hataları:`);
errors.forEach(e => console.error(` [${e.key}] ${e.error}`));
hasErrors = true;
}
}
if (hasErrors) {
process.exit(1);
}
console.log('Tüm çeviriler başarıyla doğrulandı');
Adım 4: CDN Yayımlama
Burası, modern i18n platformlarının oyunu değiştirdiği yerdir. Geleneksel yaklaşımlar, locale JSON dosyalarını deponuza koyar. Çeviriler güncellendiğinde, bir PR birleştirmeniz ve yeniden dağıtmanız gerekir. Bu yavaştır ve çeviri iş akışınızı dağıtım pipeline'ınıza bağlar.
CDN öncelikli teslimat bunları tamamen ayrıştırır:
- Uygulamanız çalışma zamanında bir CDN URL'sinden çevirileri çeker
- Bir çeviri onaylandığında CDN'e iletilir
- Kullanıcılar bir sonraki sayfa yüklemelerinde güncellenmiş çevirileri görür — dağıtım gerekmez
İki yaklaşımı karşılaştırın:
| Dosya tabanlı | CDN tabanlı | |
|---|---|---|
| Çevirileri güncelle | PR birleştir + yeniden dağıt | CDN'e aktar |
| Canlıya geçme süresi | Dakikalar ila saatler | Saniyeler |
| Geri alma | Git revert + yeniden dağıt | CDN önbellek geçersizleştirme |
| Ayrı çeviri PR'ları | Evet | Hayır |
| Feature flag'lerle çalışır | Karmaşık | Native |
Better i18n'in CDN teslimatı ile çeviriler sürümlenir ve global olarak edge node'lardan sunulur. SDK'nız uygulamanızın mevcut dağıtımı için doğru sürümü çeker, böylece çevirileri belirli uygulama sürümlerine de sabitleyebilirsiniz.
Adım 5: CI'da Kalite Kapıları
CI kapıları, sorunları dağıtımdan önce yakalar. Ancak sızıp geçen sorunları yakalamak için çalışma zamanı izlemeye de ihtiyacınız var — çeviri kapsamı olmadan eklenen anahtarlar, uygulama geliştikçe geride kalan locale'ler.
Eksik anahtar tespiti
Kaynak locale'de olan ancak hedef locale'lerde olmayan herhangi bir anahtar varsa build'i başarısız yap:
#!/bin/bash
# scripts/check-missing-keys.sh
set -euo pipefail
SOURCE_FILE=".i18n-extracted/common.json"
LOCALES=("de" "fr" "es" "ja" "pt-BR")
HAS_MISSING=false
SOURCE_KEYS=$(jq -r 'keys[]' "$SOURCE_FILE" | sort)
for locale in "${LOCALES[@]}"; do
TARGET_FILE=".i18n-translations/${locale}/common.json"
if [ ! -f "$TARGET_FILE" ]; then
echo "HATA: ${locale} için çeviri dosyası eksik"
HAS_MISSING=true
continue
fi
TARGET_KEYS=$(jq -r 'keys[]' "$TARGET_FILE" | sort)
MISSING=$(comm -23 <(echo "$SOURCE_KEYS") <(echo "$TARGET_KEYS"))
if [ -n "$MISSING" ]; then
echo "HATA: ${locale} için eksik anahtarlar:"
echo "$MISSING" | while read -r key; do
echo " - $key"
done
HAS_MISSING=true
fi
done
if [ "$HAS_MISSING" = true ]; then
echo ""
echo "Build başarısız: eksik çeviriler tespit edildi"
exit 1
fi
echo "Tüm locale'lerde tüm çeviri anahtarları mevcut"
CI'da yer tutucu doğrulaması
{placeholder} değişkenlerinin çeviriyi atlatıp atlatmadığını doğrulamak için özel bir adım ekleyin:
- name: Yer tutucu bütünlüğünü doğrula run: npx ts-node scripts/validate-translations.ts
Adım 6: Üretimde İzleme
CI kapıları, sorunları dağıtımdan önce yakalar. Ancak sızıp geçen sorunları yakalamak için çalışma zamanı izlemeye de ihtiyacınız var — çeviri kapsamı olmadan eklenen anahtarlar, uygulama geliştikçe geride kalan locale'ler.
Locale başına çeviri kapsamını takip edin
// api/i18n-coverage.ts
export async function getTranslationCoverage(): Promise<Record<string, number>> {
const sourceKeys = await fetchSourceKeys(); // TMS'nizden veya CDN'den
const coverage: Record<string, number> = {};
for (const locale of SUPPORTED_LOCALES) {
const translatedKeys = await fetchTranslatedKeys(locale);
const translatedSet = new Set(translatedKeys);
const covered = sourceKeys.filter(k => translatedSet.has(k)).length;
coverage[locale] = Math.round((covered / sourceKeys.length) * 100);
}
return coverage;
}
Kullanıcılara ulaşan eksik anahtarlar için uyarı verin
Eksik anahtarları günlüğe kaydetmek için çeviri fonksiyonunuzu sarın:
// lib/t.ts
import { track } from './analytics';
export function t(key: string, fallback: string): string {
const translation = getTranslation(key);
if (!translation) {
// İzleme için eksik anahtarı günlüğe kaydet
track('i18n.missing_key', {
key,
locale: getCurrentLocale(),
url: window.location.pathname,
});
return fallback;
}
return translation;
}
Bu olayları gözlemlenebilirlik platformunuza (Datadog, Grafana vb.) gönderin ve eksik anahtar oranları artışında uyarılar ayarlayın.
Eksiksiz GitHub Actions İş Akışı
İşte her şeyi bir araya getiren tam iş akışı dosyası:
# .github/workflows/i18n-pipeline.yml
name: i18n Otomasyon Pipeline'ı
on:
push:
branches: [main]
paths:
- 'src/**/*.{ts,tsx,js,jsx,vue,svelte}'
env:
LOCALES: de,fr,es,ja,pt-BR
SOURCE_LOCALE: en
jobs:
extract-and-sync:
name: Anahtarları Çıkar ve Çevirileri Senkronize Et
runs-on: ubuntu-latest
steps:
- name: Kodu kontrol et
uses: actions/checkout@v4
with:
fetch-depth: 2 # Diff için önceki commit gerekli
- name: Node.js kur
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Bağımlılıkları yükle
run: npm ci
# Adım 1: Kaynak koddan çeviri anahtarlarını çıkar
- name: i18n anahtarlarını çıkar
run: npx i18next-parser --config i18next-parser.config.js
# Adım 2: Son anlık görüntüye göre değişiklikleri tespit et
- name: Anahtar değişikliklerini tespit et
id: detect-changes
run: bash scripts/detect-i18n-changes.sh
# Adım 3: Yeni anahtarları çeviri API'sine yükle
- name: Çeviri için yeni anahtarları yükle
if: steps.detect-changes.outputs.has_changes == 'true'
run: |
curl -X POST "${{ secrets.BETTER_I18N_API_URL }}/api/sync" \
-H "Authorization: Bearer ${{ secrets.BETTER_I18N_API_KEY }}" \
-H "Content-Type: application/json" \
-d @.i18n-extracted/common.json
env:
PROJECT_ID: ${{ vars.I18N_PROJECT_ID }}
# Adım 4: AI çevirisi için kısaca bekle (veya hazır olana kadar sorgula)
- name: AI çevirilerini bekle
if: steps.detect-changes.outputs.has_changes == 'true'
run: |
# 5 dakikaya kadar çeviri durumunu sorgula
for i in $(seq 1 30); do
STATUS=$(curl -s \
-H "Authorization: Bearer ${{ secrets.BETTER_I18N_API_KEY }}" \
"${{ secrets.BETTER_I18N_API_URL }}/api/status/${{ vars.I18N_PROJECT_ID }}" \
| jq -r '.status'
)
if [ "$STATUS" = "ready" ]; then
echo "Çeviriler hazır"
break
fi
echo "Çeviriler bekleniyor... ($i/30)"
sleep 10
done
# Adım 5: Çevrilmiş dosyaları indir
- name: Çevirileri indir
run: |
IFS=',' read -ra LOCALE_ARRAY <<< "$LOCALES"
for locale in "${LOCALE_ARRAY[@]}"; do
mkdir -p ".i18n-translations/${locale}"
curl -s \
-H "Authorization: Bearer ${{ secrets.BETTER_I18N_API_KEY }}" \
"${{ secrets.BETTER_I18N_API_URL }}/api/export/${locale}" \
-o ".i18n-translations/${locale}/common.json"
done
# Adım 6: Çevirileri doğrula
- name: Yer tutucu bütünlüğünü doğrula
run: npx ts-node scripts/validate-translations.ts
# Adım 7: Eksik anahtarları kontrol et
- name: Eksik anahtarları kontrol et
run: bash scripts/check-missing-keys.sh
# Adım 8: Sonraki çalışma için anlık görüntüyü güncelle
- name: i18n anlık görüntüsünü güncelle
if: steps.detect-changes.outputs.has_changes == 'true'
run: |
cp .i18n-extracted/common.json .i18n-snapshot/en.json
git config user.email "ci@yourorg.com"
git config user.name "CI Bot"
git add .i18n-snapshot/
git diff --staged --quiet || git commit -m "chore: i18n anlık görüntüsünü güncelle [skip ci]"
git push
quality-gate:
name: i18n Kalite Kapısı
runs-on: ubuntu-latest
needs: extract-and-sync
steps:
- uses: actions/checkout@v4
- name: Node.js kur
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Bağımlılıkları yükle
run: npm ci
# Son kontrol: herhangi bir locale'de eksik çeviri yok
- name: Çeviri kapsamını doğrula
run: |
node -e "
const { getTranslationCoverage } = require('./dist/api/i18n-coverage');
getTranslationCoverage().then(coverage => {
const failed = Object.entries(coverage)
.filter(([, pct]) => pct < 95);
if (failed.length > 0) {
console.error('%95 altında kapsam:', failed);
process.exit(1);
}
console.log('Kapsam OK:', coverage);
});
"
Sonuç
Hedef, "geliştirici t('key') yazar" ile "kullanıcı çevrilmiş içeriği görür" arasında sıfır insan adımıdır. AST tabanlı çıkarma ile anahtar değişikliklerini otomatik olarak tespit etmek, AI çevirisiyle anında ilk geçiş sağlamak, CDN teslimatıyla yeniden dağıtım olmadan çevirileri canlıya almak ve kalite kapılarıyla CI'da yer tutucu uyuşmazlıklarını ve eksik anahtarları yakalamak — bu hedef bugün gerçekleştirilebilir.
Bunu doğru yapan ekipler i18n'e diğer altyapılarla aynı şekilde davranır: otomatik, izlenen ve bireysel dağıtımlardan bağımsız. Çeviriler bir darboğaz olmaktan çıkar ve teslimat pipeline'ının yalnızca bir parçası haline gelir.
Anahtar çıkarma script'i ve eksik anahtar CI kontrolüyle başlayın — bu iki adım, manuel zahmetin büyük çoğunluğunu tek başına ortadan kaldırır. Ardından pipeline'ı tamamlamak için çeviri API entegrasyonunu ve CDN teslimatını ekleyin.
Better i18n, modern frontend ekipleri için tasarlanmış geliştirici odaklı bir yerelleştirme platformudur. Tip güvenli SDK'lar, Git tabanlı iş akışları, CDN teslimatı ve sözlük zorunluluğuyla AI çevirisi — deponuzda locale dosyaları olmadan.