Flutter Entegrasyonlar Serisi : 1 -Localization

Önce localization’dan ne anllıyoruz. Localization’ türkçe anlamına baktığımızda yerelleştirme olduğunu görüyoruz.  googleda bir arama yaptığımda Burak Selim Şenyurt’un yazmış olduğu https://www.buraksenyurt.com/post/Localization-(Yerellestirme)-1-bsenyurt-com-dan adlı bir makale karşıma çıktı bu makalenenin ilk paragrafı oldukça açıklayıcı. Kısacası her ülkenin konuştuğu dil vss farklı yani bir yerelsellik söz konusu. Her ülkenin hatta her bölgenin dilleri lehçeleri örneğin çince geleneksel çince, basit çince gibi farklılıklar gösterbiliyor. Yani uygulamanın yerele göre tasarlanması ihtiyacı doğuyor.Bu yerelleştirmeyi olabileçek her yerele/dile göre gerçekleştirdiğimizde uygulama altık global hale tüm dünyanın her yereline(ülkeye, dile..vs)  uygun hale geliyor. Buna globalizasyon deniyor. Kısacası amerikadan giren biri uygulamayı ingilizce kullanırken Türkiye deki türkçe olarak kullanabiliyor. Uygulamayı yerel bir dile uyarlayarak localization yapmış yerelleştirmiş oluyoruz, bir den çok dile uyarlayarak globalization/ulustararası hale getirmiş oluyoruz.

Daha derine girmeden önce localizasyon işlemleri için gerekli flutter paketini yaml dosyamıza geçelim. ayrıca materyalapp içerisinde localizationsDelegates e GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate i ekleyelim.
localizationsDelegates: [

  GlobalMaterialLocalizations.delegate,
  GlobalWidgetsLocalizations.delegate,
],

name: localizationornek
description: A new Flutter project.

 
version: 1.0.0+1

environment:
  sdk: ">=2.1.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
#flutter_localizations  paketimizi dahil ettik
  flutter_localizations:
    sdk: flutter
 
  
  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2

dev_dependencies:
  flutter_test:
    sdk: flutter
 
flutter:


  uses-material-design: true 

Gelelim flutterda localization işlemine.  flutter localizasyon işlerini materialapp sınıfı yoluyla gerçekleştiriyor. MaterialApp locale(local/yerel), localizationsDelegates(yerelleri temsil edenler), supportedLocale(uygulamanın desteklediği diller) parametrelerini kullanarak uygulamanın başlangıç diline karar veriyor ve yönetiyor.  locale türü bir Locale türü. Locale sınıfıyla tanımlanıyor.Locale(‘dil kodu’, ‘Ülke Kodu’). Örneğin türkçe local, Locale(‘tr’,’TR’). locali temsil eden şey Locale sınıfının nesnesi yani amaç materialapp içerindeki locale hangi dilin atanacağına karar vermek. Bir flutter uygulaması başlatıldığında materialapp içerisindeki locale parametresine bir değer atanmışmı önce ona bakıyor. Materapp in locale parametresine bir değer atanmışsa supportedLocales/uygulamanın destekleği dillere bakıyor eğer locale atanmış dil desteklenen dillerde varsa onu kullanıyor. locale bir değer atanmamışsa sisteme soruyor cihazın işletim sisteminden/android yada ios tan gelen dili  yani sistemin dilini alıyor ve  desteklenen dillerde(supported locale) bu dil varmı bakıyor ve varsa dili uygulamanın dili olarak ayarlıyor, yani materialapp ın locale paremetresine geçiyor eğer yoksa flutter ın varsayılan dili olan ingilizceyi uygulama dili olarak geçiyor.

şöyle bir senaryo düşünelim;

Biz bir çinliyiz ve çince android yada ios telefonumuz var.

telefonumuzda yüklü herhangi bir flutter uygulamasına tıkladık.

uygulama açılıyor ve materialappın locale : parametresine bakıyor bir değer atanmış mı?

bir değer atanmamış o halde sisteme hangi dili kullandığını soruyor . sistem telefonun dil ayarının çince olduğunu bildiriyor.

daha sonra flutter materyalapp içerisinde suppotedlocale: parametresinde uygulamanın hangi dilleri desteklediğini soruyor . supportedLocales te çince destek yok. 

