web_translate_it 3.2.1 → 3.2.2

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/bin/wti +2 -5
  3. data/history.md +29 -0
  4. data/lib/web_translate_it/api_resource.rb +137 -0
  5. data/lib/web_translate_it/commands/add.rb +58 -0
  6. data/lib/web_translate_it/commands/addlocale.rb +39 -0
  7. data/lib/web_translate_it/commands/base.rb +58 -0
  8. data/lib/web_translate_it/commands/diff.rb +71 -0
  9. data/lib/web_translate_it/commands/init.rb +112 -0
  10. data/lib/web_translate_it/commands/match.rb +30 -0
  11. data/lib/web_translate_it/commands/mv.rb +67 -0
  12. data/lib/web_translate_it/commands/pull.rb +62 -0
  13. data/lib/web_translate_it/commands/push.rb +50 -0
  14. data/lib/web_translate_it/commands/rm.rb +67 -0
  15. data/lib/web_translate_it/commands/rmlocale.rb +43 -0
  16. data/lib/web_translate_it/commands/status.rb +52 -0
  17. data/lib/web_translate_it/configuration.rb +31 -21
  18. data/lib/web_translate_it/connection.rb +30 -3
  19. data/lib/web_translate_it/project.rb +13 -72
  20. data/lib/web_translate_it/runner.rb +75 -0
  21. data/lib/web_translate_it/string.rb +21 -260
  22. data/lib/web_translate_it/term.rb +15 -255
  23. data/lib/web_translate_it/term_translation.rb +16 -69
  24. data/lib/web_translate_it/translation.rb +12 -50
  25. data/lib/web_translate_it/translation_base.rb +38 -0
  26. data/lib/web_translate_it/translation_file.rb +58 -109
  27. data/lib/web_translate_it/util/concurrency.rb +37 -0
  28. data/lib/web_translate_it/util/hash_util.rb +0 -2
  29. data/lib/web_translate_it/util/http_response.rb +46 -0
  30. data/lib/web_translate_it/util/prompt.rb +49 -0
  31. data/lib/web_translate_it/util/spinner.rb +51 -0
  32. data/lib/web_translate_it/util/string_util.rb +2 -8
  33. data/lib/web_translate_it/util.rb +0 -75
  34. data/lib/web_translate_it.rb +21 -3
  35. metadata +20 -3
  36. data/lib/web_translate_it/command_line.rb +0 -521
  37. data/lib/web_translate_it/util/hash_extensions.rb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9a027a46383d8932b3776d7c614592e3bb186bed328954f0a39bd2e168f2ba3
4
- data.tar.gz: 75bd8ab7c9129e6fb4f6c3c982fee30b1793a01ce2ba8219aa4e2480cb491246
3
+ metadata.gz: 2344f55bed19e435f8334c66229e58266ff1efe3cf54e9e81f4d17287f42a93d
4
+ data.tar.gz: 89c6ea08bcc87a43741c8419d5de32e2441f896f12e8338762c15cf84759ab5e
5
5
  SHA512:
6
- metadata.gz: 20ce007c03f2a00b5eb2c4ba019f37b814ad5184792a1d14a6231351402f0c5988d470a6f64e1b732a2409c74a8f9696b542b3b12aa7ab9c267f0f1c94c5f3ef
7
- data.tar.gz: 6aafb08ddff729b498093150eddafd55410f45a1f8d0239d7a11a5c1a2a06ccff802bd53996d442ef89da955ba38b290b25aad6e0f8bb7182e15527faec2d418
6
+ metadata.gz: c4a9b5b8f2874c7779eedb0d413d826a1d9bccbd8e9db33834ab82a0b8b22b2fb719bf412e7339cb05880018ddc072ee6e70a370824f3fc96f7d6982a2f8396f
7
+ data.tar.gz: ad5b96143c805efa324ea6119af80dd24517c2e41691b451920934a3deea7115385c13f859ccff7d314c034680f64b95033aba839b1bc4728ff09aa885395a00
data/bin/wti CHANGED
@@ -28,7 +28,7 @@ show_commands = <<~COMMANDS
28
28
  COMMANDS
