From 8761ad44f8a284f0eeedacf4c086e0dec2214048 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Mon, 11 May 2020 23:16:53 +0300 Subject: [PATCH] feat: improve work, export formats, translate, settings --- README.md | 4 +- lib/core/hive_cache_manager.dart | 10 +- lib/generated/intl/messages_all.dart | 12 +- lib/generated/intl/messages_en.dart | 43 --- lib/generated/intl/messages_en_US.dart | 75 ++++++ lib/generated/intl/messages_ko_KR.dart | 24 -- lib/generated/l10n.dart | 255 +++++++++++++++++- lib/l10n/intl_en.arb | 17 -- lib/l10n/intl_en_US.arb | 46 ++++ lib/l10n/intl_ko_KR.arb | 1 - lib/project/index.dart | 2 - lib/project/project_event.dart | 1 + lib/project/project_model.dart | 182 +++++++++++-- lib/project/project_page.dart | 50 +--- lib/project/project_provider.dart | 19 -- lib/project/project_repository.dart | 11 - lib/project/project_screen.dart | 50 ++-- .../project_setting_screen.dart | 158 +++++++++-- lib/projects/projects_page.dart | 53 ++-- lib/projects/projects_screen.dart | 11 +- lib/service/notification_service.dart | 5 +- lib/service/translate_service.dart | 176 ++++++------ lib/widget/edit_key.dart | 32 +-- lib/widget/edit_lang_word.dart | 28 +- lib/widget/empty.dart | 25 ++ lib/widget/index.dart | 1 + lib/widget/lang_dropdown.dart | 18 +- lib/widget/translate_word.dart | 2 +- pubspec.yaml | 7 +- 29 files changed, 936 insertions(+), 382 deletions(-) delete mode 100644 lib/generated/intl/messages_en.dart create mode 100644 lib/generated/intl/messages_en_US.dart delete mode 100644 lib/generated/intl/messages_ko_KR.dart delete mode 100644 lib/l10n/intl_en.arb create mode 100644 lib/l10n/intl_en_US.arb delete mode 100644 lib/l10n/intl_ko_KR.arb delete mode 100644 lib/project/project_provider.dart delete mode 100644 lib/project/project_repository.dart create mode 100644 lib/widget/empty.dart diff --git a/README.md b/README.md index a885fab..8cf36d7 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# iXn \ No newline at end of file +# iXn + +Control your localization of apps with iXn! \ No newline at end of file diff --git a/lib/core/hive_cache_manager.dart b/lib/core/hive_cache_manager.dart index 06e25fa..311f4e1 100644 --- a/lib/core/hive_cache_manager.dart +++ b/lib/core/hive_cache_manager.dart @@ -5,9 +5,9 @@ import 'package:semaphore/semaphore.dart'; import 'package:pedantic/pedantic.dart'; class HiveCacheManager { - String _key = 'cache_v1'; + String _key = 'cache_v2'; String _nameLogger = 'HiveCacheManager'; - String _keyTime = 'cache_time_v1'; + String _keyTime = 'cache_time_v2'; Duration duration = Duration(hours: 8); final _sm = LocalSemaphore(1); @@ -22,9 +22,9 @@ class HiveCacheManager { } void init(String name, Duration duration) { - _key = '${name.toLowerCase()}_cache_v1'; - _nameLogger = '${name}CacheManager'; - _keyTime = '${name.toLowerCase()}_time_cache_v1'; + _key = '${name.toLowerCase()}$_key'; + _nameLogger = '$name$_nameLogger'; + _keyTime = '${name.toLowerCase()}$_keyTime'; this.duration = duration; } diff --git a/lib/generated/intl/messages_all.dart b/lib/generated/intl/messages_all.dart index 941f06a..d9ac785 100644 --- a/lib/generated/intl/messages_all.dart +++ b/lib/generated/intl/messages_all.dart @@ -15,23 +15,19 @@ import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; import 'package:intl/src/intl_helpers.dart'; -import 'messages_en.dart' as messages_en; -import 'messages_ko_KR.dart' as messages_ko_kr; +import 'messages_en_US.dart' as messages_en_us; import 'messages_ru_RU.dart' as messages_ru_ru; typedef Future LibraryLoader(); Map _deferredLibraries = { - 'en': () => new Future.value(null), - 'ko_KR': () => new Future.value(null), + 'en_US': () => new Future.value(null), 'ru_RU': () => new Future.value(null), }; MessageLookupByLibrary _findExact(String localeName) { switch (localeName) { - case 'en': - return messages_en.messages; - case 'ko_KR': - return messages_ko_kr.messages; + case 'en_US': + return messages_en_us.messages; case 'ru_RU': return messages_ru_ru.messages; default: diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart deleted file mode 100644 index 66a0db7..0000000 --- a/lib/generated/intl/messages_en.dart +++ /dev/null @@ -1,43 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a en locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'en'; - - static m0(locale, path) => - "Locale ${locale} from file ${path} doesnt supported. Use name of file like \'intl_ko_KR.arb\' or \'ko-KR.json\' where \'ko\' - target locale"; - - final messages = _notInlinedMessages(_notInlinedMessages); - static _notInlinedMessages(_) => { - "close": MessageLookupByLibrary.simpleMessage("Close"), - "discard": MessageLookupByLibrary.simpleMessage("Discard"), - "error_locale_dublicate": MessageLookupByLibrary.simpleMessage("dublicate locale"), - "error_locale_notsupport": m0, - "error_reload": MessageLookupByLibrary.simpleMessage("Reload"), - "error_unsaved": MessageLookupByLibrary.simpleMessage("The form contains some unsaved changes.\r\nDo you want to save all entered data?"), - "page_settings": MessageLookupByLibrary.simpleMessage("Project Settings"), - "project_default_locale": MessageLookupByLibrary.simpleMessage("Default Locale"), - "project_key": MessageLookupByLibrary.simpleMessage("Key"), - "project_name": MessageLookupByLibrary.simpleMessage("Project Name"), - "projects_card_locale": MessageLookupByLibrary.simpleMessage("Locale"), - "projects_card_locales": MessageLookupByLibrary.simpleMessage("Supported Locales"), - "projects_card_name": MessageLookupByLibrary.simpleMessage("Name"), - "save": MessageLookupByLibrary.simpleMessage("Save"), - "save_data": MessageLookupByLibrary.simpleMessage("Save changes") - }; -} diff --git a/lib/generated/intl/messages_en_US.dart b/lib/generated/intl/messages_en_US.dart new file mode 100644 index 0000000..6b01d4f --- /dev/null +++ b/lib/generated/intl/messages_en_US.dart @@ -0,0 +1,75 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a en_US locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'en_US'; + + static m0(locale, path) => + "Locale ${locale} from file ${path} doesnt supported. Use name of file like \'intl_ko_KR.arb\' or \'ko-KR.json\' where \'ko\' - target locale"; + + static m1(name) => "Project: ${name}"; + + static m2(text) => "429 for ${text}"; + + final messages = _notInlinedMessages(_notInlinedMessages); + static _notInlinedMessages(_) => { + "close": MessageLookupByLibrary.simpleMessage("Close"), + "discard": MessageLookupByLibrary.simpleMessage("Discard"), + "edit_lang_approve": MessageLookupByLibrary.simpleMessage("Approve after import or change"), + "edit_lang_changed": MessageLookupByLibrary.simpleMessage("Value of default locale was changed"), + "error_error": MessageLookupByLibrary.simpleMessage("Error"), + "error_formats": MessageLookupByLibrary.simpleMessage("Add formats in setting of project"), + "error_locale_dublicate": MessageLookupByLibrary.simpleMessage("dublicate locale"), + "error_locale_notsupport": m0, + "error_reload": MessageLookupByLibrary.simpleMessage("Reload"), + "error_unsaved": MessageLookupByLibrary.simpleMessage("The form contains some unsaved changes.\r\nDo you want to save all entered data?"), + "notif_ok": MessageLookupByLibrary.simpleMessage("Ok"), + "page_settings": MessageLookupByLibrary.simpleMessage("Project Settings"), + "project_add": MessageLookupByLibrary.simpleMessage("Add"), + "project_default_locale": MessageLookupByLibrary.simpleMessage("Default Locale"), + "project_empty": MessageLookupByLibrary.simpleMessage("Please, add new key for control Localizations of your app"), + "project_export": MessageLookupByLibrary.simpleMessage("Export"), + "project_import": MessageLookupByLibrary.simpleMessage("Import"), + "project_key": MessageLookupByLibrary.simpleMessage("Key"), + "project_locale": MessageLookupByLibrary.simpleMessage("Locale"), + "project_name": MessageLookupByLibrary.simpleMessage("Project Name"), + "project_name_no": MessageLookupByLibrary.simpleMessage("No name"), + "project_name_title": m1, + "project_save": MessageLookupByLibrary.simpleMessage("Save"), + "project_setting_divider": MessageLookupByLibrary.simpleMessage("Divider locale and country code"), + "project_setting_export": MessageLookupByLibrary.simpleMessage("Export settings"), + "project_setting_export_empty": MessageLookupByLibrary.simpleMessage("Please add export setting"), + "project_setting_ext": MessageLookupByLibrary.simpleMessage("File exteshion"), + "project_setting_filter": MessageLookupByLibrary.simpleMessage("Filter for supported locales"), + "project_setting_postfix": MessageLookupByLibrary.simpleMessage("Postfix"), + "project_setting_prefix": MessageLookupByLibrary.simpleMessage("Prefix"), + "projects_add": MessageLookupByLibrary.simpleMessage("Add"), + "projects_card_locale": MessageLookupByLibrary.simpleMessage("Locale"), + "projects_card_locales": MessageLookupByLibrary.simpleMessage("Supported Locales"), + "projects_card_name": MessageLookupByLibrary.simpleMessage("Name"), + "projects_emprty": MessageLookupByLibrary.simpleMessage("Please, add new project for control Localizations of your apps"), + "projects_import": MessageLookupByLibrary.simpleMessage("Import"), + "projects_remove": MessageLookupByLibrary.simpleMessage("Remove"), + "projects_show_remove": MessageLookupByLibrary.simpleMessage("Show remove button"), + "projects_title": MessageLookupByLibrary.simpleMessage("Projects"), + "save": MessageLookupByLibrary.simpleMessage("Save"), + "save_data": MessageLookupByLibrary.simpleMessage("Save changes"), + "translate_429": m2, + "value_required": MessageLookupByLibrary.simpleMessage("value required") + }; +} diff --git a/lib/generated/intl/messages_ko_KR.dart b/lib/generated/intl/messages_ko_KR.dart deleted file mode 100644 index b8ed4ce..0000000 --- a/lib/generated/intl/messages_ko_KR.dart +++ /dev/null @@ -1,24 +0,0 @@ -// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart -// This is a library that provides messages for a ko_KR locale. All the -// messages from the main program should be duplicated here with the same -// function name. - -// Ignore issues from commonly used lints in this file. -// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new -// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering -// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names - -import 'package:intl/intl.dart'; -import 'package:intl/message_lookup_by_library.dart'; - -final messages = new MessageLookup(); - -typedef String MessageIfAbsent(String messageStr, List args); - -class MessageLookup extends MessageLookupByLibrary { - String get localeName => 'ko_KR'; - - final messages = _notInlinedMessages(_notInlinedMessages); - static _notInlinedMessages(_) => {}; -} diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index f956840..765595f 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -44,6 +44,15 @@ class S { ); } + String get project_locale { + return Intl.message( + 'Locale', + name: 'project_locale', + desc: '', + args: [], + ); + } + String get projects_card_locales { return Intl.message( 'Supported Locales', @@ -160,6 +169,249 @@ class S { args: [locale, path], ); } + + String get project_name_no { + return Intl.message( + 'No name', + name: 'project_name_no', + desc: '', + args: [], + ); + } + + String project_name_title(Object name) { + return Intl.message( + 'Project: $name', + name: 'project_name_title', + desc: '', + args: [name], + ); + } + + String get project_add { + return Intl.message( + 'Add', + name: 'project_add', + desc: '', + args: [], + ); + } + + String get project_export { + return Intl.message( + 'Export', + name: 'project_export', + desc: '', + args: [], + ); + } + + String get project_import { + return Intl.message( + 'Import', + name: 'project_import', + desc: '', + args: [], + ); + } + + String get project_save { + return Intl.message( + 'Save', + name: 'project_save', + desc: '', + args: [], + ); + } + + String get error_error { + return Intl.message( + 'Error', + name: 'error_error', + desc: '', + args: [], + ); + } + + String get project_empty { + return Intl.message( + 'Please, add new key for control Localizations of your app', + name: 'project_empty', + desc: '', + args: [], + ); + } + + String get project_setting_export { + return Intl.message( + 'Export settings', + name: 'project_setting_export', + desc: '', + args: [], + ); + } + + String get project_setting_export_empty { + return Intl.message( + 'Please add export setting', + name: 'project_setting_export_empty', + desc: '', + args: [], + ); + } + + String get project_setting_prefix { + return Intl.message( + 'Prefix', + name: 'project_setting_prefix', + desc: '', + args: [], + ); + } + + String get project_setting_postfix { + return Intl.message( + 'Postfix', + name: 'project_setting_postfix', + desc: '', + args: [], + ); + } + + String get project_setting_divider { + return Intl.message( + 'Divider locale and country code', + name: 'project_setting_divider', + desc: '', + args: [], + ); + } + + String get project_setting_ext { + return Intl.message( + 'File exteshion', + name: 'project_setting_ext', + desc: '', + args: [], + ); + } + + String get project_setting_filter { + return Intl.message( + 'Filter for supported locales', + name: 'project_setting_filter', + desc: '', + args: [], + ); + } + + String get projects_title { + return Intl.message( + 'Projects', + name: 'projects_title', + desc: '', + args: [], + ); + } + + String get projects_add { + return Intl.message( + 'Add', + name: 'projects_add', + desc: '', + args: [], + ); + } + + String get projects_show_remove { + return Intl.message( + 'Show remove button', + name: 'projects_show_remove', + desc: '', + args: [], + ); + } + + String get projects_import { + return Intl.message( + 'Import', + name: 'projects_import', + desc: '', + args: [], + ); + } + + String get projects_emprty { + return Intl.message( + 'Please, add new project for control Localizations of your apps', + name: 'projects_emprty', + desc: '', + args: [], + ); + } + + String get projects_remove { + return Intl.message( + 'Remove', + name: 'projects_remove', + desc: '', + args: [], + ); + } + + String get notif_ok { + return Intl.message( + 'Ok', + name: 'notif_ok', + desc: '', + args: [], + ); + } + + String get edit_lang_approve { + return Intl.message( + 'Approve after import or change', + name: 'edit_lang_approve', + desc: '', + args: [], + ); + } + + String get edit_lang_changed { + return Intl.message( + 'Value of default locale was changed', + name: 'edit_lang_changed', + desc: '', + args: [], + ); + } + + String get value_required { + return Intl.message( + 'value required', + name: 'value_required', + desc: '', + args: [], + ); + } + + String translate_429(Object text) { + return Intl.message( + '429 for $text', + name: 'translate_429', + desc: '', + args: [text], + ); + } + + String get error_formats { + return Intl.message( + 'Add formats in setting of project', + name: 'error_formats', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { @@ -167,8 +419,7 @@ class AppLocalizationDelegate extends LocalizationsDelegate { List get supportedLocales { return const [ - Locale.fromSubtags(languageCode: 'en'), - Locale.fromSubtags(languageCode: 'ko', countryCode: 'KR'), + Locale.fromSubtags(languageCode: 'en', countryCode: 'US'), Locale.fromSubtags(languageCode: 'ru', countryCode: 'RU'), ]; } diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb deleted file mode 100644 index 9477911..0000000 --- a/lib/l10n/intl_en.arb +++ /dev/null @@ -1,17 +0,0 @@ -{ - "projects_card_name" : "Name", - "projects_card_locale" : "Locale", - "projects_card_locales" : "Supported Locales", - "project_name" : "Project Name", - "project_key" : "Key", - "project_default_locale" : "Default Locale", - "error_reload" : "Reload", - "save": "Save", - "close": "Close", - "save_data": "Save changes", - "discard": "Discard", - "error_unsaved": "The form contains some unsaved changes.\r\nDo you want to save all entered data?", - "page_settings": "Project Settings", - "error_locale_dublicate" : "dublicate locale", - "error_locale_notsupport" : "Locale {locale} from file {path} doesnt supported. Use name of file like 'intl_ko_KR.arb' or 'ko-KR.json' where 'ko' - target locale" -} \ No newline at end of file diff --git a/lib/l10n/intl_en_US.arb b/lib/l10n/intl_en_US.arb new file mode 100644 index 0000000..9b4be5c --- /dev/null +++ b/lib/l10n/intl_en_US.arb @@ -0,0 +1,46 @@ +{ + "projects_card_name": "Name", + "projects_card_locale": "Locale", + "project_locale" : "Locale", + "projects_card_locales": "Supported Locales", + "project_name": "Project Name", + "project_key": "Key", + "project_default_locale": "Default Locale", + "error_reload": "Reload", + "save": "Save", + "close": "Close", + "save_data": "Save changes", + "discard": "Discard", + "error_unsaved": "The form contains some unsaved changes.\r\nDo you want to save all entered data?", + "page_settings": "Project Settings", + "error_locale_dublicate": "dublicate locale", + "error_locale_notsupport": "Locale {locale} from file {path} doesnt supported. Use name of file like 'intl_ko_KR.arb' or 'ko-KR.json' where 'ko' - target locale", + "project_name_no": "No name", + "project_name_title": "Project: {name}", + "project_add": "Add", + "project_export": "Export", + "project_import": "Import", + "project_save": "Save", + "error_error": "Error", + "project_empty": "Please, add new key for control Localizations of your app", + "project_setting_export": "Export settings", + "project_setting_export_empty": "Please add export setting", + "project_setting_prefix": "Prefix", + "project_setting_postfix": "Postfix", + "project_setting_divider" : "Divider locale and country code", + "project_setting_ext": "File exteshion", + "project_setting_filter": "Filter for supported locales", + "projects_title": "Projects", + "projects_add": "Add", + "projects_show_remove": "Show remove button", + "projects_import": "Import", + "projects_emprty": "Please, add new project for control Localizations of your apps", + "projects_remove": "Remove", + "notif_ok": "Ok", + "edit_lang_approve": "Approve after import or change", + "edit_lang_changed": "Value of default locale was changed", + "value_required": "value required", + "translate_429": "429 for {text}", + "error_formats" : "Add formats in setting of project" + } + \ No newline at end of file diff --git a/lib/l10n/intl_ko_KR.arb b/lib/l10n/intl_ko_KR.arb deleted file mode 100644 index 9e26dfe..0000000 --- a/lib/l10n/intl_ko_KR.arb +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/lib/project/index.dart b/lib/project/index.dart index 5e43b05..18cbb04 100644 --- a/lib/project/index.dart +++ b/lib/project/index.dart @@ -3,7 +3,5 @@ export 'project_cache_manager.dart'; export 'project_event.dart'; export 'project_model.dart'; export 'project_page.dart'; -export 'project_provider.dart'; -export 'project_repository.dart'; export 'project_screen.dart'; export 'project_state.dart'; diff --git a/lib/project/project_event.dart b/lib/project/project_event.dart index 2a941f6..06b9cce 100644 --- a/lib/project/project_event.dart +++ b/lib/project/project_event.dart @@ -108,6 +108,7 @@ class LoadSettingProjectEvent extends ProjectEvent { name: project.name, defaultLocale: project.defaultLocale, locales: project.locales, + formats: project.formats, ), version: currentState.version + 1); } diff --git a/lib/project/project_model.dart b/lib/project/project_model.dart index ae4c622..3ffe7f5 100644 --- a/lib/project/project_model.dart +++ b/lib/project/project_model.dart @@ -1,16 +1,116 @@ import 'dart:convert'; +import 'package:archive/archive.dart'; import 'package:equatable/equatable.dart'; +import 'package:file_manager/file_manager.dart' as manager; import 'package:flutter/foundation.dart'; import 'package:uuid/uuid.dart'; +import 'package:doppio_dev_ixn/core/index.dart'; +import 'package:doppio_dev_ixn/service/index.dart'; + +class LocaleModel extends Equatable { + final String countryName; + final String locale; + final String countryCode; + String get key => '$locale-$countryCode'; + LocaleModel({ + this.countryName, + this.locale, + this.countryCode, + }); + + @override + List get props => [ + countryName, + locale, + countryCode, + ]; + + Map toMap() { + return { + 'countryName': countryName, + 'locale': locale, + 'countryCode': countryCode, + }; + } + + static LocaleModel fromMap(Map map) { + if (map == null) return null; + + return LocaleModel( + countryName: map['countryName']?.toString(), + locale: map['locale']?.toString(), + countryCode: map['countryCode']?.toString(), + ); + } + + String toJson() => json.encode(toMap()); + + static LocaleModel fromJson(String source) => fromMap(json.decode(source) as Map); + + static LocaleModel from(MapEntry localeCode) { + final split = localeCode.value.split('-'); + return LocaleModel(countryName: localeCode.key, locale: split[0], countryCode: split[1]); + } + + @override + String toString() => '$countryName: $locale-$countryCode'; +} + +class ExportFormatModel extends Equatable { + String prefix; + String postfix; + String divider; + String fileExtension; + ExportFormatModel({ + this.prefix, + this.postfix, + this.divider, + this.fileExtension, + }); + + Map toMap() { + return { + 'prefix': prefix, + 'postfix': postfix, + 'divider': divider, + 'fileExtension': fileExtension, + }; + } + + static ExportFormatModel fromMap(Map map) { + if (map == null) return null; + + return ExportFormatModel( + prefix: map['prefix']?.toString(), + postfix: map['postfix']?.toString(), + divider: map['divider']?.toString(), + fileExtension: map['fileExtension']?.toString(), + ); + } + + String toJson() => json.encode(toMap()); + + static ExportFormatModel fromJson(String source) => fromMap(json.decode(source) as Map); + + @override + List get props => [ + prefix, + postfix, + divider, + fileExtension, + ]; +} + class ProjectModel extends Equatable { final String id; String name; - List locales; - String defaultLocale; + List locales; + LocaleModel defaultLocale; final List keys; List words; + List formats; Map wordMap; ProjectModel({ @@ -20,6 +120,7 @@ class ProjectModel extends Equatable { this.locales, this.defaultLocale, this.words, + this.formats, }) { wordMap = {}; if (words != null) { @@ -37,6 +138,7 @@ class ProjectModel extends Equatable { locales, defaultLocale, words, + formats, ]; Map toMap() { @@ -44,8 +146,9 @@ class ProjectModel extends Equatable { 'id': id, 'name': name, 'keys': keys?.map((x) => x?.toMap())?.toList(), - 'locales': locales?.toSet()?.toList(), - 'defaultLocale': defaultLocale, + 'formats': formats?.map((x) => x?.toMap())?.toList(), + 'locales': locales?.map((x) => x?.toMap())?.toList(), + 'defaultLocale': defaultLocale?.toMap(), 'words': words?.map((x) => x?.toMap())?.toList(), }; } @@ -56,8 +159,10 @@ class ProjectModel extends Equatable { return ProjectModel( id: map['id']?.toString(), name: map['name']?.toString(), - locales: List.from(map['locales'] as Iterable ?? []), - defaultLocale: map['defaultLocale']?.toString(), + defaultLocale: LocaleModel.fromMap(map['defaultLocale'] as Map), + locales: List.from((map['locales'] as List ?? [])?.map((c) => LocaleModel.fromMap(c as Map))), + formats: + List.from((map['formats'] as List ?? [])?.map((c) => ExportFormatModel.fromMap(c as Map))), keys: List.from((map['keys'] as List ?? [])?.map((c) => KeyModel.fromMap(c as Map))), words: List.from((map['words'] as List ?? [])?.map((c) => WordModel.fromMap(c as Map))), ); @@ -70,10 +175,11 @@ class ProjectModel extends Equatable { ProjectModel copyWith({ String id, String name, - List locales, - String defaultLocale, + List locales, + LocaleModel defaultLocale, List keys, List words, + List formats, }) { return ProjectModel( id: id ?? this.id, @@ -82,13 +188,15 @@ class ProjectModel extends Equatable { defaultLocale: defaultLocale ?? this.defaultLocale, keys: keys ?? this.keys, words: words ?? this.words, + formats: formats ?? this.formats, ); } ProjectModel copySettings({ @required String name, - @required List locales, - @required String defaultLocale, + @required List locales, + @required LocaleModel defaultLocale, + @required List formats, }) { return ProjectModel( id: id, @@ -97,10 +205,11 @@ class ProjectModel extends Equatable { defaultLocale: defaultLocale ?? this.defaultLocale, keys: keys, words: words, + formats: formats ?? this.formats, ); } - void import(String locale, Map filesData) { + void import(LocaleModel locale, Map filesData) { defaultLocale ??= locale; if (!locales.contains(locale)) { locales.add(locale); @@ -111,7 +220,7 @@ class ProjectModel extends Equatable { key = KeyModel(id: Uuid().v4(), value: item); keys.add(key); } - final newKeyDiff = '${key.id}$locale'; + final newKeyDiff = '${key.id}${locale.key}'; if (wordMap.containsKey(newKeyDiff)) { // TODO: version for approve changed wordMap[newKeyDiff].value = filesData[item]; @@ -123,7 +232,44 @@ class ProjectModel extends Equatable { } } - WordModel getWord(String newkey, KeyModel key, String locale) { + Future export() async { + try { + if (formats == null || formats.isEmpty) { + throw Exception(TranslateService().locale.error_formats); + } + var index = 0; + for (var format in formats) { + var encoder = ZipEncoder(); + final archive = Archive(); + for (var locale in locales) { + final mapWord = {}; + final result = {}; + words.where((c) => c.locale == locale).map((e) => mapWord[e.keyId] = e.value).toList(); + for (var item in keys) { + // TODO: check - maybe will give bug + result['"${item.value}"'] = '"${mapWord[item.id].replaceAll('\r', '\\r').replaceAll('\n', '\\n')}"'; + } + final bytes = Utf8Codec().encode(result.toString()); + final fileName = + '${format.prefix ?? ''}${locale.locale}${format.divider ?? ''}${locale.countryCode}${format.postfix ?? ''}.${format.fileExtension}'; + final archiveFile = ArchiveFile(fileName, bytes.length, bytes); + archive.addFile(archiveFile); + } + final bytes = Utf8Codec().encode(toJson()); + final archiveFile = ArchiveFile('ixn.json', bytes.length, bytes); + archive.addFile(archiveFile); + final bytesZip = encoder.encode(archive); + await manager.saveFile('$name-ixn-$index.zip', binaryData: bytesZip); + index++; + } + // projectScreen.import(filesData); + } catch (_, stackTrace) { + log(_?.toString(), name: 'ProjectsPage', error: _, stackTrace: stackTrace); + NotificationService.showError(_?.toString()); + } + } + + WordModel getWord(String newkey, KeyModel key, LocaleModel locale) { if (wordMap.containsKey(newkey)) { return wordMap[newkey]; } @@ -186,7 +332,7 @@ class WordModel extends Equatable { final int order; final int maxLength; final String keyId; - final String locale; + final LocaleModel locale; String value; /// for compare when differen values current and import. (must be null) @@ -202,7 +348,7 @@ class WordModel extends Equatable { final List images; final String notes; - String get keyDiff => '$keyId$locale'; + String get keyDiff => '$keyId${locale.key}'; WordModel({ @required this.id, @@ -241,7 +387,7 @@ class WordModel extends Equatable { 'order': order, 'maxLength': maxLength, 'key': keyId, - 'locale': locale, + 'locale': locale.toMap(), 'value': value, 'origin': origin, 'valueNewVersion': valueNewVersion, @@ -260,7 +406,7 @@ class WordModel extends Equatable { order: map['order'] as int, maxLength: map['maxLength'] as int, keyId: map['key']?.toString(), - locale: map['locale']?.toString(), + locale: LocaleModel.fromMap(map['locale'] as Map), value: map['value']?.toString(), origin: map['origin']?.toString(), valueNewVersion: map['valueNewVersion']?.toString(), @@ -280,7 +426,7 @@ class WordModel extends Equatable { int order, int maxLength, String keyId, - String locale, + LocaleModel locale, String value, String origin, String valueNewVersion, diff --git a/lib/project/project_page.dart b/lib/project/project_page.dart index 0b31596..56c8dbb 100644 --- a/lib/project/project_page.dart +++ b/lib/project/project_page.dart @@ -1,6 +1,3 @@ -import 'dart:convert'; - -import 'package:archive/archive_io.dart'; import 'package:doppio_dev_ixn/core/index.dart'; import 'package:doppio_dev_ixn/main.dart'; import 'package:doppio_dev_ixn/project_setting/index.dart'; @@ -10,7 +7,6 @@ import 'package:doppio_dev_ixn/project/index.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:pedantic/pedantic.dart'; import 'package:uuid/uuid.dart'; -import 'package:file_manager/file_manager.dart' as manager; class ProjectPage extends StatefulWidget { static const String routeName = '/project'; @@ -50,6 +46,7 @@ class _ProjectPageState extends State { projectModel = currentState.project.copyWith(); currentVersionState = currentState.version; } + // change setting if (currentVersionState != currentState.version) { currentVersionState = currentState.version; @@ -58,13 +55,16 @@ class _ProjectPageState extends State { defaultLocale: pr.defaultLocale, locales: pr.locales, name: pr.name, + formats: pr.formats, ); } } projectScreen = ProjectScreen(projectBloc: _projectBloc, projectModel: projectModel); + final i10n = TranslateService().locale; + final name = projectModel?.name ?? i10n.project_name_no; return Scaffold( appBar: AppBar( - title: Text('Project'), + title: Text(i10n.project_name_title(name)), actions: [ IconButton( icon: Icon(Icons.settings), @@ -92,29 +92,29 @@ class _ProjectPageState extends State { onPressed: () async { _addKey(); }, - tooltip: 'Add', + tooltip: i10n.project_add, icon: Icon(Icons.add), ), Spacer(), IconButton( onPressed: () async { - await _export(projectModel); + await projectModel.export(); }, - tooltip: 'Export', + tooltip: i10n.project_export, icon: Icon(Icons.file_upload), ), IconButton( onPressed: () async { await _import(); }, - tooltip: 'Import', + tooltip: i10n.project_import, icon: Icon(Icons.file_download), ), IconButton( onPressed: () async { _projectBloc.add(SaveProjectEvent(projectModel)); }, - tooltip: 'Save', + tooltip: i10n.project_save, icon: Icon(Icons.save), ), ], @@ -130,9 +130,11 @@ class _ProjectPageState extends State { Future _import() async { try { final filesData = await TranslateService().importFiles(); - for (var locale in filesData.keys) { + for (var locale in filesData?.keys ?? []) { + var localeCode = TranslateService.countryName2Code.entries.firstWhere((element) => element.value == locale); + var localeModel = LocaleModel.from(localeCode); setState(() { - projectModel.import(locale, filesData[locale]); + projectModel.import(localeModel, filesData[locale]); }); } } catch (_, stackTrace) { @@ -147,28 +149,4 @@ class _ProjectPageState extends State { newKeys.add(KeyModel(id: Uuid().v4())); }); } - - Future _export(ProjectModel projectModel) async { - try { - var encoder = ZipEncoder(); - final archive = Archive(); - for (var locale in projectModel.locales) { - final mapWord = {}; - final result = {}; - projectModel.words.where((c) => c.locale == locale).map((e) => mapWord[e.keyId] = e.value).toList(); - for (var item in projectModel.keys) { - result['"${item.value}"'] = '"${mapWord[item.id]}"'; - } - final bytes = Utf8Codec().encode(result.toString()); - final archiveFile = ArchiveFile('$locale.json', bytes.length, bytes); - archive.addFile(archiveFile); - } - final bytesZip = encoder.encode(archive); - await manager.saveFile('${projectModel.name}-ixn.zip', binaryData: bytesZip); - // projectScreen.import(filesData); - } catch (_, stackTrace) { - log(_?.toString(), name: 'ProjectsPage', error: _, stackTrace: stackTrace); - NotificationService.showError(_?.toString()); - } - } } diff --git a/lib/project/project_provider.dart b/lib/project/project_provider.dart deleted file mode 100644 index b65359f..0000000 --- a/lib/project/project_provider.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'dart:async'; - -class ProjectProvider { - Future loadAsync(String token) async { - /// write from keystore/keychain - await Future.delayed(Duration(seconds: 2)); - } - - Future saveAsync(String token) async { - /// write from keystore/keychain - await Future.delayed(Duration(seconds: 2)); - } - - void test(bool isError) { - if (isError == true) { - throw Exception('manual error'); - } - } -} diff --git a/lib/project/project_repository.dart b/lib/project/project_repository.dart deleted file mode 100644 index 150664e..0000000 --- a/lib/project/project_repository.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:doppio_dev_ixn/project/index.dart'; - -class ProjectRepository { - final ProjectProvider _projectProvider = ProjectProvider(); - - ProjectRepository(); - - void test(bool isError) { - _projectProvider.test(isError); - } -} diff --git a/lib/project/project_screen.dart b/lib/project/project_screen.dart index 5115998..234c246 100644 --- a/lib/project/project_screen.dart +++ b/lib/project/project_screen.dart @@ -29,7 +29,7 @@ class ProjectScreenState extends State { final translator = GoogleTranslator(); - String selectedLocale; + LocaleModel selectedLocale; int currentVersionState; @@ -61,17 +61,18 @@ class ProjectScreenState extends State { child: CircularProgressIndicator(), ); } + final i10n = TranslateService().locale; if (currentState is ErrorProjectState) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(currentState.errorMessage ?? 'Error'), + Text(currentState.errorMessage ?? i10n.error_error), Padding( padding: const EdgeInsets.only(top: 32.0), child: RaisedButton( color: Colors.blue, - child: Text('reload'), + child: Text(i10n.error_reload), onPressed: _load, ), ), @@ -81,13 +82,14 @@ class ProjectScreenState extends State { if (currentState is InProjectState) { if (currentVersionState == null) { currentVersionState = currentState.version; - selectedLocale = widget.projectModel.defaultLocale; + selectedLocale = null; } + // change setting if (currentVersionState != currentState.version) { currentVersionState = currentState.version; if (!widget.projectModel.locales.contains(selectedLocale)) { - selectedLocale = widget.projectModel.defaultLocale; + selectedLocale = null; } } return SingleChildScrollView( @@ -128,7 +130,11 @@ class ProjectScreenState extends State { ); } - Expanded _body(S i10n, double widthXl) { + Widget _body(S i10n, double widthXl) { + final length = widget.projectModel.keys?.length ?? 0; + if (length == 0) { + return Expanded(child: Empty(text: i10n.project_empty)); + } return Expanded( child: ListView.builder( controller: scrollController, @@ -149,21 +155,21 @@ class ProjectScreenState extends State { divider(), Flexible( child: EditLangWord( - projectModel: widget.projectModel, - locale: widget.projectModel.defaultLocale, - title: i10n.project_default_locale, - index: index, - render: renderUpdate), + projectModel: widget.projectModel, + locale: widget.projectModel.defaultLocale, + title: i10n.project_default_locale, + index: index, + ), flex: 3, fit: FlexFit.tight, ), divider(), - if (widget.projectModel?.locales != null && widget.projectModel.locales.isNotEmpty) - ..._editLocale(selectedLocale, selectedLocale, index, widthXl), + if (widget.projectModel?.locales != null && widget.projectModel.locales.isNotEmpty && selectedLocale != null) + ..._editLocale(selectedLocale.toString(), selectedLocale, index, widthXl), ], ); }, - itemCount: widget.projectModel.keys?.length ?? 0, + itemCount: length, ), ); } @@ -200,14 +206,15 @@ class ProjectScreenState extends State { child: Container( width: 100, child: LangDropdownWidget( - options: widget.projectModel.locales, - labelText: i10n.project_default_locale, + options: widget.projectModel.locales.where((element) => element != widget.projectModel.defaultLocale).toList(), + labelText: i10n.project_locale, selectedValue: selectedLocale, // TODO: remove, how?! width: widthXl - 24, select: (newValue) { + var newLocale = LocaleModel.from(MapEntry(newValue?.toString(), TranslateService.countryName2Code[newValue?.toString()])); setState(() { - selectedLocale = newValue?.toString(); + selectedLocale = newLocale; }); }, ), @@ -227,7 +234,7 @@ class ProjectScreenState extends State { ); } - List _editLocale(String title, String locale, int index, double width) { + List _editLocale(String title, LocaleModel locale, int index, double width) { return [ Flexible( child: EditLangWord( @@ -235,7 +242,6 @@ class ProjectScreenState extends State { locale: locale, title: title, index: index, - render: renderUpdate, ), flex: 3, fit: FlexFit.tight, @@ -261,12 +267,12 @@ class ProjectScreenState extends State { return Container(); } - Widget _langAuto(String locale, int index, double width) { + Widget _langAuto(LocaleModel locale, int index, double width) { final key = widget.projectModel.keys[index]; - final newkey = '${key.id}$locale'; + final newkey = '${key.id}${widget.projectModel.defaultLocale.key}'; var word = widget.projectModel.wordMap[newkey] ?? WordModel(id: Uuid().v4(), keyId: key.id, locale: widget.projectModel.defaultLocale); // TODO: remove width - return TranslateWord(translator: translator, text: word.value, toLocale: locale, key: Key('${newkey}_auto'), width: width); + return TranslateWord(translator: translator, text: word.value, toLocale: locale.locale, key: Key('${newkey}_auto'), width: width); } Future _load([bool isError = false]) async { diff --git a/lib/project_setting/project_setting_screen.dart b/lib/project_setting/project_setting_screen.dart index bfe22c9..85ff96c 100644 --- a/lib/project_setting/project_setting_screen.dart +++ b/lib/project_setting/project_setting_screen.dart @@ -1,10 +1,11 @@ import 'package:doppio_dev_ixn/project/index.dart'; import 'package:doppio_dev_ixn/service/index.dart'; +import 'package:doppio_dev_ixn/core/index.dart'; +import 'package:doppio_dev_ixn/widget/index.dart'; import 'package:doppio_dev_ixn/widget/lang_dropdown.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:doppio_dev_ixn/project_setting/index.dart'; -import 'package:flutter_localizations/flutter_localizations.dart'; class ProjectSettingScreen extends StatefulWidget { const ProjectSettingScreen({ @@ -30,14 +31,15 @@ class ProjectSettingScreen extends StatefulWidget { class ProjectSettingScreenState extends State { ProjectSettingScreenState(); final i10n = TranslateService().locale; - List locales; - Map localesList; + List locales; @override void initState() { - locales = kMaterialSupportedLanguages.toList(); - locales.sort(); - localesList = {for (var code in locales) code: '$code - ${TranslateService.localeCountryName[code]}'}; + locales = []; + for (final key in TranslateService.countryName2Code.keys) { + final split = TranslateService.countryName2Code[key].split('-'); + locales.add(LocaleModel(countryName: key, locale: split[0], countryCode: split[1])); + } super.initState(); } @@ -55,6 +57,7 @@ class ProjectSettingScreenState extends State { ProjectSettingState currentState, ) { ContextService().buidlContext(context); + final i10n = TranslateService().locale; if (currentState is UnProjectSettingState) { _load(context); return Center( @@ -66,12 +69,12 @@ class ProjectSettingScreenState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(currentState.errorMessage ?? 'Error'), + Text(currentState.errorMessage ?? i10n.error_error), Padding( padding: const EdgeInsets.only(top: 32.0), child: RaisedButton( color: Colors.blue, - child: Text('reload'), + child: Text(i10n.error_reload), onPressed: () { _load(context); }, @@ -90,8 +93,11 @@ class ProjectSettingScreenState extends State { } TextEditingController textEditingController; + String filter; Widget _settings(InProjectSettingState currentState) { + final i10n = TranslateService().locale; + final textTheme = ContextService().textTheme; if (textEditingController == null || textEditingController.text != widget.projectModel.name) { textEditingController = TextEditingController(text: widget.projectModel.name); } @@ -118,18 +124,138 @@ class ProjectSettingScreenState extends State { selectedValue: widget.projectModel.defaultLocale, select: (newValue) { setState(() { - widget.projectModel.defaultLocale = newValue?.toString(); - if (!widget.projectModel.locales.contains(newValue)) { - widget.projectModel.locales.add(newValue.toString()); + var newLocale = LocaleModel.from(MapEntry(newValue?.toString(), TranslateService.countryName2Code[newValue?.toString()])); + widget.projectModel.defaultLocale = newLocale; + if (!widget.projectModel.locales.contains(newLocale)) { + widget.projectModel.locales.add(newLocale); } }); }, ), + Column( + children: [ + Row( + children: [ + Text( + i10n.project_setting_export, + style: textTheme.caption, + ), + IconButton( + icon: Icon(Icons.add), + onPressed: () { + widget.projectModel.formats ??= []; + var ext = 'json'; + var prefix = ''; + var divider = '-'; + if (widget.projectModel.formats.length == 1) { + ext = 'arb'; + prefix = 'intl_'; + divider = '_'; + } + setState(() { + widget.projectModel.formats.add(ExportFormatModel(fileExtension: ext, prefix: prefix, divider: divider)); + }); + }, + ), + ], + ), + if (widget.projectModel.formats == null || widget.projectModel.formats.isEmpty) + Container(width: ContextService().deviceSize.width, height: 80, child: Empty(text: i10n.project_setting_export_empty)), + if (widget.projectModel.formats != null) + ...widget.projectModel.formats.map( + (e) => Container( + width: ContextService().deviceSize.width, + child: Row( + children: [ + Flexible( + flex: 1, + child: TextFormField( + initialValue: e.prefix ?? '', + decoration: InputDecoration(labelText: i10n.project_setting_prefix), + onChanged: (value) { + setState(() { + e.prefix = value; + }); + }, + ), + ), + Flexible( + flex: 1, + child: Padding( + padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), + child: TextFormField( + initialValue: e.divider ?? '', + decoration: InputDecoration(labelText: i10n.project_setting_divider), + onChanged: (value) { + setState(() { + e.divider = value; + }); + }, + ), + ), + ), + Flexible( + flex: 1, + child: Padding( + padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), + child: TextFormField( + initialValue: e.postfix ?? '', + decoration: InputDecoration(labelText: i10n.project_setting_postfix), + onChanged: (value) { + setState(() { + e.postfix = value; + }); + }, + ), + ), + ), + Flexible( + flex: 1, + child: TextFormField( + initialValue: e.fileExtension ?? '', + decoration: InputDecoration(labelText: i10n.project_setting_ext), + onChanged: (value) { + setState(() { + e.fileExtension = value; + }); + }, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 8, 0), + child: IconButton( + icon: Icon( + Icons.delete, + ), + onPressed: () { + setState(() { + widget.projectModel.formats.remove(e); + }); + }, + ), + ), + ], + ), + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: TextFormField( + decoration: InputDecoration(labelText: i10n.project_setting_filter), + onChanged: (value) { + setState(() { + filter = value; + }); + }, + ), + ), Padding( padding: const EdgeInsets.only(top: 12, bottom: 4), child: Text( i10n.projects_card_locales, - style: ContextService().textTheme.caption, + style: textTheme.caption, ), ), _buildTargetLocales(), @@ -140,6 +266,8 @@ class ProjectSettingScreenState extends State { } Widget _buildTargetLocales() { + final filtered = + filter.isNullOrEmpty() ? locales : locales.where((element) => element.toString().toLowerCase().contains(filter.toLowerCase())).toList(); return Expanded( child: Container( decoration: BoxDecoration( @@ -148,7 +276,7 @@ class ProjectSettingScreenState extends State { ), child: ListView.builder( itemBuilder: (context, index) { - final code = locales[index]; + final code = filtered[index]; return Row( children: [ Checkbox( @@ -171,14 +299,14 @@ class ProjectSettingScreenState extends State { ), Expanded( child: Text( - '$code - ${TranslateService.localeCountryName[code]}', + '$code', overflow: TextOverflow.clip, ), ) ], ); }, - itemCount: kMaterialSupportedLanguages.length, + itemCount: filtered.length, ), ), ); diff --git a/lib/projects/projects_page.dart b/lib/projects/projects_page.dart index 23953ed..9a232ce 100644 --- a/lib/projects/projects_page.dart +++ b/lib/projects/projects_page.dart @@ -1,11 +1,11 @@ -import 'package:doppio_dev_ixn/core/logger.dart'; import 'package:doppio_dev_ixn/project/index.dart'; import 'package:doppio_dev_ixn/service/index.dart'; import 'package:flutter/material.dart'; import 'package:doppio_dev_ixn/projects/index.dart'; import 'package:uuid/uuid.dart'; -import 'package:file_access/file_access.dart' as file_access; -import 'package:doppio_dev_ixn/core/index.dart'; +// import 'package:file_access/file_access.dart' as file_access; +// import 'package:doppio_dev_ixn/core/index.dart'; +// import 'package:doppio_dev_ixn/core/logger.dart'; class ProjectsPage extends StatefulWidget { static const String routeName = '/projects'; @@ -20,10 +20,11 @@ class _ProjectsPageState extends State { @override Widget build(BuildContext context) { + final i10n = TranslateService().locale; return Scaffold( key: Key('projecs_sc'), appBar: AppBar( - title: Text('Projects'), + title: Text(i10n.projects_title), ), body: projectsScreen, persistentFooterButtons: [ @@ -35,7 +36,7 @@ class _ProjectsPageState extends State { onPressed: () async { projectsScreen.projectsBloc.add(AddProjectsEvent(projectModel: ProjectModel(id: Uuid().v4()))); }, - tooltip: 'Add', + tooltip: i10n.projects_add, icon: Icon(Icons.add), ), IconButton( @@ -43,34 +44,34 @@ class _ProjectsPageState extends State { showRemove = !showRemove; projectsScreen.projectsBloc.add(ViewProjectsEvent(showRemove: showRemove)); }, - tooltip: 'Show remove button', + tooltip: i10n.projects_show_remove, icon: Icon(Icons.remove), ), Spacer(), - IconButton( - onPressed: () async { - await _import(); - }, - tooltip: 'Import', - icon: Icon(Icons.file_download), - ), + // IconButton( + // onPressed: () async { + // await _import(); + // }, + // tooltip: i10n.projects_import, + // icon: Icon(Icons.file_download), + // ), ], ), ), ]); } - Future _import() async { - try { - final newFiles = (await file_access.open(false, false, allowedTypes: ['zip'])); - final newFile = newFiles.firstOrDefault; - print(newFile?.path); - // var text = await newFile.readAsString(); - // var json = Map.from(jsonDecode(text) as Map); - // print(json); - } catch (_, stackTrace) { - log(_?.toString(), name: 'ProjectsPage', error: _, stackTrace: stackTrace); - NotificationService.showError(_?.toString()); - } - } + // Future _import() async { + // try { + // final newFiles = (await file_access.open(false, false, allowedTypes: ['zip'])); + // final newFile = newFiles.firstOrDefault; + // print(newFile?.path); + // // var text = await newFile.readAsString(); + // // var json = Map.from(jsonDecode(text) as Map); + // // print(json); + // } catch (_, stackTrace) { + // log(_?.toString(), name: 'ProjectsPage', error: _, stackTrace: stackTrace); + // NotificationService.showError(_?.toString()); + // } + // } } diff --git a/lib/projects/projects_screen.dart b/lib/projects/projects_screen.dart index ad243c5..0a77226 100644 --- a/lib/projects/projects_screen.dart +++ b/lib/projects/projects_screen.dart @@ -3,6 +3,7 @@ import 'package:doppio_dev_ixn/project/index.dart'; import 'package:doppio_dev_ixn/project_setting/index.dart'; import 'package:doppio_dev_ixn/service/context_service.dart'; import 'package:doppio_dev_ixn/service/translate_service.dart'; +import 'package:doppio_dev_ixn/widget/index.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:doppio_dev_ixn/projects/index.dart'; @@ -45,6 +46,7 @@ class ProjectsScreenState extends State { ProjectsState currentState, ) { ContextService().buidlContext(context); + final i10n = TranslateService().locale; if (currentState is UnProjectsState) { return Center( child: CircularProgressIndicator(), @@ -55,12 +57,12 @@ class ProjectsScreenState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(currentState.errorMessage ?? 'Error'), + Text(currentState.errorMessage ?? i10n.error_error), Padding( padding: const EdgeInsets.only(top: 32.0), child: RaisedButton( color: Colors.blue, - child: Text('reload'), + child: Text(i10n.error_reload), onPressed: _load, ), ), @@ -68,6 +70,9 @@ class ProjectsScreenState extends State { )); } if (currentState is InProjectsState) { + if (currentState.projects == null || currentState.projects.isEmpty) { + return Empty(text: i10n.projects_emprty); + } return Padding( padding: const EdgeInsets.all(8.0), child: Wrap( @@ -117,7 +122,7 @@ class ProjectsScreenState extends State { padding: const EdgeInsets.all(8.0), child: RaisedButton( color: Colors.red, - child: Text('Remove'), + child: Text(i10n.projects_remove), onPressed: () { widget.projectsBloc.add(RemoveProjectsEvent(projectModel: e)); }, diff --git a/lib/service/notification_service.dart b/lib/service/notification_service.dart index 363df3c..da87e4c 100644 --- a/lib/service/notification_service.dart +++ b/lib/service/notification_service.dart @@ -1,9 +1,10 @@ import 'package:bot_toast/bot_toast.dart'; +import 'package:doppio_dev_ixn/service/index.dart'; import 'package:flutter/material.dart'; class NotificationService { static void showError(String error) { - BotToast.showNotification(duration: Duration(seconds: 4), onTap: () {}, title: (_) => Text(error)); + BotToast.showNotification(duration: Duration(seconds: 7), onTap: () {}, title: (_) => Text(error)); } static void showQuestion(String message, Function onDone) { @@ -35,7 +36,7 @@ class NotificationService { cancelFunc(); onDone(); }, - child: const Text('confirm'), + child: Text(TranslateService().locale.notif_ok), ), ), ], diff --git a/lib/service/translate_service.dart b/lib/service/translate_service.dart index 859a395..cb4275e 100644 --- a/lib/service/translate_service.dart +++ b/lib/service/translate_service.dart @@ -20,86 +20,86 @@ class TranslateService { } /// {@template flutter.localizations.material.languages} - static Map localeCountryName = { - 'af': 'Afrikaans', - 'am': 'Amharic', - 'ar': 'Arabic', - 'as': 'Assamese', - 'az': 'Azerbaijani', - 'be': 'Belarusian', - 'bg': 'Bulgarian', - 'bn': 'Bengali Bangla', - 'bs': 'Bosnian', - 'ca': 'Catalan Valencian', - 'cs': 'Czech', - 'da': 'Danish', - 'de': 'German (plus one country variation)', - 'el': 'Modern Greek', - 'en': 'English (plus 8 country variations)', - 'es': 'Spanish Castilian (plus 20 country variations)', - 'et': 'Estonian', - 'eu': 'Basque', - 'fa': 'Persian', - 'fi': 'Finnish', - 'fil': 'Filipino Pilipino', - 'fr': 'French (plus one country variation)', - 'gl': 'Galician', - 'gsw': 'Swiss German Alemannic Alsatian', - 'gu': 'Gujarati', - 'he': 'Hebrew', - 'hi': 'Hindi', - 'hr': 'Croatian', - 'hu': 'Hungarian', - 'hy': 'Armenian', - 'id': 'Indonesian', - 'is': 'Icelandic', - 'it': 'Italian', - 'ja': 'Japanese', - 'ka': 'Georgian', - 'kk': 'Kazakh', - 'km': 'Khmer Central Khmer', - 'kn': 'Kannada', - 'ko': 'Korean', - 'ky': 'Kirghiz Kyrgyz', - 'lo': 'Lao', - 'lt': 'Lithuanian', - 'lv': 'Latvian', - 'mk': 'Macedonian', - 'ml': 'Malayalam', - 'mn': 'Mongolian', - 'mr': 'Marathi', - 'ms': 'Malay', - 'my': 'Burmese', - 'nb': 'Norwegian Bokmål, which, in this library, is a synonym of `no`', - 'ne': 'Nepali', - 'nl': 'Dutch Flemish', - 'no': 'Norwegian', - 'or': 'Oriya', - 'pa': 'Panjabi Punjabi', - 'pl': 'Polish', - 'ps': 'Pushto Pashto', - 'pt': 'Portuguese (plus one country variation)', - 'ro': 'Romanian Moldavian Moldovan', - 'ru': 'Russian', - 'si': 'Sinhala Sinhalese', - 'sk': 'Slovak', - 'sl': 'Slovenian', - 'sq': 'Albanian', - 'sr': 'Serbian (plus 2 scripts)', - 'sv': 'Swedish', - 'sw': 'Swahili', - 'ta': 'Tamil', - 'te': 'Telugu', - 'th': 'Thai', - 'tl': 'Tagalog', - 'tr': 'Turkish', - 'uk': 'Ukrainian', - 'ur': 'Urdu', - 'uz': 'Uzbek', - 'vi': 'Vietnamese', - 'zh': 'Chinese (plus 2 country variations and 2 scripts)', - 'zu': 'Zulu', - }; + // static Map localeCountryName = { + // 'af': 'Afrikaans', + // 'am': 'Amharic', + // 'ar': 'Arabic', + // 'as': 'Assamese', + // 'az': 'Azerbaijani', + // 'be': 'Belarusian', + // 'bg': 'Bulgarian', + // 'bn': 'Bengali Bangla', + // 'bs': 'Bosnian', + // 'ca': 'Catalan Valencian', + // 'cs': 'Czech', + // 'da': 'Danish', + // 'de': 'German (plus one country variation)', + // 'el': 'Modern Greek', + // 'en': 'English (plus 8 country variations)', + // 'es': 'Spanish Castilian (plus 20 country variations)', + // 'et': 'Estonian', + // 'eu': 'Basque', + // 'fa': 'Persian', + // 'fi': 'Finnish', + // 'fil': 'Filipino Pilipino', + // 'fr': 'French (plus one country variation)', + // 'gl': 'Galician', + // 'gsw': 'Swiss German Alemannic Alsatian', + // 'gu': 'Gujarati', + // 'he': 'Hebrew', + // 'hi': 'Hindi', + // 'hr': 'Croatian', + // 'hu': 'Hungarian', + // 'hy': 'Armenian', + // 'id': 'Indonesian', + // 'is': 'Icelandic', + // 'it': 'Italian', + // 'ja': 'Japanese', + // 'ka': 'Georgian', + // 'kk': 'Kazakh', + // 'km': 'Khmer Central Khmer', + // 'kn': 'Kannada', + // 'ko': 'Korean', + // 'ky': 'Kirghiz Kyrgyz', + // 'lo': 'Lao', + // 'lt': 'Lithuanian', + // 'lv': 'Latvian', + // 'mk': 'Macedonian', + // 'ml': 'Malayalam', + // 'mn': 'Mongolian', + // 'mr': 'Marathi', + // 'ms': 'Malay', + // 'my': 'Burmese', + // 'nb': 'Norwegian Bokmål, which, in this library, is a synonym of `no`', + // 'ne': 'Nepali', + // 'nl': 'Dutch Flemish', + // 'no': 'Norwegian', + // 'or': 'Oriya', + // 'pa': 'Panjabi Punjabi', + // 'pl': 'Polish', + // 'ps': 'Pushto Pashto', + // 'pt': 'Portuguese (plus one country variation)', + // 'ro': 'Romanian Moldavian Moldovan', + // 'ru': 'Russian', + // 'si': 'Sinhala Sinhalese', + // 'sk': 'Slovak', + // 'sl': 'Slovenian', + // 'sq': 'Albanian', + // 'sr': 'Serbian (plus 2 scripts)', + // 'sv': 'Swedish', + // 'sw': 'Swahili', + // 'ta': 'Tamil', + // 'te': 'Telugu', + // 'th': 'Thai', + // 'tl': 'Tagalog', + // 'tr': 'Turkish', + // 'uk': 'Ukrainian', + // 'ur': 'Urdu', + // 'uz': 'Uzbek', + // 'vi': 'Vietnamese', + // 'zh': 'Chinese (plus 2 country variations and 2 scripts)', + // 'zu': 'Zulu', + // }; static Map countryName2Code = { 'Afghanistan - Dari': 'prs-AF', @@ -443,15 +443,23 @@ class TranslateService { Future>> importFiles() async { final newFiles = (await file_access.open(true, false, allowedTypes: ['json', 'arb'])); final filesData = >{}; + if (newFiles == null || newFiles.isEmpty) { + return null; + } for (var item in newFiles) { - var name = path.basename(item.path); - var locale = name.replaceAll('intl_', '').replaceAll('_', '-').replaceAll('.', '-').split('-')[0]; + var name = path.basenameWithoutExtension(item.path); + var splits = name.replaceAll('intl_', '').replaceAll('_', '-').replaceAll('.', '-').split('-'); + if (splits.length < 2) { + final country = TranslateService.localeCountry[splits[0]]; + splits.add(country); + } + var locale = '${splits[0]}-${splits[1]}'; var text = await item.readAsString(); var json = Map.from(jsonDecode(text) as Map); if (filesData.containsKey(locale)) { - throw Exception('dublicate locale'); + throw Exception(TranslateService().locale.error_locale_dublicate); } - if (!TranslateService.localeCountryName.containsKey(locale)) { + if (TranslateService.countryName2Code.values.firstWhere((c) => c == locale, orElse: () => null) == null) { throw Exception(TranslateService().locale.error_locale_notsupport(locale, item.path)); } filesData[locale] = json; diff --git a/lib/widget/edit_key.dart b/lib/widget/edit_key.dart index 7823c43..64f8b77 100644 --- a/lib/widget/edit_key.dart +++ b/lib/widget/edit_key.dart @@ -26,24 +26,20 @@ class _EditKeyState extends State { controller = TextEditingController(text: key.value); } // controller.selection = - return Tooltip( - key: Key('${key.id}_tooltip'), - message: '', - child: Container( - // color: colorDescription, - child: TextFormField( - key: Key('${key.id}_edit'), - controller: controller, - maxLines: null, - onChanged: (value) { - setState(() { - key.value = value.toString(); - }); - if (widget.render != null) { - widget.render(); - } - }, - ), + return Container( + // color: colorDescription, + child: TextFormField( + key: Key('${key.id}_edit'), + controller: controller, + maxLines: null, + onChanged: (value) { + setState(() { + key.value = value.toString(); + }); + if (widget.render != null) { + widget.render(); + } + }, ), ); } diff --git a/lib/widget/edit_lang_word.dart b/lib/widget/edit_lang_word.dart index 88d8226..c8318d3 100644 --- a/lib/widget/edit_lang_word.dart +++ b/lib/widget/edit_lang_word.dart @@ -1,5 +1,6 @@ import 'package:doppio_dev_ixn/project/index.dart'; import 'package:doppio_dev_ixn/service/index.dart'; +import 'package:doppio_dev_ixn/core/index.dart'; import 'package:flutter/material.dart'; class EditLangWord extends StatefulWidget { @@ -13,7 +14,7 @@ class EditLangWord extends StatefulWidget { }) : super(key: key); final ProjectModel projectModel; - final String locale; + final LocaleModel locale; final String title; final int index; final void Function() render; @@ -26,26 +27,29 @@ class _EditLangWordState extends State { void Function() notif; @override Widget build(BuildContext context) { + final i10n = TranslateService().locale; final key = widget.projectModel.keys[widget.index]; - final newkey = '${key.id}${widget.locale}'; - final keyOrigin = '${key.id}${widget.projectModel.defaultLocale}'; + final newkey = '${key.id}${widget.locale.key}'; + final keyOrigin = '${key.id}${widget.projectModel.defaultLocale.key}'; var word = widget.projectModel.getWord(newkey, key, widget.locale); var wordOrigin = widget.projectModel.getWord(keyOrigin, key, widget.projectModel.defaultLocale); var colorDescription = Colors.transparent; String description; if (word.origin != wordOrigin.value && widget.locale != widget.projectModel.defaultLocale) { if (word.origin == null) { - colorDescription = Colors.yellow.withOpacity(0.2); - description = 'Approve after import'; - notif = () { - setState(() { - word.origin = wordOrigin.value; - }); - widget.render(); - }; + if (!word.value.isNullOrEmpty()) { + colorDescription = Colors.yellow.withOpacity(0.2); + description = i10n.edit_lang_approve; + notif = () { + setState(() { + word.origin = wordOrigin.value; + }); + widget.render(); + }; + } } else { colorDescription = Colors.pink.withOpacity(0.2); - description = 'Default locale was changed'; + description = i10n.edit_lang_changed; } } diff --git a/lib/widget/empty.dart b/lib/widget/empty.dart new file mode 100644 index 0000000..46c31de --- /dev/null +++ b/lib/widget/empty.dart @@ -0,0 +1,25 @@ +import 'package:doppio_dev_ixn/service/index.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +class Empty extends StatelessWidget { + final String text; + const Empty({Key key, @required this.text}) : super(key: key); + + @override + Widget build(BuildContext context) { + final textTheme = ContextService().textTheme; + return Column( + children: [ + Expanded( + child: Center( + child: Padding( + padding: const EdgeInsets.all(16), + child: Text(text, textAlign: TextAlign.center, style: textTheme.headline4.copyWith()), + ), + ), + ) + ], + ); + } +} diff --git a/lib/widget/index.dart b/lib/widget/index.dart index a854ae4..07fda5c 100644 --- a/lib/widget/index.dart +++ b/lib/widget/index.dart @@ -1,4 +1,5 @@ export 'edit_key.dart'; export 'edit_lang_word.dart'; +export 'empty.dart'; export 'lang_dropdown.dart'; export 'translate_word.dart'; diff --git a/lib/widget/lang_dropdown.dart b/lib/widget/lang_dropdown.dart index ce77dee..0327198 100644 --- a/lib/widget/lang_dropdown.dart +++ b/lib/widget/lang_dropdown.dart @@ -1,14 +1,14 @@ +import 'package:doppio_dev_ixn/project/index.dart'; import 'package:doppio_dev_ixn/service/index.dart'; -import 'package:doppio_dev_ixn/service/translate_service.dart'; import 'package:flutter/material.dart'; class LangDropdownWidget extends StatefulWidget { - final List options; + final List options; final bool valueRequired; final String labelText; final Function(String value) select; - final String selectedValue; + final LocaleModel selectedValue; final double width; LangDropdownWidget({Key key, this.options, this.valueRequired = false, this.labelText, this.select, this.selectedValue, this.width}) @@ -24,17 +24,17 @@ class _LangDropdownWidgetState extends State { return FormField( validator: (String value) { if (widget.valueRequired && value == null) { - return 'value required'; + return TranslateService().locale.value_required; } return null; }, builder: (FormFieldState state) { return InputDecorator( decoration: InputDecoration(labelText: widget.labelText), - isEmpty: widget.selectedValue == null || widget.selectedValue.isEmpty, + isEmpty: widget.selectedValue == null, child: DropdownButtonHideUnderline( child: DropdownButton( - value: widget.selectedValue, + value: widget.selectedValue?.countryName, isDense: true, onChanged: (String newValue) { setState(() { @@ -42,13 +42,13 @@ class _LangDropdownWidgetState extends State { }); // FocusScope.of(context).focusInDirection(TraversalDirection.down); }, - items: widget.options.map((String _code) { + items: widget.options.map((LocaleModel _code) { return DropdownMenuItem( - value: _code, + value: _code.countryName, child: Container( width: widget.width, child: Text( - '$_code - ${TranslateService.localeCountryName[_code]}', + '$_code', overflow: TextOverflow.clip, maxLines: 1, softWrap: true, diff --git a/lib/widget/translate_word.dart b/lib/widget/translate_word.dart index db737df..978410d 100644 --- a/lib/widget/translate_word.dart +++ b/lib/widget/translate_word.dart @@ -64,7 +64,7 @@ class _TranslateWordState extends State { await Future.delayed(Duration(milliseconds: widget.text.length * 20)); var newValue = await widget.translator.translate(widget.text, to: widget.toLocale); if (newValue == null) { - NotificationService.showError('429 for ${widget.text}'); + NotificationService.showError(TranslateService().locale.translate_429(widget.text)); return; } await cache.putAsync(key, newValue); diff --git a/pubspec.yaml b/pubspec.yaml index 127b376..2a9af1c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.1.0+1 +version: 0.5.0+1 environment: sdk: ">=2.7.0 <3.0.0" @@ -33,7 +33,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.3 + cupertino_icons: 0.1.3 url_launcher: 5.4.5 url_launcher_macos: 0.0.1+5 url_launcher_web: 0.1.1+2 @@ -79,7 +79,7 @@ dev_dependencies: url: https://github.com/doppio-dev/utils ref: v0.0.4 path: config - + dwds: 3.1.0 hive_generator: #0.7.0 git: url: https://github.com/Gorniv/hive @@ -127,3 +127,4 @@ flutter: # see https://flutter.dev/custom-fonts/#from-packages flutter_intl: enabled: true + main_locale: en_US