Creating a script to sync internationalisation encodings between JS and Dart in Flutter
Published: 2022-07-25 10:51:02


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](https://docs.flutter.dev/development/accessibility-and-localization/internationalization). 

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:

<aside>
💡 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.

</aside>

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](https://github.com/flutter/website/blob/main/examples/internationalization/add_language/lib/nn_intl.dart)

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](Creating%20a%20script%20to%20sync%20internationalisation%20enc%2037d73527287c4826a87297abb14d65e2/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 

```dart
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));
}
```

1. I use the reflectClass method from [dart mirrors](https://api.dart.dev/stable/2.16.1/dart-mirrors/dart-mirrors-library.html) 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. 

<aside>
💡 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

</aside>

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.