translatomatic 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitattributes +19 -0
- data/.gitignore +0 -0
- data/.travis.yml +7 -7
- data/.yardopts +9 -0
- data/Gemfile +4 -4
- data/Guardfile +0 -0
- data/README.de.md +61 -16
- data/README.es.md +60 -15
- data/README.fr.md +61 -16
- data/README.it.md +60 -15
- data/README.ja.md +59 -14
- data/README.ko.md +137 -0
- data/README.md +58 -13
- data/README.ms.md +137 -0
- data/README.pt.md +137 -0
- data/README.ru.md +137 -0
- data/README.sv.md +137 -0
- data/README.zh.md +137 -0
- data/bin/setup +8 -8
- data/bin/translatomatic +6 -6
- data/bin/travis +1 -1
- data/config/locales/translatomatic/de.yml +104 -0
- data/config/locales/translatomatic/en.yml +101 -0
- data/config/locales/translatomatic/es.yml +105 -0
- data/config/locales/translatomatic/fr.yml +105 -0
- data/config/locales/translatomatic/it.yml +103 -0
- data/config/locales/translatomatic/ja.yml +102 -0
- data/config/locales/translatomatic/ko.yml +101 -0
- data/config/locales/translatomatic/ms.yml +103 -0
- data/config/locales/translatomatic/pt.yml +105 -0
- data/config/locales/translatomatic/ru.yml +103 -0
- data/config/locales/translatomatic/sv.yml +103 -0
- data/config/locales/translatomatic/zh.yml +102 -0
- data/db/migrate/201712170000_initial.rb +2 -1
- data/lib/translatomatic/cli/base.rb +73 -0
- data/lib/translatomatic/cli/common_options.rb +14 -0
- data/lib/translatomatic/cli/config.rb +81 -0
- data/lib/translatomatic/cli/database.rb +29 -0
- data/lib/translatomatic/cli/main.rb +112 -0
- data/lib/translatomatic/cli/translate.rb +146 -0
- data/lib/translatomatic/cli.rb +8 -216
- data/lib/translatomatic/config.rb +143 -0
- data/lib/translatomatic/converter.rb +196 -149
- data/lib/translatomatic/converter_stats.rb +18 -14
- data/lib/translatomatic/database.rb +35 -37
- data/lib/translatomatic/escaped_unicode.rb +1 -1
- data/lib/translatomatic/extractor/base.rb +2 -0
- data/lib/translatomatic/extractor/ruby.rb +1 -0
- data/lib/translatomatic/extractor.rb +1 -0
- data/lib/translatomatic/http_request.rb +43 -14
- data/lib/translatomatic/locale.rb +28 -4
- data/lib/translatomatic/logger.rb +21 -13
- data/lib/translatomatic/model/locale.rb +5 -1
- data/lib/translatomatic/model/text.rb +2 -0
- data/lib/translatomatic/model.rb +1 -0
- data/lib/translatomatic/option.rb +75 -10
- data/lib/translatomatic/progress_updater.rb +7 -3
- data/lib/translatomatic/resource_file/base.rb +41 -18
- data/lib/translatomatic/resource_file/html.rb +11 -14
- data/lib/translatomatic/resource_file/markdown.rb +13 -12
- data/lib/translatomatic/resource_file/plist.rb +3 -14
- data/lib/translatomatic/resource_file/properties.rb +21 -3
- data/lib/translatomatic/resource_file/resw.rb +1 -0
- data/lib/translatomatic/resource_file/text.rb +1 -0
- data/lib/translatomatic/resource_file/xcode_strings.rb +23 -14
- data/lib/translatomatic/resource_file/xml.rb +34 -22
- data/lib/translatomatic/resource_file/yaml.rb +39 -5
- data/lib/translatomatic/resource_file.rb +7 -5
- data/lib/translatomatic/string.rb +40 -12
- data/lib/translatomatic/tmx/document.rb +11 -12
- data/lib/translatomatic/tmx/translation_unit.rb +5 -1
- data/lib/translatomatic/tmx.rb +2 -0
- data/lib/translatomatic/translation.rb +30 -0
- data/lib/translatomatic/translation_result.rb +45 -45
- data/lib/translatomatic/translator/base.rb +128 -83
- data/lib/translatomatic/translator/frengly.rb +62 -57
- data/lib/translatomatic/translator/google.rb +35 -31
- data/lib/translatomatic/translator/microsoft.rb +41 -33
- data/lib/translatomatic/translator/my_memory.rb +68 -64
- data/lib/translatomatic/translator/yandex.rb +56 -39
- data/lib/translatomatic/translator.rb +99 -63
- data/lib/translatomatic/util.rb +31 -1
- data/lib/translatomatic/version.rb +4 -1
- data/lib/translatomatic.rb +17 -0
- data/translatomatic.gemspec +5 -4
- metadata +56 -16
@@ -1,11 +1,14 @@
|
|
1
1
|
require 'set'
|
2
2
|
|
3
|
-
module Translatomatic
|
3
|
+
module Translatomatic
|
4
|
+
# Stores results of a translation
|
4
5
|
class TranslationResult
|
6
|
+
|
7
|
+
# @return [Translatomatic::ResourceFile::Base] The resource file
|
8
|
+
attr_reader :file
|
5
9
|
|
6
|
-
# Translation results
|
7
10
|
# @return [Hash<String,String>] Translation results
|
8
|
-
attr_reader :properties
|
11
|
+
attr_reader :properties
|
9
12
|
|
10
13
|
# @return [Locale] The locale of the original strings
|
11
14
|
attr_reader :from_locale
|
@@ -17,70 +20,67 @@ module Translatomatic
|
|
17
20
|
attr_reader :untranslated
|
18
21
|
|
19
22
|
# Create a translation result
|
20
|
-
# @param [
|
21
|
-
# @param [Locale]
|
22
|
-
|
23
|
-
|
23
|
+
# @param file [Translatomatic::ResourceFile::Base] A resource file
|
24
|
+
# @param to_locale [Locale] The target locale
|
25
|
+
def initialize(file, to_locale)
|
26
|
+
@file = file
|
24
27
|
@value_to_keys = {}
|
25
28
|
@untranslated = Set.new
|
26
|
-
@from_locale =
|
27
|
-
@to_locale = to_locale
|
28
|
-
|
29
|
-
# duplicate strings
|
30
|
-
@properties = properties.transform_values { |i| i.dup }
|
31
|
-
|
32
|
-
properties.each do |key, value|
|
33
|
-
# split property value into sentences
|
34
|
-
string = string(value, from_locale)
|
29
|
+
@from_locale = file.locale
|
30
|
+
@to_locale = to_locale
|
31
|
+
|
32
|
+
# duplicate strings
|
33
|
+
@properties = file.properties.transform_values { |i| i.dup }
|
34
|
+
|
35
|
+
@properties.each do |key, value|
|
36
|
+
# split property value into sentences
|
37
|
+
string = string(value, from_locale)
|
35
38
|
string.sentences.each do |sentence|
|
36
39
|
@untranslated << sentence
|
37
40
|
keylist = (@value_to_keys[sentence.to_s] ||= [])
|
38
|
-
keylist << key
|
41
|
+
keylist << key
|
39
42
|
end
|
40
43
|
end
|
41
44
|
end
|
42
45
|
|
43
|
-
# Update result with a list of translated strings.
|
44
|
-
# @param [Array<
|
45
|
-
# @param [Array<String>] translated Translated strings
|
46
|
+
# Update result with a list of translated strings.
|
47
|
+
# @param translations [Array<Translatomatic::Translation>] Translations
|
46
48
|
# @return [void]
|
47
|
-
def update_strings(
|
48
|
-
|
49
|
-
|
50
|
-
# create list of [from, to] text conversions
|
51
|
-
conversions = []
|
52
|
-
original.zip(translated).each do |text1, text2|
|
53
|
-
conversions << [text1, text2]
|
54
|
-
end
|
55
|
-
|
56
|
-
# sort conversion list by largest offset first so that we replace
|
49
|
+
def update_strings(translations)
|
50
|
+
# sort translation list by largest offset first so that we replace
|
57
51
|
# from the end of the string to the front, so substring offsets
|
58
52
|
# are correct in the target string.
|
59
|
-
conversions.sort_by! do |t1, t2|
|
60
|
-
t1.respond_to?(:offset) ? -t1.offset : 0
|
61
|
-
end
|
62
53
|
|
63
|
-
|
64
|
-
|
54
|
+
#translations.sort_by! do |translation|
|
55
|
+
# t1 = translation.original
|
56
|
+
# t1.respond_to?(:offset) ? -t1.offset : 0
|
57
|
+
#end
|
58
|
+
translations.sort_by! { |t| -t.original.offset }
|
59
|
+
|
60
|
+
translations.each do |translation|
|
61
|
+
update(translation.original, translation.result)
|
65
62
|
end
|
66
|
-
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
67
66
|
|
68
|
-
private
|
69
|
-
|
70
67
|
include Translatomatic::Util
|
71
68
|
|
72
69
|
def update(original, translated)
|
73
70
|
keys = @value_to_keys[original.to_s]
|
74
|
-
raise "no key mapping for text '#{original}'" unless keys
|
75
|
-
keys.each do |key|
|
71
|
+
raise "no key mapping for text '#{original}'" unless keys
|
72
|
+
keys.each do |key|
|
73
|
+
#value = @properties[key]
|
76
74
|
if original.kind_of?(Translatomatic::String) && original.substring?
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
75
|
+
#log.debug("#{value[original.offset, original.length]} -> #{translated}")
|
76
|
+
@properties[key][original.offset, original.length] = translated
|
77
|
+
else
|
78
|
+
#log.debug("#{key} -> #{translated}")
|
79
|
+
@properties[key] = translated
|
80
|
+
end
|
81
81
|
end
|
82
82
|
|
83
|
-
@untranslated.delete(original)
|
83
|
+
@untranslated.delete(original) unless translated.nil?
|
84
84
|
end
|
85
85
|
end
|
86
86
|
end
|
@@ -1,83 +1,128 @@
|
|
1
|
-
require 'bing_translator'
|
2
|
-
|
3
|
-
module Translatomatic
|
4
|
-
module Translator
|
5
|
-
|
6
|
-
class
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
#
|
32
|
-
# @
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
from
|
39
|
-
|
40
|
-
|
41
|
-
translated
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
#
|
51
|
-
# opening a http connection to the given url and calling
|
52
|
-
# fetch_translation() on each string.
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
1
|
+
require 'bing_translator'
|
2
|
+
|
3
|
+
module Translatomatic
|
4
|
+
module Translator
|
5
|
+
|
6
|
+
# Base class for interfaces to translation APIs
|
7
|
+
# @abstract
|
8
|
+
class Base
|
9
|
+
include Translatomatic::DefineOptions
|
10
|
+
|
11
|
+
# Listener for translation events
|
12
|
+
attr_accessor :listener
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
@listener = options[:listener]
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [String] The name of this translator.
|
19
|
+
def name
|
20
|
+
self.class.name.demodulize
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Array<String>] A list of languages supported by this translator.
|
24
|
+
def languages
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Translate strings from one locale to another
|
29
|
+
# @param strings [Array<String>] A list of strings to translate.
|
30
|
+
# @param from [String, Translatomatic::Locale] The locale of the given strings.
|
31
|
+
# @param to [String, Translatomatic::Locale] The locale to translate to.
|
32
|
+
# @return [Array<String>] Translated strings
|
33
|
+
def translate(strings, from, to)
|
34
|
+
@updated_listener = false
|
35
|
+
strings = [strings] unless strings.kind_of?(Array)
|
36
|
+
from = locale(from)
|
37
|
+
to = locale(to)
|
38
|
+
return strings if from.language == to.language
|
39
|
+
translated = perform_translate(strings, from, to)
|
40
|
+
update_translated(translated) unless @updated_listener
|
41
|
+
translated
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
include Translatomatic::Util
|
47
|
+
|
48
|
+
TRANSLATION_RETRIES = 3
|
49
|
+
|
50
|
+
# Fetch translations for the given strings, one at a time, by
|
51
|
+
# opening a http connection to the given url and calling
|
52
|
+
# fetch_translation() on each string. Error handling and recovery
|
53
|
+
# is performed by this method.
|
54
|
+
# (subclass must implement fetch_translation if this method is used)
|
55
|
+
def perform_fetch_translations(url, strings, from, to)
|
56
|
+
translated = []
|
57
|
+
untranslated = strings.dup
|
58
|
+
request = Translatomatic::HTTPRequest.new(url)
|
59
|
+
|
60
|
+
while !untranslated.empty? # request start block
|
61
|
+
fail_count = 0 # number of consecutive translation failures
|
62
|
+
|
63
|
+
request.start do |http|
|
64
|
+
while !untranslated.empty?
|
65
|
+
# get next string to translate
|
66
|
+
string = untranslated[0]
|
67
|
+
begin
|
68
|
+
# fetch translation
|
69
|
+
result = fetch_translation(request, string, from, to)
|
70
|
+
|
71
|
+
# successful translation
|
72
|
+
fail_count = 0 # reset fail count
|
73
|
+
translated << result
|
74
|
+
update_translated(result)
|
75
|
+
untranslated.shift
|
76
|
+
|
77
|
+
rescue Exception => e
|
78
|
+
# translation error
|
79
|
+
log.error(e)
|
80
|
+
fail_count += 1
|
81
|
+
if fail_count >= TRANSLATION_RETRIES
|
82
|
+
raise e # re-raise exception
|
83
|
+
else
|
84
|
+
# need to restart http connection
|
85
|
+
# break back out to request.start block
|
86
|
+
break
|
87
|
+
end
|
88
|
+
end # exception
|
89
|
+
end # while untranslated
|
90
|
+
end # request.start
|
91
|
+
end # while untranslated
|
92
|
+
|
93
|
+
translated
|
94
|
+
end
|
95
|
+
|
96
|
+
def fetch_translation(request, strings, from, to)
|
97
|
+
raise "subclass must implement fetch_translation"
|
98
|
+
end
|
99
|
+
|
100
|
+
def update_translated(texts)
|
101
|
+
texts = [texts] unless texts.kind_of?(Array)
|
102
|
+
@updated_listener = true
|
103
|
+
@listener.translated_texts(texts) if @listener
|
104
|
+
end
|
105
|
+
|
106
|
+
def perform_translate(strings, from, to)
|
107
|
+
raise "subclass must implement perform_translate"
|
108
|
+
end
|
109
|
+
|
110
|
+
# Attempt to run a block of code up to retries times.
|
111
|
+
# Reraises the exception if the block fails retries times.
|
112
|
+
# @param retries [Number] The maximum number of times to run
|
113
|
+
# @return [Object] the return value of the block
|
114
|
+
def attempt_with_retries(retries)
|
115
|
+
fail_count = 0
|
116
|
+
begin
|
117
|
+
yield
|
118
|
+
rescue Exception => e
|
119
|
+
log.error(e.message)
|
120
|
+
fail_count += 1
|
121
|
+
retry if fail_count < retries
|
122
|
+
raise e
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -1,57 +1,62 @@
|
|
1
|
-
require 'net/http'
|
2
|
-
|
3
|
-
module Translatomatic
|
4
|
-
module Translator
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Translatomatic
|
4
|
+
module Translator
|
5
|
+
|
6
|
+
# Interface to the Frengly translation API
|
7
|
+
# @see http://www.frengly.com/api
|
8
|
+
class Frengly < Base
|
9
|
+
|
10
|
+
define_options({
|
11
|
+
name: :frengly_api_key, use_env: true,
|
12
|
+
desc: t("translator.frengly_api_key"),
|
13
|
+
},
|
14
|
+
{ name: :frengly_email, use_env: true,
|
15
|
+
desc: t("translator.email_address")
|
16
|
+
},
|
17
|
+
{ name: :frengly_password, use_env: true,
|
18
|
+
desc: t("translator.password")
|
19
|
+
})
|
20
|
+
|
21
|
+
# Create a new Frengly translator instance
|
22
|
+
def initialize(options = {})
|
23
|
+
super(options)
|
24
|
+
@key = options[:frengly_api_key] || ENV["FRENGLY_API_KEY"] # optional
|
25
|
+
@email = options[:frengly_email]
|
26
|
+
@password = options[:frengly_password]
|
27
|
+
raise t("translator.email_required") unless @email
|
28
|
+
raise t("translator.password_required") unless @password
|
29
|
+
end
|
30
|
+
|
31
|
+
# (see Translatomatic::Translator::Base#languages)
|
32
|
+
def languages
|
33
|
+
['en','fr','de','es','pt','it','nl','tl','fi','el','iw','pl','ru','sv']
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
URL = 'http://frengly.com/frengly/data/translateREST'
|
39
|
+
|
40
|
+
def perform_translate(strings, from, to)
|
41
|
+
perform_fetch_translations(URL, strings, from, to)
|
42
|
+
end
|
43
|
+
|
44
|
+
def fetch_translation(request, string, from, to)
|
45
|
+
body = {
|
46
|
+
src: from.language,
|
47
|
+
dest: to.language,
|
48
|
+
text: string,
|
49
|
+
email: @email,
|
50
|
+
password: @password,
|
51
|
+
premiumkey: @key
|
52
|
+
}.to_json
|
53
|
+
|
54
|
+
# TODO: work out what the response looks like
|
55
|
+
response = request.post(body, content_type: 'application/json')
|
56
|
+
data = JSON.parse(response.body)
|
57
|
+
data['text']
|
58
|
+
end
|
59
|
+
|
60
|
+
end # class
|
61
|
+
end # module
|
62
|
+
end
|
@@ -1,31 +1,35 @@
|
|
1
|
-
module Translatomatic
|
2
|
-
module Translator
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
1
|
+
module Translatomatic
|
2
|
+
module Translator
|
3
|
+
|
4
|
+
# Interface to the Google translation API
|
5
|
+
# @see https://cloud.google.com/translate/
|
6
|
+
class Google < Base
|
7
|
+
|
8
|
+
define_options(name: :google_api_key,
|
9
|
+
desc: t("translator.google_api_key"), use_env: true
|
10
|
+
)
|
11
|
+
|
12
|
+
# Create a new Google translator instance
|
13
|
+
def initialize(options = {})
|
14
|
+
super(options)
|
15
|
+
key = options[:google_api_key] || ENV["GOOGLE_API_KEY"]
|
16
|
+
raise t("translator.google_key_required") if key.nil?
|
17
|
+
EasyTranslate.api_key = key
|
18
|
+
end
|
19
|
+
|
20
|
+
# (see Translatomatic::Translator::Base#languages)
|
21
|
+
def languages
|
22
|
+
EasyTranslate::LANGUAGES.keys
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def perform_translate(strings, from, to)
|
28
|
+
attempt_with_retries(3) do
|
29
|
+
EasyTranslate.translate(strings, from: from.language, to: to.language)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end # class
|
34
|
+
end # module
|
35
|
+
end
|
@@ -1,33 +1,41 @@
|
|
1
|
-
require 'bing_translator'
|
2
|
-
|
3
|
-
module Translatomatic
|
4
|
-
module Translator
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
1
|
+
require 'bing_translator'
|
2
|
+
|
3
|
+
module Translatomatic
|
4
|
+
module Translator
|
5
|
+
|
6
|
+
# Interface to the Microsoft translation API
|
7
|
+
# @see https://www.microsoft.com/en-us/translator/translatorapi.aspx
|
8
|
+
class Microsoft < Base
|
9
|
+
|
10
|
+
define_options({
|
11
|
+
name: :microsoft_api_key, desc: t("translator.microsoft_api_key"), use_env: true
|
12
|
+
})
|
13
|
+
|
14
|
+
# Create a new Microsoft translator instance
|
15
|
+
def initialize(options = {})
|
16
|
+
super(options)
|
17
|
+
key = options[:microsoft_api_key] || ENV["MICROSOFT_API_KEY"]
|
18
|
+
raise t("translator.microsoft_key_required") if key.nil?
|
19
|
+
@impl = BingTranslator.new(key)
|
20
|
+
end
|
21
|
+
|
22
|
+
# TODO: implement language list
|
23
|
+
# (see Translatomatic::Translator::Base#languages)
|
24
|
+
#def languages
|
25
|
+
#end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def perform_translate(strings, from, to)
|
30
|
+
attempt_with_retries(3) do
|
31
|
+
translations = @impl.translate_array(strings,
|
32
|
+
from: from.language,
|
33
|
+
to: to.language
|
34
|
+
)
|
35
|
+
translations.collect { |i| i.to_s }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|