fastlane-plugin-translate_gpt 0.1.3 → 0.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 162ac503b51db5c9395059702dfeedca75cbcd3bb26f68702059f45eb4540ffc
4
- data.tar.gz: d73722901b8e2d68c3287187687761930962e4392e7016847bfc9946d57c3748
3
+ metadata.gz: 8389bf90c62946cf66e3abb95d1fc9422a622146488f56c83985270a692c91f3
4
+ data.tar.gz: 9980483ba5032745071ad8f04ec9227c10385626cc678d5655b861294b2e2520
5
5
  SHA512:
6
- metadata.gz: b260653373ca72b56e753aad327338559eeb330e64e602f905720b12d8cb6e6c35d0c5750e8be8199169d41cc7549dc19cd4f30a19caf652465ce3a6d10f54b1
7
- data.tar.gz: 21df8623e7bce11bf1c3658917dd498bd95929e77236dc32509ca8f3245c8491450cecee1996f2b40147e834aa2a76582facea074dcaa17b8e916a652e964193
6
+ metadata.gz: 4da07c2656000de7cbfa28333fbf2c7949d9211166a89624a928f734cdfe4a2ece7ecacdd3aedc265cff9241a9998b2b722906b5bbeda43ff50e54a772620c83
7
+ data.tar.gz: a91b4055259f18d927cb0d1a2ec29ccf39e7385b646821018f462bea16afba69f03450ecb2b54c118a011c90b2a64bf4caefc41243c5188e2455dd8cf394c84b
@@ -7,73 +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 = "I want you to act as a translator for a mobile application strings. " + \
28
- "You need to answer only with translation and nothing else. No commentaries. " + \
29
- "Try to keep length of the translated text. " + \
30
- "I will send you a text and you translate it from #{params[:source_language]} to #{params[:target_language]}. "
31
- if params[:context] && !params[:context].empty?
32
- prompt += "This app is #{params[:context]}. "
33
- end
34
- context = string.comment
35
- if context && !context.empty?
36
- prompt += "Additional context is #{context}. "
37
- end
38
- prompt += "Source text:\n#{string.value}"
39
-
40
- # translate the source string to the target language
41
- response = client.chat(
42
- parameters: {
43
- model: params[:model_name],
44
- messages: [
45
- { role: "user", content: prompt }
46
- ],
47
- temperature: params[:temperature],
48
- }
49
- )
50
- # extract the translated string from the response
51
- error = response.dig("error", "message")
52
- if error
53
- UI.error "Error translating #{key}: #{error}"
54
- else
55
- target_string = response.dig("choices", 0, "message", "content")
56
- if target_string && !target_string.empty?
57
- UI.message "Translating #{key} - #{string.value} -> #{target_string}"
58
- string.value = target_string
59
- output_hash[key] = string
60
- else
61
- UI.warning "Unable to translate #{key} - #{string.value}"
62
- end
63
- end
64
- if index < to_translate.size - 1
65
- Helper::TranslateGptHelper.timeout params[:request_timeout]
66
- end
67
- end
68
-
69
- UI.message "Writing #{output_hash.size} strings to #{params[:target_file]}..."
70
-
71
- file = LocoStrings.load(params[:target_file])
72
- file.read
73
- output_hash.each do |key, value|
74
- file.update(key, value.value, value.comment)
75
- end
76
- file.write
10
+ helper = Helper::TranslateGptHelper.new(params)
11
+ helper.prepare_hashes()
12
+ helper.log_input()
13
+ helper.translate_strings()
14
+ helper.write_output()
77
15
  end
78
16
 
79
17
  #####################################################
@@ -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.3"
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.3
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-20 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