29
29
 
30
30
  SUB_COMMANDS = %w[pull push match diff add rm mv addlocale rmlocale status st init].freeze
31
- global_options = Optimist.options do
31
+ Optimist.options do
32
32
  stop_on SUB_COMMANDS
33
33
  banner show_commands
34
34
  version "wti version #{WebTranslateIt::Util.version}"
@@ -57,13 +57,11 @@ when 'push'
57
57
  opt :locale, 'ISO code of locale(s) to push, space-separated', type: :string
58
58
  opt :target, 'Upload all target files'
59
59
  opt :force, 'Force push (bypass conditional requests to WTI)'
60
- opt :low_priority, 'Deprecated: option to process this file with a low priority'
61
60
  opt :merge, 'Force WTI to merge this file'
62
61
  opt :ignore_missing, 'Force WTI to not obsolete missing strings'
63
62
  opt :minor, 'Minor Changes. When pushing a master file, prevents target translations to be flagged as `to_verify`.'
64
63
  opt :label, 'Apply a label to the changes', type: :string
65
64
  opt :config, 'Path to a configuration file', short: '-c', default: '.wti'
66
- opt :all, 'DEPRECATED -- See `wti push --target` instead'
67
65
  opt :debug, 'Display debug information'
68
66
  end
69
67
  when 'diff'
@@ -78,7 +76,6 @@ when 'diff'
78
76
  when 'add'
79
77
  Optimist.options do
80
78
  banner 'wti add filename - Create and push a new master language file'
81
- opt :low_priority, 'Deprecated: option to process this file with a low priority'
82
79
  opt :config, 'Path to a configuration file', short: '-c', default: '.wti'
83
80
  opt :debug, 'Display debug information'
84
81
  end
@@ -135,7 +132,7 @@ end
135
132
 
136
133
  begin
137
134
  WebTranslateIt::Connection.turn_debug_on if command_options.debug
138
- WebTranslateIt::CommandLine.new(command, command_options, global_options, ARGV, File.expand_path('.'))
135
+ WebTranslateIt::Runner.new(command, command_options, ARGV, File.expand_path('.'))
139
136
  rescue Interrupt
140
137
  puts StringUtil.failure("\nQuitting...")
141
138
  exit 1
