web_translate_it 3.2.2 → 3.2.3
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 +6 -5
- data/history.md +6 -0
- data/lib/web_translate_it/api_resource.rb +3 -3
- data/lib/web_translate_it/auto_fetch.rb +1 -1
- data/lib/web_translate_it/commands/pull.rb +1 -1
- data/lib/web_translate_it/commands/push.rb +1 -1
- data/lib/web_translate_it/string.rb +10 -10
- data/lib/web_translate_it/term.rb +2 -2
- data/lib/web_translate_it/util/concurrency.rb +16 -7
- data/lib/web_translate_it/util/http_response.rb +28 -8
- data/lib/web_translate_it/util/string_util.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 660b650bae69f2f4a4282acb69b7abc72296b7e352660adaf3c1047a3bac04ce
|
|
4
|
+
data.tar.gz: acf8cc2ef71f6a897d400cd99793b86c33d28672b8aa6049b49d1f3b3a2f3c16
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dc29fee26eb5eb394705c81975adcf21f03dbe3ca7918d0e9a622a791ef0163ee9c7aa00f88d9c608eea462b5a75e8b9e4c7d530af276cfa3449ab7824ef53ec
|
|
7
|
+
data.tar.gz: 148e6238f00cb1cd2030a8a9f13c1b155ed82c26baac67f6933d86da68463998a8f91f144339f8d538512fe7eee0ec47cab15b23a2de8bdab27df48f77176b76
|
data/bin/wti
CHANGED
|
@@ -42,11 +42,12 @@ when 'pull'
|
|
|
42
42
|
wti pull [filename] - Pull target language file(s)
|
|
43
43
|
[options] are:
|
|
44
44
|
BANNER
|
|
45
|
-
opt :locale,
|
|
46
|
-
opt :all,
|
|
47
|
-
opt :force,
|
|
48
|
-
opt :
|
|
49
|
-
opt :
|
|
45
|
+
opt :locale, 'ISO code of locale(s) to pull, space-separated', type: :string
|
|
46
|
+
opt :all, 'Pull all files'
|
|
47
|
+
opt :force, 'Force pull (bypass conditional requests to WTI)'
|
|
48
|
+
opt :threads, 'Number of threads for parallel downloads', type: :integer, default: 10
|
|
49
|
+
opt :config, 'Path to a configuration file', short: '-c', default: '.wti'
|
|
50
|
+
opt :debug, 'Display debug information'
|
|
50
51
|
end
|
|
51
52
|
when 'push'
|
|
52
53
|
Optimist.options do
|
data/history.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## Version 3.2.3 / 2026-04-09
|
|
2
|
+
|
|
3
|
+
* Handle HTTP 429 (rate limit) errors: add `RateLimitError` class, retry with backoff respecting the `Retry-After` header, and fix garbled error messages when the response body is not JSON.
|
|
4
|
+
* Add `--threads N` option to `wti pull` to control the number of concurrent download threads. Defaults to 10. Use `--threads 1` for sequential pulls.
|
|
5
|
+
* Modernize code for Ruby 3.0+: use `match?` instead of `!~`, `start_with?`/`delete_prefix` instead of index slicing, array difference instead of `reject`, and remove redundant `|| nil` fallbacks.
|
|
6
|
+
|
|
1
7
|
## Version 3.2.2 / 2026-03-03
|
|
2
8
|
|
|
3
9
|
* Replace O(n²) string concatenation loop in `StringUtil#backward_truncate` with `String#ljust`.
|
|
@@ -9,9 +9,9 @@ module WebTranslateIt
|
|
|
9
9
|
def initialize(params = {}, connection: nil)
|
|
10
10
|
params = params.transform_keys(&:to_s)
|
|
11
11
|
self.connection = connection
|
|
12
|
-
self.id = params['id']
|
|
13
|
-
self.created_at = params['created_at']
|
|
14
|
-
self.updated_at = params['updated_at']
|
|
12
|
+
self.id = params['id']
|
|
13
|
+
self.created_at = params['created_at']
|
|
14
|
+
self.updated_at = params['updated_at']
|
|
15
15
|
self.translations = params['translations'] || []
|
|
16
16
|
self.new_record = true
|
|
17
17
|
assign_attributes(params)
|
|
@@ -29,7 +29,7 @@ module WebTranslateIt
|
|
|
29
29
|
|
|
30
30
|
def pull_files(files) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength, Naming/PredicateMethod
|
|
31
31
|
time = Time.now
|
|
32
|
-
results, n_threads = Concurrency.concurrent_batch(files) do |batch|
|
|
32
|
+
results, n_threads = Concurrency.concurrent_batch(files, max_threads: command_options.threads) do |batch|
|
|
33
33
|
with_connection do |conn|
|
|
34
34
|
batch.map do |file|
|
|
35
35
|
result = file.fetch(conn, command_options.force)
|
|
@@ -37,7 +37,7 @@ module WebTranslateIt
|
|
|
37
37
|
if command_options.locale
|
|
38
38
|
warn_unknown_locales(command_options.locale.split)
|
|
39
39
|
elsif command_options.target
|
|
40
|
-
configuration.target_locales
|
|
40
|
+
configuration.target_locales - [configuration.source_locale]
|
|
41
41
|
else
|
|
42
42
|
[configuration.source_locale]
|
|
43
43
|
end
|
|
@@ -16,16 +16,16 @@ module WebTranslateIt
|
|
|
16
16
|
|
|
17
17
|
protected
|
|
18
18
|
|
|
19
|
-
def assign_attributes(params) # rubocop:todo Metrics/AbcSize
|
|
20
|
-
self.key = params['key']
|
|
21
|
-
self.plural = params['plural']
|
|
22
|
-
self.type = params['type']
|
|
23
|
-
self.dev_comment = params['dev_comment']
|
|
24
|
-
self.word_count = params['word_count']
|
|
25
|
-
self.status = params['status']
|
|
26
|
-
self.category = params['category']
|
|
27
|
-
self.labels = params['labels']
|
|
28
|
-
self.file = params['file']
|
|
19
|
+
def assign_attributes(params) # rubocop:todo Metrics/AbcSize
|
|
20
|
+
self.key = params['key']
|
|
21
|
+
self.plural = params['plural']
|
|
22
|
+
self.type = params['type']
|
|
23
|
+
self.dev_comment = params['dev_comment']
|
|
24
|
+
self.word_count = params['word_count']
|
|
25
|
+
self.status = params['status']
|
|
26
|
+
self.category = params['category']
|
|
27
|
+
self.labels = params['labels']
|
|
28
|
+
self.file = params['file']
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def parse_translation_response(json)
|
|
@@ -13,8 +13,8 @@ module WebTranslateIt
|
|
|
13
13
|
protected
|
|
14
14
|
|
|
15
15
|
def assign_attributes(params)
|
|
16
|
-
self.text = params['text']
|
|
17
|
-
self.description = params['description']
|
|
16
|
+
self.text = params['text']
|
|
17
|
+
self.description = params['description']
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def parse_translation_response(json)
|
|
@@ -4,18 +4,27 @@ module WebTranslateIt
|
|
|
4
4
|
|
|
5
5
|
module Concurrency
|
|
6
6
|
|
|
7
|
-
# Execute a block with automatic retry on Timeout::Error.
|
|
7
|
+
# Execute a block with automatic retry on Timeout::Error and RateLimitError.
|
|
8
8
|
# Returns the block's return value on success, or re-raises after retries are exhausted.
|
|
9
9
|
def self.with_retries(retries: 3, delay: 5)
|
|
10
10
|
yield
|
|
11
11
|
rescue Timeout::Error
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
raise
|
|
12
|
+
raise unless (retries -= 1).positive?
|
|
13
|
+
|
|
14
|
+
log_retry('Request timeout', delay)
|
|
15
|
+
retry
|
|
16
|
+
rescue RateLimitError => e
|
|
17
|
+
raise unless (retries -= 1).positive?
|
|
18
|
+
|
|
19
|
+
log_retry('Rate limited', e.retry_after || delay)
|
|
20
|
+
retry
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.log_retry(message, wait)
|
|
24
|
+
puts "#{message}. Will retry in #{wait} seconds."
|
|
25
|
+
sleep(wait)
|
|
18
26
|
end
|
|
27
|
+
private_class_method :log_retry
|
|
19
28
|
|
|
20
29
|
# Process items in parallel using a thread pool.
|
|
21
30
|
# Yields each batch (array of items) to the block; collects return values.
|
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
module WebTranslateIt
|
|
4
4
|
|
|
5
|
+
class RateLimitError < StandardError
|
|
6
|
+
|
|
7
|
+
attr_reader :retry_after
|
|
8
|
+
|
|
9
|
+
def initialize(retry_after: nil)
|
|
10
|
+
@retry_after = retry_after
|
|
11
|
+
super("Rate limited#{", retry after #{retry_after}s" if retry_after}")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
end
|
|
15
|
+
|
|
5
16
|
module HttpResponse
|
|
6
17
|
|
|
7
18
|
STATUS_LABELS = {
|
|
@@ -31,15 +42,24 @@ module WebTranslateIt
|
|
|
31
42
|
|
|
32
43
|
def self.raise_on_error!(response)
|
|
33
44
|
code = response.code.to_i
|
|
34
|
-
if code
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
elsif code == 503
|
|
39
|
-
raise 'Error: Locked (another import in progress)'
|
|
40
|
-
end
|
|
45
|
+
raise_on_rate_limit!(response) if code == 429
|
|
46
|
+
raise "Error: #{error_message(response)}" if code >= 400 && code < 500
|
|
47
|
+
raise 'Error: Server temporarily unavailable (Error 500).' if code == 500
|
|
48
|
+
raise 'Error: Locked (another import in progress)' if code == 503
|
|
41
49
|
end
|
|
42
|
-
|
|
50
|
+
|
|
51
|
+
def self.raise_on_rate_limit!(response)
|
|
52
|
+
retry_after = response['Retry-After']&.to_i
|
|
53
|
+
raise RateLimitError.new(retry_after: retry_after)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.error_message(response)
|
|
57
|
+
MultiJson.load(response.body)['error']
|
|
58
|
+
rescue StandardError
|
|
59
|
+
response.body.to_s
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private_class_method :raise_on_error!, :raise_on_rate_limit!, :error_message
|
|
43
63
|
|
|
44
64
|
end
|
|
45
65
|
|
|
@@ -25,8 +25,8 @@ class StringUtil
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def self.array_to_columns(array)
|
|
28
|
-
if array[0]
|
|
29
|
-
"*#{backward_truncate(array[0]
|
|
28
|
+
if array[0].start_with?('*')
|
|
29
|
+
"*#{backward_truncate(array[0].delete_prefix('*'))} | #{array[1]} #{array[2]}\n"
|
|
30
30
|
else
|
|
31
31
|
" #{backward_truncate(array[0])} | #{array[1]} #{array[2]}\n"
|
|
32
32
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: web_translate_it
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.2.
|
|
4
|
+
version: 3.2.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Edouard Briere
|
|
@@ -113,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
113
113
|
- !ruby/object:Gem::Version
|
|
114
114
|
version: '0'
|
|
115
115
|
requirements: []
|
|
116
|
-
rubygems_version:
|
|
116
|
+
rubygems_version: 4.0.6
|
|
117
117
|
specification_version: 4
|
|
118
118
|
summary: A CLI tool to sync locale files with WebTranslateIt.com.
|
|
119
119
|
test_files: []
|