flutter telefonun çince kullanılmış olduğunu ama uygulamanın çinceyi desteklemediğini öğrendiği için flutterın varsayılan dili olan ingiliceyi uygulamanın varsayılan dili olarak kabul ediyor.

 
MaterialApp(
         locale: //Başlangıç ​​yerel ayarı.Eğer 'yerel' boşsa o zaman sistemin yerel dili kullanılır.
                           localizationsDelegates: [
   // bizim localization nesnemizi temsil eden temisilci/delegete sınıfımız 
GlobalMaterialLocalizations.delegate,
   GlobalWidgetsLocalizations.delegate,
   GlobalCupertinoLocalizations.delegate,
 ],
//uygulamanın desteklediği diller
 supportedLocales: [
    const Locale('en'), // English
    const Locale('tr'), //türkçe 
  ],
.....
)
  

Buraya kadar ne biliyoruz:

flutter yerelleştirme işlerini materyalapp yürütüyor.

materialapp içerisinde locale  parametresi ilk bakılan yer,

eğer uygulamaya bir yerel destek ekleyeceksek yani bir dil ekleyeceksek, mutlaka supportedLocale içerisine temsil ettiği dili Locale(‘dilkodu’,”) türünden eklememiz gerekecek.,

peki ya localizationsDelegates te neyin nesi?

Delege : bir şeyi temsil eden şey

localizationsDelegates uygulamanın tüm yerellerini temsil edenlerin bir listesi.

bunu bir tarafa not edip konumuza geri dönelim. biz uygulamamızın çok dilli olmasını istiyoruz bir dil ekleyelim.

Materialapp içerisinde locale’e arapça localini atayalım ve desteklediği dillere arapça ekleyelim.

şimdi uygulama açıldığında varsayılan dil arapça oldu. Flutter uygulamanın arapça olduğunu varsayıyor. Uygulamadaki bir tetxtfield e tıklayıp yazmaya başladığımızda sağdan sola doğru yazmaya başladığını göreceğiz.

 Bu şekilde sadece tek bir dile/yerele sahip uygulamamız oldu. Sadece arapça kullanıcılarına hitap ediyor bunu globalleştirmemiz gerekecek.

 

 
MaterialApp(
         locale: Locale('ar',''), //uygulamanın varsayılan dili arapça                          localizationsDelegates: [
   // bu kısma daha sonra geleceğiz
GlobalMaterialLocalizations.delegate,
   GlobalWidgetsLocalizations.delegate,
   GlobalCupertinoLocalizations.delegate,
 ],
//uygulamanın desteklediği dillere arapça ekliyoruz
 supportedLocales: [
    const Locale('tr'), //arapça
  ],
.....
)
  

Önce ideal bir senaryo hayal edelim:

bir localization/yerelleştirme sınıfımız olsun.

Localization sınıfımızın bir cevir metodu olsun o anda uygulamamız handi dilde ise  o dilde çevrilmiş metinleri getirsin.

