# iOS Localization ve i18n: Çok Dilli Uygulama Geliştirme
Uygulamanızı tek bir dille sınırlamak, potansiyel kullanıcılarınızın büyük bir kısmını kaybetmek demektir. App Store'daki en başarılı uygulamalar 30+ dili destekler. Xcode 15+ ile gelen String Catalogs sayesinde localization artık çok daha kolay. Bu rehberde sıfırdan profesyonel seviyeye i18n implementasyonu yapacağız.
İçindekiler
- String Catalogs
- Localizable.xcstrings
- Pluralization
- SwiftUI Localization
- Date, Number & Currency
- RTL Desteği
- Asset Localization
- Testing
- App Store Localization
- Best Practices
1. String Catalogs (Xcode 15+)
Xcode 15 ile gelen String Catalogs, eski .strings ve .stringsdict dosyalarını tek bir .xcstrings dosyasında birleştirir.
Geçiş Adımları
Eski Sistem | Yeni Sistem | Avantaj |
|---|---|---|
Localizable.strings | Localizable.xcstrings | Tek dosya, visual editor |
Localizable.stringsdict | Otomatik dahil | Pluralization görsel |
InfoPlist.strings | InfoPlist.xcstrings | Ayrı catalog |
Manuel key yönetimi | Otomatik extraction | Build time detection |
swift
1// Xcode otomatik olarak String literal'ları yakalar2// SwiftUI'de Text() zaten LocalizedStringKey kullanır3 4struct WelcomeView: View {5 let userName: String6 let itemCount: Int7 8 var body: some View {9 VStack {10 // Otomatik olarak String Catalog'a eklenir11 Text("Hoş Geldiniz")12 13 // String interpolation da desteklenir14 Text("Merhaba, \(userName)!")15 16 // Pluralization otomatik17 Text("\(itemCount) öğe seçildi")18 }19 }20}Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
2. Programmatic Localization
swift
1// Eski yöntem (hâlâ çalışır)2let greeting = NSLocalizedString("greeting", comment: "Ana ekran karşılama mesajı")3 4// Modern yöntem - String(localized:)5let greeting = String(localized: "greeting", comment: "Ana ekran karşılama mesajı")6 7// Farklı table'dan okuma8let settingsTitle = String(9 localized: "settings.title",10 table: "Settings",11 comment: "Ayarlar ekranı başlığı"12)13 14// Bundle'dan okuma (framework/SPM)15let frameworkString = String(16 localized: "button.save",17 bundle: .module,18 comment: "Kaydet butonu"19)LocalizedStringResource (iOS 16+)
swift
1// Daha güçlü ve type-safe localization2struct NotificationContent {3 let title: LocalizedStringResource4 let body: LocalizedStringResource5}6 7let content = NotificationContent(8 title: "Yeni Mesaj",9 body: "\(senderName) size bir mesaj gönderdi"10)11 12// String'e dönüştürme13let titleString = String(localized: content.title)3. Pluralization
Türkçe basit olsa da, Rusça gibi diller 6 farklı plural form'a sahip. String Catalogs bunu görsel olarak yönetir.
swift
1// String Catalog'da otomatik plural desteği2Text("\(count) dosya silindi")3// Catalog'da:4// zero: "Dosya silinmedi"5// one: "1 dosya silindi"6// other: "%lld dosya silindi"7 8// Custom plural fonksiyon9func fileCountDescription(_ count: Int) -> String {10 String(localized: "\(count) file(s) deleted",11 comment: "Silinen dosya sayısı mesajı")12}Dil | Plural Rules | Kategoriler |
|---|---|---|
**Türkçe** | 2 form | one, other |
**İngilizce** | 2 form | one, other |
**Arapça** | 6 form | zero, one, two, few, many, other |
**Rusça** | 4 form | one, few, many, other |
**Japonca** | 1 form | other |
**Lehçe** | 4 form | one, few, many, other |
4. SwiftUI Localization Patterns
swift
1// 1. Text otomatik localize eder2Text("Kaydet") // ✅ LocalizedStringKey3 4// 2. String parametreler localize ETMEZ5let dynamicString = "Kaydet"6Text(dynamicString) // ❌ String olarak kalır7Text(LocalizedStringKey(dynamicString)) // ✅ Explicit conversion8 9// 3. Label ve Button10Button("Sil") { deleteItem() } // ✅ Otomatik11Label("Ayarlar", systemImage: "gear") // ✅ Otomatik12 13// 4. TextField placeholder14TextField("E-posta adresiniz", text: $email) // ✅ Otomatik15 16// 5. Accessibility17Image("hero")18 .accessibilityLabel("Ana sayfa görseli") // ✅ Otomatik19 20// 6. Environment ile dil değiştirme (preview'da kullanışlı)21struct ContentView_Previews: PreviewProvider {22 static var previews: some View {23 ContentView()24 .environment(\.locale, Locale(identifier: "tr"))25 ContentView()26 .environment(\.locale, Locale(identifier: "en"))27 ContentView()28 .environment(\.locale, Locale(identifier: "ar"))29 .environment(\.layoutDirection, .rightToLeft)30 }31}5. Date, Number & Currency Formatting
swift
1// Tarih - her zaman Formatter kullanın, manuel format YASAK!2let date = Date()3 4// SwiftUI otomatik locale-aware5Text(date, style: .date) // "9 Şubat 2025"6Text(date, style: .time) // "14:30"7Text(date, style: .relative) // "2 saat önce"8 9// Programmatic10let formatted = date.formatted(11 .dateTime.day().month(.wide).year()12 .locale(Locale(identifier: "tr_TR"))13) // "9 Şubat 2025"14 15// Sayı formatlama16let number = 1234567.8917Text(number, format: .number) // "1.234.567,89" (TR)18 19// Para birimi20let price = Decimal(29.99)21Text(price, format: .currency(code: "TRY")) // "₺29,99"22Text(price, format: .currency(code: "USD")) // "$29.99"23 24// Ölçü birimleri25let distance = Measurement(value: 5.2, unit: UnitLength.kilometers)26Text(distance, format: .measurement(width: .wide)) // "5,2 kilometre"6. RTL (Sağdan Sola) Desteği
swift
1// SwiftUI otomatik RTL desteği sağlar2// Ama dikkat edilmesi gerekenler:3 4// ✅ leading/trailing kullanın (left/right DEĞİL)5HStack {6 Text("İsim")7 Spacer()8 Text("Değer")9}10.padding(.leading, 16) // ✅ RTL'de otomatik sağa döner11// .padding(.left, 16) // ❌ RTL'de yanlış tarafta kalır12 13// ✅ Flipped image'lar14Image(systemName: "arrow.right")15 .flipsForRightToLeftLayoutDirection(true)16 17// ✅ TextAlignment18Text("مرحبا")19 .multilineTextAlignment(.leading) // RTL'de sağa hizalanır7. Asset Localization
swift
1// Asset catalog'da localized images2// 1. Asset'e sağ tık → Localize3// 2. Her dil için farklı görsel ekle4 5// Localized asset kullanımı6Image("onboarding_hero") // Otomatik doğru dili seçer7 8// App Icon localization (Info.plist)9// CFBundleIcons → CFBundleAlternateIcons ile dil bazlı ikon8. Localization Testing
swift
1// Unit test2func testLocalization() {3 let bundle = Bundle(for: type(of: self))4 let languages = ["tr", "en", "de", "ar", "ja"]5 6 for lang in languages {7 guard let path = bundle.path(forResource: lang, ofType: "lproj"),8 let langBundle = Bundle(path: path) else {9 XCTFail("Missing localization: \(lang)")10 continue11 }12 13 let greeting = langBundle.localizedString(14 forKey: "greeting", value: nil, table: nil15 )16 XCTAssertNotEqual(greeting, "greeting",17 "Untranslated key 'greeting' in \(lang)")18 }19}20 21// Scheme'de pseudo-localization22// Edit Scheme → Run → Arguments → -NSDoubleLocalizedStrings YES23// Bu tüm string'leri 2x uzatır → layout sorunlarını yakalar24 25// -AppleLanguages "(ar)" ile Arapça test26// -NSForceRightToLeftWritingDirection YES ile RTL test9. App Store Localization
Öğe | Max Karakter | Dikkat |
|---|---|---|
**App Name** | 30 | Her dilde farklı olabilir |
**Subtitle** | 30 | Anahtar kelime fırsatı |
**Keywords** | 100 | Dile özel SEO |
**Description** | 4000 | İlk 3 satır kritik |
**Promotional Text** | 170 | Review olmadan güncellenebilir |
**Screenshots** | 10 adet | Localized text overlay |
10. Export/Import Workflow ve Çeviri Entegrasyonu
Büyük projelerde localization yönetimi manuel yapılamaz. Xcode'un XLIFF export/import sistemi ve third-party entegrasyonlar bu süreci otomatize eder.
XLIFF Export/Import
bash
1# Tüm localize edilebilir string'leri XLIFF formatında export et2xcodebuild -exportLocalizations -project MyApp.xcodeproj \3 -localizationPath ./Localizations \4 -exportLanguage tr -exportLanguage en -exportLanguage de -exportLanguage ar5 6# Çeviri tamamlandıktan sonra import et7xcodebuild -importLocalizations -project MyApp.xcodeproj \8 -localizationPath ./Localizations/de.xclocÇeviri Servisleri Entegrasyonu
Servis | Avantaj | Dezavantaj | Fiyat Modeli |
|---|---|---|---|
**Lokalise** | Xcode entegrasyonu, OTA update | Pahalı | Per-word |
**Phrase** | GitHub/GitLab CI entegrasyonu | Kurulumu karmaşık | Per-user |
**POEditor** | Basit UI, hızlı başlangıç | Sınırlı CI | Freemium |
**Crowdin** | Açık kaynak desteği, geniş API | UI karmaşık | Per-word |
**Transifex** | Güçlü API, webhook desteği | Eski arayüz | Per-word |
CI/CD Localization Pipeline
swift
1// Fastlane ile localization otomasyonu2// Fastfile3 4lane :sync_translations do5 // 1. Export edilebilir string'leri çıkar6 sh("xcodebuild -exportLocalizations -project ../MyApp.xcodeproj -localizationPath ../Translations")7 8 // 2. Çeviri servisine gönder (Lokalise örneği)9 sh("lokalise2 file upload --project-id PROJECT_ID --file ../Translations/en.xcloc --lang-iso en")10 11 // 3. Çevirileri indir12 sh("lokalise2 file download --project-id PROJECT_ID --format xliff --dest ../Translations/")13 14 // 4. Xcode'a import et15 sh("xcodebuild -importLocalizations -project ../MyApp.xcodeproj -localizationPath ../Translations/tr.xcloc")16endString Catalog Doğrulama Script'i
swift
1// Eksik çevirileri bulan bir script2import Foundation3 4func findMissingTranslations(in xcstringsPath: String, for language: String) {5 guard let data = FileManager.default.contents(atPath: xcstringsPath),6 let catalog = try? JSONSerialization.jsonObject(with: data) as? [String: Any],7 let strings = catalog["strings"] as? [String: Any] else {8 print("Catalog okunamadı")9 return10 }11 12 var missingCount = 013 for (key, value) in strings {14 guard let entry = value as? [String: Any],15 let localizations = entry["localizations"] as? [String: Any] else {16 print("EKSIK: \(key) — hiç çeviri yok")17 missingCount += 118 continue19 }20 21 if localizations[language] == nil {22 print("EKSIK [\(language)]: \(key)")23 missingCount += 124 }25 }26 print("Toplam eksik: \(missingCount) string")27}11. Best Practices Checklist
- String Catalogs kullan (Xcode 15+)
- Hardcoded string YASAK — tümü localize
- Pluralization tüm diller için doğru
- Date/Number/Currency → Formatter kullan
- RTL layout test edilmiş
- Pseudo-localization ile layout test
- Minimum 10 dil desteği hedefle
- Professional çeviri (makine çevirisi → human review)
- App Store metadata her dil için optimize
- Accessibility label'ları localize
ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.
Okuyucu Ödülü
Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var:

