flutter_polyglot_cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +129 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +92 -0
  5. data/LICENSE +21 -0
  6. data/README.md +72 -0
  7. data/Rakefile +2 -0
  8. data/bin/console +15 -0
  9. data/bin/setup +8 -0
  10. data/exe/polyflut +52 -0
  11. data/flutter-polyglot-cli.gemspec +34 -0
  12. data/lib/flutter_polyglot_cli/api/base.rb +18 -0
  13. data/lib/flutter_polyglot_cli/api/language.rb +18 -0
  14. data/lib/flutter_polyglot_cli/api/project.rb +7 -0
  15. data/lib/flutter_polyglot_cli/api/session.rb +6 -0
  16. data/lib/flutter_polyglot_cli/api/translation.rb +8 -0
  17. data/lib/flutter_polyglot_cli/api/translation_key.rb +32 -0
  18. data/lib/flutter_polyglot_cli/commands/login.rb +31 -0
  19. data/lib/flutter_polyglot_cli/commands/projects.rb +59 -0
  20. data/lib/flutter_polyglot_cli/commands/pull.rb +89 -0
  21. data/lib/flutter_polyglot_cli/commands/setup.rb +65 -0
  22. data/lib/flutter_polyglot_cli/error_handler.rb +28 -0
  23. data/lib/flutter_polyglot_cli/helpers/depaginate.rb +17 -0
  24. data/lib/flutter_polyglot_cli/helpers/general.rb +122 -0
  25. data/lib/flutter_polyglot_cli/helpers/terminal.rb +15 -0
  26. data/lib/flutter_polyglot_cli/io/config.rb +18 -0
  27. data/lib/flutter_polyglot_cli/io/token.rb +22 -0
  28. data/lib/flutter_polyglot_cli/serializers/localizations/loc_serializer.rb +29 -0
  29. data/lib/flutter_polyglot_cli/serializers/localizations/localization_delegate_serializer.rb +42 -0
  30. data/lib/flutter_polyglot_cli/serializers/localizations/localization_keys_serializer.rb +27 -0
  31. data/lib/flutter_polyglot_cli/serializers/localizations/localization_serializer.rb +47 -0
  32. data/lib/flutter_polyglot_cli/serializers/translations/translations_serializer.rb +25 -0
  33. data/lib/flutter_polyglot_cli/version.rb +3 -0
  34. data/lib/flutter_polyglot_cli.rb +44 -0
  35. metadata +190 -0