(örneğin uygulama ingilizce kullanılıyor ise butonumuz

RaisedButton(child: Text(“Ok”),onpressd:..)

//Türkçe ise

RaisedButton(child: Text(“Tamam”),onpressd:..)

şunu gibi..

 
//ingilizce 
RaisedButton(child: Text(localization.cevir('tamambutonu'),onpressd:..)
//artık uygulama türkçe ise Text içerisinnde Tamam yazacak,  ingilizce ise Ok yazacak. 

tabii ki bu kadar basit değil ama böyle bişey istiyoruz.

Ayrıca uygulamamızın bir sürü bölümleri var: hakkında kısmı, faklı test içerene butonlar,nasıl kullanılır metinler vss yani bir sürü çevrilmesi gereken metin var. bunları bir map formatında ve ayrı dosyalarda saklayabiliriz.

şöye olabilir,


// assets/localization/en.json
Map<String, String> en={'tamam': 'OK',
'iptal': 'Cancel'}



//diğer dosya 
// assets/localization/tr.json
tr={'tamam': 'Tamam',
'iptal': 'İptal'}
 

şimdi materialapp içerisinde supportedLocales e en ve tr yide ekleyeceğiz. ayrıca uygulamanın herhangi biryerinden çevirmek istediğimiz metni anahar olarak verip çevrilmiş halini alabileceğiz.

Artık ihtiyaçlarımızın bir listesini yapma vakti:

Gerekli olanlar:

 

1.metinler için LangKey sınıfı : çevrilecek metinleri getirmek için kullanacağımız anahtarlar(map te ki değerler ulaşmak için) Bunlar oldukça uzun olabilir, ayrı bir sınıfta tutmak daha derli toplu ve ulaşılabilir olabilir. 

2.desteklediğimiz dilleri tutacağımız LangSupported sınıfımız. Destekledğimiz diller listesi de oldukça uzun olabilir ayrı bir sınıfta tutmakta fayda var.

3.ve nihayet olmazsa olmaz Localizaton sınıfımız.işleri bu sınıfla yapacağız.

 

 
//LangKey sınıfı, keyleri içinde tutan abstract sınıf


//çevrilecek anahtar/key metinlerini her yerden ulaşılır olamasını ve değiştilmemesini istiyoruz.static const
//ayrıca sınıfımızın kurulmasına ihtiyaç duymadığımız için soyut sanal bir abstract sınıfı olarak tanımladık

abstract class LangKey {
  static const button_geri = "button_geri";
  static const button_iptal = "button_iptal";
}
...
  
 

//LangSupported sınıfımız, uygulamanın desteklediği dilleri ve bunların farklı küçük bir kaç sunumunu içerir. 

 

abstract class LangSupported {

//desteklenen dillerin dil kodlarının listesini verir: ['en','tr'..] gibi 
  static List<String> get languageCodeList => languagesMap.entries.map((e) => e.key).toList(growable: false);

//desteklenen dillerin locale türünden listesini verir: [Locale('en',''),Locale('tr',''),..] gibi  materialapp ta supportedLocales için ..
  static List<Locale> get locales => languagesMap.entries.map((e) => Locale(e.key, '')).toList(growable: false);
  // static List<String> get languageNamesList =>languagesMap.entries.map((e) => e.value).toList(growable: false);
 

  static final Map<String, String> languagesMap = <String, String>{
    "en": "English",
    "tr": "Türkçe",
     "ar": "Arapça"
  };


}
  

Şimdi sıra Localization sınıfımıza geldi fakat;

yukarıda flutter uygulaması ilk açıldığında locali nasıl belirlediğini hayal etmeye çalıştığımız senaryoya dönersek,

 Bir flutter uygulaması başlatıldığında materialapp içerisindeki locale parametresine bir değer atanmışmı önce ona bakıyor.

 Materapp in locale özelliğinebir değer atanmışsa supportedLocales/uygulamanın destekleği dillere bakıyor eğer locale atanmış dil desteklenen dillerde varsa onu kullanıyor. 

nasıl kullanıyor ?

işte burda bizim Localization nesnemizi temsil eden delegate miz/ temsilci sınıfımız yoluyla.

 
localizationsDelegates: [
BizimLocalizationSınıfımızıTemsiledenDelegateSınıfı(),
              GlobalMaterialLocalizations.delegate,
              GlobalWidgetsLocalizations.delegate,
            ],

  

Senaryoya devam edersek.  

Materapp in locale özelliğine bir değer atanmışsa supportedLocales/uygulamanın destekleği dillere bakıyor eğer locale atanmış dil desteklenen dillerde varsa  onu kullanıyor.

Biz de uygulama içerinden desteklediğimiz dillerin çevrilmiş metinlerine ihtiyacımız olduğunda localization sınıfımıza erişip kuallanabilecek duruma gelmiş oluyoruz.  Burada kendi localization sınıfımızı bir yerden başlatmamız gerekiyor.(sınıfımızın bir örneğini yani nesnesini elde etmemiz gerekiyor) Bu işi flutter localizationsDelegates içerinde kendi sağlıyor.Bunun için kendi localization sınıfımızı temsil edecek bir Mylocalizationtemsilci/ MyLocalizationDelegate sınıfı tanımlamamız ve localizationsDelegates listesine nesne olarak eklememiz yeterli.

Flutter buradaki delegates/temsilci neslerini alıyor ve onların load(Locale locale) metodunu çalıştırıp temsil ettikleri sınıfın nesnesini(yani MyLocalization nesnesini) oluşturup bunları bir Type : nesne  olacak şekilde bir map e aktarıyor görünüyor. final Map<Type, dynamic> output = <Type, dynamic>{}; . Peki uyguma içerinden kendi Localization nesnemize nasıl ulaşacağız.

Örneğin kendi tanımladığımız localization sınıfımız MyLocalization olsun,

Şunu gibi bir map olmalı;

key bir Type, değeri bu tipteki nesne

final Map<Type, dynamic> tumlocalizasyonlar={MyLocalization :MyLocalization()};  

o halde uygulamanın  bir yerinden bu map e ulaşıp bir Tpye verirsek bize kendi MyLocalization nesnemizi verecek.

Flutterda tanımlanmış olan Localization sınıfının static of metodu bunu bize sağlıyor. 

Localizations.of<MyLocalization >(context, MyLocalization); verirsek bu metod bize yukarıdaki tumlocalizasyonlar mapinden MyLocalization nesnesini o contexteki widgete vereceini kabul edersek. O halde bunu daha kolay hale getirmek için kendi MyLocalization snıfımıza bir static metod yazalım. Bu metoda sadece bir Type ve contex verelim bize bunu sağlasın şunun gibi;

 

 
class MyLocalizations {
 

  static MyLocalizations of(BuildContext context) {
    return Localizations.of<DemoLocalizations>(context, MyLocalizations );
  }
 


Map<String,String> ceviriler;
 Strin cevir(String langKey) => cevriler['langKey'] ?? 'key bulunamadı!';
}
  

Sıra geldi kendi MyLocalization sınıfımız temsil edecek delegate/temsilci sınıfımızı yazmaya. Bunun adın ada MyLocalizationDelegate diyelim yani MyLocalization sınıfını temsil edecek olan temsilci sınıfı. Flutter MyLocalization  sınıfımızın nesnesini oluşturacağı zaman MyLocalizationDelegate sınıfının load yöntemini çaşıtıracak. O halde load metodu bir MyLocalization nesnesi döndürmeli(MyLocalization() ). Flutter kendi temsilci sınıfımızı tanımlarken LocalizationsDelegate<KendiTemsilciSınıfımız> ten miras almamızı şart koşuyor. Bu sayede bunun bir temsilci sınıfı olduğunu bilecek ve bizim temsilci sınıfımızda bizi bir şeyleri yapmaya zorlayacak en nihayetinde de yukarıdaki o Map i ve bir takım ihtiyacı olan şeyleri gerçekleştirebilecek. 

O halde MyLocalization sınıfımız temsil edecek MyLocalizationDelegate temsilci sınıfımız yazalım;

 

 


 
class MyLocalizations {
 

  static MyLocalizations of(BuildContext context) {
    return Localizations.of<DemoLocalizations>(context, MyLocalizations );
  }
 


Map<String,String> ceviriler;
 Strin cevir(String langKey) => cevriler['langKey'] ?? 'key bulunamadı!';
}




class MyLocalizationDelegate extends LocalizationsDelegate<MyLocalization> {
  const MyLocalizationDelegate();

  @override
  bool isSupported(Locale locale);

  @override
  Future<MyLocalization> load(Locale locale)   {
  return SynchronousFuture<MyLocalization>(MyLocalization());
    });
  }

  @override
  bool shouldReload(MyLocalizationDelegate old) => false;
}
  

