Skip to content

Commit

Permalink
write nested keys as nested objects
Browse files Browse the repository at this point in the history
  • Loading branch information
TarekkMA committed Aug 20, 2021
1 parent 724c6dc commit 34be477
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 41 deletions.
145 changes: 113 additions & 32 deletions bin/generate.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:collection';
import 'dart:convert';
import 'dart:io';

Expand Down Expand Up @@ -164,69 +165,149 @@ void generateFile(List<FileSystemEntity> files, Directory outputPath,
printError('Format not support');
}

classBuilder.writeln('}');
generatedFile.writeAsStringSync(classBuilder.toString());

printInfo('All done! File generated in ${outputPath.path}');
}

Future _writeKeys(StringBuffer classBuilder, List<FileSystemEntity> files,
bool? skipUnnecessaryKeys) async {
class _NestedTranslationObject {
final bool isRootObject;
final String className;
final Map<String, dynamic> translations;
final bool shouldHaveValueGetter;
final String value;

_NestedTranslationObject({
required this.className,
required this.translations,
required this.isRootObject,
this.shouldHaveValueGetter = false,
this.value = '',
});
}

Future _writeKeys(
StringBuffer classBuilder,
List<FileSystemEntity> files,
bool? skipUnnecessaryKeys,
) async {
var file = '''
// DO NOT EDIT. This is code generated via package:easy_localization/generate.dart
abstract class LocaleKeys {
''';

final fileData = File(files.first.path);

Map<String, dynamic> translations =
json.decode(await fileData.readAsString());

file += _resolve(translations, skipUnnecessaryKeys);
final processingQueue = Queue<_NestedTranslationObject>.from([
_NestedTranslationObject(
className: 'LocaleKeys',
translations: translations,
isRootObject: true,
)
]);

while (processingQueue.isNotEmpty) {
file += _processKeys(processingQueue, skipUnnecessaryKeys);
}

classBuilder.writeln(file);
}

String _resolve(Map<String, dynamic> translations, bool? skipUnnecessaryKeys,
[String? accKey]) {
var fileContent = '';
bool _containsOnlyPreservedKeywords(Map<String, dynamic> map) =>
map.keys.every((element) => _preservedKeywords.contains(element));

bool _containsPreservedKeywords(Map<String, dynamic> map) =>
map.keys.any((element) => _preservedKeywords.contains(element));

String _processKeys(
Queue<_NestedTranslationObject> processingQueue,
bool? skipUnnecessaryKeys,
) {
var classContent = '';

final nestedObject = processingQueue.removeFirst();

classContent += nestedObject.isRootObject ? 'abstract class' : 'class';
classContent += ' ${nestedObject.className} ';
classContent += '{\n';
classContent +=
nestedObject.isRootObject ? '' : ' const ${nestedObject.className}();\n';
if (nestedObject.shouldHaveValueGetter) {
classContent += ' String val() => \'${nestedObject.value}\';\n';
}

final translations = nestedObject.translations;

final sortedKeys = translations.keys.toList();

final canIgnoreKeys = skipUnnecessaryKeys == true;

bool containsPreservedKeywords(Map<String, dynamic> map) =>
map.keys.any((element) => _preservedKeywords.contains(element));

for (var key in sortedKeys) {
var ignoreKey = false;
if (translations[key] is Map) {
// If key does not contain keys for plural(), gender() etc. and option is enabled -> ignore it
ignoreKey = !containsPreservedKeywords(
translations[key] as Map<String, dynamic>) &&
canIgnoreKeys;
final nestedValue =
nestedObject.isRootObject ? key : '${nestedObject.value}.$key';

var nextAccKey = key;
if (accKey != null) {
nextAccKey = '$accKey.$key';
}
if (translations[key] is Map &&
!_containsOnlyPreservedKeywords(translations[key])) {
final nestedClassName = nestedObject.isRootObject
? '_$key'
: '${nestedObject.className}_$key';

fileContent +=
_resolve(translations[key], skipUnnecessaryKeys, nextAccKey);
}
final ignoreKey = !_containsPreservedKeywords(
translations[key] as Map<String, dynamic>) &&
canIgnoreKeys;

if (!_preservedKeywords.contains(key)) {
accKey != null && !ignoreKey
? fileContent +=
' static const ${accKey.replaceAll('.', '_')}\_$key = \'$accKey.$key\';\n'
: !ignoreKey
? fileContent += ' static const $key = \'$key\';\n'
: null;
processingQueue.addLast(_NestedTranslationObject(
className: nestedClassName,
translations: translations[key],
isRootObject: false,
value: nestedValue,
shouldHaveValueGetter: !ignoreKey,
));

classContent += _writeNestedObjectField(
name: key,
className: nestedClassName,
isRootObject: nestedObject.isRootObject,
);
} else if (!_preservedKeywords.contains(key)) {
classContent += _writeKeyField(
name: key,
value: nestedValue,
isRootObjcet: nestedObject.isRootObject,
);
}
}

return fileContent;
return classContent + '}\n\n';
}

String _writeNestedObjectField({
required String name,
required String className,
required bool isRootObject,
}) {
var field = ' ';
field += isRootObject ? 'static const ' : 'final ';
field += name;
field += ' = ';
field += isRootObject ? '$className()' : 'const $className()';
return field + ';\n';
}

String _writeKeyField({
required String name,
required String value,
required bool isRootObjcet,
}) {
var field = ' ';
field += isRootObjcet ? 'static const ' : 'final ';
field += name;
field += ' = ';
field += "'$value'";
return field + ';\n';
}

Future _writeJson(
Expand Down
29 changes: 22 additions & 7 deletions example/lib/generated/locale_keys.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,29 @@ abstract class LocaleKeys {
static const msg = 'msg';
static const msg_named = 'msg_named';
static const clickMe = 'clickMe';
static const reset_password_label = 'reset_password.label';
static const reset_password_username = 'reset_password.username';
static const reset_password_password = 'reset_password.password';
static const profile_reset_password = 'profile.reset_password';
static const profile = 'profile';
static const profile = _profile();
static const clicked = 'clicked';
static const amount = 'amount';
static const gender_with_arg = 'gender.with_arg';
static const gender = 'gender';
static const gender = _gender();
static const reset_locale = 'reset_locale';
}

class _profile {
const _profile();
final reset_password = const _profile_reset_password();
}

class _gender {
const _gender();
String val() => 'gender';
final with_arg = 'gender.with_arg';
}

class _profile_reset_password {
const _profile_reset_password();
final label = 'profile.reset_password.label';
final username = 'profile.reset_password.username';
final password = 'profile.reset_password.password';
}


4 changes: 2 additions & 2 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,14 @@ class _MyHomePageState extends State<MyHomePage> {
flex: 1,
),
Text(
LocaleKeys.gender_with_arg,
LocaleKeys.gender.with_arg,
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 19,
fontWeight: FontWeight.bold),
).tr(args: ['aissat'], gender: _gender ? 'female' : 'male'),
Text(
tr(LocaleKeys.gender, gender: _gender ? 'female' : 'male'),
tr(LocaleKeys.gender.val(), gender: _gender ? 'female' : 'male'),
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 15,
Expand Down

0 comments on commit 34be477

Please sign in to comment.