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
@@ -1,521 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'tempfile'
4
-
5
- module WebTranslateIt
6
-
7
- class CommandLine # rubocop:todo Metrics/ClassLength
8
-
9
- attr_accessor :configuration, :global_options, :command_options, :parameters
10
-
11
- def initialize(command, command_options, _global_options, parameters, project_path) # rubocop:todo Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/AbcSize
12
- self.command_options = command_options
13
- self.parameters = parameters
14
- unless command == 'init'
15
- message = case command
16
- when 'pull'
17
- 'Pulling files'
18
- when 'push'
19
- 'Pushing files'
20
- when 'diff'
21
- 'Diffing files'
22
- when 'add'
23
- 'Creating master files'
24
- when 'rm'
25
- 'Deleting files'
26
- when 'mv'
27
- 'Moving files'
28
- when 'addlocale'
29
- 'Adding locale'
30
- when 'rmlocale'
31
- 'Deleting locale'
32
- else
33
- 'Gathering information'
34
- end
35
- throb do
36
- print " #{message}"
37
- self.configuration = WebTranslateIt::Configuration.new(project_path, configuration_file_path)
38
- print " #{message} on #{configuration.project_name}"
39
- end
40
- end
41
- success = send(command)
42
- exit 1 unless success
43
- end
44
-
45
- def pull # rubocop:todo Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
46
- complete_success = true
47
- $stdout.sync = true
48
- before_pull_hook
49
- # Selecting files to pull
50
- files = []
51
- fetch_locales_to_pull.each do |locale|
52
- files |= configuration.files.find_all { |file| file.locale == locale }
53
- end
54
- found_files = []
55
- parameters.each do |parameter|
56
- found_files += files.find_all { |file| File.fnmatch(parameter, file.file_path) }
57
- end
58
- files = found_files if parameters.any?
59
- files = files.uniq.sort { |a, b| a.file_path <=> b.file_path }
60
- if files.empty?
61
- puts 'No files to pull.'
62
- else
63
- # Now actually pulling files
64
- time = Time.now
65
- threads = []
66
- n_threads = [(files.size.to_f / 3).ceil, 10].min
67
- files.each_slice((files.size.to_f / n_threads).round).each do |file_array|
68
- next if file_array.empty?
69
-
70
- threads << Thread.new(file_array) do |f_array|
71
- WebTranslateIt::Connection.new(configuration.api_key) do |conn|
72
- f_array.each do |file|
73
- success = file.fetch(conn.http_connection, command_options.force)
74
- complete_success = false unless success
75
- end
76
- end
77
- end
78
- end
79
- threads.each(&:join)
80
- time = Time.now - time
81
- puts "Pulled #{files.size} files at #{(files.size / time).round} files/sec, using #{n_threads} threads."
82
- after_pull_hook
83
- complete_success
84
- end
85
- end
86
-
87
- def before_pull_hook
88
- return unless configuration.before_pull
89
-
90
- output = `#{configuration.before_pull}`
91
- if $CHILD_STATUS.success?
92
- puts output
93
- else
94
- abort "Error: before_pull command exited with: #{output}"
95
- end
96
- end
97
-
98
- def after_pull_hook
99
- return unless configuration.after_pull
100
-
101
- output = `#{configuration.after_pull}`
102
- if $CHILD_STATUS.success?
103
- puts output
104
- else
105
- abort "Error: after_pull command exited with: #{output}"
106
- end
107
- end
108
-
109
- def push # rubocop:todo Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
110
- puts 'The `--low-priority` option in `wti push --low-priority` was removed and does nothing' if command_options.low_priority
111
- complete_success = true
112
- $stdout.sync = true
113
- before_push_hook
114
- WebTranslateIt::Connection.new(configuration.api_key) do |conn|
115
- fetch_locales_to_push(configuration).each do |locale|
116
- files = if parameters.any?
117
- configuration.files.find_all { |file| parameters.include?(file.file_path) }.sort { |a, b| a.file_path <=> b.file_path }
118
- else
119
- configuration.files.find_all { |file| file.locale == locale }.sort { |a, b| a.file_path <=> b.file_path }
120
- end
121
- if files.empty?
122
- puts "Couldn't find any local files registered on WebTranslateIt to push."
123
- else
124
- files.each do |file|
125
- success = file.upload(conn.http_connection, command_options[:merge], command_options.ignore_missing, command_options.label, command_options[:minor], command_options.force)
126
- complete_success = false unless success
127
- end
128
- end
129
- end
130
- end
131
- after_push_hook
132
- complete_success
133
- end
134
-
135
- def before_push_hook
136
- return unless configuration.before_push
137
-
138
- output = `#{configuration.before_push}`
139
- if $CHILD_STATUS.success?
140
- puts output
141
- else
142
- abort "Error: before_push command exited with: #{output}"
143
- end
144
- end
145
-
146
- def after_push_hook
147
- return unless configuration.after_push
148
-
149
- output = `#{configuration.after_push}`
150
- if $CHILD_STATUS.success?
151
- puts output
152
- else
153
- abort "Error: after_push command exited with: #{output}"
154
- end
155
- end
156
-
157
- def diff # rubocop:todo Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
158
- complete_success = true
159
- $stdout.sync = true
160
- WebTranslateIt::Connection.new(configuration.api_key) do |conn| # rubocop:todo Metrics/BlockLength
161
- files = if parameters.any?
162
- configuration.files.find_all { |file| parameters.include?(file.file_path) }.sort { |a, b| a.file_path <=> b.file_path }
163
- else
164
- configuration.files.find_all { |file| file.locale == configuration.source_locale }.sort { |a, b| a.file_path <=> b.file_path }
165
- end
166
- if files.empty?
167
- puts "Couldn't find any local files registered on WebTranslateIt to diff."
168
- else
169
- files.each do |file|
170
- if File.exist?(file.file_path)
171
- remote_content = file.fetch_remote_content(conn.http_connection)
172
- if remote_content
173
- temp_file = Tempfile.new('wti')
174
- temp_file.write(remote_content)
175
- temp_file.close
176
- puts "Diff for #{file.file_path}:"
177
- system "diff #{temp_file.path} #{file.file_path}"
178
- temp_file.unlink
179
- else
180
- puts StringUtil.failure("Couldn't fetch remote file #{file.file_path}")
181
- complete_success = false
182
- end
183
- else
184
- puts StringUtil.failure("Can't diff #{file.file_path}. File doesn't exist locally.")
185
- complete_success = false
186
- end
187
- end
188
- end
189
- end
190
- complete_success
191
- end
192
-
193
- def add # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
194
- complete_success = true
195
- $stdout.sync = true
196
- if parameters == []
197
- puts StringUtil.failure('Error: You must provide the path to the master file to add.')
198
- puts 'Usage: wti add path/to/master_file_1 path/to/master_file_2 ...'
199
- exit
200
- end
201
- WebTranslateIt::Connection.new(configuration.api_key) do |conn|
202
- added = configuration.files.find_all { |file| file.locale == configuration.source_locale }.to_set { |file| File.expand_path(file.file_path) }
203
- to_add = parameters.reject { |param| added.include?(File.expand_path(param)) }
204
- if to_add.any?
205
- to_add.each do |param|
206
- file = TranslationFile.new(nil, param.gsub(/ /, '\\ '), nil, configuration.api_key)
207
- success = file.create(conn.http_connection)
208
- complete_success = false unless success
209
- end
210
- else
211
- puts 'No new master file to add.'
212
- end
213
- end
214
- complete_success
215
- end
216
-
217
- def rm # rubocop:todo Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
218
- complete_success = true
219
- $stdout.sync = true
220
- if parameters == []
221
- puts StringUtil.failure('Error: You must provide the path to the master file to remove.')
222
- puts 'Usage: wti rm path/to/master_file_1 path/to/master_file_2 ...'
223
- exit
224
- end
225
- WebTranslateIt::Connection.new(configuration.api_key) do |conn| # rubocop:todo Metrics/BlockLength
226
- parameters.each do |param|
227
- next unless Util.ask_yes_no("Are you sure you want to delete the master file #{param}?\nThis will also delete its target files and translations.", false)
228
-
229
- files = configuration.files.find_all { |file| file.file_path == param }
230
- if files.any?
231
- files.each do |master_file|
232
- master_file.delete(conn.http_connection)
233
- # delete files
234
- if File.exist?(master_file.file_path)
235
- success = File.delete(master_file.file_path)
236
- puts StringUtil.success("Deleted master file #{master_file.file_path}.") if success
237
- end
238
- complete_success = false unless success
239
- configuration.files.find_all { |file| file.master_id == master_file.id }.each do |target_file|
240
- if File.exist?(target_file.file_path)
241
- success = File.delete(target_file.file_path)
242
- puts StringUtil.success("Deleted target file #{target_file.file_path}.") if success
243
- else
244
- puts StringUtil.failure("Target file #{target_file.file_path} doesn’t exist locally")
245
- end
246
- complete_success = false unless success
247
- end
248
- end
249
- puts StringUtil.success('All done.') if complete_success
250
- else
251
- puts StringUtil.failure("#{param}: File doesn’t exist on project.")
252
- end
253
- end
254
- end
255
- complete_success
256
- end
257
-
258
- def mv # rubocop:todo Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
259
- complete_success = true
260
- $stdout.sync = true
261
- if parameters.count != 2
262
- puts StringUtil.failure('Error: You must provide the source path and destination path of the master file to move.')
263
- puts 'Usage: wti mv path/to/master_file_old_path path/to/master_file_new_path ...'
264
- exit
265
- end
266
- source = parameters[0]
267
- destination = parameters[1]
268
- WebTranslateIt::Connection.new(configuration.api_key) do |conn|
269
- if Util.ask_yes_no("Are you sure you want to move the master file #{source} and its target files?", true)
270
- configuration.files.find_all { |file| file.file_path == source }.each do |master_file|
271
- master_file.upload(conn.http_connection, false, false, nil, false, true, true, destination)
272
- # move master file
273
- if File.exist?(source)
274
- success = File.rename(source, destination) if File.exist?(source)
275
- puts StringUtil.success("Moved master file #{master_file.file_path}.") if success
276
- end
277
- complete_success = false unless success
278
- configuration.files.find_all { |file| file.master_id == master_file.id }.each do |target_file|
279
- if File.exist?(target_file.file_path)
280
- success = File.delete(target_file.file_path)
281
- complete_success = false unless success
282
- end
283
- end
284
- configuration.reload
285
- configuration.files.find_all { |file| file.master_id == master_file.id }.each do |target_file|
286
- success = target_file.fetch(conn.http_connection)
287
- complete_success = false unless success
288
- end
289
- puts StringUtil.success('All done.') if complete_success
290
- end
291
- end
292
- end
293
- complete_success
294
- end
295
-
296
- def addlocale # rubocop:todo Metrics/MethodLength
297
- $stdout.sync = true
298
- if parameters == []
299
- puts StringUtil.failure('Locale code missing.')
300
- puts 'Usage: wti addlocale fr es ...'
301
- exit 1
302
- end
303
- parameters.each do |param|
304
- print StringUtil.success("Adding locale #{param.upcase}... ")
305
- WebTranslateIt::Connection.new(configuration.api_key) do |conn|
306
- WebTranslateIt::Project.create_locale(conn, param)
307
- end
308
- puts 'Done.'
309
- end
310
- end
311
-
312
- def rmlocale # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
313
- $stdout.sync = true
314
- if parameters == []
315
- puts StringUtil.failure('Error: You must provide the locale code to remove.')
316
- puts 'Usage: wti rmlocale fr es ...'
317
- exit 1
318
- end
319
- parameters.each do |param|
320
- next unless Util.ask_yes_no("Are you certain you want to delete the locale #{param.upcase}?\nThis will also delete its files and translations.", false)
321
-
322
- print StringUtil.success("Deleting locale #{param.upcase}... ")
323
- WebTranslateIt::Connection.new(configuration.api_key) do |conn|
324
- WebTranslateIt::Project.delete_locale(conn, param)
325
- end
326
- puts 'Done.'
327
- end
328
- end
329
-
330
- def init # rubocop:todo Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
331
- puts '# Initializing project'
332
- if parameters.any?
333
- api_key = parameters[0]
334
- path = '.wti'
335
- else
336
- api_key = Util.ask(' Project API Key:')
337
- path = Util.ask(' Path to configuration file:', '.wti')
338
- end
339
- FileUtils.mkpath(path.split('/')[0..(path.split('/').size - 2)].join('/')) unless path.split('/').size == 1
340
- project = JSON.parse WebTranslateIt::Project.fetch_info(api_key)
341
- project_info = project['project']
342
- if File.exist?(path) && !File.writable?(path)
343
- puts StringUtil.failure("Error: `#{path}` file is not writable.")
344
- exit 1
345
- end
346
- File.open(path, 'w') { |file| file << generate_configuration(api_key, project_info) }
347
- puts ''
348
- puts " The project #{project_info['name']} was successfully initialized."
349
- puts ''
350
- if project_info['source_locale']['code'].nil? || project_info['target_locales'].size <= 1 || project_info['project_files'].none?
351
- puts ''
352
- puts ' There are a few more things to set up:'
353
- puts ''
354
- end
355
- if project_info['source_locale']['code'].nil?
356
- puts " *) You don't have a source locale setup."
357
- puts ' Add the source locale with: `wti addlocale <locale_code>`'
358
- puts ''
359
- end
360
- if project_info['target_locales'].size <= 1
361
- puts " *) You don't have a target locale setup."
362
- puts ' Add the first target locale with: `wti addlocale <locale_code>`'
363
- puts ''
364
- end
365
- if project_info['project_files'].none?
366
- puts " *) You don't have linguistic files setup."
367
- puts ' Add a master file with: `wti add <path/to/file.xml>`'
368
- puts ''
369
- end
370
- puts 'You can now use `wti` to push and pull your language files.'
371
- puts 'Check `wti --help` for help.'
372
- true
373
- end
374
-
375
- def match # rubocop:todo Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
376
- configuration.files.find_all { |mf| mf.locale == configuration.source_locale }.each do |master_file|
377
- if File.exist?(master_file.file_path)
378
- puts StringUtil.important(master_file.file_path) + " (#{master_file.locale})"
379
- else
380
- puts StringUtil.failure(master_file.file_path) + " (#{master_file.locale})"
381
- end
382
- configuration.files.find_all { |f| f.master_id == master_file.id }.each do |file|
383
- if File.exist?(file.file_path)
384
- puts "- #{file.file_path}" + " (#{file.locale})"
385
- else
386
- puts StringUtil.failure("- #{file.file_path}") + " (#{file.locale})"
387
- end
388
- end
389
- end
390
- true
391
- end
392
-
393
- def status # rubocop:todo Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
394
- if parameters.any?
395
- file = configuration.files.find { |f| parameters.first.strip == f.file_path }
396
- abort "File '#{parameters.first}' not found." unless file
397
-
398
- file_id = file.master_id || file.id
399
- puts "Statistics for '#{parameters.first}':"
400
- end
401
- stats = JSON.parse(Project.fetch_stats(configuration.api_key, file_id))
402
- completely_translated = true
403
- completely_proofread = true
404
- stats.each do |locale, values|
405
- percent_translated = Util.calculate_percentage(values['count_strings_to_proofread'].to_i + values['count_strings_done'].to_i + values['count_strings_to_verify'].to_i, values['count_strings'].to_i)
406
- percent_completed = Util.calculate_percentage(values['count_strings_done'].to_i, values['count_strings'].to_i)
407
- completely_translated = false if percent_translated != 100
408
- completely_proofread = false if percent_completed != 100
409
- puts "#{locale}: #{percent_translated}% translated, #{percent_completed}% completed."
410
- end
411
- exit 100 unless completely_translated
412
- exit 101 unless completely_proofread
413
- true
414
- end
415
-
416
- def fetch_locales_to_pull # rubocop:todo Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
417
- if command_options.locale
418
- command_options.locale.split.each do |locale|
419
- puts "Locale #{locale} doesn't exist -- `wti addlocale #{locale}` to add it." unless configuration.target_locales.include?(locale)
420
- end
421
- locales = command_options.locale.split
422
- elsif configuration.needed_locales.any?
423
- locales = configuration.needed_locales
424
- else
425
- locales = configuration.target_locales
426
- configuration.ignore_locales.each { |locale_to_delete| locales.delete(locale_to_delete) } if configuration.ignore_locales.any?
427
- end
428
- locales.push(configuration.source_locale) if command_options.all
429
- locales.uniq
430
- end
431
-
432
- def fetch_locales_to_push(configuration) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
433
- if command_options.locale
434
- command_options.locale.split.each do |locale|
435
- puts "Locale #{locale} doesn't exist -- `wti addlocale #{locale}` to add it." unless configuration.target_locales.include?(locale)
436
- end
437
- locales = command_options.locale.split
438
- else
439
- locales = [configuration.source_locale]
440
- end
441
- if command_options.all
442
- puts '`wti push --all` was deprecated in wti 2.3. Use `wti push --target` instead.'
443
- return []
444
- elsif command_options.target
445
- locales = configuration.target_locales.reject { |locale| locale == configuration.source_locale }
446
- end
447
- locales.uniq
448
- end
449
-
450
- def configuration_file_path
451
- return command_options.config if command_options.config
452
-
453
- return '.wti' unless File.exist?('config/translation.yml')
454
-
455
- puts 'Warning: `config/translation.yml` is deprecated in favour of a `.wti` file.'
456
- return 'config/translation.yml' unless Util.ask_yes_no('Would you like to migrate your configuration now?', true)
457
-
458
- return '.wti' if FileUtils.mv('config/translation.yml', '.wti')
459
-
460
- puts 'Couldn’t move `config/translation.yml`.'
461
- false
462
- end
463
-
464
- def generate_configuration(api_key, project_info)
465
- <<~FILE
466
- # Required - The Project API Token from WebTranslateIt.com
467
- # More information: https://github.com/webtranslateit/webtranslateit/wiki#configuration-file
468
-
469
- api_key: #{api_key}
470
-
471
- # Optional - Locales not to sync with WebTranslateIt.
472
- # Takes a string, a symbol, or an array of string or symbol.
473
-
474
- # ignore_locales: [#{project_info['source_locale']['code']}]
475
-
476
- # Optional - Locales to sync with WebTranslateIt.
477
- # Takes a string, a symbol, or an array of string or symbol.
478
-
479
- # needed_locales: #{project_info['target_locales'].map { |locale| locale['code'] }}
480
-
481
- # Optional: files not to sync with WebTranslateIt.
482
- # Takes an array of globs.
483
-
484
- # ignore_files: ['somefile*.csv']
485
-
486
- # Optional - Hooks
487
- # Takes a string containing a command to run.
488
-
489
- # before_pull: "echo 'some unix command'" # Command executed before pulling files
490
- # after_pull: "touch tmp/restart.txt" # Command executed after pulling files
491
-
492
- # before_push: "echo 'some unix command'" # Command executed before pushing files
493
- # after_push: "touch tmp/restart.txt" # Command executed after pushing files
494
-
495
- FILE
496
- end
497
-
498
- def throb # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
499
- throb = %w[⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏]
500
- throb.reverse! if rand > 0.5
501
- i = rand throb.length
502
-
503
- thread = Thread.new do
504
- dot = lambda do
505
- print "\r#{throb[i]}\e[?25l"
506
- i = (i + 1) % throb.length
507
- sleep 0.1 and dot.call
508
- end
509
- dot.call
510
- end
511
- yield
512
- ensure
513
- if thread
514
- thread.kill
515
- puts "\r\e[0G#\e[?25h"
516
- end
517
- end
518
-
519
- end
520
-
521
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Hash
4
-
5
- # Destructively convert all keys to strings.
6
- def stringify_keys!
7
- keys.each do |key|
8
- self[key.to_s] = delete(key)
9
- end
10
- self
11
- end
12
-
13
- end