fastlane-plugin-translate_gpt 0.1.2 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d78d5a0107d725ae3b713c7e44f1b3aaca9eff005b1d5d2fb7e4ab75dbca40aa
4
- data.tar.gz: 3b45a4747d8222ed5d0d9b7d04f178728e679609940e60dff556cbc7b65e1e99
3
+ metadata.gz: 8389bf90c62946cf66e3abb95d1fc9422a622146488f56c83985270a692c91f3
4
+ data.tar.gz: 9980483ba5032745071ad8f04ec9227c10385626cc678d5655b861294b2e2520
5
5
  SHA512:
6
- metadata.gz: e57f34690dea08a9eb24f7d1f975646c9500fccd983d933ff0b3a8809af91ce3b875e75691dfe7845f9aad88fcaeace425e8ead8dff3e09316c61d03ce5be682
7
- data.tar.gz: e6e76c62b2ac4fe8b3b16d756bdd2acf4adb8d5ed35a626b9c0e5be3fb4e94511868e961f5261570af63640dafe714cf0d47fc8cc69f520b8e5b552ce27f67b2
6
+ metadata.gz: 4da07c2656000de7cbfa28333fbf2c7949d9211166a89624a928f734cdfe4a2ece7ecacdd3aedc265cff9241a9998b2b722906b5bbeda43ff50e54a772620c83
7
+ data.tar.gz: a91b4055259f18d927cb0d1a2ec29ccf39e7385b646821018f462bea16afba69f03450ecb2b54c118a011c90b2a64bf4caefc41243c5188e2455dd8cf394c84b
@@ -7,66 +7,11 @@ module Fastlane
7
7
  module Actions
8
8
  class TranslateGptAction < Action
9
9
  def self.run(params)
10
- client = OpenAI::Client.new(
11
- access_token: params[:api_token],
12
- request_timeout: params[:request_timeout]
13
- )
14
-
15
- input_hash = Helper::TranslateGptHelper.get_strings(params[:source_file])
16
- output_hash = Helper::TranslateGptHelper.get_strings(params[:target_file])
17
-
18
- if params[:skip_translated]
19
- to_translate = input_hash.reject { |k, v| output_hash[k] }
20
- else
21
- to_translate = input_hash
22
- end
23
-
24
- UI.message "Translating #{to_translate.size} strings..."
25
-
26
- to_translate.each_with_index do |(key, string), index|
27
- prompt = "Translate the following string from #{params[:source_language]} to #{params[:target_language]}: #{string.value}"
28
- context = string.comment
29
- if context && !context.empty?
30
- prompt += "\n\nAdditional context:\n#{context}"
31
- end
32
- if params[:context] && !params[:context].empty?
33
- prompt += "\n\nCommon context:\n#{params[:context]}"
34
- end
35
- # translate the source string to the target language
36
- response = client.chat(
37
- parameters: {
38
- model: params[:model_name],
39
- messages: [{ role: "user", content: prompt}],
40
- temperature: params[:temperature],
41
- }
42
- )
43
- # extract the translated string from the response
44
- error = response.dig("error", "message")
45
- if error
46
- UI.error "Error translating #{key}: #{error}"
47
- else
48
- target_string = response.dig("choices", 0, "message", "content")
49
- if target_string && !target_string.empty?
50
- UI.message "Translating #{key} - #{string.value} -> #{target_string}"
51
- string.value = target_string
52
- output_hash[key] = string
53
- else
54
- UI.warning "Unable to translate #{key} - #{string.value}"
55
- end
56
- end
57
- if index < to_translate.size - 1
58
- Helper::TranslateGptHelper.timeout params[:request_timeout]
59
- end
60
- end
61
-
62
- UI.message "Writing #{output_hash.size} strings to #{params[:target_file]}..."
63
-
64
- file = LocoStrings.load(params[:target_file])
65
- file.read
66
- output_hash.each do |key, value|
67
- file.update(key, value.value, value.comment)
68
- end
69
- file.write
10
+ helper = Helper::TranslateGptHelper.new(params)
11
+ helper.prepare_hashes()
12
+ helper.log_input()
13
+ helper.translate_strings()
14
+ helper.write_output()
70
15
  end
71
16
 
72
17
  #####################################################
