DEV Community

Joseph-Peter Yoofi Brown-Pobee
Joseph-Peter Yoofi Brown-Pobee

Posted on

Creating a script to sync internationalisation encodings between JS and Dart in Flutter

Created: March 20, 2022 9:58 PM
Published: Yes

I was recently working on translations in flutter and had to provide support for the Kinyarwanda language. I followed the boilerplate set up and set up phrases for Kinyarwanda translations as described in the flutter documentation.

When I tried to switch my locale within the app it broke which was weird because it didn’t for the other locales i.e Spanish, Swahili, French. The error indicated that the MaterialLocalizations.delegate did not have support for the locale ‘rw’ (for Kinyarwanda)

I then found from the documentation that at the time, app that needed to provide support for a language that is not included in GlobalMaterializations had some extra work to do. According to the documentation:

💡 An app that needs to support a language that’s not included in `[GlobalMaterialLocalizations](https://api.flutter.dev/flutter/flutter_localizations/GlobalMaterialLocalizations-class.html)`has to do some extra work: it must provide about 70 translations (“localizations”) for words or phrases and the date patterns and symbols for the locale.

This meant creating a sub class of GlobalMaterialLocalizations to define the localizations that the Material library would depend on as well as a sub class of LocalizationsDelegate to serve as the factory for the GlobalMaterialLocalizations class. All this is made easier as flutter has provided a link to a sample of the above classes in the documentation. All that needs to be changed is the respective formats and translations required for the target translation. The source code can be found here

However, I wanted to use the the English GlobalMaterializations subclass as my base for Kinyarwanda as I did not have the requisite translations and wanted English to be the fallback.

After some digging around the source code I found the English subclass and could obtain the English versions of date patterns and specific translations required by Material

Screenshot 2022-04-16 at 12.12.10 AM.png

In this way I could use the English fallback while I worked on getting the Kinyarwanda translations.

I wanted to obtain a json document with the english source texts that I could hand to the operations team to fill in the translations. At first this would mean going through all the 70 necessary strings shown above and copying them to a document (Yuck)

Dart mirrors to the rescue

Dart mirrors allowed me to take the class and introspect all its getters, dynamically invoking them and writing them to a json file

List omitMethods = ['hashCode', 'runtimeType'];

class TestMirror {
    String get name => 'Yoofi';
}

void main() async {
  var bucket = {};
  var instance = reflect(TestMirror());//4
  var im = reflectClass(TestMirror); //1
  im.instanceMembers.entries.forEach((entry) {
    var symbol = entry.key;
    var methodMirror = entry.value;
    var name = MirrorSystem.getName(methodMirror.simpleName);//2
    if (methodMirror.isGetter && !omitMethods.contains(name)) {//3
      var result = instance.getField(symbol).reflectee;//6
      bucket['$result'] = " ";
    }
  });
  await File('rw_strings.json').writeAsString(jsonEncode(bucket));
}
Enter fullscreen mode Exit fullscreen mode
  1. I use the reflectClass method from dart mirrors to obtain ClassMirror of my target class (TestMirror in this case). A ClassMirror allows as to obtain the methods, getters and setters of an instance of the class through its instanceMembers property. instanceMembers returns a Map with a Symbol representation of the method/getter/setter as they key and a MethodMirror as the value. MethodMirrors allow us to obtain the name of method/getters/setters as created by the class. In the above we can obtain “name” from the MethodMirrors simpleName property.
  2. The MirrorSystem class has a getName method that can derive the name of a symbol as string. Dart classes have the methods hashCode and runTimeType built in hence we want to exclude these from our document in 3
  3. We create an InstanceMirror which is actual instance of the class in 4 and call its getField method on the methods symbol to invoke the getter. In the example, the getter name would be invoked by calling getField on the InstanceMirror created and passing the symbol representation of name.
  4. The reflectee property in 6 is what will return the result of the call to be placed in result. I then add it to the map with a value of an empty string. When the final document is created this is what the operations team would have to fill in.
  5. I finally write the array to a document and have my final output to send.

There are a lot of useful classes and methods that will allow us to introspect class and dynamically invoke their methods using dart mirrors.

💡 As at the time of writing dart mirrors is unstable and its API might change slightly as a result of user feedback. This library is only supported by the Dart VM and only available on some platforms. It is not available on flutter

That was a summary on how used dart mirrors to automate what would have been a mundane task and the usefulness of a class introspection and dynamic invocation would certainly transcend this. Do play around with the library more if you wish.

Top comments (0)