Skip to content
Open
50 changes: 33 additions & 17 deletions lib/src/config/pubspec_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import '../utils/file_utils.dart';
import 'config_exception.dart';

class PubspecConfig {
bool? _flutter = false;
bool? _enabled;
String? _className;
String? _proxyName;
String? _mainLocale;
String? _arbDir;
String? _outputDir;
Expand All @@ -26,37 +28,51 @@ class PubspecConfig {
"Failed to extract config from the 'pubspec.yaml' file.\nExpected YAML map but got ${pubspecYaml.runtimeType}.");
}

var flutterIntlConfig = pubspecYaml['flutter_intl'];
if (flutterIntlConfig == null) {
return;
}
var intlConfig = pubspecYaml['flutter_intl'];
if (intlConfig == null) {
intlConfig = pubspecYaml['intl'];

if( intlConfig==null)
throw ConfigException(
"Failed to detect flutter or dart context: no 'flutter_intl' or 'intl' keys found into 'pubspec.yaml' file.");

_flutter = false;
} else
_flutter = true;

_enabled = flutterIntlConfig['enabled'] is bool
? flutterIntlConfig['enabled']
_enabled = intlConfig['enabled'] is bool
? intlConfig['enabled']
: null;
_className = flutterIntlConfig['class_name'] is String
? flutterIntlConfig['class_name']
_className = intlConfig['class_name'] is String
? intlConfig['class_name']
: null;
_mainLocale = flutterIntlConfig['main_locale'] is String
? flutterIntlConfig['main_locale']
_proxyName = intlConfig['proxy_class_name'] is String
? intlConfig['proxy_class_name']
: null;
_arbDir = flutterIntlConfig['arb_dir'] is String
? flutterIntlConfig['arb_dir']
_mainLocale = intlConfig['main_locale'] is String
? intlConfig['main_locale']
: null;
_outputDir = flutterIntlConfig['output_dir'] is String
? flutterIntlConfig['output_dir']
_arbDir = intlConfig['arb_dir'] is String
? intlConfig['arb_dir']
: null;
_useDeferredLoading = flutterIntlConfig['use_deferred_loading'] is bool
? flutterIntlConfig['use_deferred_loading']
_outputDir = intlConfig['output_dir'] is String
? intlConfig['output_dir']
: null;
_useDeferredLoading = intlConfig['use_deferred_loading'] is bool
? intlConfig['use_deferred_loading']
: null;
_localizelyConfig =
LocalizelyConfig.fromConfig(flutterIntlConfig['localizely']);
LocalizelyConfig.fromConfig(intlConfig['localizely']);
}

bool? get flutter => _flutter;

bool? get enabled => _enabled;

String? get className => _className;

String? get proxyClassName => _proxyName;

String? get mainLocale => _mainLocale;

String? get arbDir => _arbDir;
Expand Down
1 change: 1 addition & 0 deletions lib/src/constants/constants.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:path/path.dart';

const defaultFlutter = false;
const defaultClassName = 'S';
const defaultMainLocale = 'en';
final defaultArbDir = join('lib', 'l10n');
Expand Down
33 changes: 30 additions & 3 deletions lib/src/generator/generator.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:convert';

import 'package:intl_utils/src/generator/templates_proxy.dart';

