bing_translator 5.1.0 → 6.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 +5 -5
- data/lib/bing_translator.rb +120 -141
- metadata +7 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5f88718168ee707a700ed926c13ff7539155d5e94535145c912672520a166f72
|
4
|
+
data.tar.gz: 0d5afc1c573bb1c613a7ca77fc7cc8ae2373477677845b3b21f4e425b6ea2fa1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61a293de90f373bbe12a79989c32a416949b557761dcfb3a773b7fb5c2f9a3bb1657bc084661dee065aa603c0497ada6bf3e6fbc66ea17c99a4f2bfb200a7b6f
|
7
|
+
data.tar.gz: 0d9afa36690c92b84987392f5b5ca1d970e5b057144f355fe8733e304a4acc444522159fd0fc8bbc5510c6a2417872972b84aa77124dbfd2d45899e9e2a5a6d1
|
data/lib/bing_translator.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
2
|
+
|
3
3
|
# (c) 2011-present. Ricky Elrod <ricky@elrod.me>
|
4
4
|
# Released under the MIT license.
|
5
5
|
require 'rubygems'
|
@@ -7,186 +7,165 @@ require 'cgi'
|
|
7
7
|
require 'uri'
|
8
8
|
require 'net/http'
|
9
9
|
require 'net/https'
|
10
|
-
require 'nokogiri'
|
11
10
|
require 'json'
|
12
|
-
require 'savon'
|
13
11
|
|
14
12
|
class BingTranslator
|
15
|
-
WSDL_URI = 'http://api.microsofttranslator.com/V2/soap.svc?wsdl'
|
16
|
-
NAMESPACE_URI = 'http://api.microsofttranslator.com/V2'
|
17
|
-
COGNITIVE_ACCESS_TOKEN_URI = URI.parse('https://api.cognitive.microsoft.com/sts/v1.0/issueToken').freeze
|
18
|
-
|
19
13
|
class Exception < StandardError; end
|
20
14
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
15
|
+
class ApiClient
|
16
|
+
API_HOST = 'https://api.cognitive.microsofttranslator.com'.freeze
|
17
|
+
COGNITIVE_ACCESS_TOKEN_URI =
|
18
|
+
URI.parse('https://api.cognitive.microsoft.com/sts/v1.0/issueToken').freeze
|
25
19
|
|
26
|
-
|
27
|
-
|
20
|
+
def initialize(subscription_key, skip_ssl_verify, read_timeout = 60, open_timeout = 60)
|
21
|
+
@subscription_key = subscription_key
|
22
|
+
@skip_ssl_verify = skip_ssl_verify
|
23
|
+
@read_timeout = read_timeout
|
24
|
+
@open_timeout = open_timeout
|
25
|
+
end
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
'from' => params[:from].to_s,
|
33
|
-
'to' => params[:to].to_s,
|
34
|
-
'category' => 'general',
|
35
|
-
'contentType' => params[:content_type] || 'text/plain'
|
36
|
-
}
|
27
|
+
def get(path, params: {}, headers: {}, authorization: false)
|
28
|
+
uri = request_uri(path, params)
|
29
|
+
request = Net::HTTP::Get.new(uri.request_uri, default_headers(authorization).merge(headers))
|
37
30
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
def translate_array(texts, params = {})
|
42
|
-
raise "Must provide :to." if params[:to].nil?
|
43
|
-
|
44
|
-
# Important notice: param order makes sense in SOAP. Do not reorder or delete!
|
45
|
-
params = {
|
46
|
-
'texts' => { 'arr:string' => texts },
|
47
|
-
'from' => params[:from].to_s,
|
48
|
-
'to' => params[:to].to_s,
|
49
|
-
'category' => 'general',
|
50
|
-
'contentType' => params[:content_type] || 'text/plain'
|
51
|
-
}
|
52
|
-
|
53
|
-
array_wrap(result(:translate_array, params)[:translate_array_response]).map{|r| r[:translated_text]}
|
54
|
-
end
|
31
|
+
json_response(uri, request)
|
32
|
+
end
|
55
33
|
|
56
|
-
|
57
|
-
|
34
|
+
def post(path, params: {}, headers: {}, data: {}, authorization: true)
|
35
|
+
uri = request_uri(path, params)
|
58
36
|
|
59
|
-
|
60
|
-
|
61
|
-
'texts' => { 'arr:string' => texts },
|
62
|
-
'from' => params[:from].to_s,
|
63
|
-
'to' => params[:to].to_s,
|
64
|
-
'category' => 'general',
|
65
|
-
'contentType' => params[:content_type] || 'text/plain'
|
66
|
-
}
|
37
|
+
request = Net::HTTP::Post.new(uri.request_uri, default_headers(authorization).merge(headers))
|
38
|
+
request.body = data
|
67
39
|
|
68
|
-
|
69
|
-
|
40
|
+
json_response(uri, request)
|
41
|
+
end
|
70
42
|
|
71
|
-
|
72
|
-
params = {
|
73
|
-
'text' => text.to_s,
|
74
|
-
'language' => '',
|
75
|
-
}
|
43
|
+
private
|
76
44
|
|
77
|
-
|
78
|
-
|
45
|
+
def default_headers(authorization = true)
|
46
|
+
headers = { 'Content-Type' => 'application/json' }
|
47
|
+
headers['Authorization'] = "Bearer #{access_token}" if authorization
|
48
|
+
headers
|
79
49
|
end
|
80
|
-
end
|
81
50
|
|
82
|
-
|
83
|
-
|
84
|
-
# options: 'MinSize' [default] or 'MaxQuality'
|
85
|
-
def speak(text, params = {})
|
86
|
-
raise "Must provide :language" if params[:language].nil?
|
51
|
+
def json_response(uri, request)
|
52
|
+
http = http_client(uri)
|
87
53
|
|
88
|
-
|
89
|
-
'text' => text.to_s,
|
90
|
-
'language' => params[:language].to_s,
|
91
|
-
'format' => params[:format] || 'audio/wav',
|
92
|
-
'options' => params[:options] || 'MinSize',
|
93
|
-
}
|
54
|
+
response = http.request(request)
|
94
55
|
|
95
|
-
|
56
|
+
raise Exception.new("Unsuccessful API call: Code: #{response.code}") unless response.code == '200'
|
57
|
+
JSON.parse(response.body)
|
58
|
+
end
|
96
59
|
|
97
|
-
|
98
|
-
|
60
|
+
def http_client(uri)
|
61
|
+
http = Net::HTTP.new(uri.host, 443)
|
99
62
|
http.use_ssl = true
|
100
63
|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @skip_ssl_verify
|
64
|
+
http.read_timeout = @read_timeout
|
65
|
+
http.open_timeout = @open_timeout
|
66
|
+
http
|
101
67
|
end
|
102
|
-
results = http.get(uri.to_s, {'Authorization' => "Bearer #{get_access_token['access_token']}"})
|
103
68
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
69
|
+
def request_uri(path, params)
|
70
|
+
encoded_params = URI.encode_www_form(params.merge('api-version' => '3.0'))
|
71
|
+
uri = URI.parse("#{API_HOST}#{path}")
|
72
|
+
uri.query = encoded_params
|
73
|
+
uri
|
74
|
+
end
|
75
|
+
|
76
|
+
def access_token
|
77
|
+
if @access_token.nil? || Time.now >= @access_token_expiration_time
|
78
|
+
@access_token = request_new_access_token
|
79
|
+
end
|
80
|
+
@access_token
|
81
|
+
end
|
82
|
+
|
83
|
+
def request_new_access_token
|
84
|
+
headers = {
|
85
|
+
'Ocp-Apim-Subscription-Key' => @subscription_key
|
86
|
+
}
|
87
|
+
|
88
|
+
http = Net::HTTP.new(COGNITIVE_ACCESS_TOKEN_URI.host, COGNITIVE_ACCESS_TOKEN_URI.port)
|
89
|
+
http.use_ssl = true
|
90
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @skip_ssl_verify
|
91
|
+
|
92
|
+
response = http.post(COGNITIVE_ACCESS_TOKEN_URI.path, '', headers)
|
93
|
+
if response.code != '200'
|
94
|
+
raise Exception.new('Invalid credentials')
|
95
|
+
else
|
96
|
+
@access_token_expiration_time = Time.now + 480
|
97
|
+
response.body
|
98
|
+
end
|
109
99
|
end
|
110
100
|
end
|
111
101
|
|
112
|
-
def
|
113
|
-
|
102
|
+
def initialize(subscription_key, options = {})
|
103
|
+
skip_ssl_verify = options.fetch(:skip_ssl_verify, false)
|
104
|
+
read_timeout = options.fetch(:read_timeout, 60)
|
105
|
+
open_timeout = options.fetch(:open_timeout, 60)
|
106
|
+
@api_client = ApiClient.new(subscription_key, skip_ssl_verify, read_timeout, open_timeout)
|
114
107
|
end
|
115
108
|
|
116
|
-
def
|
117
|
-
|
118
|
-
|
109
|
+
def translate(text, params)
|
110
|
+
translate_array([text], params).first
|
111
|
+
end
|
112
|
+
|
113
|
+
def translate_array(texts, params = {})
|
114
|
+
translations = translation_request(texts, params)
|
115
|
+
translations.map do |translation|
|
116
|
+
translation['text'] if translation.is_a?(Hash)
|
119
117
|
end
|
118
|
+
end
|
120
119
|
|
121
|
-
|
120
|
+
def translate_array2(texts, params = {})
|
121
|
+
translations = translation_request(texts, params.merge('includeAlignment' => true))
|
122
|
+
translations.map do |translation|
|
123
|
+
[translation['text'], translation['alignment']['proj']] if translation.is_a?(Hash)
|
124
|
+
end
|
122
125
|
end
|
123
126
|
|
124
|
-
|
127
|
+
def detect(text)
|
128
|
+
data = [{ 'Text' => text }].to_json
|
125
129
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
# Returns existing @access_token if we don't need a new token yet,
|
130
|
-
# or returns the one just obtained.
|
131
|
-
def get_access_token
|
132
|
-
headers = {
|
133
|
-
'Ocp-Apim-Subscription-Key' => @subscription_key
|
134
|
-
}
|
135
|
-
|
136
|
-
http = Net::HTTP.new(COGNITIVE_ACCESS_TOKEN_URI.host, COGNITIVE_ACCESS_TOKEN_URI.port)
|
137
|
-
http.use_ssl = true
|
138
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @skip_ssl_verify
|
139
|
-
|
140
|
-
response = http.post(COGNITIVE_ACCESS_TOKEN_URI.path, "", headers)
|
141
|
-
|
142
|
-
@access_token = {
|
143
|
-
'access_token' => response.body,
|
144
|
-
'expires_at' => Time.now + 480
|
145
|
-
}
|
130
|
+
response_json = api_client.post('/detect', data: data)
|
131
|
+
best_detection = response_json.sort_by { |detection| -detection['score'] }.first
|
132
|
+
best_detection['language'].to_sym
|
146
133
|
end
|
147
134
|
|
148
|
-
|
149
|
-
|
150
|
-
soap_client.call(action, message: build_soap_message(params), &block).body[:"#{action}_response"][:"#{action}_result"]
|
151
|
-
rescue Savon::SOAPFault => e
|
152
|
-
raise Exception, e.message
|
135
|
+
def speak(text, params = {})
|
136
|
+
raise Exception.new('Not supported since 3.0.0')
|
153
137
|
end
|
154
138
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
139
|
+
def supported_language_codes
|
140
|
+
response_json = api_client.get('/languages',
|
141
|
+
params: { scope: 'translation' },
|
142
|
+
authorization: false)
|
143
|
+
response_json['translation'].keys
|
160
144
|
end
|
161
145
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
@client = Savon.client(
|
172
|
-
wsdl: WSDL_URI,
|
173
|
-
namespace: NAMESPACE_URI,
|
174
|
-
namespace_identifier: :v2,
|
175
|
-
namespaces: {
|
176
|
-
'xmlns:arr' => 'http://schemas.microsoft.com/2003/10/Serialization/Arrays'
|
177
|
-
},
|
178
|
-
headers: {'Authorization' => "Bearer #{get_access_token['access_token']}"},
|
179
|
-
)
|
146
|
+
def language_names(codes, locale = 'en')
|
147
|
+
response_json = api_client.get('/languages',
|
148
|
+
params: { scope: 'translation' },
|
149
|
+
headers: { 'Accept-Language' => locale },
|
150
|
+
authorization: false)
|
151
|
+
codes.map do |code|
|
152
|
+
response = response_json['translation'][code.to_s]
|
153
|
+
response['name'] unless response.nil?
|
154
|
+
end
|
180
155
|
end
|
181
156
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
157
|
+
private
|
158
|
+
|
159
|
+
attr_reader :api_client
|
160
|
+
|
161
|
+
def translation_request(texts, params)
|
162
|
+
raise Exception.new('Must provide :to.') if params[:to].nil?
|
163
|
+
|
164
|
+
data = texts.map { |text| { 'Text' => text } }.to_json
|
165
|
+
response_json = api_client.post('/translate', params: params, data: data)
|
166
|
+
response_json.map do |translation|
|
167
|
+
# There should be just one translation, but who knows...
|
168
|
+
translation['translations'].find { |result| result['to'] == params[:to].to_s }
|
190
169
|
end
|
191
170
|
end
|
192
171
|
end
|
metadata
CHANGED
@@ -1,57 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bing_translator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 6.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ricky Elrod
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-03-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: nokogiri
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.8.1
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 1.8.1
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: json
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
30
16
|
requirements:
|
31
|
-
- - "
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 1.8.0
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 1.8.0
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: savon
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
17
|
+
- - ">="
|
46
18
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
19
|
+
version: '0'
|
48
20
|
type: :runtime
|
49
21
|
prerelease: false
|
50
22
|
version_requirements: !ruby/object:Gem::Requirement
|
51
23
|
requirements:
|
52
|
-
- - "
|
24
|
+
- - ">="
|
53
25
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
26
|
+
version: '0'
|
55
27
|
description: Translate strings using the Bing HTTP API. Requires that you have a Client
|
56
28
|
ID and Secret. See README.md for information.
|
57
29
|
email: ricky@elrod.me
|
@@ -80,7 +52,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
80
52
|
version: '0'
|
81
53
|
requirements: []
|
82
54
|
rubyforge_project:
|
83
|
-
rubygems_version: 2.6.
|
55
|
+
rubygems_version: 2.7.6.2
|
84
56
|
signing_key:
|
85
57
|
specification_version: 4
|
86
58
|
summary: Translate using the Bing HTTP API
|