translatomatic 0.1.1 → 0.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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +19 -0
  3. data/.gitignore +0 -0
  4. data/.travis.yml +7 -7
  5. data/.yardopts +9 -0
  6. data/Gemfile +4 -4
  7. data/Guardfile +0 -0
  8. data/README.de.md +61 -16
  9. data/README.es.md +60 -15
  10. data/README.fr.md +61 -16
  11. data/README.it.md +60 -15
  12. data/README.ja.md +59 -14
  13. data/README.ko.md +137 -0
  14. data/README.md +58 -13
  15. data/README.ms.md +137 -0
  16. data/README.pt.md +137 -0
  17. data/README.ru.md +137 -0
  18. data/README.sv.md +137 -0
  19. data/README.zh.md +137 -0
  20. data/bin/setup +8 -8
  21. data/bin/translatomatic +6 -6
  22. data/bin/travis +1 -1
  23. data/config/locales/translatomatic/de.yml +104 -0
  24. data/config/locales/translatomatic/en.yml +101 -0
  25. data/config/locales/translatomatic/es.yml +105 -0
  26. data/config/locales/translatomatic/fr.yml +105 -0
  27. data/config/locales/translatomatic/it.yml +103 -0
  28. data/config/locales/translatomatic/ja.yml +102 -0
  29. data/config/locales/translatomatic/ko.yml +101 -0
  30. data/config/locales/translatomatic/ms.yml +103 -0
  31. data/config/locales/translatomatic/pt.yml +105 -0
  32. data/config/locales/translatomatic/ru.yml +103 -0
  33. data/config/locales/translatomatic/sv.yml +103 -0
  34. data/config/locales/translatomatic/zh.yml +102 -0
  35. data/db/migrate/201712170000_initial.rb +2 -1
  36. data/lib/translatomatic/cli/base.rb +73 -0
  37. data/lib/translatomatic/cli/common_options.rb +14 -0
  38. data/lib/translatomatic/cli/config.rb +81 -0
  39. data/lib/translatomatic/cli/database.rb +29 -0
  40. data/lib/translatomatic/cli/main.rb +112 -0
  41. data/lib/translatomatic/cli/translate.rb +146 -0
  42. data/lib/translatomatic/cli.rb +8 -216
  43. data/lib/translatomatic/config.rb +143 -0
  44. data/lib/translatomatic/converter.rb +196 -149
  45. data/lib/translatomatic/converter_stats.rb +18 -14
  46. data/lib/translatomatic/database.rb +35 -37
  47. data/lib/translatomatic/escaped_unicode.rb +1 -1
  48. data/lib/translatomatic/extractor/base.rb +2 -0
  49. data/lib/translatomatic/extractor/ruby.rb +1 -0
  50. data/lib/translatomatic/extractor.rb +1 -0
  51. data/lib/translatomatic/http_request.rb +43 -14
  52. data/lib/translatomatic/locale.rb +28 -4
  53. data/lib/translatomatic/logger.rb +21 -13
  54. data/lib/translatomatic/model/locale.rb +5 -1
  55. data/lib/translatomatic/model/text.rb +2 -0
  56. data/lib/translatomatic/model.rb +1 -0
  57. data/lib/translatomatic/option.rb +75 -10
  58. data/lib/translatomatic/progress_updater.rb +7 -3
  59. data/lib/translatomatic/resource_file/base.rb +41 -18
  60. data/lib/translatomatic/resource_file/html.rb +11 -14
  61. data/lib/translatomatic/resource_file/markdown.rb +13 -12
  62. data/lib/translatomatic/resource_file/plist.rb +3 -14
  63. data/lib/translatomatic/resource_file/properties.rb +21 -3
  64. data/lib/translatomatic/resource_file/resw.rb +1 -0
  65. data/lib/translatomatic/resource_file/text.rb +1 -0
  66. data/lib/translatomatic/resource_file/xcode_strings.rb +23 -14
  67. data/lib/translatomatic/resource_file/xml.rb +34 -22
  68. data/lib/translatomatic/resource_file/yaml.rb +39 -5
  69. data/lib/translatomatic/resource_file.rb +7 -5
  70. data/lib/translatomatic/string.rb +40 -12
  71. data/lib/translatomatic/tmx/document.rb +11 -12
  72. data/lib/translatomatic/tmx/translation_unit.rb +5 -1
  73. data/lib/translatomatic/tmx.rb +2 -0
  74. data/lib/translatomatic/translation.rb +30 -0
  75. data/lib/translatomatic/translation_result.rb +45 -45
  76. data/lib/translatomatic/translator/base.rb +128 -83
  77. data/lib/translatomatic/translator/frengly.rb +62 -57
  78. data/lib/translatomatic/translator/google.rb +35 -31
  79. data/lib/translatomatic/translator/microsoft.rb +41 -33
  80. data/lib/translatomatic/translator/my_memory.rb +68 -64
  81. data/lib/translatomatic/translator/yandex.rb +56 -39
  82. data/lib/translatomatic/translator.rb +99 -63
  83. data/lib/translatomatic/util.rb +31 -1
  84. data/lib/translatomatic/version.rb +4 -1
  85. data/lib/translatomatic.rb +17 -0
  86. data/translatomatic.gemspec +5 -4
  87. 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 [Hash<String,String>] properties Untranslated properties