import '../config/pubspec_config.dart';
import '../constants/constants.dart';
import '../utils/file_utils.dart';
Expand All @@ -12,16 +14,25 @@ import 'templates.dart';
/// The generator of localization files.
class Generator {
late String _className;
late String _proxyClassName;
late String _mainLocale;
late String _arbDir;
late String _outputDir;
late bool _useDeferredLoading;
late bool _otaEnabled;
late bool _flutter;

/// Creates a new generator with configuration from the 'pubspec.yaml' file.
Generator() {
var pubspecConfig = PubspecConfig();

_flutter = defaultFlutter;
if( pubspecConfig.flutter!=null )
_flutter = pubspecConfig.flutter!;
else {
warning("Config parameter 'flutter_int' or 'intl' required.");
}

_className = defaultClassName;
if (pubspecConfig.className != null) {
if (isValidClassName(pubspecConfig.className!)) {
Expand All @@ -32,6 +43,16 @@ class Generator {
}
}

_proxyClassName = defaultClassName;
if (pubspecConfig.proxyClassName != null) {
if (isValidClassName(pubspecConfig.proxyClassName!)) {
_proxyClassName = pubspecConfig.proxyClassName!;
} else {
warning(
"Config parameter 'proxy_class_name' requires valid 'UpperCamelCase' value.");
}
}

_mainLocale = defaultMainLocale;
if (pubspecConfig.mainLocale != null) {
if (isValidLocale(pubspecConfig.mainLocale!)) {
Expand Down Expand Up @@ -86,12 +107,18 @@ class Generator {
Future<void> _updateGeneratedDir() async {
var labels = _getLabelsFromMainArbFile();
var locales = _orderLocales(getLocales(_arbDir));
var content =
generateL10nDartFileContent(_className, labels, locales, _otaEnabled);
var formattedContent = formatDartContent(content, 'l10n.dart');

var content = generateL10nDartFileContent(_flutter, _className, labels, locales, _otaEnabled);
var formattedContent = formatDartContent(content, 'l10n.dart');
await updateL10nDartFile(formattedContent, _outputDir);

if( _proxyClassName!=null && _proxyClassName.isNotEmpty ) {
content = generateL10nProxyDartFileContent(_flutter, _proxyClassName, labels, locales);
formattedContent = formatDartContent(content, 'l10n_proxy.dart');
await updateL10nProxyDartFile(formattedContent, _outputDir);
info("$_proxyClassName generated");
}

var intlDir = getIntlDirectory(_outputDir);
if (intlDir == null) {
await createIntlDirectory(_outputDir);
Expand Down
55 changes: 54 additions & 1 deletion lib/src/generator/templates.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import '../utils/utils.dart';
import 'label.dart';

String generateL10nDartFileContent(
String className, List<Label> labels, List<String> locales,
bool flutter, String className, List<Label> labels, List<String> locales,
[bool otaEnabled = false]) {
if( flutter )
return """
// GENERATED CODE - DO NOT MODIFY BY HAND
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -84,7 +85,59 @@ ${locales.map((locale) => _generateLocale(locale)).join("\n")}
}
"""
.trim();
else
return """
// GENERATED CODE - DO NOT MODIFY BY HAND
import 'package:intl/intl.dart';${otaEnabled ? '\n${_generateLocalizelySdkImport()}' : ''}
import 'package:intl/locale.dart';
import 'intl/messages_all.dart';

// **************************************************************************
// Generator: Intl IDE plugin
// Made by acorn371
// **************************************************************************

// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars
// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each
// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes

class $className {
$className();

static $className? _current;

static $className get current {
assert(_current != null, 'No instance of $className was loaded. Try to initialize the $className delegate before accessing $className.current.');
return _current!;
}

static Future<$className> init([Locale? locale]) {
if( locale==null)
locale = Locale.parse(Intl.getCurrentLocale());

return load(locale);
}

static Future<$className> load(Locale locale) {
final name = (locale.countryCode?.isEmpty ?? false) ? locale.languageCode : locale.toString();
final localeName = Intl.canonicalizedLocale(name);${otaEnabled ? '\n${_generateMetadataSetter()}' : ''}
return initializeMessages(localeName).then((_) {
Intl.defaultLocale = localeName;
final instance = $className();
$className._current = instance;

return instance;
});
}


${otaEnabled ? '\n${_generateMetadata(labels)}\n' : ''}
${labels.map((label) => label.generateDartGetter()).join("\n\n")}
}
"""
.trim();
}


String _generateLocale(String locale) {
var parts = locale.split('_');
Expand Down
67 changes: 67 additions & 0 deletions lib/src/generator/templates_proxy.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import 'label.dart';

String generateL10nProxyDartFileContent( bool flutter, String className, List<Label> labels, List<String> locales) {
return """
// GENERATED CODE - DO NOT MODIFY BY HAND
import 'package:intl/message_lookup_by_library.dart';
${_generateLocaleImport(locales)}

// **************************************************************************
// Generator: Intl IDE plugin
// Made by acorn371
// **************************************************************************

// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars
// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each
// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes

class _DummyMessageLookup extends MessageLookupByLibrary {
final String localeName;

_DummyMessageLookup(this.localeName);

dynamic operator [](String messageName) => MessageLookupByLibrary.simpleMessage("(unknown locale '\$localeName') \$messageName");

@override
Map<String, dynamic> get messages => {};
}

MessageLookupByLibrary _find(String localeName) {
switch (localeName) {
${_generateLocaleCase(locales, "\t\t")}
default:
return _DummyMessageLookup(localeName);
}
}

class $className {
${_generateMessages(labels,"\t")}
}

""".trim();
}


String _generateLocaleImport(List<String> locales) {
final StringBuffer buffer = StringBuffer();
for( final locale in locales)
buffer.writeln("import 'intl/messages_$locale.dart' as messages_$locale;");

return buffer.toString();
}

String _generateLocaleCase(List<String> locales, String padding) {
final StringBuffer buffer = StringBuffer();
for( final locale in locales)
buffer..writeln("${padding}case '$locale':")..writeln("${padding}\treturn messages_$locale.messages;");

return buffer.toString();
}

String _generateMessages(List<Label> labels, String padding) {
final StringBuffer buffer = StringBuffer();
for( final label in labels)
buffer.writeln("${padding}static String ${label.name}(String localeName) => _find(localeName)['${label.name}']();");

return buffer.toString();
}
10 changes: 5 additions & 5 deletions lib/src/intl_translation/extract_messages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ class MessageFindingVisitor extends GeneralizingAstVisitor {
/// encountered before seeing the Intl.message call.
@override
void visitMethodDeclaration(MethodDeclaration node) {
name = node.name.name;
name = node.name.lexeme;
parameters = node.parameters?.parameters ?? _emptyParameterList;
documentation = node.documentationComment;
super.visitMethodDeclaration(node);
Expand All @@ -285,7 +285,7 @@ class MessageFindingVisitor extends GeneralizingAstVisitor {
/// encountered before seeing the Intl.message call.
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
name = node.name.name;
name = node.name.lexeme;
parameters =
node.functionExpression.parameters?.parameters ?? _emptyParameterList;
documentation = node.documentationComment;
Expand All @@ -302,7 +302,7 @@ class MessageFindingVisitor extends GeneralizingAstVisitor {
// We don't support names in list declarations,
// e.g. String first, second = Intl.message(...);
if (node.fields.variables.length == 1) {
name = node.fields.variables.first.name.name;
name = node.fields.variables.first.name.lexeme;
} else {
name = null;
}
Expand All @@ -321,7 +321,7 @@ class MessageFindingVisitor extends GeneralizingAstVisitor {
// We don't support names in list declarations,
// e.g. String first, second = Intl.message(...);
if (node.variables.variables.length == 1) {
name = node.variables.variables.first.name.name;
name = node.variables.variables.first.name.lexeme;
} else {
name = null;
}
Expand Down Expand Up @@ -435,7 +435,7 @@ class MessageFindingVisitor extends GeneralizingAstVisitor {
message.sourcePosition = node.offset;
message.endPosition = node.end;
message.arguments = parameters
?.map((x) => x.identifier?.name)
?.map((x) => x.name?.lexeme)
.where((x) => x != null)
.cast<String>()
.toList();
Expand Down
8 changes: 4 additions & 4 deletions lib/src/intl_translation/src/intl_message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ abstract class Message {
.toList();
NamedExpression? args = namedExpArgs.isNotEmpty ? namedExpArgs.first : null;

var parameterNames = outerArgs.map((x) => x.identifier?.name).toList();
var parameterNames = outerArgs.map((x) => x.name?.lexeme).toList();
var hasArgs = args != null;
var hasParameters = outerArgs.isNotEmpty;
if (!nameAndArgsGenerated && !hasArgs && hasParameters) {
Expand Down Expand Up @@ -291,16 +291,16 @@ abstract class Message {
/// For a method foo in class Bar we allow either "foo" or "Bar_Foo" as the
/// name.
static String? classPlusMethodName(MethodInvocation node, String? outerName) {
ClassOrMixinDeclaration? classNode(n) {
ClassDeclaration? classNode(n) {
if (n == null) return null;
if (n is ClassOrMixinDeclaration) return n;
if (n is ClassDeclaration) return n;
return classNode(n.parent);
}

var classDeclaration = classNode(node);
return classDeclaration == null
? null
: '${classDeclaration.name.token}_$outerName';
: '${classDeclaration.name}_$outerName';
}

/// Turn a value, typically read from a translation file or created out of an
Expand Down
Loading