Создано: 20 марта 2022 9:58 PM
Опубликовано: Да
Недавно я работал над переводами во Flutter и должен был обеспечить поддержку языка Kinyarwanda. Я следовал шаблонной установке и настроил фразы для переводов на киньяруанду, как описано в документации flutter.
Когда я попытался переключить локаль в приложении, оно сломалось, что было странно, поскольку для других локалей, таких как испанский, суахили, французский, этого не произошло. Ошибка указывала на то, что делегат MaterialLocalizations.delegate не поддерживает локаль ‘rw’ (для киньяруанды).
Затем я обнаружил в документации, что в то время приложения, которым требовалось обеспечить поддержку языка, не включенного в GlobalMaterializations, должны были проделать дополнительную работу. Согласно документации:
💡 Приложение, которому требуется поддержка языка, не включенного в `[GlobalMaterialLocalizations](https://api.flutter.dev/flutter/flutter_localizations/GlobalMaterialLocalizations-class.html)`, должно проделать дополнительную работу: оно должно предоставить около 70 переводов («локализаций») для слов или фраз, а также шаблоны и символы дат для данной локали.
Это означает создание подкласса GlobalMaterialLocalizations
для определения локализаций, от которых будет зависеть библиотека Material, а также подкласса LocalizationsDelegate
, который будет служить фабрикой для класса GlobalMaterialLocalizations
. Все это стало проще, поскольку flutter предоставил ссылку на пример вышеупомянутых классов в документации. Все, что нужно изменить, — это соответствующие форматы и переводы, необходимые для целевого перевода. Исходный код можно найти здесь
Однако я хотел использовать английский подкласс GlobalMaterializations в качестве базового для киньяруанды, поскольку у меня не было необходимых переводов, и я хотел, чтобы английский был запасным вариантом.
После некоторого копания в исходном коде я нашел подкласс English и смог получить английские версии шаблонов дат и специфических переводов, требуемых Material
Таким образом, я мог использовать английский язык в качестве запасного варианта, пока работал над переводом на киньяруанду.
Я хотел получить json-документ с английскими исходными текстами, который я мог бы передать операционной команде для заполнения переводов. Сначала это означало бы перебор всех 70 необходимых строк, показанных выше, и копирование их в документ (фу).
Зеркала Dart на помощь
Зеркала Dart позволили мне взять класс и проанализировать все его геттеры, динамически вызывая их и записывая в json-файл.
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));
}
- Я использую метод reflectClass из dart mirrors для получения
ClassMirror
моего целевого класса (TestMirror
в данном случае). ЗеркалоClassMirror
позволяет получить методы, геттеры и сеттеры экземпляра класса через его свойствоinstanceMembers
.instanceMembers
возвращает Map сSymbol
представлением метода/геттера/сеттера в качестве ключа иMethodMirror
в качестве значения.MethodMirrors
позволяет нам получить имя метода/геттеров/сеттеров, созданных классом. В примере выше мы можем получить «name
» из свойстваMethodMirrors
simpleName
. - Класс MirrorSystem имеет метод getName, который может получить имя символа в виде строки. Классы Dart имеют встроенные методы
hashCode
иrunTimeType
, поэтому мы хотим исключить их из нашего документа в3
. - Мы создаем
InstanceMirror
, который является действительным экземпляром класса в4
и вызываем его метод getField на символе methods для вызова геттера. В примере геттерname
будет вызван вызовомgetField
на созданномInstanceMirror
и передачей символьного представленияname
. - Свойство
reflectee
в6
— это то, что вернет результат вызова, который будет помещен в result. Затем я добавляю его в карту со значением пустой строки. Когда будет создан окончательный документ, именно это значение придется заполнить операционной команде. - Наконец, я записываю массив в документ и получаю окончательный результат для отправки.
Есть много полезных классов и методов, которые позволят нам интроспектировать класс и динамически вызывать их методы с помощью зеркал dart.
💡 На момент написания статьи dart mirrors нестабилен, и его API может немного измениться в результате отзывов пользователей. Эта библиотека поддерживается только Dart VM и доступна только на некоторых платформах. Она недоступна на flutter
Это было краткое описание того, как с помощью зеркал dart можно автоматизировать то, что было бы рутинной задачей, но полезность интроспекции классов и динамического вызова, безусловно, превосходит это. Если хотите, поиграйте с библиотекой еще.