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.
- checksums.yaml +4 -4
- data/bin/wti +2 -5
- data/history.md +29 -0
- data/lib/web_translate_it/api_resource.rb +137 -0
- data/lib/web_translate_it/commands/add.rb +58 -0
- data/lib/web_translate_it/commands/addlocale.rb +39 -0
- data/lib/web_translate_it/commands/base.rb +58 -0
- data/lib/web_translate_it/commands/diff.rb +71 -0
- data/lib/web_translate_it/commands/init.rb +112 -0
- data/lib/web_translate_it/commands/match.rb +30 -0
- data/lib/web_translate_it/commands/mv.rb +67 -0
- data/lib/web_translate_it/commands/pull.rb +62 -0
- data/lib/web_translate_it/commands/push.rb +50 -0
- data/lib/web_translate_it/commands/rm.rb +67 -0
- data/lib/web_translate_it/commands/rmlocale.rb +43 -0
- data/lib/web_translate_it/commands/status.rb +52 -0
- data/lib/web_translate_it/configuration.rb +31 -21
- data/lib/web_translate_it/connection.rb +30 -3
- data/lib/web_translate_it/project.rb +13 -72
- data/lib/web_translate_it/runner.rb +75 -0
- data/lib/web_translate_it/string.rb +21 -260
- data/lib/web_translate_it/term.rb +15 -255
- data/lib/web_translate_it/term_translation.rb +16 -69
- data/lib/web_translate_it/translation.rb +12 -50
- data/lib/web_translate_it/translation_base.rb +38 -0
- data/lib/web_translate_it/translation_file.rb +58 -109
- data/lib/web_translate_it/util/concurrency.rb +37 -0
- data/lib/web_translate_it/util/hash_util.rb +0 -2
- data/lib/web_translate_it/util/http_response.rb +46 -0
- data/lib/web_translate_it/util/prompt.rb +49 -0
- data/lib/web_translate_it/util/spinner.rb +51 -0
- data/lib/web_translate_it/util/string_util.rb +2 -8
- data/lib/web_translate_it/util.rb +0 -75
- data/lib/web_translate_it.rb +21 -3
- metadata +20 -3
- data/lib/web_translate_it/command_line.rb +0 -521
- 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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2344f55bed19e435f8334c66229e58266ff1efe3cf54e9e81f4d17287f42a93d
|
|
4
|
+
data.tar.gz: 89c6ea08bcc87a43741c8419d5de32e2441f896f12e8338762c15cf84759ab5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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::
|
|
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
|