↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 12 — Konu 61: Yerelleştirme (Localization / i18n)

4 dk okuma #flutter
Dizi · 61/61 Flutter Türkçe Tutorial
  1. Bölüm 1 — Konu 1: Flutter Nedir, Mimarisi (Widget → Element → RenderObject, Skia/Impeller)
  2. Bölüm 1 — Konu 2: Ortam Kurulumu (Flutter SDK, Android Studio/VS Code, Emulator/Simulator, DevTools'a İlk Bakış)
  3. Bölüm 1 — Konu 3: İlk Proje Yapısı (`pubspec.yaml`, `lib/`, Klasör Mimarisi, `flutter create` Anatomisi)
  4. Bölüm 1 — Konu 4: Hot Reload / Hot Restart Ne Yapıyor, Neden Önemli
  5. Bölüm 2 — Konu 5: "Her Şey Widget'tır" Felsefesi, Widget Ağacı
  6. Bölüm 2 — Konu 6: StatelessWidget vs StatefulWidget + `setState()` Derinlemesine (Rebuild Mekanizması)
  7. Bölüm 2 — Konu 7: Temel Layout — Container, Row, Column, Stack, Padding, Align, Center
  8. Bölüm 2 — Konu 8: Constraint Sistemi — "Constraints Go Down, Sizes Go Up, Parent Sets Position"
  9. Bölüm 2 — Konu 9: `Expanded`, `Flexible`, `Spacer`, Intrinsic Widget'lar
  10. Bölüm 3 — Konu 10: MaterialApp/CupertinoApp, Scaffold, AppBar
  11. Bölüm 3 — Konu 11: `ListView`, `GridView`, `SingleChildScrollView` (+ Builder Pattern)
  12. Bölüm 3 — Konu 12: Text, TextStyle, Icon, Image, Asset Yönetimi
  13. Bölüm 3 — Konu 13: Custom Widget Yazma Prensipleri (Composition Over Inheritance)
  14. Bölüm 3 — Konu 14: Navigator 1.0 — Push/Pop, Named Routes
  15. Bölüm 4 — Konu 15: Form, TextField/TextFormField, GlobalKey<FormState>, Validasyon
  16. Bölüm 4 — Konu 16: Theme Sistemi (ThemeData, ColorScheme)
  17. Bölüm 4 — Konu 17: Responsive & Adaptive Tasarım (MediaQuery, LayoutBuilder, OrientationBuilder, Breakpoint Stratejileri)
  18. Bölüm 5 — Konu 18: Future/Async-Await Flutter Bağlamında, FutureBuilder
  19. Bölüm 5 — Konu 19: `Stream`, `StreamBuilder`
  20. Bölüm 5 — Konu 20: HTTP İstekleri (`http` / `dio` Paketleri)
  21. Bölüm 5 — Konu 21: JSON Serialization (Manuel → `json_serializable`/`freezed`)
  22. Bölüm 5 — Konu 22: Local Storage — `shared_preferences` → `Hive` → `sqflite`/Drift
  23. Bölüm 6 — Konu 23: InheritedWidget ve InheritedModel — "Neden setState Yetmiyor" Sorusunun Cevabı
  24. Bölüm 6 — Konu 24: Provider Paketi
  25. Bölüm 6 — Konu 25: Riverpod (Modern Yaklaşım, Provider'ın Halefi)
  26. Bölüm 6 — Konu 26: BLoC/Cubit Pattern (`flutter_bloc`)
  27. Bölüm 6 — Konu 27: GetX (Tartışmalı Ama Yaygın)
  28. Bölüm 6 — Konu 28: Karşılaştırma — Hangi Projede Hangisi?
  29. Bölüm 7 — Konu 29: Navigator 2.0 (Router, RouteInformationParser, RouterDelegate)
  30. Bölüm 7 — Konu 30: `go_router` Paketi (Pratik ve Modern Çözüm)
  31. Bölüm 7 — Konu 31: Deep Linking
  32. Bölüm 7 — Konu 32: Repository Pattern, Katmanlı Mimari (Data/Domain/Presentation)
  33. Bölüm 7 — Konu 33: Dependency Injection (`get_it`, `injectable`)
  34. Bölüm 7 — Konu 34: Clean Architecture Uyarlaması, SOLID Prensipleri
  35. Bölüm 8 — Konu 35: Implicit Animasyonlar
  36. Bölüm 8 — Konu 36: Explicit Animasyonlar (`AnimationController`, `Tween`, `Curve`)
  37. Bölüm 8 — Konu 37: Hero Animasyonları
  38. Bölüm 8 — Konu 38: `CustomPainter` / `Canvas`
  39. Bölüm 8 — Konu 39: Rive / Lottie Entegrasyonu
  40. Bölüm 9 — Konu 40: Platform Channels
  41. Bölüm 9 — Konu 41: Permission Yönetimi (`permission_handler`)
  42. Bölüm 9 — Konu 42: Kamera, Konum, Sensörler
  43. Bölüm 9 — Konu 43: Push Notification (Firebase Cloud Messaging)
  44. Bölüm 9 — Konu 44: Android/iOS Build Sistemleri
  45. Bölüm 10 — Konu 45: Test Yazımı (Unit, Widget, Integration, Golden)
  46. Bölüm 10 — Konu 46: CI/CD
  47. Bölüm 10 — Konu 47: Rebuild Optimizasyonu (`const`, `key` Kullanımı)
  48. Bölüm 10 — Konu 48: DevTools Profiling
  49. Bölüm 10 — Konu 49: Lazy Loading, Pagination, Büyük Liste Performansı
  50. Bölüm 11 — Konu 50: Firebase Ekosistemi (Auth, Firestore, Storage, Functions)
  51. Bölüm 11 — Konu 51: Supabase Alternatifi
  52. Bölüm 11 — Konu 52: GraphQL (Opsiyonel)
  53. Bölüm 11 — Konu 53: Store Yayınlama Süreci (İmzalama, Listing, Versiyonlama)
  54. Bölüm 11 — Konu 54: App Size, Obfuscation, Flavor Yönetimi
  55. Bölüm 12 — Konu 55: Flutter Web / Desktop
  56. Bölüm 12 — Konu 56: Custom `RenderObject` Yazımı
  57. Bölüm 12 — Konu 57: Engine Mimarisi Derinlemesine (Impeller vs Skia)
  58. Bölüm 12 — Konu 58: Plugin Geliştirme, pub.dev'e Paket Yayınlama
  59. Bölüm 12 — Konu 59: Monorepo Mimarisi (Melos)
  60. Bölüm 12 — Konu 60: Erişilebilirlik (Accessibility) Derinlemesine
  61. Bölüm 12 — Konu 61: Yerelleştirme (Localization / i18n)
Dizinin sayfası →
İçindekiler 12 başlık
  1. İki Terim: i18n ve l10n
  2. Flutter'ın Yerelleşme Altyapısı — flutter_localizations + intl
  3. .arb Dosyaları — Çevirilerin Kaynağı
  4. Kod Üretimi ve Kullanım
  5. Dil Değiştirme — locale Parametresi
  6. Tarih, Sayı ve Para Birimi Formatlama
  7. RTL (Sağdan Sola) Diller
  8. Test Etme
  9. Pratik Bir Uyarı: Ne Zaman Gerekli?
  10. 🎯 Bu Dersten Çıkarılması Gerekenler
  11. 📝 Ödevler
  12. 🎓 Tebrikler — Flutter Tutorial'ı Tamamladın!

Bu tutorial'ın son konusuna hoş geldin. Bir uygulamayı tek bir dilde yazmak kolaydır — ama gerçek dünyada, kullanıcıların farklı dillerde, farklı tarih/sayı formatlarında konuştuğu bir dünyada yaşıyoruz (sen bile, Türkiye'den Azerbaycan'a taşındıktan sonra, muhtemelen birden fazla dilde arayüzlerle karşılaşıyorsundur). Bu ders, Flutter'ın resmi yerelleştirme altyapısını — intl paketi ve .arb dosyalarını — baştan sona işliyor.

İki Terim: i18n ve l10n

  • i18n (internationalization / uluslararasılaştırma): Uygulamanı, birden fazla dili destekleyecek şekilde hazırlama süreci — metinleri koddan ayırma, tarih/sayı formatlarını esnek hale getirme.
  • l10n (localization / yerelleştirme): Belirli bir dile/bölgeye özgü içeriği (çeviriler, tarih formatları) gerçekten ekleme süreci.

(Sayılar, kelimelerin ilk ve son harfleri arasındaki harf sayısını temsil eder — "internationalization" = i + 18 harf + n, "localization" = l + 10 harf + n. Sektörde çok yaygın bir kısaltma.)

Flutter'ın Yerelleşme Altyapısı — flutter_localizations + intl

yaml
# pubspec.yaml
dependencies:
  flutter_localizations:
    sdk: flutter
  intl: ^0.19.0

flutter:
  generate: true   # .arb dosyalarından OTOMATİK Dart kodu üretimini AÇAR

generate: true, Bölüm 6'da Riverpod'un @riverpod code generation'ını (Konu 25'te "Bir Adım İleri" bölümünde değinmiştik) ya da Bölüm 8'de gördüğümüz code generation mantığına benzer bir fikir — sen veri (çeviri metinleri) yazarsın, Flutter senin için tip-güvenli Dart kodu üretir.

.arb Dosyaları — Çevirilerin Kaynağı

ARB (Application Resource Bundle), JSON tabanlı bir format — her dil için ayrı bir dosya:

json
// lib/l10n/app_tr.arb
{
  "@@locale": "tr",
  "merhaba": "Merhaba, {isim}!",
  "@merhaba": {
    "description": "Kullanıcıyı isimle karşılayan selamlama",
    "placeholders": {
      "isim": { "type": "String" }
    }
  },
  "sepettekiUrunSayisi": "{sayi, plural, =0{Sepetiniz boş} =1{Sepetinizde 1 ürün var} other{Sepetinizde {sayi} ürün var}}"
}
json
// lib/l10n/app_en.arb
{
  "@@locale": "en",
  "merhaba": "Hello, {isim}!",
  "sepettekiUrunSayisi": "{sayi, plural, =0{Your cart is empty} =1{You have 1 item} other{You have {sayi} items}}"
}

plural sözdizimi özellikle önemli — Bölüm 2'de Dart'ın kontrol yapılarını öğrendiğimizde, bir sayının "tekil/çoğul" olmasına göre farklı metin göstermek için if/else yazardık; ama diller arası çoğul kuralları çok farklıdır (Türkçe'de "0 ürün" ile "5 ürün" aynı biçimde çekimlenir, İngilizce'de "0 items" ile "1 item" farklı çekimlenir — bazı dillerde, örneğin Arapça'da, 6 farklı çoğul kategorisi vardır). intl'in plural sözdizimi, bu dil-özel kuralları senin yerine yönetir — sen sadece =0, =1, other durumlarını doldurursun, hangi dilde hangi kuralın uygulanacağını paket halleder.

Kod Üretimi ve Kullanım

bash
flutter gen-l10n   # .arb dosyalarından lib/l10n/app_localizations.dart üretir (elle DÜZENLENMEZ)
dart
// main.dart
MaterialApp(
  localizationsDelegates: AppLocalizations.localizationsDelegates,
  supportedLocales: AppLocalizations.supportedLocales,
  // ...
)
dart
// herhangi bir widget içinde:
Text(AppLocalizations.of(context)!.merhaba('Ayşe'))
// çıktı: "Merhaba, Ayşe!" (tr) ya da "Hello, Ayşe!" (en) — cihazın DİL AYARINA göre OTOMATİK

Text(AppLocalizations.of(context)!.sepettekiUrunSayisi(3))
// çıktı: "Sepetinizde 3 ürün var" (tr) ya da "You have 3 items" (en)

AppLocalizations.of(context), Bölüm 6 Konu 23'te öğrendiğimiz InheritedWidget deseninin tam bir uygulamasıdır — Theme.of(context), MediaQuery.of(context) ile birebir aynı mekanizma: widget ağacında yukarı doğru en yakın Localizations widget'ını bulur ve o context'e uygun dildeki çevirileri döndürür.

Dil Değiştirme — locale Parametresi

dart
// Cihazın SİSTEM DİLİNİ otomatik kullanmak istersen (varsayılan davranış):
MaterialApp(
  supportedLocales: [Locale('tr'), Locale('en'), Locale('az')],  // desteklenen diller
  // locale belirtilmezse, sistem dili kullanılır (destekleniyorsa)
)

// Kullanıcının UYGULAMA İÇİNDEN dil seçebilmesini istersen:
MaterialApp(
  locale: seciliDil,   // Bölüm 6'da öğrendiğimiz state management ile YÖNETİLEN bir değişken
  supportedLocales: [Locale('tr'), Locale('en'), Locale('az')],
)

seciliDil, tipik olarak Bölüm 6'da öğrendiğimiz Riverpod/Provider ile global state olarak tutulur — kullanıcı bir dil seçtiğinde, bu state değişir, MaterialApp yeniden build olur (Bölüm 3'teki setState/rebuild mantığının aynısı), ve tüm AppLocalizations.of(context) çağrıları otomatik olarak yeni dile göre günceller.

Tarih, Sayı ve Para Birimi Formatlama

Çeviri metinlerinin ötesinde, sayıların kendisi de dile göre değişir:

dart
import 'package:intl/intl.dart';

// Tarih:
DateFormat.yMMMMd('tr').format(DateTime.now());   // "29 Eylül 2026"
DateFormat.yMMMMd('en').format(DateTime.now());   // "September 29, 2026"

// Sayı (ondalık ayracı diller arası FARKLIDIR!):
NumberFormat.decimalPattern('tr').format(1234.56);  // "1.234,56"  (Türkçe: nokta=binlik, virgül=ondalık)
NumberFormat.decimalPattern('en').format(1234.56);  // "1,234.56"  (İngilizce: TERSİ)

// Para birimi:
NumberFormat.currency(locale: 'tr', symbol: '₺').format(1234.56);  // "₺1.234,56"
NumberFormat.currency(locale: 'az', symbol: '₼').format(1234.56);  // Azerbaycan manatı

Bu, i18n'in en sık gözden kaçan tuzağıdır: Bir geliştirici, sayıyı elle '${fiyat.toStringAsFixed(2)}' gibi formatlarsa, her dilde aynı (yanlış) formatı gösterir — NumberFormat/DateFormat kullanmak, Bölüm 2'de öğrendiğimiz "string interpolasyonunu doğru kullanma" disiplininin, yerelleştirme bağlamındaki kritik uzantısıdır.

RTL (Sağdan Sola) Diller

Arapça, İbranice gibi diller sağdan sola yazılır — Flutter, bunu büyük ölçüde otomatik yönetir:

dart
// Row/Column yerine, YÖN-BAĞIMSIZ eşdeğerlerini kullanmak RTL desteğini kolaylaştırır:
Padding(
  padding: EdgeInsetsDirectional.only(start: 16),  // 'left' YERİNE — RTL'de OTOMATİK ters döner
  child: ...,
)

Row(
  textDirection: TextDirection.rtl,  // genelde ELLE ayarlamana gerek YOK, Localizations bunu sistem dilinden anlar
)

EdgeInsetsDirectional (start/end) ile EdgeInsets (left/right) arasındaki fark önemli — start, LTR dillerde "sol", RTL dillerde "sağ" anlamına gelir ve otomatik döner; left her zaman fiziksel olarak sol demektir. Baştan Directional varyantlarını kullanmak, sonradan RTL desteği eklemeyi çok kolaylaştırır — tıpkı Bölüm 4'te öğrendiğimiz responsive tasarımın, baştan esnek yazıldığında sonradan kolayca uyarlanabilmesi gibi.

Test Etme

dart
testWidgets('Türkçe çeviri doğru gösteriliyor', (tester) async {
  await tester.pumpWidget(
    MaterialApp(
      locale: Locale('tr'),
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      home: KarsilamaEkrani(),
    ),
  );

  expect(find.text('Merhaba, Ayşe!'), findsOneWidget);  // Bölüm 10 Konu 45'teki widget test mantığı
});

Bu, Bölüm 10 Konu 45'te öğrendiğimiz widget testlerinin, her desteklenen dil için tekrarlanabilir bir versiyonu — özellikle plural kurallarının (0/1/çoğul) doğru çalıştığını doğrulamak için değerlidir.

Pratik Bir Uyarı: Ne Zaman Gerekli?

Bu tutorial boyunca yazdığın öğrenme amaçlı projelerde tek dil (Türkçe) yeterliydi — ama gerçek bir yayınlanan uygulamada (Bölüm 11'i hatırla, Play Store/App Store'a yayınlama), hedef pazarının dillerini düşünmek, kullanıcı tabanını doğrudan büyütür. Azerbaycan'da yaşadığını düşünürsek, örneğin Türkçe + Azerbaycanca + İngilizce desteği, gerçek bir uygulamada düşük maliyetli, yüksek getirili bir yatırımdır — özellikle .arb dosyaları, çeviri metinlerini tek bir yerde toplayarak, bir çevirmene (senin kendi Dart/Flutter bilgin olmadan) kolayca gönderilebilir hale getirir.


🎯 Bu Dersten Çıkarılması Gerekenler

  • i18n (uluslararasılaştırma) = koda hazırlık; l10n (yerelleştirme) = belirli bir dile içerik ekleme.
  • .arb dosyaları, dile göre ayrılmış çeviri kaynaklarıdır; flutter gen-l10n, bunlardan tip-güvenli Dart kodu üretir.
  • plural sözdizimi, dil-özel çoğul kurallarını (Türkçe/İngilizce/Arapça farklı davranır) senin yerine yönetir.
  • AppLocalizations.of(context), Bölüm 6'daki InheritedWidget mekanizmasının (Theme.of, MediaQuery.of ile aynı desen) birebir bir uygulamasıdır.
  • NumberFormat/DateFormat (intl paketi), sayı/tarih/para birimi formatlarını dile göre doğru gösterir — elle string formatlamak, farklı dillerde yanlış sonuç üretir.
  • EdgeInsetsDirectional (start/end), RTL dilleri (Arapça, İbranice) desteklemek için EdgeInsets (left/right)'a tercih edilmelidir.
  • Gerçek bir yayınlanan uygulamada, hedef pazarın dillerini desteklemek düşük maliyetli, yüksek getirili bir yatırımdır.

📝 Ödevler

  • [ ] Bu tutorial boyunca yazdığın bir uygulamaya flutter_localizations/intl ekle, en az iki dil (tr, en) için .arb dosyaları oluştur.
  • [ ] En az bir plural içeren çeviri anahtarı yaz (örn. "X ürün var") ve 0/1/çoklu durumlarda doğru çalıştığını gözlemle.
  • [ ] Uygulamana, kullanıcının dil seçebileceği bir ayarlar ekranı ekle (Bölüm 6'daki state management ile locale değerini yönet).
  • [ ] NumberFormat.currency ile bir fiyatı hem tr hem en locale'de formatlayıp farkı gözlemle.
  • [ ] Kendi cümlelerinle, "AppLocalizations.of(context)'in neden Theme.of(context) ile aynı mekanizmayı kullandığını" açıkla.

🎓 Tebrikler — Flutter Tutorial'ı Tamamladın!

Bölüm 1'de MaterialApp/Scaffold ile başladığın bu yolculuk, 61 konu ve 12 bölüm sonunda, Custom RenderObject'lere, engine mimarisine, federated plugin'lere ve monorepo yönetimine kadar uzandı. Dart'taki 66 konuyla birlikte düşünürsen, artık dilin temellerinden, framework'ün iç mekanizmalarına, üretim/yayınlama süreçlerine kadar uçtan uca bir haritan var.

Bundan sonrası, gerçek projeler inşa ederek pekiştirme zamanı — istersen, öğrendiklerini birleştiren kapsamlı bir bitirme projesi (state management + backend + testler + CI/CD + yayınlama adımlarının hepsini bir araya getiren) için birlikte bir yol haritası çıkarabiliriz, ya da belirli bir konuyu (örneğin bir konuyu daha derinlemesine, ya da güncellik taraması yaptığımız gibi tüm Flutter derslerini bir kez daha tarayabiliriz) tekrar ele alabiliriz.