Flutter parse array from localization JSON file

1,116

I know this might not be the best way to do this but it's just a way I found. You could also save your values in a different json asset file and parse it normally using json.decode() but I needed this to handle it easier with localization.

So I found a simple solution to this instead of adding function to localization class.

I get my array in the Widget as string from my localization file (like any other localization string) like so:

var list = AppLocalization.of(context).translate('cities');//this will return [Paris, Lyon, Nice] as a string

then I do the following:

list = list.replaceAll(RegExp("\\[|\\]"), ''); //removes square brackets from the string
var cityList = list.split(','); //splits the city names by the comma as a 
print(cityList); //prints [Paris, Lyon, Nice] as List<String>

Also I implemented another way to get nested json array like in

{
  "title" : "My account",
  "name" : "John",
  "cities" : ["Paris", "Lyon", "Nice"],
  "areas" : 
  {
     "paris" : ["area1", "area2", "area3"], 
     "lyon": ["area1", "area2", "area3"]
  }
}

I do this to get paris areas...

  var areas = AppLocalization.of(context).translate('areas'); //this gets the whole map as a string.
  areas = areas.replaceAll(RegExp("\\[|\\]|\\{|\\}"), '');//this removes leading and trailing {[ ]}
  var splitValues = areas.split(':');
  var mapValues = Map.fromIterable(test, key: (item) => splitValues[0],value: (item) => item.split(','),);

    var parisAreas = mapValues.map((key, value) =>
    MapEntry<String, List<String>>(key, List<String>.from(value)));
Share:
1,116
AhWagih
Author by

AhWagih

Updated on December 24, 2022

Comments

  • AhWagih
    AhWagih over 1 year

    I'm trying to add an array of cities' names to my localization json file but I can't decode it back to an array after it is converted to string.

    en-US.json

    {
      "title" : "My account",
      "name" : "John",
      "cities" : ["Paris", "Lyon", "Nice"]
    }
    

    AppLocalization.dart

    class AppLocalizations {
      final Locale locale;
    
      AppLocalizations(this.locale);
    
      static AppLocalizations of(BuildContext context) {
        return Localizations.of<AppLocalizations>(context, AppLocalizations);
      }
    
      static const LocalizationsDelegate<AppLocalizations> delegate =
          _AppLocalizationsDelegate();
    
      Map<String, String> _localizationStrings;
    
      Future<bool> load() async {
        String jsonString = await rootBundle.loadString(
            'assets/translations/${locale.languageCode}-${locale.countryCode}.json');
    
        Map<String, dynamic> jsonMap = json.decode(jsonString);
    
        _localizationStrings = jsonMap.map((key, value) {
          return MapEntry(key, value.toString());
        });
        return true;
      }
    
      Future<void> setLocale(Locale locale) async {
        final SharedPreferences _prefs = await SharedPreferences.getInstance();
        final _languageCode = locale.languageCode;
        await _prefs.setString('locale', _languageCode);
        print('locale saved!');
      }
    
      static Future<Locale> getLocale() async {
        final SharedPreferences _prefs = await SharedPreferences.getInstance();
        final String _languageCode = _prefs.getString('locale');
        if (_languageCode == null) return null;
    
        Locale _locale;
        _languageCode == 'en'
            ? _locale = Locale('en', 'US')
            : _locale = Locale('ar', 'EG');
        return _locale;
      }
    
      String translate(String key) {
        return _localizationStrings[key];
      }
    }
    
    class _AppLocalizationsDelegate
        extends LocalizationsDelegate<AppLocalizations> {
      const _AppLocalizationsDelegate();
    
      @override
      bool isSupported(Locale locale) {
        return ['en', 'ar'].contains(locale.languageCode);
      }
    
      @override
      Future<AppLocalizations> load(Locale locale) async {
        AppLocalizations localization = AppLocalizations(locale);
        await localization.load();
        return localization;
      }
    
      @override
      bool shouldReload(LocalizationsDelegate<AppLocalizations> old) {
        return false;
      }
    }
    

    In my widget I try to access the array through List<String> _cities = AppLocalization.of(context).translate('cities');

    this works and if I print _cities.toString() it prints [Paris, Lyon, Nice].

    THE PROBLEM

    When I try to decode _cities to an array using json.decode(_cities) I always get a format error Unhandled Exception: FormatException: Unexpected character (at character 2).

    I believe the array is converted to String in this function

    _localizationStrings = jsonMap.map((key, value) {
      return MapEntry(key, value.toString());
    });
    

    How can I parse it back to an array??

    I'm open to all kind of suggestions. Thank you

  • AhWagih
    AhWagih over 3 years
    Solution doesn't work because _localizationStrings[key] is of type List<dynamic> so I get an error but to get it work I did the following Map<String, dynamic> _localizationStrings; instead of Map<String, String> _localizationStrings; and here I removed the toString on value _localizationStrings = jsonMap.map((key, value) { return MapEntry(key, value); }); List<dynamic> localizedList(String key) { return _localizationStrings[key] as List<String>; } in my widget I now have to cast the returned List<dynamic> to List<String> list = list.cast<String>().toList();
  • AhWagih
    AhWagih over 3 years
    But I'm not comfortable with all these changes in the localization class. My use case is to use the list to fill a dropdown menu (with city name in both languages) but the list is much bigger than in the example so I don't want to hard code it. maybe there's a better solution to this?
  • AhWagih
    AhWagih over 3 years
    Thanks for your help but the new answer doesn't solve the issue either because I get my array string from my localization JSON file. I found a more elegant solution and I will post it.