6

I am new to flutter.

I apply localization in my flutter project and everything work fine until I edit my json language file to have nested object.

How to call the nested json object correctly?

I try call it by using dot, but it throwing error 'A non-null String must be provided...'

AppLocalizations.of(context).translate('Intro.Header')

Here is my json

{
  "Intro": {
    "Header": "Introduction",
    "Content": "This is...."
  },
  "Test": "This is test",
}

I have no issue if I call "Test" directly.

AppLocalizations.of(context).translate('Test')

How to read Header and Content?

4
  • Try this: AppLocalizations.of(context).translate["Intro"]["Header"] Commented Aug 2, 2019 at 9:52
  • It is not working. Error state 'the operator '[]' isn't defined for the class....'. It pointing the error to "Intro" Commented Aug 2, 2019 at 16:53
  • I want to know how to do this. Any solution? Commented Mar 29, 2020 at 13:43
  • @Ooto you can have a look to my answer. It may help you Commented Apr 1, 2020 at 17:29

1 Answer 1

2

I know the question is pretty old, but I found it as I had the same problem and was searching for a solution. I was not able to find one on the Internet so I was trying to solve it by myself and as I made it, I want to present here my solution. This solution might not be the most nice one as I am pretty new to Dart/Flutter but at least it works.

As the Owner of the question did not provide his setup I will take the state of this Video as given.

[...] 
Map<String, String> _localizeStrings;
[...]
_localizeStrings = jsonMap.map((key, value) {
  return MapEntry(key, value.toString());
});
[...]
String translate(String key) {
  return _localizeStrings[key];
}
[...]

That is the given state, which is totally setup for a flat json structure. To be able to work with a nested structure our Map _localizeStrings should be of type Map<String, dynamic> as the key will always be a String but the value could either be a String or another map. As a next step we need to remove the .toString() from value. As a last step we will implement a function which is splitting the given key by '.' and will climb down the map key by key. The last value it gets we will return with .toString() as this should be the string we want to have.

So here is my solution:

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

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class AppLocalizations {
  final Locale locale;
  static const LocalizationsDelegate<AppLocalizations> delegate =
      _AppLocalizationsDelegate();

  AppLocalizations(this.locale);

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

  Map<String, dynamic> _localizeStrings;

  Future<bool> load() async {
    String jsonString =
        await rootBundle.loadString('lang/${locale.languageCode}.json');
    Map<String, dynamic> jsonMap = json.decode(jsonString);

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

    return true;
  }

  String translate(String key) {
    var nestedMap = _getNestedValue(key);    
    return nestedMap.toString();
  }

  dynamic _getNestedValue(String keyPath) {
    Map keyMap = keyPath.split('.').asMap();
    var nestedMap;
    keyMap.forEach((index, key) {
      if (index == 0) {
        nestedMap = _localizeStrings[key];
      } else
        nestedMap = nestedMap[key];
    });
    return nestedMap;
  }
}

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

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

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

  @override
  bool shouldReload(LocalizationsDelegate<AppLocalizations> old) => false;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. I've been struggling to solve similar problem for the past hour.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.