data/history.md CHANGED
@@ -1,3 +1,32 @@
1
+ ## Version 3.2.2 / 2026-03-03
2
+
3
+ * Replace O(n²) string concatenation loop in `StringUtil#backward_truncate` with `String#ljust`.
4
+ * Move `$stdout.sync = true` and parameter validation into `Base`. Add `Base#require_parameters!` helper, replacing 5 duplicate `validate_parameters!` methods. Drop `Metrics/MethodLength` `rubocop:todo` from Pull.
5
+ * Refactor all command classes for consistency: extract small private methods, use guard clauses, and drop all `rubocop:todo` annotations for `Metrics/AbcSize`, `Metrics/MethodLength`, `Metrics/CyclomaticComplexity`, and `Metrics/PerceivedComplexity` across Add, Addlocale, Diff, Init, Match, Mv, Rm, Rmlocale, and Status. Add `Configuration#target_files_for` to centralize `master_id` lookups.
6
+ * Ensure `Addlocale#call` and `Rmlocale#call` return `true` on success. Previously they returned `nil`, causing `Runner` to `exit 1` even after successful operations.
7
+ * Remove dead code: commented-out `ignore_files` method in `Configuration`, unused `last_modification` in `TranslationFile`, unused `titleize` in `StringUtil`.
8
+ * Replace `Object#in?` (ActiveSupport) with `Array#include?` in `fetch_translations`. #435
9
+ * `Configuration#initialize` now defaults to `Dir.pwd` instead of `Rails.root`, removing the hard Rails dependency. #435
10
+ * Add `Configuration#files_for` to centralize locale and path filtering. Replaces duplicated `find_all`/`sort` logic in Pull, Push, Diff, and Match commands. #436
11
+ * Split `Util` class into focused modules: `HttpResponse`, `Concurrency`, and `Prompt`. `Util` retains only `version`, `calculate_percentage`, and `can_display_colors?`. #434
12
+ * Extract `Configuration#load_project_data` to eliminate duplicated logic between `initialize` and `reload`. #433
13
+ * Refactor `Util.handle_response` to return response body consistently and let callers decide what to print. Extract `status_label` for display formatting. #423
14
+ * Remove `Hash#stringify_keys!` monkey-patch in favour of `Hash#transform_keys(&:to_s)`. #432
15
+ * Extract `Spinner` class from `Runner#throb`. Thread management, ANSI codes, and frame animation are now testable and reusable. #426
16
+ * Add `Connection#get`, `#post`, `#put`, `#delete` wrappers to eliminate repeated `Net::HTTP::*.new` / `Util.add_fields` / `http_connection.request` boilerplate across all API call sites. #424
17
+ * Simplify `fetch_locales` in `Pull` and `Push` commands. Extract `warn_unknown_locales` helper. Remove deprecated `--all` and `--low-priority` options from `push` and `add` commands. #422
18
+ * Extract `TranslationBase` base class from `Translation` and `TermTranslation` to share `initialize`, `save`, `to_json`, and API path logic. #425
19
+ * `Util.with_retries` now re-raises `Timeout::Error` after exhausting retries instead of returning `false`. Methods like `find_all`, `find`, `create`, and `update` now return consistent types. #411
20
+ * Standardize `to_hash`/`to_json` pattern across all model classes. Make `to_hash` private in `ApiResource`, `String`, and `Term`. Convert positional boolean to keyword argument. #410
21
+ * Extract threading logic into `Util.concurrent_batch`. Simplify `Pull#pull_files` and fix thread-safety bug with shared mutable state. Use `Thread#value` for error propagation. #409
22
+ * Separate display logic from business methods in `TranslationFile`. `fetch`, `upload`, `create`, and `delete` now return a `Result` struct instead of printing to stdout, enabling programmatic use without terminal side-effects. Remove broken `modified_remotely?` dead code. #407
23
+ * Refactor long parameter lists to use keyword arguments in `TranslationFile#upload` and `TranslationFile#initialize`. Extract `TranslationFile.from_api` factory method. Remove unused `_global_options` parameter from `Runner#initialize`. #405
24
+ * Replace fragile path splitting with `File.dirname` in `TranslationFile#fetch`. Add specs. #408
25
+ * Decompose `CommandLine` god class into 11 focused command classes under `Commands::` namespace, rename to `Runner`. #404
26
+ * Extract `ApiResource` base class from `String` and `Term` to remove duplicated CRUD logic. #403
27
+ * Fix generic `rescue` blocks to capture and re-raise `StandardError`. #406
28
+ * Extract `Util.with_retries` helper to deduplicate retry logic. #402
29
+
1
30
  ## Version 3.2.1 / 2026-03-02
2
31
 