Görüldüğü gibi  LocalizationsDelegate bizi bir şeyleri yapmaya zorladı.

@override
bool isSupported(Locale locale) =>bu kısmı tamamlamamızı söylüyor. uygulama dili destekliyorsa true desteklemiyorsa false vermeli diyor. bunu sorgulayabilmemiz için desteklediğimiz dillerin bir listeine ihtiyacımız var.

//bu kısmıda doldurmamızı ve geriye MyLocalization nesnemizi döndürmemizi istiyor. SynchronousFuture : nesnemizin üretilmesi bir future döndürmez hemen gerçekleşir ama sanırım future gibi davranmasını fakat asencron olmamasını istiyor.sanırım delegelerin temsil ettiği sınıflardan nesne üretirken bazıları future döndürüyor o yuzden bunu hemen gerçekleştirip diğerlerini sonraya bıraksın istemiyor.herneyse aynene yazalım

 

 

@override
Future<MyLocalization> load(Locale locale) {
return SynchronousFuture<MyLocalization>(MyLocalization(locale));
}

//bu kısımı aynen bırakıyorum

@override
bool shouldReload(MyLocalizationDelegate old) => false;
} 

Artık bir MyLocalization sınıfımız ve onu temsil eden MyLocalizationDelegate sınıfımız var. Ve uygulamanın herhangi bir widgetinden MyLocalization.of(context) dediğimizde MyLocalization  ulaşabileceğiz ama hala çevrilmiş metinleri Map şeklinde tutan ceviriler değişkeni boş.sayfaları ve mapleri oluşturalım.

