Search code examples
flutterdartflutter-getx

Load GetX translations from Firestore using Flutter


How can I load translations from Firestore and then use them for GetX translations/internationalization?

I want to have a language collection inside two documents (US & DE) that contain all the string value translations. Therefore instead of this...

import 'package:get/get.dart';

class Messages extends Translations {
  @override
  Map<String, Map<String, String>> get keys => {
        'en_US': {
          'hello': 'Hello World',
        },
        'de_DE': {
          'hello': 'Hallo Welt',
        }
      };
}

I want something like this...

//Get data from Firestore as dataFromFirestore_US & dataFromFirestore_DE

import 'package:get/get.dart';

class Messages extends Translations {
  @override
  Map<String, Map<String, String>> get keys => {
        'en_US': {
          dataFromFirestore_US,
        },
        'de_DE': {
          dataFromFirestore_DE,
        }
      };
}

I tried loading data from Firestore and get the error message:

type 'Null' is not a subtype of type 'Map<String, String>' See also: https://flutter.dev/docs/ testing/errors

To get this error:

I have a LanguageController.dart file that loads the language as follows:

    final languages = LanguageModel().obs;
    
      Future getLanguages() async {
        QuerySnapshot data =
            await FirebaseFirestore.instance.collection("languages").get();
        data.docs.forEach((doc) {
          if (doc.id == "en") {
            var enLang = "'void': 'GO ONLINE'";
            Map<String, dynamic> data = doc.data() as Map<String, dynamic>;
            for (String key in data.keys) {
              enLang = '$enLang, "${key.toString()}": "${data[key].toString()}"';
            } 
            languages.value.en = enLang;
            en.value = enLang;
          }
    
        });
      }

The LanguageModel.dart looks like this:

    class LanguageModel {
      dynamic en;
      dynamic se;
    
      LanguageModel({this.en, this.se});
      Map<String, Map<String, String>> toJson() {
        return {
          "en": en,
          "se": se,
        };
      }
    }

The Translation part looks like:


  static final locale = Locale('se', 'SE');

  static final fallbackLocale = Locale('en', 'US');


  static final langs = [
    'Swedish',
    'English',
  ];

  static final locales = [
    Locale('se', 'SE'),
    Locale('en', 'US'),
  ];
  @override
  // TODO: implement keys

  @override
 
  final LanguageController l = Get.find();
  @override
  Map<String, Map<String, String>> get keys => {
        'en_US': l.languages.value.en, 
        'se_SE': seSE, 
      };
  // Gets locale from language, and updates the locale
  void changeLocale(String lang) {
    final locale = _getLocaleFromLanguage(lang);
    Get.updateLocale(locale!);
  }

Any help or advice would be appreciated.


Solution

  • I managed to get everything working well and can switch between languages loaded from Firestore, I hope this helps anyone else wanting to do the same.

    SOLUTION

    1. Firestore collection, documents, and data

    Create a collection called languages, I need English and Swedish therefore I have 2 documents called en and se containing the language data as strings such as: hello: 'Hello'

    2. LanguageController.dart (Get data from Firestore)

    import 'package:get/get.dart'; import
    'package:cloud_firestore/cloud_firestore.dart';
    
        class LanguageController extends GetxController {
          RxString language = "Swedish".obs;
        
          RxMap<String, String> langEN = <String, String>{}.obs;
          RxMap<String, String> langSE = <String, String>{}.obs;
        
          void updateLanguage(String data) {
            language.value = data;
          }
        
          Future getLanguages() async {
            //Get all languages in languages collection
            QuerySnapshot data =
                await FirebaseFirestore.instance.collection("languages").get();
        
            data.docs.forEach((doc) {
              if (doc.id == "en") {
                final data = doc.data() as Map<String, dynamic>;
                for (String key in data.keys) {
                  langEN['${key.toString()}'] = '${data[key].toString()}';
                }
              } else if (doc.id == "se") {
                final data = doc.data() as Map<String, dynamic>;
                for (String key in data.keys) {
                  langSE['${key.toString()}'] = '${data[key].toString()}';
                }
              }
            });
          }
        }
    

    3. languages.dart

        import 'dart:ui';
        import 'package:flutter/material.dart';
        import 'package:get/get.dart';
        
        import '../controllers/LanguageController.dart';
        
        class Languages extends Translations {
          // Default locale
          static final locale = Locale('se', 'SE');
        
          // fallbackLocale saves the day when the locale gets in trouble
          static final fallbackLocale = Locale('en', 'US');
        
          // Supported languages
          // Needs to be same order with locales
          static final langs = [
            'Swedish',
            'English',
          ];
        
          // Supported locales
          // Needs to be same order with langs
          static final locales = [
            Locale('se', 'SE'),
            Locale('en', 'US'),
          ];
          final LanguageController l = Get.find();
        
          @override
          Map<String, Map<String, String>> get keys =>
              {'en_US': l.langEN, 'se_SE': l.langSE};
        
          // Gets locale from language, and updates the locale
          void changeLocale(String lang) {
            final locale = _getLocaleFromLanguage(lang);
            Get.updateLocale(locale!);
          }
        
          // Finds language in `langs` list and returns it as Locale
          Locale? _getLocaleFromLanguage(String lang) {
            for (int i = 0; i < langs.length; i++) {
              if (lang == langs[i]) return locales[i];
            }
            return Get.locale;
          }
        }
    

    If anyone has any issues comment and I will try and help out.