Ooto
Ooto

Reputation: 1247

Flutter Internationalization: How to access to nested data in json

I want to know how to access to nested data in json.

AppLocalizations.of(context).translate('Information.about');

en.json

{  
  "Information" : {
      "about": "About"
  }

}

I tried like the way above but it cannot access to the nested data.

And here is translate method.

  class AppLocalizations {
      final Locale locale;

  AppLocalizations(this.locale);

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

  // Static member to get access to the delegate from 'main.dart' file
  static const LocalizationsDelegate<AppLocalizations> delegate = _AppLocalizationsDelegate();

  Map<String, String> _localizedValues;

  Future<bool> load() async {
    // Load a language JSON file from the 'i18n' folder
    String value = await rootBundle.loadString('i18n/${locale.languageCode}.json');
    Map<String, dynamic> jsonMap = jsonDecode(value);
    _localizedValues = jsonMap.map((key, value) {
      return MapEntry(key, value.toString());
    });
    return true;
  }

  String translate(String key) {
    // Returns a localized text
    return _localizedValues[key];
  }
}

Upvotes: 5

Views: 7133

Answers (5)

hamza
hamza

Reputation: 31

Here is my answer, it looks like @Morez answer but it works for me:

  Map<String,dynamic> _localizedValues;
  Map<String,dynamic> _localizedValues2;

  Future<void> load() async {
    String jsonStringValues = await rootBundle.loadString('lib/jsonLanguages/${locale.languageCode}.json');
    //print(jsonStringValues);
    Map<String,dynamic> mappedJson = json.decode(jsonStringValues);
    _localizedValues = mappedJson.map((key, value) => MapEntry(key, value));
  }

  String translate(String key,String subkey){
    String test = _localizedValues[key].toString();
    test = test.replaceAllMapped(RegExp(r'(\b\w+( +\w+)*\b)|(\b\w+\b)'), (match) {return '"${match.group(0)}"';});
    //print(test);
    Map<String,dynamic> mappedJson2 = json.decode(test);
    _localizedValues2 = mappedJson2.map((subkey, value) => MapEntry(subkey, value));
    return _localizedValues2[subkey];
  }

Upvotes: 0

Javeed Ishaq
Javeed Ishaq

Reputation: 7105

If you are using easy_localization flutter package for localization you can access object value from JSON like below with dot notation

"Information.about".tr()

Upvotes: 0

Ania
Ania

Reputation: 321

This is my solution based on https://stackoverflow.com/a/60924513

  Map flattenTranslations(Map<String, dynamic> json, [String prefix = '']) {
    final Map<String, String> translations = {};
    json.forEach((String key, dynamic value) {
      if (value is Map) {
        translations.addAll(flattenTranslations(value, '$prefix$key.'));
      } else {
        translations['$prefix$key'] = value.toString();
      }
    });
    return translations;
  }

  Future<bool> load() async {
    String jsonString = await rootBundle.loadString('assets/i18n/${locale.languageCode}.json');
    Map<String, dynamic> jsonMap = jsonDecode(jsonString);
    _localizedStrings = flattenTranslations(jsonMap);
    return true;
  }

And this is all app_localizations.dart file

import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/services.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> _localizedStrings;

  Map flattenTranslations(Map<String, dynamic> json, [String prefix = '']) {
    final Map<String, String> translations = {};
    json.forEach((String key, dynamic value) {
      if (value is Map) {
        translations.addAll(flattenTranslations(value, '$prefix$key.'));
      } else {
        translations['$prefix$key'] = value.toString();
      }
    });
    return translations;
  }

  Future<bool> load() async {
    String jsonString = await rootBundle.loadString('assets/i18n/${locale.languageCode}.json');
    Map<String, dynamic> jsonMap = jsonDecode(jsonString);
    _localizedStrings = flattenTranslations(jsonMap);
    return true;
  }

  String translate(String key) {
    return _localizedStrings[key] ?? key;
  }
}

class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
  const _AppLocalizationsDelegate();

  @override
  bool isSupported(Locale locale) {
    return ['en', 'pl'].contains(locale.languageCode);
  }

  @override
  Future<AppLocalizations> load(Locale locale) async {
    AppLocalizations localizations = new AppLocalizations(locale);
    await localizations.load();
    return localizations;
  }

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


Upvotes: 3

Garrison
Garrison

Reputation: 99

This will allow you to access nested JSON regardless of depth. Just modify your translate method like so:

  String translate(String key) {
    List<dynamic> keys = key.split('.');
    var value = keys.fold(_localizedValues, (obj, key) => obj[key]);
    return value;
  }

Then you are able to access your nested JSON in your code like this:

AppLocalizations.of(context).translate('key1.key2.key3');

Just make sure there won't be any periods in your JSON keys

Upvotes: 2

Morez
Morez

Reputation: 2229

Try this:

First Method:

AppLocalizations.of(context).translate(“about”);

And change your translate function to this:

  String translate(String key) {
// Returns a localized text
  return _localizedValues[“Information”][key];
 }

Or you can do this:

Second Method:

AppLocalizations.of(context).translate(”Information”,“about”);

And change your translate function to this:

 String translate(String parentkey, String nestedKey) {
// Returns a localized text
  return _localizedValues[parentKey][nestedKey];
 }

This might help.

Also, This is a good article to learn how to parse complex json files

UPDATED ANSWER:

After trying the code, I could understand the problem.

The problem is your _localizedValues["Information"] will be a String not a map becuase we converted the value to value.toString() and that's why you cannot use a second key because the returned object is not a Map but it's a String.

So _localizedValues["Information"] is "{about: About}".

To solve the problem, use the code below:

  Map<String, dynamic> _localizedValues; //your values are not String anymore and we use dynamic instead
Future<bool> load() async {
    // Load a language JSON file from the 'i18n' folder
    String value = await rootBundle.loadString('i18n/${locale.languageCode}.json');
    Map<String, dynamic> jsonMap = jsonDecode(value);
    _localizedValues = jsonMap.map((key, value) {
      return MapEntry(key, value); //not value.toString() so the value will be a map
    });
    return true;
  }
String translate(String parentkey, String nestedKey) {
    // Returns a localized text
      return _localizedValues[parentKey][nestedKey];
     }

And then you have to get "About" from the code below:

AppLocalizations.of(context).translate('Information','about');

Upvotes: 2

Related Questions