@@ -97,6 +42,7 @@ module Fastlane
97
42
  key: :request_timeout,
98
43
  env_name: "GPT_REQUEST_TIMEOUT",
99
44
  description: "Timeout for the request in seconds",
45
+ type: Integer,
100
46
  default_value: 30
101
47
  ),
102
48
  FastlaneCore::ConfigItem.new(
@@ -6,10 +6,122 @@ module Fastlane
6
6
 
7
7
  module Helper
8
8
  class TranslateGptHelper
9
+ def initialize(params)
10
+ @params = params
11
+ @client = OpenAI::Client.new(
12
+ access_token: params[:api_token],
13
+ request_timeout: params[:request_timeout]
14
+ )
15
+ @timeout = params[:request_timeout]
16
+ end
17
+
18
+ # Get the strings from a file
19
+ def prepare_hashes()
20
+ @input_hash = get_strings(@params[:source_file])
21
+ @output_hash = get_strings(@params[:target_file])
22
+ @to_translate = filter_translated(@params[:skip_translated], @input_hash, @output_hash)
23
+ end
24
+
25
+ # Log information about the input strings
26
+ def log_input()
27
+ @translation_count = @to_translate.size
28
+ number_of_strings = Colorizer::colorize("#{@translation_count}", :blue)
29
+ UI.message "Translating #{number_of_strings} strings..."
30
+ if @translation_count > 0
31
+ estimated_string = Colorizer::colorize("#{@translation_count * @params[:request_timeout]}", :white)
32
+ UI.message "Estimated time: #{estimated_string} seconds"
33
+ end
34
+ end
35
+
36
+ # Cycle through the input strings and translate them
37
+ def translate_strings()
38
+ @to_translate.each_with_index do |(key, string), index|
39
+ prompt = prepare_prompt string
40
+
41
+ max_retries = 10
42
+ times_retried = 0
43
+
44
+ # translate the source string to the target language
45
+ begin
46
+ request_translate(key, string, prompt, index)
47
+ rescue Net::ReadTimeout => error
48
+ if times_retried < max_retries
49
+ times_retried += 1
50
+ UI.important "Failed to request translation, retry #{times_retried}/#{max_retries}"
51
+ wait 1
52
+ retry
53
+ else
54
+ UI.error "Can't translate #{key}: #{error}"
55
+ end
56
+ end
57
+ if index < @translation_count - 1 then wait end
58
+ end
59
+ end
60
+
61
+ # Prepare the prompt for the GPT API
62
+ def prepare_prompt(string)
63
+ prompt = "I want you to act as a translator for a mobile application strings. " + \
64
+ "Try to keep length of the translated text. " + \
65
+ "You need to answer only with the translation and nothing else until I say to stop it. No commentaries."
66
+ if @params[:context] && !@params[:context].empty?
67
+ prompt += "This app is #{@params[:context]}. "
68
+ end
69
+ context = string.comment
70
+ if context && !context.empty?
71
+ prompt += "Additional context is #{context}. "
72
+ end
73
+ prompt += "Translate next text from #{@params[:source_language]} to #{@params[:target_language]}:\n" +
74
+ "#{string.value}"
75
+ return prompt
76
+ end
77
+
78
+ # Request a translation from the GPT API
79
+ def request_translate(key, string, prompt, index)
80
+ response = @client.chat(
81
+ parameters: {
82
+ model: @params[:model_name],
83
+ messages: [
84
+ { role: "user", content: prompt }
85
+ ],
86
+ temperature: @params[:temperature],
87
+ }
88
+ )
89
+ # extract the translated string from the response
90
+ error = response.dig("error", "message")
91
+ key_log = Colorizer::colorize(key, :blue)
92
+ index_log = Colorizer::colorize("[#{index + 1}/#{@translation_count}]", :white)
93
+ if error
94
+ UI.error "#{index_log} Error translating #{key_log}: #{error}"
95
+ else
96
+ target_string = response.dig("choices", 0, "message", "content")
97
+ if target_string && !target_string.empty?
98
+ UI.message "#{index_log} Translating #{key_log} - #{string.value} -> #{target_string}"
99
+ string.value = target_string
100
+ @output_hash[key] = string
101
+ else
102
+ UI.important "#{index_log} Unable to translate #{key_log} - #{string.value}"
103
+ end
104
+ end
105
+ end
106
+
107
+ # Write the translated strings to the target file
108
+ def write_output()
109
+ number_of_strings = Colorizer::colorize("#{@output_hash.size}", :blue)
110
+ target_string = Colorizer::colorize(@params[:target_file], :white)
111
+ UI.message "Writing #{number_of_strings} strings to #{target_string}..."
112
+
113
+ file = LocoStrings.load(@params[:target_file])
114
+ file.read
115
+ @output_hash.each do |key, value|
116
+ file.update(key, value.value, value.comment)
117
+ end
118
+ file.write
119
+ end
120
+
9
121
  # Read the strings file into a hash
10
122
  # @param localization_file [String] The path to the strings file
11
123
  # @return [Hash] The strings file as a hash
12
- def self.get_strings(localization_file)
124
+ def get_strings(localization_file)
13
125
  file = LocoStrings.load(localization_file)
14
126
  return file.read
15
127
  end
@@ -18,25 +130,36 @@ module Fastlane
18
130
  # @param localization_file [String] The path to the strings file
19
131
  # @param localization_key [String] The localization key
20
132
  # @return [String] The context associated with the localization key
21
- def self.get_context(localization_file, localization_key)
133
+ def get_context(localization_file, localization_key)
22
134
  file = LocoStrings.load(localization_file)
23
135
  string = file.read[localization_key]
24
136
  return string.comment
25
137
  end
26
138
 
139
+ def filter_translated(need_to_skip, base, target)
140
+ if need_to_skip
141
+ return base.reject { |k, v| target[k] }
142
+ else
143
+ return base
144
+ end
145
+ end
146
+
27
147
  # Sleep for a specified number of seconds, displaying a progress bar
28
148
  # @param seconds [Integer] The number of seconds to sleep
29
- def self.timeout(total)
149
+ def wait(seconds = @timeout)
30
150
  sleep_time = 0
31
- while sleep_time < total
32
- percent_complete = (sleep_time.to_f / total.to_f) * 100.0
151
+ while sleep_time < seconds
152
+ percent_complete = (sleep_time.to_f / seconds.to_f) * 100.0
33
153
  progress_bar_width = 20
34
154
  completed_width = (progress_bar_width * percent_complete / 100.0).round
35
155
  remaining_width = progress_bar_width - completed_width
36
- print "\rTimeout ["
156
+ print "\rTimeout ["
157
+ print Colorizer::code(:green)
37
158
  print "=" * completed_width
38
159
  print " " * remaining_width
39
- print "] %.2f%%" % percent_complete
160
+ print Colorizer::code(:reset)
161
+ print "]"
162
+ print " %.2f%%" % percent_complete
40
163
  $stdout.flush
41
164
  sleep(1)
42
165
  sleep_time += 1
@@ -45,5 +168,28 @@ module Fastlane
45
168
  $stdout.flush
46
169
  end
47
170
  end
171
+
172
+ # Helper class for bash colors
173
+ class Colorizer
174
+ COLORS = {
175
+ black: 30,
176
+ red: 31,
177
+ green: 32,
178
+ yellow: 33,
179
+ blue: 34,
180
+ magenta: 35,
181
+ cyan: 36,
182
+ white: 37,
183
+ reset: 0,
184
+ }
185
+
186
+ def self.colorize(text, color)
187
+ color_code = COLORS[color.to_sym]
188
+ "\e[#{color_code}m#{text}\e[0m"
189
+ end
190
+ def self.code(color)
191
+ "\e[#{COLORS[color.to_sym]}m"
192
+ end
193
+ end
48
194
  end
49
195
  end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module TranslateGpt
3
- VERSION = "0.1.2"
3
+ VERSION = "0.1.4"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-translate_gpt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aleksei Cherepanov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-02 00:00:00.000000000 Z
11
+ date: 2023-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-openai
@@ -209,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
209
  - !ruby/object:Gem::Version
210
210
  version: '0'
211
211
  requirements: []
212
- rubygems_version: 3.4.10
212
+ rubygems_version: 3.4.13
213
213
  signing_key:
214
214
  specification_version: 4
215
215
  summary: This fastlane plugin provides an easy way to use the OpenAI GPT language