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
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));
}
- I use the reflectClass method from dart mirrors to obtain
ClassMirror
of my target class (TestMirror
in this case). AClassMirror
allows as to obtain the methods, getters and setters of an instance of the class through itsinstanceMembers
property.instanceMembers
returns a Map with aSymbol
representation of the method/getter/setter as they key and aMethodMirror
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 theMethodMirrors
simpleName
property. - The MirrorSystem class has a getName method that can derive the name of a symbol as string. Dart classes have the methods
hashCode
andrunTimeType
built in hence we want to exclude these from our document in3
- We create an
InstanceMirror
which is actual instance of the class in4
and call its getField method on the methods symbol to invoke the getter. In the example, the gettername
would be invoked by callinggetField
on theInstanceMirror
created and passing the symbol representation ofname
. - The
reflectee
property in6
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. - 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)