flutter_polyglot_cli 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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