简体   繁体   中英

Flutter parse array from localization JSON file

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

If you want to access the localized data as an array don't flatten the array into a string. Depending on what your use case is it might not be appropriate to store this kind of data in the localization file anyway.

However, if you still want to store the list in your localization file, then I suggest that you don't flatten the decoded json map and return a dynamic value instead of String from translate . When you retrieve the localized value you will have to type cast it into the type you expect so you might want to add some helper functions (see below) to DRY it up a bit.

import 'dart:convert';

var cities = '''
{
  \"cities\" : [\"Paris\", \"Lyon\", \"Nice\"]
}
''';

class Localization {
  final Map<String,dynamic> data;
  
  Localization(this.data);

  String localizedString(String key) {
    return data[key] as String;
  }

  List<String> localizedList(String key) {
    return List<String>.from(data[key]);
  }
}

void main(){
  var data = jsonDecode(cities); //  jsonDecode(cities);
  var l = Localization(data);

  for(var city in l.localizedList("cities")) {
    print("$city");
  }    
}

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)));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM