Flutter'da Çoklu Dil Desteği: Internationalization ve Localization
Flutter'da resmi lokalizasyon çözümü flutter_localizations paketi ve intl kütüphanesidir. ARB (Application Resource Bundle) dosyalarına metinler yazılır, build_runner ile Dart kodu üretilir ve widget'larda AppLocalizations.of(context) ile erişilir.
Kurulum ve Yapılandırma
// pubspec.yaml
// dependencies:
// flutter_localizations:
// sdk: flutter
// intl: ^0.19.0
// flutter:
// generate: true # bu satır zorunlu!
// lib/l10n/l10n.yaml
// arb-dir: lib/l10n
// template-arb-file: app_tr.arb
// output-localization-file: app_localizations.dart
// MaterialApp yapılandırması
MaterialApp(
locale: const Locale('tr'),
supportedLocales: const [Locale('tr'), Locale('en')],
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
// Sistem diline göre otomatik geçiş
localeResolutionCallback: (locale, supportedLocales) {
for (final supported in supportedLocales) {
if (supported.languageCode == locale?.languageCode) return supported;
}
return supportedLocales.first;
},
home: const HomePage(),
)
ARB Dosyaları
// lib/l10n/app_tr.arb
{
"@@locale": "tr",
"appTitle": "Alışveriş Uygulaması",
"@appTitle": { "description": "Uygulama başlığı" },
"greeting": "Merhaba, {name}!",
"@greeting": {
"description": "Kullanıcı selamlama mesajı",
"placeholders": {
"name": { "type": "String", "example": "Ahmet" }
}
},
"itemCount": "{count, plural, =0{Ürün yok} =1{1 ürün} other{{count} ürün}}",
"@itemCount": {
"description": "Sepetteki ürün sayısı",
"placeholders": {
"count": { "type": "int" }
}
},
"orderStatus": "{status, select, pending{Bekliyor} shipped{Kargoda} delivered{Teslim Edildi} other{Bilinmiyor}}",
"@orderStatus": {
"placeholders": {
"status": { "type": "String" }
}
},
"lastUpdated": "Son güncelleme: {date}",
"@lastUpdated": {
"placeholders": {
"date": {
"type": "DateTime",
"format": "d MMMM yyyy"
}
}
},
"price": "Fiyat: {amount}",
"@price": {
"placeholders": {
"amount": {
"type": "double",
"format": "currency",
"optionalParameters": {
"symbol": "₺",
"decimalDigits": 2
}
}
}
}
}
// lib/l10n/app_en.arb
{
"@@locale": "en",
"appTitle": "Shopping App",
"greeting": "Hello, {name}!",
"itemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}",
"orderStatus": "{status, select, pending{Pending} shipped{Shipped} delivered{Delivered} other{Unknown}}",
"lastUpdated": "Last updated: {date}",
"price": "Price: {amount}"
}
Kod Üretimi ve Kullanım
// Kod üret:
// flutter gen-l10n
// veya
// dart run build_runner build
// Kullanım
class ProductDetailPage extends StatelessWidget {
final Product product;
const ProductDetailPage({super.key, required this.product});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(title: Text(l10n.appTitle)),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(l10n.greeting('Uğur')), // Merhaba, Uğur!
Text(l10n.price(product.price)), // Fiyat: ₺299,90
Text(l10n.itemCount(cart.count)), // 3 ürün
Text(l10n.lastUpdated(product.updatedAt)), // 15 Ocak 2026
Text(l10n.orderStatus(order.status)), // Kargoda
],
),
),
);
}
}
Dinamik Dil Değiştirme
// Cubit ile dil yönetimi
class LocaleCubit extends Cubit<Locale> {
static const _key = 'app_locale';
final SharedPreferences _prefs;
LocaleCubit(this._prefs)
: super(Locale(_prefs.getString(_key) ?? 'tr'));
Future<void> changeLocale(String languageCode) async {
await _prefs.setString(_key, languageCode);
emit(Locale(languageCode));
}
}
// MaterialApp'ta dinle
BlocBuilder<LocaleCubit, Locale>(
builder: (context, locale) => MaterialApp(
locale: locale,
supportedLocales: const [Locale('tr'), Locale('en')],
localizationsDelegates: AppLocalizations.localizationsDelegates,
home: const HomePage(),
),
)
// Dil değiştirme butonu
DropdownButton<String>(
value: context.watch<LocaleCubit>().state.languageCode,
items: const [
DropdownMenuItem(value: 'tr', child: Text('🇹🇷 Türkçe')),
DropdownMenuItem(value: 'en', child: Text('🇬🇧 English')),
],
onChanged: (code) {
if (code != null) context.read<LocaleCubit>().changeLocale(code);
},
)
intl ile İleri Biçimlendirme
import 'package:intl/intl.dart';
class FormatHelper {
static String currency(double amount, String locale) =>
NumberFormat.currency(locale: locale, symbol: locale == 'tr' ? '₺' : '\$')
.format(amount);
static String date(DateTime dt, String locale) =>
DateFormat.yMMMMd(locale).format(dt);
static String relativeTime(DateTime dt) {
final diff = DateTime.now().difference(dt);
if (diff.inMinutes < 60) return '${diff.inMinutes} dakika önce';
if (diff.inHours < 24) return '${diff.inHours} saat önce';
return '${diff.inDays} gün önce';
}
}
// Kullanım
Text(FormatHelper.currency(product.price, 'tr')); // ₺1.299,90
Text(FormatHelper.date(order.createdAt, 'tr')); // 15 Ocak 2026
Text(FormatHelper.relativeTime(message.sentAt)); // 5 dakika önce
ARB dosyalarını her özellikte güncel tutun ve çevirmenlerinize yorum (description) alanlarını doldurun. Çoğul ve cinsiyet gibi karmaşık dil kuralları için ICU mesaj formatını doğru kullanmak, makine çevirisi kalitesini önemli ölçüde artırır.