Todo stores editor documents too, so the document, the editor, its view, the format bar, links, the clipboard, scripts and EditorBlocks move, with their strings and tests, to a shared package on the same appflowy_editor fork. What is Learn's alone stays in Learn on top of it, and what the editor took from it directly is inverted: EditorBlocks gains textMarks, formatActions and onEditorClosed, RichContentEditor a wrapEditor, and an InsertableBlock's name takes the BuildContext. Learn's screens use LearnContentEditor and LearnContentView, which add Learn's blocks and the grammar check. Learn behaves as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
81 lines
2.4 KiB
Dart
81 lines
2.4 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
/// The messages of the `.arb` file [name] in `lib/l10n/`, without metadata.
|
|
Map<String, String> _messages(String name) => {
|
|
for (final MapEntry(:key, :value) in (jsonDecode(
|
|
File('lib/l10n/$name').readAsStringSync(),
|
|
) as Map<String, Object?>).entries)
|
|
if (!key.startsWith('@')) key: value! as String,
|
|
};
|
|
|
|
/// The names of the placeholders in [message], with plural and select
|
|
/// arguments; the words of their branches don't count. (gen-l10n reads
|
|
/// quotes as text here: `use-escaping` is off.)
|
|
Set<String> _placeholders(String message) {
|
|
final names = <String>{};
|
|
// Even depths hold text and placeholders, odd ones a plural's branches.
|
|
var depth = 0;
|
|
for (var i = 0; i < message.length; i++) {
|
|
switch (message[i]) {
|
|
case '{':
|
|
final name = RegExp(r'\{\s*(\w+)\s*[,}]').matchAsPrefix(message, i);
|
|
if (name != null && depth.isEven) {
|
|
names.add(name.group(1)!);
|
|
}
|
|
depth++;
|
|
case '}':
|
|
depth--;
|
|
}
|
|
}
|
|
return names;
|
|
}
|
|
|
|
void main() {
|
|
final template = _messages('halkyon_editor_en.arb');
|
|
|
|
test('German has every message, with the same placeholders', () {
|
|
final german = _messages('halkyon_editor_de.arb');
|
|
|
|
expect(
|
|
template.keys.where((key) => !german.containsKey(key)),
|
|
isEmpty,
|
|
reason: 'untranslated',
|
|
);
|
|
expect(
|
|
german.keys.where((key) => !template.containsKey(key)),
|
|
isEmpty,
|
|
reason: 'not in the template',
|
|
);
|
|
for (final MapEntry(:key, :value) in german.entries) {
|
|
expect(_placeholders(value), _placeholders(template[key]!), reason: key);
|
|
}
|
|
});
|
|
|
|
test("the English variants override the template's messages only, with "
|
|
'the same placeholders', () {
|
|
for (final variant in [
|
|
'halkyon_editor_en_GB.arb',
|
|
'halkyon_editor_en_US.arb',
|
|
]) {
|
|
for (final MapEntry(:key, :value) in _messages(variant).entries) {
|
|
expect(template, contains(key), reason: '$variant: $key');
|
|
expect(
|
|
_placeholders(value),
|
|
_placeholders(template[key]!),
|
|
reason: '$variant: $key',
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
test('a placeholder is found also inside a plural', () {
|
|
expect(
|
|
_placeholders('{count, plural, =1{1 card} other{{count} cards in {t}}}'),
|
|
{'count', 't'},
|
|
);
|
|
expect(_placeholders("Couldn't open {name}"), {'name'});
|
|
});
|
|
}
|