3
32
  * Refactor `Connection` class to eliminate class variables. #254
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebTranslateIt
4
+
5
+ class ApiResource # rubocop:todo Metrics/ClassLength
6
+
7
+ attr_accessor :id, :created_at, :updated_at, :translations, :new_record, :connection
8
+
9
+ def initialize(params = {}, connection: nil)
10
+ params = params.transform_keys(&:to_s)
11
+ self.connection = connection
12
+ self.id = params['id'] || nil
13
+ self.created_at = params['created_at'] || nil
14
+ self.updated_at = params['updated_at'] || nil
15
+ self.translations = params['translations'] || []
16
+ self.new_record = true
17
+ assign_attributes(params)
18
+ end
19
+
20
+ def self.find_all(connection, params = {}) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
21
+ params = params.transform_keys(&:to_s)
22
+ url = "/api/projects/#{connection.api_key}/#{resource_path}"
23
+ url += "?#{HashUtil.to_params(filter_params(params))}" unless params.empty?
24
+
25
+ Concurrency.with_retries do
26
+ records = []
27
+ loop do
28
+ response = connection.get(url)
29
+ return [] unless response.code.to_i < 400
30
+
31
+ JSON.parse(response.body).each do |record_response|
32
+ record = new(record_response, connection: connection)
33
+ record.new_record = false
34
+ records.push(record)
35
+ end
36
+ break unless response['Link']&.include?('rel="next"')
37
+
38
+ url = response['Link'].match(/<(.*)>; rel="next"/)[1]
39
+ end
40
+ records
41
+ end
42
+ end
43
+
44
+ def self.find(connection, id)
45
+ Concurrency.with_retries do
46
+ response = connection.get("/api/projects/#{connection.api_key}/#{resource_path}/#{id}")
47
+ return nil if response.code.to_i == 404
48
+
49
+ record = new(JSON.parse(response.body), connection: connection)
50
+ record.new_record = false
51
+ return record
52
+ end
53
+ end
54
+
55
+ def self.resource_path
56
+ raise NotImplementedError, "#{name} must implement self.resource_path"
57
+ end
58
+
59
+ def self.filter_params(params)
60
+ params
61
+ end
62
+
63
+ def save
64
+ new_record ? create : update
65
+ end
66
+
67
+ def delete
68
+ Concurrency.with_retries do
69
+ HttpResponse.handle_response(connection.delete("/api/projects/#{connection.api_key}/#{self.class.resource_path}/#{id}"))
70
+ end
71
+ end
72
+
73
+ def translation_for(locale) # rubocop:todo Metrics/AbcSize
74
+ translation = translations.detect { |t| t.locale == locale }
75
+ return translation if translation
76
+ return nil if new_record
77
+
78
+ Concurrency.with_retries do
79
+ response = HttpResponse.handle_response(connection.get("/api/projects/#{connection.api_key}/#{self.class.resource_path}/#{id}/locales/#{locale}/translations"))
80
+ json = JSON.parse(response)
81
+ return nil if json.empty?
82
+
83
+ parse_translation_response(json)
84
+ end
85
+ end
86
+
87
+ protected
88
+
89
+ def assign_attributes(_params)
90
+ # Override in subclasses to set resource-specific attributes
91
+ end
92
+
93
+ def parse_translation_response(_json)
94
+ raise NotImplementedError, "#{self.class.name} must implement parse_translation_response"
95
+ end
96
+
97
+ def assign_translation_parent_id(_translation)
98
+ raise NotImplementedError, "#{self.class.name} must implement assign_translation_parent_id"
99
+ end
100
+
101
+ def update
102
+ translations.each do |translation|
103
+ assign_translation_parent_id(translation)
104
+ translation.connection = connection
105
+ translation.save
106
+ end
107
+
108
+ Concurrency.with_retries do
109
+ HttpResponse.handle_response(connection.put("/api/projects/#{connection.api_key}/#{self.class.resource_path}/#{id}", body: to_json))
110
+ end
111
+ end
112
+
113
+ def create
114
+ Concurrency.with_retries do
115
+ raw = connection.post("/api/projects/#{connection.api_key}/#{self.class.resource_path}", body: to_json(with_translations: true))
116
+ response = JSON.parse(HttpResponse.handle_response(raw))
117
+ self.id = response['id']
118
+ self.new_record = false
119
+ return true
120
+ end
121
+ end
122
+
123
+ def to_json(*_args, with_translations: false)
124
+ hash = to_hash
125
+ hash['translations'] = translations.map(&:to_hash) if translations.any? && with_translations
126
+ MultiJson.dump(hash)
127
+ end
128
+
129
+ private
130
+
131
+ def to_hash
132
+ {'id' => id}
133
+ end
134
+
135
+ end
136
+
137
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebTranslateIt
4
+
5
+ module Commands
6
+
7
+ class Add < Base
8
+
9
+ def call
10
+ require_parameters!(
11
+ min: 1,
12
+ error: 'Error: You must provide the path to the master file to add.',
13
+ usage: 'wti add path/to/master_file_1 path/to/master_file_2 ...'
14
+ )
15
+ complete_success = true
16
+ with_connection do |conn|
17
+ complete_success = add_files(conn)
18
+ end
19
+ complete_success
20
+ end
21
+
22
+ private
23
+
24
+ def validate_parameters!
25
+ return unless parameters == []
26
+
27
+ puts StringUtil.failure('Error: You must provide the path to the master file to add.')
28
+ puts 'Usage: wti add path/to/master_file_1 path/to/master_file_2 ...'
29
+ exit
30
+ end
31
+
32
+ def add_files(conn)
33
+ to_add = new_master_files
34
+ if to_add.empty?
35
+ puts 'No new master file to add.'
36
+ return true
37
+ end
38
+ to_add.all? { |param| create_file(param, conn) }
39
+ end
40
+
41
+ def create_file(param, conn)
42
+ file = TranslationFile.new(nil, param.gsub(/ /, '\\ '), nil, configuration.api_key)
43
+ result = file.create(conn)
44
+ puts StringUtil.array_to_columns(result.output)
45
+ result.success
46
+ end
47
+
48
+ def new_master_files
49
+ existing = configuration.files_for(locale: configuration.source_locale)
50
+ .to_set { |file| File.expand_path(file.file_path) }
51
+ parameters.reject { |param| existing.include?(File.expand_path(param)) }
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebTranslateIt
4
+
5
+ module Commands
6
+
7
+ class Addlocale < Base
8
+
9
+ def call
10
+ require_parameters!(
11
+ min: 1,
12
+ error: 'Locale code missing.',
13
+ usage: 'wti addlocale fr es ...'
14
+ )
15
+ parameters.each do |param|
16
+ print StringUtil.success("Adding locale #{param.upcase}... ")
17
+ with_connection do |conn|
18
+ WebTranslateIt::Project.create_locale(conn, param)
19
+ end
20
+ puts 'Done.'
21
+ end
22
+ true
23
+ end
24
+
25
+ private
26
+
27
+ def validate_parameters!
28
+ return unless parameters == []
29
+
30
+ puts StringUtil.failure('Locale code missing.')
31
+ puts 'Usage: wti addlocale fr es ...'
32
+ exit 1
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebTranslateIt
4
+
5
+ module Commands
6
+
7
+ class Base
8
+
9
+ attr_accessor :configuration, :command_options, :parameters
10
+
11
+ def initialize(configuration, command_options, parameters)
12
+ self.configuration = configuration
13
+ self.command_options = command_options
14
+ self.parameters = parameters
15
+ $stdout.sync = true
16
+ end
17
+
18
+ def call
19
+ raise NotImplementedError, "#{self.class.name} must implement #call"
20
+ end
21
+
22
+ protected
23
+
24
+ def require_parameters!(error:, usage:, min: 0, max: nil)
25
+ return if parameters.size >= min && (max.nil? || parameters.size <= max)
26
+
27
+ puts StringUtil.failure(error)
28
+ puts "Usage: #{usage}"
29
+ exit 1
30
+ end
31
+
32
+ def with_connection(&block)
33
+ WebTranslateIt::Connection.new(configuration.api_key, &block)
34
+ end
35
+
36
+ def run_hook(hook_command, label)
37
+ return unless hook_command
38
+
39
+ output = `#{hook_command}`
40
+ if $CHILD_STATUS.success?
41
+ puts output
42
+ else
43
+ abort "Error: #{label} command exited with: #{output}"
44
+ end
45
+ end
46
+
47
+ def warn_unknown_locales(locales)
48
+ locales.each do |locale|
49
+ puts "Locale #{locale} doesn't exist -- `wti addlocale #{locale}` to add it." unless configuration.target_locales.include?(locale)
50
+ end
51
+ locales
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+
5
+ module WebTranslateIt
6
+
7
+ module Commands
8
+
9
+ class Diff < Base
10
+
11
+ def call
12
+ complete_success = true
13
+ with_connection do |conn|
14
+ complete_success = diff_all_files(conn)
15
+ end
16
+ complete_success
17
+ end
18
+
19
+ private
20
+
21
+ def diff_all_files(conn)
22
+ files = select_files
23
+ if files.empty?
24
+ puts "Couldn't find any local files registered on WebTranslateIt to diff."
25
+ return true
26
+ end
27
+ files.all? { |file| diff_file(file, conn) }
28
+ end
29
+
30
+ def select_files
31
+ if parameters.any?
32
+ configuration.files_for(paths: parameters)
33
+ else
34
+ configuration.files_for(locale: configuration.source_locale)
35
+ end
36
+ end
37
+
38
+ def diff_file(file, conn)
39
+ return local_missing(file) unless File.exist?(file.file_path)
40
+
41
+ remote_content = file.fetch_remote_content(conn)
42
+ return remote_missing(file) unless remote_content
43
+
44
+ run_diff(file, remote_content)
45
+ end
46
+
47
+ def local_missing(file)
48
+ puts StringUtil.failure("Can't diff #{file.file_path}. File doesn't exist locally.")
49
+ false
50
+ end
51
+
52
+ def remote_missing(file)
53
+ puts StringUtil.failure("Couldn't fetch remote file #{file.file_path}")
54
+ false
55
+ end
56
+
57
+ def run_diff(file, remote_content)
58
+ Tempfile.create('wti') do |temp_file|
59
+ temp_file.write(remote_content)
60
+ temp_file.close
61
+ puts "Diff for #{file.file_path}:"
62
+ system "diff #{temp_file.path} #{file.file_path}"
63
+ end
64
+ true
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebTranslateIt
4
+
5
+ module Commands
6
+
7
+ class Init < Base
8
+
9
+ def call
10
+ puts '# Initializing project'
11
+ api_key, path = read_parameters
12
+ ensure_directory_exists(path)
13
+ project_info = JSON.parse(WebTranslateIt::Project.fetch_info(api_key))['project']
14
+ write_configuration(path, api_key, project_info)
15
+ print_success(project_info)
16
+ true
17
+ end
18
+
19
+ private
20
+
21
+ def read_parameters
22
+ if parameters.any?
23
+ [parameters[0], '.wti']
24
+ else
25
+ [Prompt.ask(' Project API Key:'), Prompt.ask(' Path to configuration file:', '.wti')]
26
+ end
27
+ end
28
+
29
+ def print_success(project_info)
30
+ puts ''
31
+ puts " The project #{project_info['name']} was successfully initialized."
32
+ puts ''
33
+ print_setup_hints(project_info)
34
+ puts 'You can now use `wti` to push and pull your language files.'
35
+ puts 'Check `wti --help` for help.'
36
+ end
37
+
38
+ def ensure_directory_exists(path)
39
+ dir = File.dirname(path)
40
+ FileUtils.mkpath(dir) unless dir == '.'
41
+ end
42
+
43
+ def write_configuration(path, api_key, project_info)
44
+ if File.exist?(path) && !File.writable?(path)
45
+ puts StringUtil.failure("Error: `#{path}` file is not writable.")
46
+ exit 1
47
+ end
48
+ File.open(path, 'w') { |file| file << generate_configuration(api_key, project_info) }
49
+ end
50
+
51
+ def print_setup_hints(project_info) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
52
+ if project_info['source_locale']['code'].nil? || project_info['target_locales'].size <= 1 || project_info['project_files'].none?
53
+ puts ''
54
+ puts ' There are a few more things to set up:'
55
+ puts ''
56
+ end
57
+ if project_info['source_locale']['code'].nil?
58
+ puts " *) You don't have a source locale setup."
59
+ puts ' Add the source locale with: `wti addlocale <locale_code>`'
60
+ puts ''
61
+ end
62
+ if project_info['target_locales'].size <= 1
63
+ puts " *) You don't have a target locale setup."
64
+ puts ' Add the first target locale with: `wti addlocale <locale_code>`'
65
+ puts ''
66
+ end
67
+ return unless project_info['project_files'].none?
68
+
69
+ puts " *) You don't have linguistic files setup."
70
+ puts ' Add a master file with: `wti add <path/to/file.xml>`'
71
+ puts ''
72
+ end
73
+
74
+ def generate_configuration(api_key, project_info)
75
+ <<~FILE
76
+ # Required - The Project API Token from WebTranslateIt.com
77
+ # More information: https://github.com/webtranslateit/webtranslateit/wiki#configuration-file
78
+
79
+ api_key: #{api_key}
80
+
81
+ # Optional - Locales not to sync with WebTranslateIt.
82
+ # Takes a string, a symbol, or an array of string or symbol.
83
+
84
+ # ignore_locales: [#{project_info['source_locale']['code']}]
85
+
86
+ # Optional - Locales to sync with WebTranslateIt.
87
+ # Takes a string, a symbol, or an array of string or symbol.
88
+
89
+ # needed_locales: #{project_info['target_locales'].map { |locale| locale['code'] }}
90
+
91
+ # Optional: files not to sync with WebTranslateIt.
92
+ # Takes an array of globs.
93
+
94
+ # ignore_files: ['somefile*.csv']
95
+
96
+ # Optional - Hooks
97
+ # Takes a string containing a command to run.
98
+
99
+ # before_pull: "echo 'some unix command'" # Command executed before pulling files
100
+ # after_pull: "touch tmp/restart.txt" # Command executed after pulling files
101
+
102
+ # before_push: "echo 'some unix command'" # Command executed before pushing files
103
+ # after_push: "touch tmp/restart.txt" # Command executed after pushing files
104
+
105
+ FILE
106
+ end
107
+
108
+ end
109
+
110
+ end
111
+
112
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebTranslateIt
4
+
5
+ module Commands
6
+
7
+ class Match < Base
8
+
9
+ def call
10
+ configuration.files_for(locale: configuration.source_locale).each do |master_file|
11
+ print_file_status(master_file)
12
+ configuration.target_files_for(master_file).each do |file|
13
+ print_file_status(file, prefix: '- ')
14
+ end
15
+ end
16
+ true
17
+ end
18
+
19
+ private
20
+
21
+ def print_file_status(file, prefix: '')
22
+ label = "#{prefix}#{file.file_path} (#{file.locale})"
23
+ puts File.exist?(file.file_path) ? "#{prefix}#{StringUtil.important(file.file_path)} (#{file.locale})" : StringUtil.failure(label)
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebTranslateIt
4
+
5
+ module Commands
6
+
7
+ class Mv < Base
8
+
9
+ def call
10
+ require_parameters!(
11
+ min: 2, max: 2,
12
+ error: 'Error: You must provide the source path and destination path of the master file to move.',
13
+ usage: 'wti mv path/to/master_file_old_path path/to/master_file_new_path'
14
+ )
15
+ source = parameters[0]
16
+ destination = parameters[1]
17
+ complete_success = true
18
+ with_connection do |conn|
19
+ complete_success = move_file(source, destination, conn)
20
+ end
21
+ complete_success
22
+ end
23
+
24
+ private
25
+
26
+ def move_file(source, destination, conn)
27
+ return true unless Prompt.ask_yes_no("Are you sure you want to move the master file #{source} and its target files?", true)
28
+
29
+ complete_success = true
30
+ configuration.files_for(paths: [source]).each do |master_file|
31
+ upload_and_move_master(master_file, source, destination, conn)
32
+ delete_old_target_files(master_file)
33
+ configuration.reload
34
+ complete_success = fetch_new_target_files(master_file, conn)
35
+ puts StringUtil.success('All done.') if complete_success
36
+ end
37
+ complete_success
38
+ end
39
+
40
+ def upload_and_move_master(master_file, source, destination, conn)
41
+ result = master_file.upload(conn, force: true, rename_others: true, destination_path: destination)
42
+ puts StringUtil.array_to_columns(result.output)
43
+ return unless File.exist?(source)
44
+
45
+ File.rename(source, destination)
46
+ puts StringUtil.success("Moved master file #{master_file.file_path}.")
47
+ end
48
+
49
+ def delete_old_target_files(master_file)
50
+ configuration.target_files_for(master_file).each do |target_file|
51
+ FileUtils.rm_f(target_file.file_path)
52
+ end
53
+ end
54
+
55
+ def fetch_new_target_files(master_file, conn)
56
+ configuration.target_files_for(master_file).all? do |target_file|
57
+ result = target_file.fetch(conn)
58
+ print StringUtil.array_to_columns(result.output)
59
+ result.success
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end