translatomatic 0.1.0
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 +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +51 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +137 -0
- data/LICENSE.txt +21 -0
- data/README.md +74 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/translatomatic +6 -0
- data/db/database.yml +9 -0
- data/db/migrate/201712170000_initial.rb +23 -0
- data/lib/translatomatic/cli.rb +92 -0
- data/lib/translatomatic/config.rb +26 -0
- data/lib/translatomatic/converter.rb +157 -0
- data/lib/translatomatic/converter_stats.rb +27 -0
- data/lib/translatomatic/database.rb +105 -0
- data/lib/translatomatic/escaped_unicode.rb +90 -0
- data/lib/translatomatic/model/locale.rb +22 -0
- data/lib/translatomatic/model/text.rb +13 -0
- data/lib/translatomatic/model.rb +4 -0
- data/lib/translatomatic/option.rb +24 -0
- data/lib/translatomatic/resource_file/base.rb +137 -0
- data/lib/translatomatic/resource_file/html.rb +33 -0
- data/lib/translatomatic/resource_file/plist.rb +29 -0
- data/lib/translatomatic/resource_file/properties.rb +60 -0
- data/lib/translatomatic/resource_file/text.rb +28 -0
- data/lib/translatomatic/resource_file/xcode_strings.rb +65 -0
- data/lib/translatomatic/resource_file/xml.rb +64 -0
- data/lib/translatomatic/resource_file/yaml.rb +80 -0
- data/lib/translatomatic/resource_file.rb +74 -0
- data/lib/translatomatic/translation_result.rb +68 -0
- data/lib/translatomatic/translator/base.rb +47 -0
- data/lib/translatomatic/translator/frengly.rb +64 -0
- data/lib/translatomatic/translator/google.rb +30 -0
- data/lib/translatomatic/translator/microsoft.rb +32 -0
- data/lib/translatomatic/translator/my_memory.rb +55 -0
- data/lib/translatomatic/translator/yandex.rb +37 -0
- data/lib/translatomatic/translator.rb +63 -0
- data/lib/translatomatic/util.rb +24 -0
- data/lib/translatomatic/version.rb +3 -0
- data/lib/translatomatic.rb +27 -0
- data/translatomatic.gemspec +46 -0
- metadata +329 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
module Translatomatic::ResourceFile
|
2
|
+
class Properties < Base
|
3
|
+
|
4
|
+
def self.extensions
|
5
|
+
%w{properties}
|
6
|
+
end
|
7
|
+
|
8
|
+
# (see Translatomatic::ResourceFile::Base#initialize)
|
9
|
+
def initialize(path, locale = nil)
|
10
|
+
super(path, locale)
|
11
|
+
@valid = true
|
12
|
+
@properties = @path.exist? ? read(@path) : {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# (see Translatomatic::ResourceFile::Base#save(target))
|
16
|
+
def save(target = path)
|
17
|
+
out = ""
|
18
|
+
properties.each do |key, value|
|
19
|
+
# TODO: maintain original line ending format?
|
20
|
+
value = value.gsub("\n", "\\n") # convert newlines to \n
|
21
|
+
out += "#{key} = #{value}\n"
|
22
|
+
end
|
23
|
+
# escape unicode characters
|
24
|
+
out = Translatomatic::EscapedUnicode.escape(out)
|
25
|
+
target.write(out)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# parse key = value property file
|
31
|
+
def read(path)
|
32
|
+
contents = path.read
|
33
|
+
# convert escaped unicode characters into unicode
|
34
|
+
contents = Translatomatic::EscapedUnicode.unescape(contents)
|
35
|
+
result = {}
|
36
|
+
contents.gsub!(/\\\s*\n\s*/m, '') # put multi line strings on one line
|
37
|
+
lines = contents.split("\n")
|
38
|
+
|
39
|
+
lines.each do |line|
|
40
|
+
line.strip!
|
41
|
+
next if line.length == 0
|
42
|
+
equal_idx = line.index("=")
|
43
|
+
|
44
|
+
if line[0] == ?! || line[0] == ?#
|
45
|
+
# comment
|
46
|
+
# TODO: translate comments or keep originals?
|
47
|
+
next
|
48
|
+
elsif equal_idx.nil?
|
49
|
+
@valid = false
|
50
|
+
return {}
|
51
|
+
end
|
52
|
+
name, value = line.split(/\s*=\s*/, 2)
|
53
|
+
value = value.gsub("\\n", "\n") # convert \n to newlines
|
54
|
+
result[name] = value
|
55
|
+
end
|
56
|
+
result
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Translatomatic::ResourceFile
|
2
|
+
class Text < Base
|
3
|
+
|
4
|
+
def self.extensions
|
5
|
+
%w{txt md text}
|
6
|
+
end
|
7
|
+
|
8
|
+
# (see Translatomatic::ResourceFile::Base#initialize)
|
9
|
+
def initialize(path, locale = nil)
|
10
|
+
super(path, locale)
|
11
|
+
@valid = true
|
12
|
+
@properties = @path.exist? ? read(@path) : {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# (see Translatomatic::ResourceFile::Base#save(target))
|
16
|
+
def save(target = path)
|
17
|
+
values = @properties.values.collect { |i| i.strip + "\n" }
|
18
|
+
target.write(values.join)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def read(path)
|
24
|
+
text = path.read
|
25
|
+
{ "text" => text }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Translatomatic::ResourceFile
|
2
|
+
|
3
|
+
# XCode strings file
|
4
|
+
# @see https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/LoadingResources/Strings/Strings.html
|
5
|
+
class XCodeStrings < Base
|
6
|
+
|
7
|
+
def self.extensions
|
8
|
+
%w{strings}
|
9
|
+
end
|
10
|
+
|
11
|
+
# (see Translatomatic::ResourceFile::Base#initialize)
|
12
|
+
def initialize(path, locale = nil)
|
13
|
+
super(path, locale)
|
14
|
+
@valid = true
|
15
|
+
@properties = @path.exist? ? read(@path) : {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# (see Translatomatic::ResourceFile::Base#locale_path)
|
19
|
+
# @note localization files in XCode use the following file name
|
20
|
+
# convention: Project/locale.lproj/filename
|
21
|
+
def locale_path(locale)
|
22
|
+
if path.to_s.match(/\/([-\w]+).lproj\/.+.strings$/)
|
23
|
+
# xcode style
|
24
|
+
filename = path.basename
|
25
|
+
path.parent.parent + (locale.to_s + ".lproj") + filename
|
26
|
+
else
|
27
|
+
super(locale)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# (see Translatomatic::ResourceFile::Base#save(target))
|
32
|
+
def save(target = path)
|
33
|
+
out = ""
|
34
|
+
properties.each do |key, value|
|
35
|
+
key = escape(key)
|
36
|
+
value = escape(value)
|
37
|
+
out += %Q{"#{key}" = "#{value}";\n}
|
38
|
+
end
|
39
|
+
target.write(out)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def read(path)
|
45
|
+
result = {}
|
46
|
+
content = path.read
|
47
|
+
uncommented = content.gsub(/\/\*.*?\*\//, '')
|
48
|
+
key_values = uncommented.scan(/"(.*?[^\\])"\s*=\s*"(.*?[^\\])"\s*;/m)
|
49
|
+
key_values.each do |entry|
|
50
|
+
key, value = entry
|
51
|
+
result[unescape(key)] = unescape(value)
|
52
|
+
end
|
53
|
+
result
|
54
|
+
end
|
55
|
+
|
56
|
+
def unescape(string)
|
57
|
+
string ? string.gsub(/\\(["'])/) { |i| i } : ''
|
58
|
+
end
|
59
|
+
|
60
|
+
def escape(string)
|
61
|
+
string ? string.gsub(/["']/) { |i| "\\#{i}" } : ''
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Translatomatic::ResourceFile
|
2
|
+
class XML < Base
|
3
|
+
|
4
|
+
def self.extensions
|
5
|
+
%w{xml}
|
6
|
+
end
|
7
|
+
|
8
|
+
# (see Translatomatic::ResourceFile::Base#initialize)
|
9
|
+
def initialize(path, locale = nil)
|
10
|
+
super(path, locale)
|
11
|
+
@valid = true
|
12
|
+
@properties = @path.exist? ? read(@path) : {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# (see Translatomatic::ResourceFile::Base#set)
|
16
|
+
def set(key, value)
|
17
|
+
super(key, value)
|
18
|
+
@nodemap[key].content = value if @nodemap.include?(key)
|
19
|
+
end
|
20
|
+
|
21
|
+
# (see Translatomatic::ResourceFile::Base#save(target))
|
22
|
+
def save(target = path)
|
23
|
+
target.write(@doc.to_xml) if @doc
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# initialize nodemap from nokogiri document
|
29
|
+
# returns property hash
|
30
|
+
def init_nodemap(doc)
|
31
|
+
# map of key1 => node, key2 => node, ...
|
32
|
+
@nodemap = flatten_xml(doc)
|
33
|
+
# map of key => node content
|
34
|
+
@nodemap.transform_values { |v| v.content }
|
35
|
+
end
|
36
|
+
|
37
|
+
# parse key = value property file
|
38
|
+
def read(path)
|
39
|
+
begin
|
40
|
+
# parse xml with nokogiri
|
41
|
+
@doc = Nokogiri::XML(path.open) do |config|
|
42
|
+
config.noblanks
|
43
|
+
end
|
44
|
+
init_nodemap(@doc)
|
45
|
+
rescue Exception
|
46
|
+
@valid = false
|
47
|
+
{}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def flatten_xml(doc)
|
52
|
+
result = {}
|
53
|
+
text_nodes = doc.search(text_nodes_xpath)
|
54
|
+
text_nodes.each_with_index do |node, i|
|
55
|
+
result["key#{i + 1}"] = node
|
56
|
+
end
|
57
|
+
result
|
58
|
+
end
|
59
|
+
|
60
|
+
def text_nodes_xpath
|
61
|
+
'//text()'
|
62
|
+
end
|
63
|
+
end # class
|
64
|
+
end # module
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Translatomatic::ResourceFile
|
4
|
+
class YAML < Base
|
5
|
+
|
6
|
+
def self.extensions
|
7
|
+
%w{yml yaml}
|
8
|
+
end
|
9
|
+
|
10
|
+
# (see Translatomatic::ResourceFile::Base#initialize)
|
11
|
+
def initialize(path, locale = nil)
|
12
|
+
super(path, locale)
|
13
|
+
@valid = true
|
14
|
+
@data = {}
|
15
|
+
@properties = @path.exist? ? read : {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# (see Translatomatic::ResourceFile::Base#locale_path)
|
19
|
+
# @note localization files in rails use the following file name
|
20
|
+
# convention: config/locales/en.yml.
|
21
|
+
def locale_path(locale)
|
22
|
+
if path.to_s.match(/config\/locales\/[-\w]+.yml$/)
|
23
|
+
# rails style
|
24
|
+
filename = locale.to_s + path.extname
|
25
|
+
path.dirname + filename
|
26
|
+
else
|
27
|
+
super(locale)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# (see Translatomatic::ResourceFile::Base#set)
|
32
|
+
def set(key, value)
|
33
|
+
super(key, value)
|
34
|
+
|
35
|
+
hash = @data
|
36
|
+
path = key.split(/\./)
|
37
|
+
last_key = path.pop
|
38
|
+
path.each { |i| hash = (hash[i] ||= {}) }
|
39
|
+
hash[last_key] = value
|
40
|
+
end
|
41
|
+
|
42
|
+
# (see Translatomatic::ResourceFile::Base#save(target))
|
43
|
+
def save(target = path)
|
44
|
+
out = @data.to_yaml
|
45
|
+
out.sub!(/^---\n/m, '')
|
46
|
+
target.write(out)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def read
|
52
|
+
begin
|
53
|
+
@data = ::YAML.load_file(@path) || {}
|
54
|
+
flatten_data(@data)
|
55
|
+
rescue Exception
|
56
|
+
@valid = false
|
57
|
+
{}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def flatten_data(data)
|
62
|
+
result = {}
|
63
|
+
unless data.kind_of?(Hash)
|
64
|
+
@valid = false
|
65
|
+
return {}
|
66
|
+
end
|
67
|
+
data.each do |key, value|
|
68
|
+
if value.kind_of?(Hash)
|
69
|
+
children = flatten_data(value)
|
70
|
+
children.each do |ck, cv|
|
71
|
+
result[key + "." + ck] = cv
|
72
|
+
end
|
73
|
+
else
|
74
|
+
result[key] = value
|
75
|
+
end
|
76
|
+
end
|
77
|
+
result
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
|
2
|
+
module Translatomatic
|
3
|
+
module ResourceFile
|
4
|
+
class << self
|
5
|
+
include Translatomatic::Util
|
6
|
+
end
|
7
|
+
|
8
|
+
# Load a resource file. If locale is not specified, the locale of the
|
9
|
+
# file will be determined from the filename, or else the current default
|
10
|
+
# locale will be used.
|
11
|
+
# @param [String] path Path to the resource file
|
12
|
+
# @param [String] locale Locale of the resource file
|
13
|
+
# @return [Translatomatic::ResourceFile::Base] The resource file, or nil
|
14
|
+
# if the file type is unsupported.
|
15
|
+
def self.load(path, locale = nil)
|
16
|
+
path = path.kind_of?(Pathname) ? path : Pathname.new(path)
|
17
|
+
modules.each do |mod|
|
18
|
+
# match on entire filename to support extensions containing locales
|
19
|
+
if extension_match(mod, path)
|
20
|
+
log.debug("attempting to load #{path.to_s} using #{mod.name.demodulize}")
|
21
|
+
file = mod.new(path, locale)
|
22
|
+
return file if file.valid?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
# Find all resource files under the given directory. Follows symlinks.
|
29
|
+
# @param [String, Pathname] path The path to search from
|
30
|
+
# @return [Array<Translatomatic::ResourceFile>] Resource files found
|
31
|
+
def self.find(path, options = {})
|
32
|
+
files = []
|
33
|
+
include_dot_directories = options[:include_dot_directories]
|
34
|
+
path = Pathname.new(path) unless path.kind_of?(Pathname)
|
35
|
+
path.find do |file|
|
36
|
+
if !include_dot_directories && file.basename.to_s[0] == ?.
|
37
|
+
Find.prune
|
38
|
+
else
|
39
|
+
resource = load(file)
|
40
|
+
files << resource if resource
|
41
|
+
end
|
42
|
+
end
|
43
|
+
files
|
44
|
+
end
|
45
|
+
|
46
|
+
# Find all configured resource file classes
|
47
|
+
# @return [Array<Class>] Available resource file classes
|
48
|
+
def self.modules
|
49
|
+
self.constants.map { |c| self.const_get(c) }.select do |klass|
|
50
|
+
klass.is_a?(Class) && klass != Base
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def self.extension_match(mod, path)
|
57
|
+
filename = path.basename.to_s.downcase
|
58
|
+
mod.extensions.each do |extension|
|
59
|
+
# don't match end of line in case file has locale extension
|
60
|
+
return true if filename.match(/\.#{extension}/)
|
61
|
+
end
|
62
|
+
false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
require 'translatomatic/resource_file/base'
|
68
|
+
require 'translatomatic/resource_file/yaml'
|
69
|
+
require 'translatomatic/resource_file/properties'
|
70
|
+
require 'translatomatic/resource_file/text'
|
71
|
+
require 'translatomatic/resource_file/xml'
|
72
|
+
require 'translatomatic/resource_file/html'
|
73
|
+
require 'translatomatic/resource_file/plist'
|
74
|
+
require 'translatomatic/resource_file/xcode_strings'
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Translatomatic
|
4
|
+
class TranslationResult
|
5
|
+
|
6
|
+
# Translation results
|
7
|
+
# @return [Hash<String,String>] Translation results
|
8
|
+
attr_reader :properties
|
9
|
+
|
10
|
+
# @return [Locale] The locale of the original strings
|
11
|
+
attr_reader :from_locale
|
12
|
+
|
13
|
+
# @return [Locale] The target locale
|
14
|
+
attr_reader :to_locale
|
15
|
+
|
16
|
+
# @return [Set<String>] Untranslated strings
|
17
|
+
attr_reader :untranslated
|
18
|
+
|
19
|
+
# 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)
|
24
|
+
@properties = properties.dup
|
25
|
+
@value_to_keys = {}
|
26
|
+
@untranslated = Set.new
|
27
|
+
properties.each do |key, value|
|
28
|
+
@untranslated << value
|
29
|
+
keylist = (@value_to_keys[value] ||= [])
|
30
|
+
keylist << key
|
31
|
+
end
|
32
|
+
@from_locale = from_locale
|
33
|
+
@to_locale = to_locale
|
34
|
+
end
|
35
|
+
|
36
|
+
# Update result with a list of translated strings.
|
37
|
+
# @param [Array<String>] original Original strings
|
38
|
+
# @param [Array<String>] translated Translated strings
|
39
|
+
# @return [void]
|
40
|
+
def update_strings(original, translated)
|
41
|
+
raise "strings length mismatch" unless original.length == translated.length
|
42
|
+
original.zip(translated).each do |text1, text2|
|
43
|
+
update(text1, text2)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Update result with texts from the database.
|
48
|
+
# @param [Array<Translatomatic::Model::Text>] list Texts from database
|
49
|
+
# @return [void]
|
50
|
+
def update_db_strings(list)
|
51
|
+
list.each do |t|
|
52
|
+
original = t.from_text.value
|
53
|
+
translated = t.value
|
54
|
+
update(original, translated)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def update(original, translated)
|
61
|
+
keys = @value_to_keys[original]
|
62
|
+
raise "no key mapping for text '#{original}'" unless keys
|
63
|
+
keys.each { |key| @properties[key] = translated }
|
64
|
+
|
65
|
+
@untranslated.delete(original)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,47 @@
|
|
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
|
+
# @return [String] The name of this translator.
|
15
|
+
def name
|
16
|
+
self.class.name.demodulize
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Array<String>] A list of languages supported by this translator.
|
20
|
+
def languages
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Translate strings from one locale to another
|
25
|
+
# @param [Array<String>] strings A list of strings to translate.
|
26
|
+
# @param [String, Locale] from The locale of the given strings.
|
27
|
+
# @param [String, Locale] to The locale to translate to.
|
28
|
+
# @return [Array<String>] Translated strings
|
29
|
+
def translate(strings, from, to)
|
30
|
+
strings = [strings] unless strings.kind_of?(Array)
|
31
|
+
from = parse_locale(from) if from.kind_of?(String)
|
32
|
+
to = parse_locale(to) if to.kind_of?(String)
|
33
|
+
return strings if from.language == to.language
|
34
|
+
perform_translate(strings, from, to)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
include Translatomatic::Util
|
40
|
+
|
41
|
+
def perform_translate(strings, from, to)
|
42
|
+
raise "subclasses must implement perform_translate"
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,64 @@
|
|
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
|
+
@key = options[:frengly_api_key] || ENV["FRENGLY_API_KEY"] # optional
|
19
|
+
@email = options[:frengly_email]
|
20
|
+
@password = options[:frengly_password]
|
21
|
+
raise "email address required" unless @email
|
22
|
+
raise "password required" unless @password
|
23
|
+
end
|
24
|
+
|
25
|
+
# (see Translatomatic::Translator::Base#languages)
|
26
|
+
def languages
|
27
|
+
['en','fr','de','es','pt','it','nl','tl','fi','el','iw','pl','ru','sv']
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
URL = 'http://frengly.com/frengly/data/translateREST'
|
33
|
+
|
34
|
+
def perform_translate(strings, from, to)
|
35
|
+
translated = []
|
36
|
+
uri = URI.parse(URL)
|
37
|
+
|
38
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
39
|
+
strings.each do |string|
|
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
|
+
req = Net::HTTP::Post.new(uri)
|
51
|
+
req.body = body
|
52
|
+
req.content_type = 'application/json'
|
53
|
+
response = http.request(req)
|
54
|
+
raise response.body unless response.kind_of? Net::HTTPSuccess
|
55
|
+
data = JSON.parse(response.body)
|
56
|
+
translated << data['text']
|
57
|
+
end
|
58
|
+
translated
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end # class
|
63
|
+
end # module
|
64
|
+
end
|
@@ -0,0 +1,30 @@
|
|
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
|
+
key = options[:google_api_key] || ENV["GOOGLE_API_KEY"]
|
13
|
+
raise "google api key required" if key.nil?
|
14
|
+
EasyTranslate.api_key = key
|
15
|
+
end
|
16
|
+
|
17
|
+
# (see Translatomatic::Translator::Base#languages)
|
18
|
+
def languages
|
19
|
+
EasyTranslate::LANGUAGES.keys
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def perform_translate(strings, from, to)
|
25
|
+
EasyTranslate.translate(strings, from: from.language, to: to.language)
|
26
|
+
end
|
27
|
+
|
28
|
+
end # class
|
29
|
+
end # module
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
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
|
+
key = options[:microsoft_api_key] || ENV["MICROSOFT_API_KEY"]
|
15
|
+
raise "microsoft api key required" if key.nil?
|
16
|
+
@impl = BingTranslator.new(key)
|
17
|
+
end
|
18
|
+
|
19
|
+
# TODO: implement language list
|
20
|
+
# (see Translatomatic::Translator::Base#languages)
|
21
|
+
#def languages
|
22
|
+
#end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def perform_translate(strings, from, to)
|
27
|
+
@impl.translate_array(strings, from: from.language, to: to.language)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'bing_translator'
|
2
|
+
|
3
|
+
module Translatomatic
|
4
|
+
module Translator
|
5
|
+
|
6
|
+
class MyMemory < Base
|
7
|
+
|
8
|
+
define_options(
|
9
|
+
{ name: :mymemory_api_key, desc: "MyMemory API key", use_env: true },
|
10
|
+
{ name: :mymemory_email, desc: "Email address", use_env: true }
|
11
|
+
)
|
12
|
+
|
13
|
+
# Create a new MyMemory translator instance
|
14
|
+
def initialize(options = {})
|
15
|
+
@key = options[:mymemory_api_key] || ENV["MYMEMORY_API_KEY"]
|
16
|
+
@email = options[:mymemory_email] || ENV["MYMEMORY_EMAIL"]
|
17
|
+
end
|
18
|
+
|
19
|
+
# TODO: implement language list
|
20
|
+
# (see Translatomatic::Translator::Base#languages)
|
21
|
+
#def languages
|
22
|
+
#end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
URL = 'https://api.mymemory.translated.net/get'
|
27
|
+
|
28
|
+
def perform_translate(strings, from, to)
|
29
|
+
translated = []
|
30
|
+
uri = URI.parse(URL)
|
31
|
+
|
32
|
+
http_options = { use_ssl: uri.scheme == "https" }
|
33
|
+
Net::HTTP.start(uri.host, uri.port, http_options) do |http|
|
34
|
+
strings.each do |string|
|
35
|
+
query = {
|
36
|
+
langpair: from.to_s + "|" + to.to_s,
|
37
|
+
q: string
|
38
|
+
}
|
39
|
+
query.merge!(de: @email) if @email
|
40
|
+
query.merge!(key: @key) if @key
|
41
|
+
uri.query = URI.encode_www_form(query)
|
42
|
+
|
43
|
+
req = Net::HTTP::Get.new(uri)
|
44
|
+
response = http.request(req)
|
45
|
+
raise response.body unless response.kind_of? Net::HTTPSuccess
|
46
|
+
data = JSON.parse(response.body)
|
47
|
+
translated << data['responseData']['translatedText']
|
48
|
+
end
|
49
|
+
translated
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|