Neler gerekli?

1.assets altında bir localizations klasörü ve altında dil kolarıyla isimlendirilmiş içerinde map olarak çevirilerini içeren dosyalar. bu dosyaları yaml a tanıtalım

2.MyLocalizatation sınıfı ve MyLocalizationDelegate sınıfı

3.LangKey sınıfı :maplerdeki değerler ulamak için 

4.LangSupported sınıfı:

 

 
//main.dart ----------------------

void main() {
  runApp(RunApp());
}


 
//RunApp.dart ----------------------------------------------------

class RunApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      locale: Locale("en", ""),
      supportedLocales: LangSupported.locales,
      localizationsDelegates: [
MyLocalizationDelegate(),
GlobalMaterialLocalizations.delegate,
 GlobalWidgetsLocalizations.delegate,
],
      home: Home(),
    );
  }
}



//Home.dart ---------------------------------------

class Home extends StatelessWidget {
  const Home({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Uygulamanın Dili : ${MyLocalization.of(context).locale.languageCode} - Telefonun Dili: + ${window.locale.languageCode}"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
        TextField( decoration: InputDecoration(hintText: "uygulamanın dili arapça ise sağa yaslanır", helperText:'bir şeyler yazın' ),),
          
            RaisedButton(child: Text(MyLocalization.of(context).cevir(LangKey.button_Ok)),onPressed:(){}),
            RaisedButton(child: Text(MyLocalization.of(context).cevir(LangKey.button_Iptal)),onPressed:(){}),

            Text("-----------------------------------------------------"),
            RaisedButton(child: Text("Arapça Yap"),onPressed:(){}),
            RaisedButton(child: Text("İngilizce Yap"),onPressed:(){}),
            RaisedButton(child: Text("Türkçe Yap"),onPressed:(){}),
          ],
        ),
      ),
     // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}




//------------------------------------------------------------------------
// assets/localizations/ar.json
{
    "button_Ok": "حسنا",
    "button_Iptal": "إلغاء"
}

// assets/localizations/en.json
{
    "button_Ok": "Ok",
    "button_Iptal": "Cancel"
}
// assets/localizations/tr.json
{
    "button_Ok": "Tamam",
    "button_Iptal": "İptal"
}


//LangKey.dart ----------------------------

class LangKey {
  static const button_Ok = "button_Ok";
  static const button_Iptal = "button_Iptal";
}



//LangSupported.dart --------------------------

class LangSupported {
  static final Map<String, String> languagesMap = <String, String>{
  "ar": "Arapça",
  "en": "English",
  "tr": "Türkçe"
  };

  static List<String> get languageCodeList => languagesMap.entries.map((e) => e.key).toList(growable: false);
  static List<String> get languageNamesList =>languagesMap.entries.map((e) => e.value).toList(growable: false);
  static List<Locale> get locales => languagesMap.entries.map((e) => Locale(e.key, '')).toList(growable: false);
}





//MyLocalization.dart --------------------------------------------------

class MyLocalization {
  MyLocalization(this.locale);

  final Locale locale;

  Map<dynamic, dynamic> _cevrilmisMap;

  String cevir(String langKey) =>_cevrilmisMap[langKey] ?? '** $langKey not found';

  Future<void> load() async {
    String jsonLang = await rootBundle.loadString("assets/localizations/${locale.languageCode}.json");
    _cevrilmisMap = json.decode(jsonLang);
  }

  static MyLocalization of(BuildContext context) {
    return Localizations.of<MyLocalization>(context, MyLocalization);
  }
}



//MyLocalizationDelegate timiz-

class MyLocalizationDelegate extends LocalizationsDelegate<MyLocalization> {
  const MyLocalizationDelegate();

  @override
  bool isSupported(Locale locale) =>
      LangSupported.languageCodeList.contains(locale.languageCode);