21
- # @param [Locale] from_locale The locale of the untranslated strings
22
- # @param [Locale] to_locale The target locale
23
- def initialize(properties, from_locale, to_locale)
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 = 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<String>] original Original strings
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(original, translated)
48
- raise "strings length mismatch" unless original.length == translated.length
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
- conversions.each do |text1, text2|
64
- update(text1, text2)
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
- @properties[key][original.offset, original.length] = translated
78
- else
79
- @properties[key] = translated
80
- end
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
- # @abstract
6
- class Base
7
-
8
- class << self
9
- attr_reader :options
10
- private
11
- include Translatomatic::DefineOptions
12
- end
13
-
14
- # @private
15
- attr_accessor :listener
16
-
17
- def initialize(options = {})
18
- @listener = options[:listener]
19
- end
20
-
21
- # @return [String] The name of this translator.
22
- def name
23
- self.class.name.demodulize
24
- end
25
-
26
- # @return [Array<String>] A list of languages supported by this translator.
27
- def languages
28
- []
29
- end
30
-
31
- # Translate strings from one locale to another
32
- # @param [Array<String>] strings A list of strings to translate.
33
- # @param [String, Translatomatic::Locale] from The locale of the given strings.
34
- # @param [String, Translatomatic::Locale] to The locale to translate to.
35
- # @return [Array<String>] Translated strings
36
- def translate(strings, from, to)
37
- strings = [strings] unless strings.kind_of?(Array)
38
- from = locale(from)
39
- to = locale(to)
40
- return strings if from.language == to.language
41
- translated = perform_translate(strings, from, to)
42
- update_translated(translated) unless @updated_listener
43
- translated
44
- end
45
-
46
- private
47
-
48
- include Translatomatic::Util
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.
53
- # (subclass must implement fetch_translation if this method is used)
54
- def perform_fetch_translations(url, strings, from, to)
55
- translated = []
56
- request = Translatomatic::HTTPRequest.new(url)
57
- request.start do |http|
58
- strings.each do |string|
59
- result = fetch_translation(request, string, from, to)
60
- translated << result
61
- update_translated(result)
62
- end
63
- end
64
- translated
65
- end
66
-
67
- def fetch_translation(request, strings, from, to)
68
- raise "subclass must implement fetch_translation"
69
- end
70
-
71
- def update_translated(texts)
72
- texts = [texts] unless texts.kind_of?(Array)
73
- @updated_listener = true
74
- @listener.translated_texts(texts) if @listener
75
- end
76
-
77
- def perform_translate(strings, from, to)
78
- raise "subclass must implement perform_translate"
79
- end
80
-
81
- end
82
- end
83
- end
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
- class Frengly < Base
7
-
8
- define_options({
9
- name: :frengly_api_key, desc: "Frengly API key", use_env: true
10
- },
11
- { name: :frengly_email, desc: "Email address", use_env: true
12
- },
13
- { name: :frengly_password, desc: "Password", use_env: true
14
- })
15
-
16
- # Create a new Frengly translator instance
17
- def initialize(options = {})
18
- super(options)
19
- @key = options[:frengly_api_key] || ENV["FRENGLY_API_KEY"] # optional
20
- @email = options[:frengly_email]
21
- @password = options[:frengly_password]
22
- raise "email address required" unless @email
23
- raise "password required" unless @password
24
- end
25
-
26
- # (see Translatomatic::Translator::Base#languages)
27
- def languages
28
- ['en','fr','de','es','pt','it','nl','tl','fi','el','iw','pl','ru','sv']
29
- end
30
-
31
- private
32
-
33
- URL = 'http://frengly.com/frengly/data/translateREST'
34
-
35
- def perform_translate(strings, from, to)
36
- perform_fetch_translations(URL, strings, from, to)
37
- end
38
-
39
- def fetch_translation(request, string, from, to)
40
- body = {
41
- src: from.language,
42
- dest: to.language,
43
- text: string,
44
- email: @email,
45
- password: @password,
46
- premiumkey: @key
47
- }.to_json
48
-
49
- # TODO: work out what the response looks like
50
- response = request.post(body, content_type: 'application/json')
51
- data = JSON.parse(response.body)
52
- data['text']
53
- end
54
-
55
- end # class
56
- end # module
57
- end
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
- class Google < Base
5
-
6
- define_options({ name: :google_api_key, desc: "Google API key",
7
- use_env: true
8
- })
9
-
10
- # Create a new Google translator instance
11
- def initialize(options = {})
12
- super(options)
13
- key = options[:google_api_key] || ENV["GOOGLE_API_KEY"]
14
- raise "google api key required" if key.nil?
15
- EasyTranslate.api_key = key
16
- end
17
-
18
- # (see Translatomatic::Translator::Base#languages)
19
- def languages
20
- EasyTranslate::LANGUAGES.keys
21
- end
22
-
23
- private
24
-
25
- def perform_translate(strings, from, to)
26
- EasyTranslate.translate(strings, from: from.language, to: to.language)
27
- end
28
-
29
- end # class
30
- end # module
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
- class Microsoft < Base
7
-
8
- define_options({
9
- name: :microsoft_api_key, desc: "Microsoft API key", use_env: true
10
- })
11
-
12
- # Create a new Microsoft translator instance
13
- def initialize(options = {})
14
- super(options)
15
- key = options[:microsoft_api_key] || ENV["MICROSOFT_API_KEY"]
16
- raise "microsoft api key required" if key.nil?
17
- @impl = BingTranslator.new(key)
18
- end
19
-
20
- # TODO: implement language list
21
- # (see Translatomatic::Translator::Base#languages)
22
- #def languages
23
- #end
24
-
25
- private
26
-
27
- def perform_translate(strings, from, to)
28
- @impl.translate_array(strings, from: from.language, to: to.language)
29
- end
30
-
31
- end
32
- end
33
- end
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