lokalise_manager 5.1.1 → 5.1.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/CHANGELOG.md +5 -1
- data/lib/lokalise_manager/error.rb +1 -1
- data/lib/lokalise_manager/task_definitions/base.rb +36 -26
- data/lib/lokalise_manager/task_definitions/exporter.rb +35 -38
- data/lib/lokalise_manager/task_definitions/importer.rb +6 -17
- data/lib/lokalise_manager/version.rb +1 -1
- data/lokalise_manager.gemspec +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 109786a5056b1b17efbc2214df611dcbef3b774169f04b71a1d5ee3f531bc514
|
4
|
+
data.tar.gz: 250e0e838b6d3166e3e9fa5135e6417af1bf5b853719712ddecbef181015ee48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1206ab988a7545b40838b3e053d2e5af54ef3b2bd8bf63a4d48e8d4544d8b005c4e74f6b4549010f8263b5a1c469abbe9a09b9371a43627c0ecfdfc4b76390e4
|
7
|
+
data.tar.gz: 90887ab6f6b8b5d5d05688ca20a8e0e0d6afe195a4b3edee1a6021fc5f481f830fa5be856b60fe95c0ddbe07eb6deaecfb5ff29744187b601cc2b4506dfaa4e8
|
data/CHANGELOG.md
CHANGED
@@ -5,9 +5,8 @@ require 'pathname'
|
|
5
5
|
|
6
6
|
module LokaliseManager
|
7
7
|
module TaskDefinitions
|
8
|
-
# Base class for LokaliseManager task definitions,
|
9
|
-
#
|
10
|
-
# client interactions and configuration merging.
|
8
|
+
# Base class for LokaliseManager task definitions, providing common methods and logic
|
9
|
+
# for importer and exporter classes. Handles API client interactions and configuration merging.
|
11
10
|
class Base
|
12
11
|
using LokaliseManager::Utils::HashUtils
|
13
12
|
|
@@ -18,31 +17,15 @@ module LokaliseManager
|
|
18
17
|
# @param custom_opts [Hash] Custom configurations for specific tasks.
|
19
18
|
# @param global_config [Object] Reference to the global configuration.
|
20
19
|
def initialize(custom_opts = {}, global_config = LokaliseManager::GlobalConfig)
|
21
|
-
|
22
|
-
|
23
|
-
.filter { |m| m.to_s.end_with?('=') }
|
24
|
-
.each_with_object({}) do |method, opts|
|
25
|
-
reader = method.to_s.delete_suffix('=')
|
26
|
-
opts[reader.to_sym] = global_config.send(reader)
|
27
|
-
end
|
28
|
-
|
29
|
-
all_opts = primary_opts.deep_merge(custom_opts)
|
30
|
-
|
31
|
-
config_klass = Struct.new(*all_opts.keys, keyword_init: true)
|
32
|
-
|
33
|
-
@config = config_klass.new all_opts
|
20
|
+
merged_opts = merge_configs(global_config, custom_opts)
|
21
|
+
@config = build_config_class(merged_opts)
|
34
22
|
end
|
35
23
|
|
36
|
-
#
|
24
|
+
# Retrieves or creates a Lokalise API client based on configuration.
|
37
25
|
#
|
38
26
|
# @return [RubyLokaliseApi::Client] Lokalise API client.
|
39
27
|
def api_client
|
40
|
-
|
41
|
-
|
42
|
-
client_opts = [config.api_token, config.timeouts]
|
43
|
-
client_method = config.use_oauth2_token ? :oauth2_client : :client
|
44
|
-
|
45
|
-
@api_client = ::RubyLokaliseApi.send(client_method, *client_opts)
|
28
|
+
@api_client ||= create_api_client
|
46
29
|
end
|
47
30
|
|
48
31
|
# Resets API client
|
@@ -54,15 +37,42 @@ module LokaliseManager
|
|
54
37
|
|
55
38
|
private
|
56
39
|
|
40
|
+
# Creates a Lokalise API client based on configuration.
|
41
|
+
def create_api_client
|
42
|
+
client_opts = [config.api_token, config.timeouts]
|
43
|
+
client_method = config.use_oauth2_token ? :oauth2_client : :client
|
44
|
+
|
45
|
+
::RubyLokaliseApi.public_send(client_method, *client_opts)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Merges global and custom configurations.
|
49
|
+
def merge_configs(global_config, custom_opts)
|
50
|
+
primary_opts = global_config
|
51
|
+
.singleton_methods
|
52
|
+
.select { |m| m.to_s.end_with?('=') }
|
53
|
+
.each_with_object({}) do |method, opts|
|
54
|
+
reader = method.to_s.delete_suffix('=')
|
55
|
+
opts[reader.to_sym] = global_config.public_send(reader)
|
56
|
+
end
|
57
|
+
|
58
|
+
primary_opts.deep_merge(custom_opts)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Builds a config class with the given options.
|
62
|
+
def build_config_class(all_opts)
|
63
|
+
config_klass = Struct.new(*all_opts.keys, keyword_init: true)
|
64
|
+
config_klass.new(all_opts)
|
65
|
+
end
|
66
|
+
|
57
67
|
# Checks and validates task options, raising errors if configurations are missing.
|
58
68
|
def check_options_errors!
|
59
69
|
errors = []
|
60
70
|
errors << 'Project ID is not set!' if config.project_id.nil? || config.project_id.empty?
|
61
71
|
errors << 'Lokalise API token is not set!' if config.api_token.nil? || config.api_token.empty?
|
62
|
-
raise LokaliseManager::Error, errors.join(' ')
|
72
|
+
raise LokaliseManager::Error, errors.join(' ') unless errors.empty?
|
63
73
|
end
|
64
74
|
|
65
|
-
#
|
75
|
+
# Checks if the file has the correct extension based on the configuration.
|
66
76
|
#
|
67
77
|
# @param raw_path [String, Pathname] Path to check.
|
68
78
|
# @return [Boolean] True if the extension matches, false otherwise.
|
@@ -91,7 +101,7 @@ module LokaliseManager
|
|
91
101
|
# Until this is fixed, we revert to this quick'n'dirty solution.
|
92
102
|
EXCEPTIONS = [JSON::ParserError, RubyLokaliseApi::Error::TooManyRequests].freeze
|
93
103
|
|
94
|
-
#
|
104
|
+
# Handles retries with exponential backoff for specific exceptions.
|
95
105
|
def with_exp_backoff(max_retries)
|
96
106
|
return unless block_given?
|
97
107
|
|
@@ -15,13 +15,9 @@ module LokaliseManager
|
|
15
15
|
def export!
|
16
16
|
check_options_errors!
|
17
17
|
|
18
|
-
queued_processes =
|
19
|
-
|
20
|
-
|
21
|
-
parallel_upload(files_group).each do |thr|
|
22
|
-
raise_on_fail(thr) if config.raise_on_export_fail
|
23
|
-
|
24
|
-
queued_processes.push thr
|
18
|
+
queued_processes = all_files.each_slice(MAX_THREADS).flat_map do |files_group|
|
19
|
+
parallel_upload(files_group).tap do |threads|
|
20
|
+
threads.each { |thr| raise_on_fail(thr) if config.raise_on_export_fail }
|
25
21
|
end
|
26
22
|
end
|
27
23
|
|
@@ -36,17 +32,19 @@ module LokaliseManager
|
|
36
32
|
#
|
37
33
|
# @param files_group [Array] Group of files to be uploaded.
|
38
34
|
# @return [Array] Array of threads handling the file uploads.
|
39
|
-
|
40
35
|
def parallel_upload(files_group)
|
41
36
|
files_group.map do |file_data|
|
42
37
|
Thread.new { do_upload(*file_data) }
|
43
38
|
end.map(&:value)
|
44
39
|
end
|
45
40
|
|
41
|
+
# Raises an error if a file upload thread failed.
|
42
|
+
#
|
43
|
+
# @param thread [Struct] The result of the file upload thread.
|
46
44
|
def raise_on_fail(thread)
|
47
45
|
return if thread.success
|
48
46
|
|
49
|
-
raise
|
47
|
+
raise thread.error.class, "Error while trying to upload #{thread.path}: #{thread.error.message}"
|
50
48
|
end
|
51
49
|
|
52
50
|
# Performs the actual upload of a file to Lokalise.
|
@@ -56,56 +54,55 @@ module LokaliseManager
|
|
56
54
|
# @return [Struct] A struct with the success status, process details, and any error information.
|
57
55
|
def do_upload(f_path, r_path)
|
58
56
|
proc_klass = Struct.new(:success, :process, :path, :error, keyword_init: true)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
63
|
-
proc_klass.new(success: true, process: process, path: f_path)
|
64
|
-
rescue StandardError => e
|
65
|
-
proc_klass.new(success: false, path: f_path, error: e)
|
57
|
+
|
58
|
+
process = with_exp_backoff(config.max_retries_export) do
|
59
|
+
api_client.upload_file(project_id_with_branch, opts(f_path, r_path))
|
66
60
|
end
|
61
|
+
|
62
|
+
proc_klass.new(success: true, process: process, path: f_path)
|
63
|
+
rescue StandardError => e
|
64
|
+
proc_klass.new(success: false, path: f_path, error: e)
|
67
65
|
end
|
68
66
|
|
69
67
|
# Prints a completion message to standard output.
|
70
68
|
def print_completion_message
|
71
|
-
$stdout.
|
69
|
+
$stdout.puts 'Task complete!'
|
72
70
|
end
|
73
71
|
|
74
|
-
#
|
72
|
+
# Retrieves all translation files from the specified directory.
|
73
|
+
#
|
74
|
+
# @return [Array] Array of [Pathname, Pathname] pairs representing full and relative paths.
|
75
75
|
def all_files
|
76
|
-
loc_path = config.locales_path
|
77
|
-
Dir["#{loc_path}/**/*"].filter_map do |f|
|
78
|
-
full_path = Pathname.new f
|
76
|
+
loc_path = Pathname.new(config.locales_path)
|
79
77
|
|
80
|
-
|
81
|
-
|
82
|
-
|
78
|
+
Dir["#{loc_path}/**/*"].filter_map do |file|
|
79
|
+
full_path = Pathname.new(file)
|
80
|
+
next unless file_matches_criteria?(full_path)
|
83
81
|
|
82
|
+
relative_path = full_path.relative_path_from(loc_path)
|
84
83
|
[full_path, relative_path]
|
85
84
|
end
|
86
85
|
end
|
87
86
|
|
88
|
-
# Generates
|
87
|
+
# Generates options for file upload to Lokalise.
|
89
88
|
#
|
90
|
-
# @
|
91
|
-
# @param
|
92
|
-
# @
|
89
|
+
# @param full_p [Pathname] Full path to the file.
|
90
|
+
# @param relative_p [Pathname] Relative path within the project.
|
91
|
+
# @return [Hash] Options for the Lokalise API upload.
|
93
92
|
def opts(full_p, relative_p)
|
94
|
-
content = File.read
|
93
|
+
content = File.read(full_p).strip
|
95
94
|
|
96
|
-
|
97
|
-
data: Base64.strict_encode64(content
|
98
|
-
filename: relative_p,
|
95
|
+
{
|
96
|
+
data: Base64.strict_encode64(content),
|
97
|
+
filename: relative_p.to_s,
|
99
98
|
lang_iso: config.lang_iso_inferer.call(content, full_p)
|
100
|
-
}
|
101
|
-
|
102
|
-
initial_opts.merge config.export_opts
|
99
|
+
}.merge(config.export_opts)
|
103
100
|
end
|
104
101
|
|
105
|
-
# Checks whether the specified file
|
102
|
+
# Checks whether the specified file meets the criteria for upload.
|
106
103
|
#
|
107
|
-
# @
|
108
|
-
# @
|
104
|
+
# @param full_path [Pathname] Full path to the file.
|
105
|
+
# @return [Boolean] True if the file matches criteria, false otherwise.
|
109
106
|
def file_matches_criteria?(full_path)
|
110
107
|
full_path.file? && proper_ext?(full_path) &&
|
111
108
|
!config.skip_file_export.call(full_path)
|
@@ -6,9 +6,8 @@ require 'fileutils'
|
|
6
6
|
|
7
7
|
module LokaliseManager
|
8
8
|
module TaskDefinitions
|
9
|
-
# The Importer class
|
10
|
-
# and importing them into the specified project directory.
|
11
|
-
# which provides shared functionality and configuration management.
|
9
|
+
# The Importer class handles downloading translation files from Lokalise
|
10
|
+
# and importing them into the specified project directory.
|
12
11
|
class Importer < Base
|
13
12
|
# Initiates the import process by checking configuration, ensuring safe mode conditions,
|
14
13
|
# downloading files, and processing them. Outputs task completion status.
|
@@ -46,11 +45,7 @@ module LokaliseManager
|
|
46
45
|
# @param path [String] The URL or local path to the ZIP archive.
|
47
46
|
def open_and_process_zip(path)
|
48
47
|
Zip::File.open_buffer(open_file_or_remote(path)) do |zip|
|
49
|
-
zip.each
|
50
|
-
next unless proper_ext?(entry.name)
|
51
|
-
|
52
|
-
process_entry(entry)
|
53
|
-
end
|
48
|
+
zip.each { |entry| process_entry(entry) if proper_ext?(entry.name) }
|
54
49
|
end
|
55
50
|
rescue StandardError => e
|
56
51
|
raise e.class, "Error processing ZIP file: #{e.message}"
|
@@ -80,8 +75,7 @@ module LokaliseManager
|
|
80
75
|
|
81
76
|
$stdout.puts "The target directory #{config.locales_path} is not empty!"
|
82
77
|
$stdout.print 'Enter Y to continue: '
|
83
|
-
|
84
|
-
answer.to_s.strip == 'Y'
|
78
|
+
$stdin.gets.strip.upcase == 'Y'
|
85
79
|
end
|
86
80
|
|
87
81
|
# Opens a local file or a remote URL using the provided path, safely handling different path schemes.
|
@@ -89,13 +83,8 @@ module LokaliseManager
|
|
89
83
|
# @param path [String] The path to the file, either a local path or a URL.
|
90
84
|
# @return [IO] Returns an IO object for the file.
|
91
85
|
def open_file_or_remote(path)
|
92
|
-
|
93
|
-
|
94
|
-
if parsed_path&.scheme&.include?('http')
|
95
|
-
parsed_path.open
|
96
|
-
else
|
97
|
-
File.open path
|
98
|
-
end
|
86
|
+
uri = URI.parse(path)
|
87
|
+
uri.scheme&.start_with?('http') ? uri.open : File.open(path)
|
99
88
|
end
|
100
89
|
|
101
90
|
# Loads translations from the ZIP file.
|
data/lokalise_manager.gemspec
CHANGED
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
|
|
33
33
|
spec.add_development_dependency 'rubocop', '~> 1.0'
|
34
34
|
spec.add_development_dependency 'rubocop-performance', '~> 1.5'
|
35
35
|
spec.add_development_dependency 'rubocop-rake', '~> 0.6'
|
36
|
-
spec.add_development_dependency 'rubocop-rspec', '~>
|
36
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 3.0'
|
37
37
|
spec.add_development_dependency 'simplecov', '~> 0.16'
|
38
38
|
spec.add_development_dependency 'simplecov-lcov', '~> 0.8'
|
39
39
|
spec.add_development_dependency 'webmock', '~> 3.18'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lokalise_manager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.1.
|
4
|
+
version: 5.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ilya Krukowski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-lokalise-api
|
@@ -142,14 +142,14 @@ dependencies:
|
|
142
142
|
requirements:
|
143
143
|
- - "~>"
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: '
|
145
|
+
version: '3.0'
|
146
146
|
type: :development
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
150
|
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: '
|
152
|
+
version: '3.0'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: simplecov
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -238,7 +238,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
238
238
|
- !ruby/object:Gem::Version
|
239
239
|
version: '0'
|
240
240
|
requirements: []
|
241
|
-
rubygems_version: 3.5.
|
241
|
+
rubygems_version: 3.5.22
|
242
242
|
signing_key:
|
243
243
|
specification_version: 4
|
244
244
|
summary: Lokalise integration for Ruby
|