  @override
  Future<MyLocalization> load(Locale locale) {
 MyLocalization myLocalization =MyLocalization(locale);
 return myLocalization.load().then((onValue)=>SynchronousFuture<MyLocalization>(myLocalization));
    }
 

  
  @override
  bool shouldReload(MyLocalizationDelegate old) => false;
}


 


 
//pubspect.yaml ----------------------------------------------------
name: localizationornek
description: A new Flutter project.

 
version: 1.0.0+1

environment:
  sdk: ">=2.1.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2

dev_dependencies:
  flutter_test:
    sdk: flutter
 
flutter:


  uses-material-design: true


  # localizations altındaki tüm dosyalar
  assets:
    - assets/localizations/
  
  

Ne yaptik:

assets/localizations altında json dosyalarımızdaki map değerleri 
Map<dynamic, dynamic> _cevrilmisMap;
  String cevir(String langKey) =>_cevrilmisMap[langKey] ?? ‘** $langKey not found’;
  Future load() async {
    String jsonLang = await rootBundle.loadString(“assets/localizations/${locale.languageCode}.json”);
    _cevrilmisMap = json.decode(jsonLang);
  }
load yöntemiyle _cevrilmisMap değişkenimize aktardık. 
MyLocalizationDelegate içindeki load (Locale locale) metodu çalıştığında MyLocalization içerisindeki final locale alanına, materialapp’ın locale inden gelen local değeri aktarılarak MyLocalization nesnesi oluşturulur ve load yöntemi çalıştırılarak assets/localizations altında localin dil kodu ile isimlendirilmiş json dosyamızdaki map değerleri  _cevrilmisMap alanına aktarılır. Biz MaterialApp içerindeki locale alanına ingilizce verdik ve bu final bir değer o halde bu değeri uygulama içerinden değiştiremiyoruz. değiştirmemiz için materialapp ın yeniden oluşturulması gerek. 
 
Öyleyse bazı şeyleri maddler halinde tespit edelim;
1. materialapp ın locale alanı kilit noktası. locale ye atanan değer değişirse uygulamanın dil değişir. fakat locale bir final değişken.
1.şu durumda materialapp ın locale alanına bir değer atamaz isek flutter sistemin dilini desteklenen dillerde arayacak ve bulursa sorun yok. yani kullanıcı dili değiştirirse uygulama açıldığında bizim desteklediğimiz dillerde ise sorunsuz çalışacak. ama uygulama içerisinden bir dil değiştirme seceneği sunamayacağız çünkü materalapp in locale alanı final.
2. locali değiştirmek için materalapp ı rebuild etmemiz gerekmekte.
 
burada bir not düşmeliyim. internette bazı çoğu örneklerde bu durum göz ardı edilebiliyor. şöyleki;
 
class MyLocalization {
  MyLocalization(this.locale);

    Locale locale;

  Map<dynamic, dynamic> _cevrilmisMap;

  String cevir(String langKey) =>_cevrilmisMap[langKey] ?? '** $langKey not found';

  Future<void> load() async {
    String jsonLang = await rootBundle.loadString("assets/localizations/${locale.languageCode}.json");
    _cevrilmisMap = json.decode(jsonLang);
  }



  Future<void> setLocale(Locale newlocale) async {
    locale=newlocale;
    String jsonLang = await rootBundle.loadString("assets/localizations/${locale.languageCode}.json");
    _cevrilmisMap = json.decode(jsonLang);
  }


  static MyLocalization of(BuildContext context) {
    return Localizations.of<MyLocalization>(context, MyLocalization);
  }
}

  

Yukarıdakine benzer setLocale(Locale newlocale) metoduyla uygulamanın içerisinden dili değiştirebilme olanağına sahibiz fakat flutter hala locale atanan ilk değeri tanır,

Örneğin ugulama açıldığında materialapp içerinde locale bir değer ataması yapmadığımızı varsayalım ve telefonun dil ayarıda türkçe olsun,

flutter materyalapp içerisinde locale alanına türkçe localini atar,

