diff --git a/README.md b/README.md index 58049ca..2a7145a 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,15 @@ Then we can use the `t()` function to translate our key path. ```dart import 'package:mineral_i18n/mineral_i18n.dart'; -final String sentence = t(Lang.fr, 'foo.bar'); -print(sentence); // bar en français ! +class Foo extends MineralEvent with Translation { + Future handle (Ready event) async { + final String sentence = t(Lang.fr, 'foo.bar'); + print(sentence); // bar en français ! -final String sentence = t(Lang.en_GB, 'foo.bar'); -print(sentence); // bar in english ! + final String sentence = t(Lang.en_GB, 'foo.bar'); + print(sentence); // bar in english ! + } +} ``` ## Injecting variables @@ -61,6 +65,10 @@ Our string is now waiting for a variable named xx which we will give it when we ```dart import 'package:mineral_i18n/mineral_i18n.dart'; -final String sentence = t(Lang.en_GB, 'foo.bar', { 'framework': 'Mineral' }); -print(sentence); // Mineral is my favourite framework ! +class Foo extends MineralEvent with Translation { + Future handle (Ready event) async { + final String sentence = t(Lang.en_GB, 'foo.bar', { 'framework': 'Mineral' }); + print(sentence); // Mineral is my favourite framework ! + } +} ``` diff --git a/lib/mineral_i18n.dart b/lib/mineral_i18n.dart index 2235815..b6887d5 100644 --- a/lib/mineral_i18n.dart +++ b/lib/mineral_i18n.dart @@ -2,3 +2,6 @@ library i18n; export 'src/i18n.dart'; export 'src/lang.dart'; +export 'src/contracts/i18n_contract.dart'; +export 'src/mixins/translation.dart'; + diff --git a/lib/src/contracts/i18n_contract.dart b/lib/src/contracts/i18n_contract.dart new file mode 100644 index 0000000..63fb21d --- /dev/null +++ b/lib/src/contracts/i18n_contract.dart @@ -0,0 +1,8 @@ +import 'dart:io'; + +import 'package:mineral_i18n/src/lang.dart'; + +abstract class I18nContract { + List get languages; + Directory get langPath; +} diff --git a/lib/src/i18n.dart b/lib/src/i18n.dart index f7a09d4..f2b57b4 100644 --- a/lib/src/i18n.dart +++ b/lib/src/i18n.dart @@ -1,17 +1,23 @@ import 'dart:io'; +import 'package:mineral_i18n/src/contracts/i18n_contract.dart'; import 'package:mineral_i18n/src/lang.dart'; -import 'package:mineral_i18n/src/translation.dart'; -import 'package:mineral_ioc/ioc.dart'; +import 'package:mineral_i18n/src/translation_manager.dart'; +import 'package:mineral_package/mineral_package.dart'; import 'package:path/path.dart'; import 'package:yaml/yaml.dart'; -class I18n { - final String label = 'I18n'; - static String get namespace => 'Mineral/Plugins/I18n'; - late final Directory root; +class I18n extends MineralPackage implements I18nContract { + @override + String namespace = 'Mineral/Plugins/I18n'; - Translation translation = Translation(); + @override + String label = 'I18n'; + + @override + String description = ''; + + TranslationManager translationManager = TranslationManager(); final List _languages; final String folder; @@ -21,29 +27,33 @@ class I18n { /// ```dart /// final List allowedLanguages = i18n.languages; /// ``` + @override List get languages => _languages; + /// ## Languages root directory /// ```dart - /// final Directory folder = i18n.langDirectory; + /// final Directory folder = i18n.langPath; /// ``` - Directory get langDirectory => Directory(join(root.path, folder)); + @override + Directory get langPath => Directory(join(root.path, folder)); /// Insert languages into i18n instance void registerLanguages() { for (final Lang lang in _languages) { - translation.cache.putIfAbsent(lang.normalize, () => {}); + translationManager.cache.putIfAbsent(lang.normalize, () => {}); } } /// Initialize i18n package + @override Future init () async { - if (!await langDirectory.exists()) { + if (!await langPath.exists()) { throw Exception('Missing $folder folder'); } registerLanguages(); - _walk(langDirectory); + _walk(langPath); } /// Recursively browses folders to extract translations @@ -53,7 +63,7 @@ class I18n { for (final item in items) { if (item is Directory) { - translation.cache.putIfAbsent(location, () => {}); + translationManager.cache.putIfAbsent(location, () => {}); _walk(item); } @@ -61,40 +71,14 @@ class I18n { final filename = item.path.split(separator).last.split('.').first; final content = loadYaml(item.readAsStringSync()); - if (translation.cache[filename] is Map) { - if (item.parent.path == langDirectory.path) { - translation.cache[location] = content; + if (translationManager.cache[filename] is Map) { + if (item.parent.path == langPath.path) { + translationManager.cache[location] = content; } else { - translation.cache[filename].putIfAbsent(location, () => content); + translationManager.cache[filename].putIfAbsent(location, () => content); } } } } } } - -/// Translates the sentence defined by the key set into the requested language. -/// Replacement parameters can be injected. -/// ```dart -/// final String sentence = t(Lang.enGB, 'foo.bar'); -/// print(sentence) 👈 'Hello {user}' -/// -/// final String sentence = t(Lang.enGB, 'foo.bar', { 'user': 'Freeze' }); -/// print(sentence) 👈 'Hello Freeze' -/// ``` -String t (Lang lang, String key, { Map? replacers }) { - final I18n i18n = ioc.singleton(I18n.namespace); - dynamic target = i18n.translation.cache[lang.normalize]; - - for (final element in key.split('.')) { - target = target[element]; - } - - if (replacers != null) { - for (final replacer in replacers.entries) { - target = target.toString().replaceAll('{${replacer.key}}', replacer.value); - } - } - - return target; -} diff --git a/lib/src/mixins/translation.dart b/lib/src/mixins/translation.dart new file mode 100644 index 0000000..f237724 --- /dev/null +++ b/lib/src/mixins/translation.dart @@ -0,0 +1,33 @@ +import 'package:mineral_i18n/mineral_i18n.dart'; +import 'package:mineral_ioc/ioc.dart'; + +mixin Translation { + I18n _getPlugin () { + final dynamic pluginManager = ioc.services.entries.firstWhere((element) => element.key.toString() == 'PluginManagerCraft').value; + return pluginManager.use(); + } + + /// Translates the sentence defined by the key set into the requested language. + /// Replacement parameters can be injected. + /// ```dart + /// final String sentence = t(Lang.enGB, 'foo.bar'); + /// print(sentence) 👈 'Hello {user}' + /// + /// final String sentence = t(Lang.enGB, 'foo.bar', { 'user': 'Freeze' }); + /// print(sentence) 👈 'Hello Freeze' + /// ``` + String t (Lang lang, String key, { Map? replacers }) { + dynamic target = _getPlugin().translationManager.cache[lang.normalize]; + for (final element in key.split('.')) { + target = target[element]; + } + + if (replacers != null) { + for (final replacer in replacers.entries) { + target = target.toString().replaceAll('{${replacer.key}}', replacer.value); + } + } + + return target; + } +} diff --git a/lib/src/translation.dart b/lib/src/translation_manager.dart similarity index 95% rename from lib/src/translation.dart rename to lib/src/translation_manager.dart index 6f0259d..8e113a3 100644 --- a/lib/src/translation.dart +++ b/lib/src/translation_manager.dart @@ -1,6 +1,6 @@ import 'package:mineral_i18n/src/lang.dart'; -class Translation { +class TranslationManager { final Map _cache = {}; Map get cache => _cache; diff --git a/pubspec.lock b/pubspec.lock index ae62471..cd7f34c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -35,28 +35,28 @@ packages: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.16.0" + version: "1.17.0" convert: dependency: transitive description: name: convert url: "https://pub.dartlang.org" source: hosted - version: "3.0.2" + version: "3.1.0" coverage: dependency: transitive description: name: coverage url: "https://pub.dartlang.org" source: hosted - version: "1.6.0" + version: "1.6.1" crypto: dependency: transitive description: @@ -84,7 +84,7 @@ packages: name: glob url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" http_multi_server: dependency: transitive description: @@ -98,7 +98,7 @@ packages: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "4.0.1" + version: "4.0.2" io: dependency: transitive description: @@ -112,21 +112,21 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.4" + version: "0.6.5" lints: dependency: "direct dev" description: name: lints url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" logging: dependency: transitive description: name: logging url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.1.0" matcher: dependency: transitive description: @@ -147,14 +147,21 @@ packages: name: mime url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" mineral_ioc: dependency: "direct main" description: name: mineral_ioc url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "2.0.0" + mineral_package: + dependency: "direct main" + description: + name: mineral_package + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" node_preamble: dependency: transitive description: @@ -175,7 +182,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.2" + version: "1.8.3" pool: dependency: transitive description: @@ -189,14 +196,14 @@ packages: name: pub_semver url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.3" shelf: dependency: transitive description: name: shelf url: "https://pub.dartlang.org" source: hosted - version: "1.3.2" + version: "1.4.0" shelf_packages_handler: dependency: transitive description: @@ -217,14 +224,14 @@ packages: name: shelf_web_socket url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" source_map_stack_trace: dependency: transitive description: name: source_map_stack_trace url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" source_maps: dependency: transitive description: @@ -252,7 +259,7 @@ packages: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: @@ -308,7 +315,7 @@ packages: name: watcher url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.0.2" web_socket_channel: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index fc95cc5..f77eca9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,8 @@ platforms: windows: dependencies: - mineral_ioc: ^1.0.0 + mineral_ioc: ^2.0.0 + mineral_package: ^1.0.0 path: ^1.8.2 yaml: ^3.1.1 diff --git a/test/mineral_i18n_test.dart b/test/mineral_i18n_test.dart index 3d94f96..f901b45 100644 --- a/test/mineral_i18n_test.dart +++ b/test/mineral_i18n_test.dart @@ -1,19 +1,27 @@ import 'package:mineral_i18n/mineral_i18n.dart'; +import 'package:mineral_i18n/src/mixins/translation.dart'; import 'package:mineral_ioc/ioc.dart'; import 'package:test/test.dart'; +class Foo with Translation { + Function get translator => t; +} + void main() { + final foo = Foo(); final String targetTranslation = 'foo.bar'; final i18n = I18n([Lang.fr, Lang.enGB]) ..registerLanguages(); - i18n.translation + i18n.translationManager ..addTranslations(Lang.fr, { targetTranslation: 'Salut {user}' }) ..addTranslations(Lang.enGB, { targetTranslation: 'Hello {user}' }); test('can register i18n into mineral ioc', () { - ioc.bind(namespace: I18n.namespace, service: i18n); - expect(ioc.singleton(I18n.namespace), equals(i18n)); + final dynamic pluginManager = ioc.services.entries.firstWhere((element) => element.key.toString() == 'PluginManagerCraft').value; + pluginManager.bind((ioc) => I18n([Lang.fr, Lang.enGB])); + + expect(pluginManager.use(), equals(i18n)); }); test('registered lang is two', () { @@ -22,12 +30,12 @@ void main() { }); test('can translate sentence without variables', () { - expect(t(Lang.fr, targetTranslation), equals('Salut {user}')); - expect(t(Lang.enGB, targetTranslation), equals('Hello {user}')); + expect(foo.translator(Lang.fr, targetTranslation), equals('Salut {user}')); + expect(foo.translator(Lang.enGB, targetTranslation), equals('Hello {user}')); }); test('can translate sentence with variables', () { - expect(t(Lang.fr, targetTranslation, replacers: { 'user': 'Freeze' }), equals('Salut Freeze')); - expect(t(Lang.enGB, targetTranslation, replacers: { 'user': 'Freeze' }), equals('Hello Freeze')); + expect(foo.translator(Lang.fr, targetTranslation, replacers: { 'user': 'Freeze' }), equals('Salut Freeze')); + expect(foo.translator(Lang.enGB, targetTranslation, replacers: { 'user': 'Freeze' }), equals('Hello Freeze')); }); }