From 5915aafb95bb5b634baebbc5b40cdf496ed39aba Mon Sep 17 00:00:00 2001 From: Sambo Chea Date: Thu, 17 Jun 2021 15:25:40 +0700 Subject: [PATCH] Task: Add i18n translator and factory for translatable for text formatter and default factory --- .gitignore | 1 + example/cubetiq_text_formatter_example.dart | 4 +-- example/cubetiq_translator_example.dart | 36 ++++++++++++++++++++ lib/i18n_translator.dart | 4 +++ lib/src/i18n/translator_factory.dart | 34 +++++++++++++++++++ lib/src/i18n/translator_provider.dart | 9 +++++ lib/src/text/extension.dart | 7 ++-- lib/src/text/functions.dart | 14 +++++--- lib/src/text/text_formatter.dart | 18 +++++++--- lib/src/xlog/xlog_provider.dart | 2 +- test/cubetiq_i18n_test.dart | 37 +++++++++++++++++++++ test/cubetiq_text_formatter_test.dart | 4 +-- 12 files changed, 154 insertions(+), 16 deletions(-) create mode 100644 example/cubetiq_translator_example.dart create mode 100644 lib/i18n_translator.dart create mode 100644 lib/src/i18n/translator_factory.dart create mode 100644 lib/src/i18n/translator_provider.dart create mode 100644 test/cubetiq_i18n_test.dart diff --git a/.gitignore b/.gitignore index 65c34dc..b9db26b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ build/ # Omit committing pubspec.lock for library packages; see # https://dart.dev/guides/libraries/private-files#pubspeclock. pubspec.lock +.dccache \ No newline at end of file diff --git a/example/cubetiq_text_formatter_example.dart b/example/cubetiq_text_formatter_example.dart index bbeac3a..f920ae9 100644 --- a/example/cubetiq_text_formatter_example.dart +++ b/example/cubetiq_text_formatter_example.dart @@ -3,8 +3,8 @@ import 'package:cubetiq/text.dart'; void main(List args) { var text1 = 'Hello, {0}, then do this it by {1}!'; var text2 = 'Hello, {firstName}, then do this it by {lastName}!'; - var result1 = TextFormatter(text1).format(['Sambo', 'Chea']); - var result2 = TextFormatter(text2).decorate({ + var result1 = TextFormatter(text1).format(args: ['Sambo', 'Chea']); + var result2 = TextFormatter(text2).decorate(params: { 'firstName': 'Sambo', 'lastName': 'Chea', }); diff --git a/example/cubetiq_translator_example.dart b/example/cubetiq_translator_example.dart new file mode 100644 index 0000000..6e76d5c --- /dev/null +++ b/example/cubetiq_translator_example.dart @@ -0,0 +1,36 @@ +import 'package:cubetiq/text.dart'; +import 'package:cubetiq/i18n_translator.dart'; + +void main(List args) { + var text = 'Your name is {name}!'; + // Before set provider + var result = StringUtils.decorator(text, translate: true, params: { + 'name': 'Sambo Chea', + }); + print('Result Before => $result'); + + TranslatorFactory.setProvider(TranslatorProviderExample()); + + // After set provider + result = StringUtils.decorator(text, translate: true, params: { + 'name': 'Sambo Chea', + }); + print('Result After => $result'); +} + +class TranslatorProviderExample implements TranslatorProvider { + @override + bool hasKey(String key) { + return false; + } + + @override + String translate(String key, + {Map? params, String? fallback}) { + if (key == 'Your name is {name}!') { + return 'ឈ្មោះរបស់អ្នកគឺ {name}!'; + } + + return key; + } +} diff --git a/lib/i18n_translator.dart b/lib/i18n_translator.dart new file mode 100644 index 0000000..dc2f37d --- /dev/null +++ b/lib/i18n_translator.dart @@ -0,0 +1,4 @@ +library i18n_translator; + +export 'src/i18n/translator_provider.dart'; +export 'src/i18n/translator_factory.dart'; diff --git a/lib/src/i18n/translator_factory.dart b/lib/src/i18n/translator_factory.dart new file mode 100644 index 0000000..7331115 --- /dev/null +++ b/lib/src/i18n/translator_factory.dart @@ -0,0 +1,34 @@ +import 'translator_provider.dart'; + +/// Translate Factory +/// +/// @author sombochea +/// @since 1.0.0 +class TranslatorFactory { + static TranslatorProvider? _provider; + static void setProvider(TranslatorProvider provider) { + _provider = provider; + } + + static bool hasProvider() => _provider != null; + + static bool hasKey(String key) { + if (hasProvider()) { + return _provider!.hasKey(key); + } + + return false; + } + + static String translate( + String key, { + Map? params, + String? fallback, + }) { + if (hasProvider()) { + return _provider!.translate(key, params: params, fallback: fallback); + } + + return fallback ?? key; + } +} diff --git a/lib/src/i18n/translator_provider.dart b/lib/src/i18n/translator_provider.dart new file mode 100644 index 0000000..26ba015 --- /dev/null +++ b/lib/src/i18n/translator_provider.dart @@ -0,0 +1,9 @@ +/// Translate Provider +/// +/// @author sombochea +/// @since 1.0.0 +abstract class TranslatorProvider { + bool hasKey(String key); + String translate(String key, + {Map? params, String? fallback}); +} diff --git a/lib/src/text/extension.dart b/lib/src/text/extension.dart index c1d011a..a0820ce 100644 --- a/lib/src/text/extension.dart +++ b/lib/src/text/extension.dart @@ -7,10 +7,11 @@ extension StringExtensionOnNonull on String { } extension StringExtensionOnNullable on String? { - String? textFormat(List args) => StringUtils.textFormat(this, args); + String? textFormat({List? args, bool translate = false}) => + StringUtils.textFormat(this, args: args, translate: translate); - String? decorator(Map params) => - StringUtils.decorator(this, params); + String? decorator({Map? params, bool translate = false}) => + StringUtils.decorator(this, params: params, translate: translate); bool get isBlank { if (this == null) return true; diff --git a/lib/src/text/functions.dart b/lib/src/text/functions.dart index a35e252..6908ede 100644 --- a/lib/src/text/functions.dart +++ b/lib/src/text/functions.dart @@ -13,13 +13,19 @@ class StringUtils { return n.toStringAsFixed(n.truncateToDouble() == n ? precision : precision); } + /// Text formatter with custom args + static TextFormatter textFormatter(String? text, {bool translate = false}) => + TextFormatter(text).translate(translate: translate); + /// Text format with custom args - static String? textFormat(String? text, List args) => - TextFormatter(text).format(args); + static String? textFormat(String? text, + {List? args, bool translate = false}) => + textFormatter(text, translate: translate).format(args: args); /// Text decorator with custom key/value params - static String? decorator(String? text, Map params) => - TextFormatter(text).decorate(params); + static String? decorator(String? text, + {Map? params, bool translate = false}) => + textFormatter(text, translate: translate).decorate(params: params); static String? asLowerCaseThenTrim(String? text) => text?.toLowerCase().trim(); diff --git a/lib/src/text/text_formatter.dart b/lib/src/text/text_formatter.dart index a3871f1..b59f712 100644 --- a/lib/src/text/text_formatter.dart +++ b/lib/src/text/text_formatter.dart @@ -1,3 +1,5 @@ +import 'package:cubetiq/i18n_translator.dart'; + /// Text Formatter /// /// @author sombochea @@ -9,12 +11,20 @@ class TextFormatter { this.text = text; } - String? format(List args) { + TextFormatter translate({bool translate = true}) { + if (translate && text != null && text?.isNotEmpty == true) { + text = TranslatorFactory.translate(text!); + } + + return this; + } + + String? format({List? args}) { if (text == null) { return null; } - if (args.isEmpty) { + if (args == null || args.isEmpty == true) { return text; } @@ -31,12 +41,12 @@ class TextFormatter { return msg; } - String? decorate(Map params) { + String? decorate({Map? params}) { if (text == null) { return null; } - if (params.isEmpty) { + if (params == null || params.isEmpty == true) { return text; } diff --git a/lib/src/xlog/xlog_provider.dart b/lib/src/xlog/xlog_provider.dart index 60bfba7..32d603c 100644 --- a/lib/src/xlog/xlog_provider.dart +++ b/lib/src/xlog/xlog_provider.dart @@ -25,7 +25,7 @@ abstract class XLogProvider { if (args == null || args.isEmpty) { content = data; } else { - content = StringUtils.textFormat(data, args) ?? 'null'; + content = StringUtils.textFormat(data, args: args) ?? 'null'; } var text = '[$type] ${nowToString()}: $prefix => $content'.trim(); diff --git a/test/cubetiq_i18n_test.dart b/test/cubetiq_i18n_test.dart new file mode 100644 index 0000000..00a8652 --- /dev/null +++ b/test/cubetiq_i18n_test.dart @@ -0,0 +1,37 @@ +import 'package:cubetiq/i18n_translator.dart'; +import 'package:cubetiq/src/xlog/xlog.dart'; +import 'package:cubetiq/text.dart'; +import 'package:test/test.dart'; + +void main() { + test('test translate function', () { + var text1 = 'Hello, my name is {0}!'; + var result1 = TextFormatter(text1).translate().format(args: ['Sambo']); + XLog.success(result1); + + expect('Hello, my name is Sambo!', result1); + + TranslatorFactory.setProvider(TranslatorProviderExample()); + + result1 = TextFormatter(text1).translate().format(args: ['Sambo']); + XLog.success(result1); + expect('ឈ្មោះរបស់អ្នកគឺ Sambo!', result1); + }); +} + +class TranslatorProviderExample implements TranslatorProvider { + @override + bool hasKey(String key) { + return false; + } + + @override + String translate(String key, + {Map? params, String? fallback}) { + if (key == 'Hello, my name is {0}!') { + return 'ឈ្មោះរបស់អ្នកគឺ {0}!'; + } + + return key; + } +} diff --git a/test/cubetiq_text_formatter_test.dart b/test/cubetiq_text_formatter_test.dart index b7a9939..360e97c 100644 --- a/test/cubetiq_text_formatter_test.dart +++ b/test/cubetiq_text_formatter_test.dart @@ -4,10 +4,10 @@ import 'package:test/test.dart'; void main() { test('text formatter function format', () { var text1 = 'Hello, {0}!'; - var result1 = TextFormatter(text1).format(['Sambo']); + var result1 = TextFormatter(text1).format(args: ['Sambo']); var text2 = 'Hello, {name}!'; - var result2 = TextFormatter(text2).decorate({'name': 'Chea'}); + var result2 = TextFormatter(text2).decorate(params: {'name': 'Chea'}); expect('Hello, Sambo!', result1); expect('Hello, Chea!', result2);