daha sonra uygulma içerisinden MyLocalization.of(context). setLocale(Locale(‘ar’,”) yöntemiyle dili değiştirdiğimizde uygulamanın içerisindeki metinler arapça ya dönmesine karşın

hala sola yaslı olacaktır. ayrıca bir textfield alanlarımızda yazılar soldan yazılmaya başlanacaktır. Bunun nedeni flutter  uygulamanızın local dilini hala türkçe zannediyor oluşu en asılı

materyalapp içindeli locale nin değimemiş olmasıdır.

 

Öyleyse materapp ın rebuildi gerekli,

ayrıca dilin uygulama içerinden değiştirildiğinde bunu bir yere kaydetmeli ve uygulama yeniden açıldığında hatırlamalıyız.

Yazıyı daha fazla uzatmamak adına…

Çözüm:

 


//yaml dosyamız

name: localizationornek
description: A new Flutter project.

 
version: 1.0.0+1

environment:
  sdk: ">=2.1.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  provider: ^4.0.4
  shared_preferences: ^0.5.6+3
  
  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2

dev_dependencies:
  flutter_test:
    sdk: flutter
 
flutter:


  uses-material-design: true


  # localizations altındaki tüm dosyalar
  assets:
    - assets/localizations/
  



//main.dart------------------
 
import 'package:flutter/material.dart';
import 'RunApp.dart';

void main() {
  runApp(RunApp());
}
 


//RunApp.dart------------
 
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'GsLocalization/GsLocalization.dart';
import 'GsLocalization/LangSupported.dart';
import 'Wp/WpSplash/WpSplash.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

class RunApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<GsLocalization>(create: (_) => GsLocalization(),lazy: false),
      ],
      child: Consumer<GsLocalization>(
        builder: (context, gsLocalization, child) {
          return MaterialApp(
            debugShowCheckedModeBanner: false,
            locale: gsLocalization.locale,
            supportedLocales: LangSupported.locales,
			localizationsDelegates: [
              GlobalMaterialLocalizations.delegate,
              GlobalWidgetsLocalizations.delegate,
            ],
            home: child,
          );
        },
        child: WpSplash(),
      ),
    );
  }
}




//WpSplash.dart------------

import 'package:flutter/material.dart';
import 'package:localizationornek/GsLocalization/GsLocalization.dart';
import 'package:provider/provider.dart';
import '../WpHome.dart';


class WpSplash extends StatefulWidget {
  @override
  _WpSplashState createState() => _WpSplashState();
}

class _WpSplashState extends State<WpSplash> {

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) async {
      
      final _gsLocalization = Provider.of<GsLocalization>(context, listen: false);

      await _gsLocalization.load();

    Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (BuildContext context) => WpHome()));
 
    });
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.blueAccent,
      body: Center(
        child: Padding(
          padding: EdgeInsets.all(10),
          child: Column(mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text( "Slash Ekranı"  ,style: TextStyle(fontSize: 30,color: Colors.white,fontWeight: FontWeight.bold),),
              Text( "Uygulamanın başlangıç ayarlarını yapmak için en doğru yer...",style: TextStyle(fontSize: 13,color: Colors.white ),),
            ],
          ),
        ),
      ),
    );
  }
}





//WpHome.dart------------

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:localizationornek/GsLocalization/GsLocalization.dart';
import 'package:localizationornek/GsLocalization/LangKey.dart';
import 'package:provider/provider.dart';
 

class WpHome extends StatelessWidget {
  const WpHome({Key key}) : super(key: key);


  @override
  Widget build(BuildContext context) {

           final gsLocalization = Provider.of<GsLocalization>(context, listen: false);

           
    return Scaffold(
      appBar: AppBar(
        title: Text("Uygulamanın Dili : ${gsLocalization.locale.languageCode} - Telefonun Dili: ${window.locale.languageCode}"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
        TextField( decoration: InputDecoration(hintText: "uygulamanın dili arapça ise sağa yaslanır", helperText:'bir şeyler yazın' ),),
          
            RaisedButton(child: Text(gsLocalization.getTranslated(LangKey.button_Ok)), onPressed:(){}),
            RaisedButton(child: Text(gsLocalization.getTranslated(LangKey.button_Iptal)), onPressed:(){}),

            Text("-----------------------------------------------------"),
            RaisedButton(child: Text("Arapça Yap"),onPressed:()=>gsLocalization.setLocale('ar')),
            RaisedButton(child: Text("İngilizce Yap"),onPressed:()=>gsLocalization.setLocale('en')),
            RaisedButton(child: Text("Türkçe Yap"),onPressed:()=>gsLocalization.setLocale('tr')),
          ],
        ),
      ),
 
    );
  }
}






//GsLocalization.dart------------------
 
import 'dart:convert';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'LangSupported.dart';
 