@@ -0,0 +1,89 @@
1
+ module PolyglotFlutter
2
+ module Command
3
+ class Pull
4
+ include Helper::Terminal
5
+ include Helper::General
6
+
7
+ def self.init
8
+ new.call
9
+ end
10
+
11
+ def call
12
+ prompt.say('Fetching translations...')
13
+ generate_translations(project_configs, programming_language, mandatory_language)
14
+ success('Translations successfully generated!')
15
+ end
16
+
17
+ private
18
+
19
+ def generate_translations(projects, _programming_language, mandatory_language)
20
+ projects.each do |project|
21
+ project_id = project[:id]
22
+ languages = pull_languages(project_id)
23
+ translation_keys = pull_translation_keys(project_id)
24
+
25
+ write_translations(translation_keys, languages, project[:path])
26
+ write_localization(project[:sourceFilesPath])
27
+ write_localization_delegate(languages, project[:sourceFilesPath])
28
+ write_localization_keys(translation_keys, languages, project[:sourceFilesPath], mandatory_language)
29
+
30
+ FileUtils.mkdir_p "#{project[:sourceFilesPath]}initialize_i18n"
31
+ script = "flutter pub pub run intl_translation:generate_from_arb --output-dir=#{project[:sourceFilesPath]}initialize_i18n --no-use-deferred-loading #{project[:sourceFilesPath]}localization_keys.dart #{project[:path]}intl_*.arb"
32
+ `#{script}`
33
+
34
+ # josonaj command
35
+ end
36
+ end
37
+
38
+ # Serializing data
39
+
40
+ def write_translations(translation_keys, languages, translations_path)
41
+ return if translations_path.to_s.empty?
42
+
43
+ languages.each do |language|
44
+ PolyglotFlutter::Serializer::Translation
45
+ .write(translation_keys, language, translations_path)
46
+ end
47
+ end
48
+
49
+ def write_localization(translations_path)
50
+ return if translations_path.to_s.empty?
51
+
52
+ PolyglotFlutter::Serializer::Localization::Localization
53
+ .new
54
+ .save(translations_path)
55
+ end
56
+
57
+ def write_localization_delegate(languages, translations_path)
58
+ return if translations_path.to_s.empty?
59
+
60
+ PolyglotFlutter::Serializer::Localization::LocalizationDelegate
61
+ .new(languages: languages)
62
+ .save(translations_path)
63
+ end
64
+
65
+ def write_localization_keys(translation_keys, languages, translations_path, mandatory_language)
66
+ return if translations_path.to_s.empty?
67
+
68
+ PolyglotFlutter::Serializer::Localization::LocalizationKeys
69
+ .new(languages: languages, translation_keys: translation_keys, mandatory_language: mandatory_language)
70
+ .save(translations_path)
71
+ end
72
+
73
+ # Pulling data
74
+
75
+ def pull_languages(project_id)
76
+ PolyglotFlutter::Resource::Language
77
+ .token(token)
78
+ .depaginate(project_id: project_id)
79
+ end
80
+
81
+ def pull_translation_keys(project_id)
82
+ PolyglotFlutter::Resource::TranslationKey
83
+ .token(token)
84
+ .depaginate(project_id: project_id)
85
+ .sort_by { |key| key.name.downcase }
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,65 @@
1
+ require 'commander/blank'
2
+ require 'commander/command'
3
+
4
+ module PolyglotFlutter
5
+ module Command
6
+ class Setup < Projects
7
+ include Helper::Terminal
8
+ include Helper::General
9
+
10
+ def self.init(options = Commander::Command::Options.new)
11
+ new(options).call
12
+ end
13
+
14
+ def initialize(options = Commander::Command::Options.new)
15
+ @options = options
16
+ end
17
+
18
+ def call
19
+ project_id = project_id_prompt
20
+ language = 'dart'
21
+ translations_path = translations_path_prompt
22
+ sources_path = sources_path_prompt
23
+ mandatory_language = mandatory_language_prompt
24
+ project = {
25
+ id: project_id,
26
+ path: translations_path,
27
+ sourceFilesPath: sources_path
28
+ }
29
+ config = {
30
+ language: language,
31
+ projects: [project],
32
+ mandatory_language: mandatory_language
33
+ }
34
+ PolyglotFlutter::IO::Config.write(config)
35
+ success
36
+ end
37
+
38
+ private
39
+
40
+ # Project ID
41
+
42
+ def project_id_prompt
43
+ prompt.select('Choose a project: ', filtered_projects)
44
+ end
45
+
46
+ # Translations path
47
+
48
+ def translations_path_prompt
49
+ prompt.ask('Where would you like to store translation files (.arb)?', default: './assets/i18n/')
50
+ end
51
+
52
+ # Sources path
53
+
54
+ def sources_path_prompt
55
+ prompt.ask('Where would you like to store source files?', default: './lib/util/localization/')
56
+ end
57
+
58
+ # Mandatory language
59
+
60
+ def mandatory_language_prompt
61
+ prompt.ask('What is the mandatory (main) language on polyglot?', default: 'en')
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,28 @@
1
+ module PolyglotFlutter
2
+ class ErrorHandler
3
+ extend Helper::Terminal
4
+
5
+ class << self
6
+ def rescuable
7
+ yield
8
+ rescue StandardError => e
9
+ handle(e)
10
+ end
11
+
12
+ def handle(e)
13
+ prompt.error(
14
+ case e
15
+ when JsonApiClient::Errors::NotAuthorized
16
+ "You are not authorized. Please call `polyflut login` and provide correct credentials."
17
+ when JsonApiClient::Errors::AccessDenied
18
+ "You don't have the permission to access requested project."
19
+ when Errno::ENOENT
20
+ "We could not find a file that we need:\n\n#{e.message}"
21
+ else
22
+ "An error happened. This might help:\n\n#{e.message}"
23
+ end
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ module PolyglotFlutter
2
+ module Helper
3
+ module Depaginate
4
+ PER_PAGE = 100
5
+
6
+ def depaginate_query(query)
7
+ depaginated_array = []
8
+ page = query.page(1).per(PER_PAGE).all
9
+ until page.nil?
10
+ depaginated_array += page
11
+ page = page.pages.next
12
+ end
13
+ depaginated_array
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,122 @@
1
+ module PolyglotFlutter
2
+ module Helper
3
+ module General
4
+ ESCAPE_KEYWORDS = %w[abstract as assert async await break case
5
+ catch class const continue covariant default deferred do
6
+ else enum export extends external factory false final
7
+ finally for Function get hide if import in interface
8
+ is library mixin new null on operator part rethrow return
9
+ set static super switch sync this throw true try typedef var
10
+ void while with yield _].freeze
11
+ def token
12
+ @token ||= PolyglotFlutter::IO::Token.read
13
+ if @token.to_s.empty?
14
+ PolyglotFlutter::Command::Login.init
15
+ @token = PolyglotFlutter::IO::Token.read
16
+ end
17
+ @token
18
+ end
19
+
20
+ def config
21
+ @config ||= PolyglotFlutter::IO::Config.read.with_indifferent_access
22
+ end
23
+
24
+ def project_configs
25
+ @project_configs ||= config[:projects]
26
+ end
27
+
28
+ def programming_language
29
+ @programming_language ||= config[:language]
30
+ end
31
+
32
+ def mandatory_language
33
+ @mandatory_language ||= config[:mandatory_language]
34
+ end
35
+
36
+ def use_old_naming
37
+ @useOldNaming ||= config[:useOldNaming]
38
+ end
39
+
40
+ def indent(level = 0, initial = '')
41
+ (1..level)
42
+ .to_a.reduce('') { |result, _value| result + ' ' }
43
+ .concat(initial)
44
+ end
45
+
46
+ def clean_enum_name(name)
47
+ clean_name = name
48
+ .split(/[^0-9a-zA-Z]/)
49
+ .reject(&:empty?)
50
+ .map(&:capitalize)
51
+ .join
52
+
53
+ escape_with_underscore_if_needed(clean_name)
54
+ end
55
+
56
+ def clean_variable_name(name)
57
+ clean_name = name
58
+ .split(/[^0-9a-zA-Z]/)
59
+ .reject(&:empty?)
60
+ .each_with_index
61
+ .map { |value, index| index == 0 ? value.downcase : value.capitalize }
62
+ .join
63
+
64
+ escaped_underscore = escape_with_underscore_if_needed(clean_name)
65
+ escape_keyword_if_needed(escaped_underscore)
66
+ end
67
+
68
+ def escape_with_underscore_if_needed(name)
69
+ return name if name.match(/^[A-Za-z_]/)
70
+
71
+ '_' + name
72
+ end
73
+
74
+ def escape_keyword_if_needed(name)
75
+ return name unless ESCAPE_KEYWORDS.include? name
76
+
77
+ "`#{name}`"
78
+ end
79
+
80
+ def generate_locales(languages)
81
+ language_locales = languages.each.map { |value| value['locale'].split('_').first }.join("', '")
82
+ "['#{language_locales}']"
83
+ end
84
+
85
+ def generate_localization_keys(languages, translation_keys, mandatory_language)
86
+ result_string = ''
87
+ language = nil
88
+ languages.each do |lang|
89
+ if lang['locale'].split('_').first == mandatory_language
90
+ language = lang
91
+ end
92
+ end
93
+
94
+ dict = extract_translations(translation_keys, language)
95
+ dict.each do |key, value|
96
+ next if value.nil?
97
+
98
+ clean_var_name = clean_variable_name(key)
99
+ if value.match(/{(arg\d)}/)
100
+ matches = value.scan(/{(arg\d)}/)
101
+ first_arguments = matches.join(', dynamic ')
102
+ second_arguments = matches.join(', ')
103
+ result_string += "String #{clean_var_name}(dynamic #{first_arguments}) => Intl.message('', name: '#{clean_var_name}', args: <dynamic>[#{second_arguments}]);\n"
104
+ else
105
+ result_string += "String get #{clean_var_name} => Intl.message('#{clean_var_name}');\n"
106
+ end
107
+ end
108
+
109
+ result_string
110
+ end
111
+
112
+ def extract_translations(translation_keys, language)
113
+ translation_keys.each_with_object({}) do |translation_key, strings|
114
+ key_name = translation_key.name
115
+ next if key_name.nil?
116
+
117
+ strings[key_name] = translation_key.clean_translation(language)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,15 @@
1
+ require 'tty-prompt'
2
+
3
+ module PolyglotFlutter
4
+ module Helper
5
+ module Terminal
6
+ def success(message = 'Success!')
7
+ prompt.ok(message)
8
+ end
9
+
10
+ def prompt
11
+ @prompt ||= TTY::Prompt.new(interrupt: :exit)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ module PolyglotFlutter
2
+ module IO
3
+ class Config
4
+
5
+ CONFIG_FILE_PATH = "#{Dir.pwd}/polyflut.yml".freeze
6
+
7
+ class << self
8
+ def write(data)
9
+ File.open(CONFIG_FILE_PATH, 'w') { |file| YAML.dump(data, file) }
10
+ end
11
+
12
+ def read
13
+ YAML.load_file(CONFIG_FILE_PATH)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ module PolyglotFlutter
2
+ module IO
3
+ class Token
4
+ KEYCHAIN_SERVICE = 'polyglot'
5
+ KEYCHAIN_TOKEN_KEY = 'polyglot_token'
6
+
7
+ class << self
8
+ def write(token)
9
+ keychain[KEYCHAIN_SERVICE, KEYCHAIN_TOKEN_KEY] = token
10
+ end
11
+
12
+ def read
13
+ keychain[KEYCHAIN_SERVICE, KEYCHAIN_TOKEN_KEY]
14
+ end
15
+
16
+ def keychain
17
+ @keychain ||= OSXKeychain.new
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ module PolyglotFlutter
2
+ module Serializer
3
+ module Localization
4
+ class Base
5
+ include ERB::Util
6
+ include Helper::General
7
+ attr_accessor :languages, :translation_keys
8
+
9
+ def initialize(languages: nil, translation_keys: nil, mandatory_language: nil)
10
+ @languages = languages
11
+ @translation_keys = translation_keys
12
+ @mandatory_language = mandatory_language
13
+ end
14
+
15
+ def render
16
+ ERB.new(template, nil, '-').result(binding)
17
+ end
18
+
19
+ def template
20
+ raise NotImplementedError, 'Abstract Method'
21
+ end
22
+
23
+ def save(_path)
24
+ raise NotImplementedError, 'Abstract Method'
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,42 @@
1
+ require_relative 'loc_serializer'
2
+
3
+ module PolyglotFlutter
4
+ module Serializer
5
+ module Localization
6
+ class LocalizationDelegate < Base
7
+ def save(sources_path)
8
+ FileUtils.mkdir_p sources_path unless File.exist? sources_path
9
+ output_path = File.join(sources_path, 'localization_delegate.dart')
10
+ File.write(output_path, render)
11
+ end
12
+
13
+ def template()
14
+ <<-TEMPLATE
15
+ import 'dart:async';
16
+ import 'dart:ui';
17
+ import 'package:flutter/foundation.dart';
18
+ import 'package:flutter/material.dart';
19
+ import 'localization.dart';
20
+
21
+ // AUTO GENERATED FILE. DO NOT CHANGE.
22
+ class LocalizationDelegate extends LocalizationsDelegate<Localization> {
23
+ const LocalizationDelegate();
24
+
25
+ @override
26
+ bool isSupported(Locale locale) => <String><%= generate_locales(languages) %>.contains(locale.languageCode);
27
+
28
+ @override
29
+ Future<Localization> load(Locale locale) {
30
+ Localization.load(locale);
31
+ return SynchronousFuture<Localization>(Localization(locale.languageCode));
32
+ }
33
+
34
+ @override
35
+ bool shouldReload(LocalizationDelegate old) => false;
36
+ }
37
+ TEMPLATE
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,27 @@
1
+ require_relative 'loc_serializer'
2
+
3
+ module PolyglotFlutter
4
+ module Serializer
5
+ module Localization
6
+ class LocalizationKeys < Base
7
+ def save(sources_path)
8
+ FileUtils.mkdir_p sources_path unless File.exist? sources_path
9
+ output_path = File.join(sources_path, 'localization_keys.dart')
10
+ File.write(output_path, render)
11
+ end
12
+
13
+ def template
14
+ <<~TEMPLATE
15
+ import 'package:intl/intl.dart';
16
+ import 'localization.dart';
17
+
18
+ // AUTO GENERATED FILE. DO NOT CHANGE.
19
+ extension LocalizationKeys on Localization {
20
+
21
+ <%= generate_localization_keys(languages, translation_keys, mandatory_language) %>}
22
+ TEMPLATE
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,47 @@
1
+ require_relative 'loc_serializer'
2
+
3
+ module PolyglotFlutter
4
+ module Serializer
5
+ module Localization
6
+ class Localization < Base
7
+ def save(sources_path)
8
+ FileUtils.mkdir_p sources_path unless File.exist? sources_path
9
+ output_path = File.join(sources_path, 'localization.dart')
10
+ File.write(output_path, render)
11
+ end
12
+
13
+ def template
14
+ <<~TEMPLATE
15
+ import 'dart:async';
16
+ import 'package:flutter/material.dart';
17
+ import 'package:intl/intl.dart';
18
+ import 'initialize_i18n/messages_all.dart';
19
+
20
+ // AUTO GENERATED FILE. DO NOT CHANGE.
21
+ class Localization {
22
+ Localization(this.localeName);
23
+
24
+ static Future<Localization> load(Locale locale) {
25
+ final String name =
26
+ locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
27
+ final String localeName = Intl.canonicalizedLocale(name);
28
+
29
+ return initializeMessages(localeName).then((bool _) {
30
+ Intl.defaultLocale = localeName;
31
+ return Localization(localeName);
32
+ });
33
+ }
34
+
35
+ static Localization of(BuildContext context) {
36
+ return Localizations.of<Localization>(context, Localization);
37
+ }
38
+
39
+ final String localeName;
40
+ }
41
+
42
+ TEMPLATE
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ module PolyglotFlutter
2
+ module Serializer
3
+ class Translation
4
+ class << self
5
+ include Helper::General
6
+ def write(translation_keys, language, translations_path)
7
+ translations = extract_translations(translation_keys, language)
8
+
9
+ data = translations
10
+ .sort
11
+ .map { |key, value| "\t\"#{clean_variable_name(key)}\": \"#{value.gsub(/\n/, '\\n').gsub(/"/, '\\"')}\",\n\t\"@#{clean_variable_name(key)}\": { \"type\": \"text\" }" }
12
+ .join(",\n")
13
+ .concat("\n")
14
+ data = "{\n\t\"@@locale\": \"#{language['locale'].split('_').first}\",\n#{data}}"
15
+ unless File.exist? translations_path
16
+ FileUtils.mkdir_p translations_path
17
+ end
18
+ locale_code = language.code(true)
19
+ output_path = File.join(translations_path, "intl_#{locale_code}.arb")
20
+ File.write(output_path, data)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module PolyglotFlutter
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,44 @@
1
+ # Parsing
2
+ require 'yaml'
3
+ require 'osx_keychain'
4
+ require 'erb'
5
+ require 'active_support/core_ext/hash/indifferent_access'
6
+
7
+ # Helpers
8
+ require 'flutter_polyglot_cli/helpers/depaginate'
9
+ require 'flutter_polyglot_cli/helpers/terminal'
10
+ require 'flutter_polyglot_cli/helpers/general'
11
+
12
+ # JSON API Resources
13
+ require 'json_api_client'
14
+ require 'flutter_polyglot_cli/api/base'
15
+ require 'flutter_polyglot_cli/api/language'
16
+ require 'flutter_polyglot_cli/api/project'
17
+ require 'flutter_polyglot_cli/api/session'
18
+ require 'flutter_polyglot_cli/api/translation'
19
+ require 'flutter_polyglot_cli/api/translation_key'
20
+
21
+ # File Accessors
22
+ require 'flutter_polyglot_cli/io/token'
23
+ require 'flutter_polyglot_cli/io/config'
24
+
25
+ # Serializers
26
+ require 'flutter_polyglot_cli/serializers/translations/translations_serializer'
27
+ require 'flutter_polyglot_cli/serializers/localizations/loc_serializer'
28
+ require 'flutter_polyglot_cli/serializers/localizations/localization_serializer'
29
+ require 'flutter_polyglot_cli/serializers/localizations/localization_delegate_serializer'
30
+ require 'flutter_polyglot_cli/serializers/localizations/localization_keys_serializer'
31
+
32
+ # Commands
33
+ require 'flutter_polyglot_cli/commands/login'
34
+ require 'flutter_polyglot_cli/commands/projects'
35
+ require 'flutter_polyglot_cli/commands/setup'
36
+ require 'flutter_polyglot_cli/commands/pull'
37
+
38
+ # Error Handler
39
+ require 'flutter_polyglot_cli/error_handler'
40
+ require 'flutter_polyglot_cli/version'
41
+
42
+ module PolyglotFlutter
43
+
44
+ end