class GsLocalization with ChangeNotifier {
  Locale _locale = Locale(LangSupported.languageCodeList.contains(window.locale.languageCode) ? window.locale.languageCode : 'en','');
  Locale get locale => _locale;


 Map<dynamic, dynamic> _translated;
  String getTranslated(String langKey) =>_translated[langKey] ?? '** $langKey not found';

  Future<void> load() async {
    final SharedPreferences mSharedPreferences = await SharedPreferences.getInstance();
    String tempLanguageCode = mSharedPreferences.getString("languageCode") ?? "notfound";

 if(_locale.languageCode == tempLanguageCode ){
    String jsonLang = await rootBundle.loadString("assets/localizations/${_locale.languageCode}.json");
    _translated = json.decode(jsonLang);
    return;
   }

   
   if(tempLanguageCode == "notfound"){
    await mSharedPreferences.setString("languageCode", _locale.languageCode);
    String jsonLang = await rootBundle.loadString("assets/localizations/${_locale.languageCode}.json");
    _translated = json.decode(jsonLang);
      return;
   }


    _locale = Locale(tempLanguageCode, '');
    String jsonLang = await rootBundle.loadString("assets/localizations/${_locale.languageCode}.json");
    _translated = json.decode(jsonLang);
     notifyListeners();
  }

 
  Future<void> setLocale(String languageCode) async {
  if(_locale.languageCode == languageCode )  return;
   final SharedPreferences mSharedPreferences = await SharedPreferences.getInstance();
    await mSharedPreferences.setString("languageCode",  languageCode);
    _locale = Locale(languageCode, '');
    String jsonLang = await rootBundle.loadString("assets/localizations/${_locale.languageCode}.json");
    _translated = json.decode(jsonLang);
      notifyListeners();
  }
}




//LangKey.dart------------------

abstract class LangKey {
  static const button_Ok = "button_Ok";
  static const button_Iptal = "button_Iptal";
}


 

//LangSupported.dart------------------

import 'package:flutter/material.dart';

abstract class LangSupported {
  static List<String> get languageCodeList => languagesMap.entries.map((e) => e.key).toList(growable: false);
  static List<Locale> get locales => languagesMap.entries.map((e) => Locale(e.key, '')).toList(growable: false);
  // static List<String> get languageNamesList =>languagesMap.entries.map((e) => e.value).toList(growable: false);
 

  static final Map<String, String> languagesMap = <String, String>{
  "ar": "Arapça",
  "en": "English",
  "tr": "Türkçe"
  };

}
 

Kullanımı:

1. bir metnin çevrilmiş haline ulaşmak için:
final gsLocalization = Provider.of<GsLocalization>(context, listen: false);
gsLocalization.getTranslated(LangKey.button_Ok)

2. uygulamanın dilini değiştimek için:
final gsLocalization = Provider.of<GsLocalization>(context, listen: false);
gsLocalization.setLocale(‘en’);

 

Özelleştirmek için:
A.Yeni bir dil tanımlamak için:
örneğin fransızca dili ekleyelim:

1.
– assets/localizations/ altına yen bir fr.json dosyası ekleyelim ve fransızcaya çevrilmiş değerleri keylerin karşılıklarına girelim.

2. LangSupported sınıfında languagesMap map te “fr” : “français”, yı ekleyelim

static final Map<String, String> languagesMap = <String, String>{
“ar”: “Arapça”,
“en”: “English”,
“tr”: “Türkçe”,
“fr” : “Français”
};

}

bu kadar..

B.Yeni bir metin çevirisi girmek için:
örneğin oyla sözcüğünü girelim.

1.

LangKey sınıfımıza bir key girelim

abstract class LangKey {
static const button_Ok = “button_Ok”;
static const button_Iptal = “button_Iptal”;

static const oyla = “oyla”;

}

2. assets/localizations/ altındaki tüm json dosylarındaki her map e oyla key ini girip ilgili dildeki çevirisi atayalım

assets/localizations/tr.json
{
“button_Ok”: “Tamam”,
“button_Iptal”: “İptal”
“oyla” : “Oy ver”
}

assets/localizations/en.json
{
“button_Ok”: “Ok”,
“button_Iptal”: “Cancel”
“oyla” : “Rate”
}

bu kadar…

motivastonunuzu yüksek tutmanız dileğiyle….

Bir cevap yazın