bing_translator 5.2.0 → 6.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/bing_translator.rb +165 -192
  3. metadata +7 -102
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 30d94b0e0875f32aeadcdaba47c6c9744dab2628
4
- data.tar.gz: 60ca01d9b5ceb5eed85b52ed8d2bd0512bdfb149
3
+ metadata.gz: 9106de975569bef3303a4940318a78b536beaf10
4
+ data.tar.gz: e40c556e6f474d185b74f6753635a448720600f6
5
5
  SHA512:
6
- metadata.gz: a6b42ea3aae1e84110313d12c37499c2639399ad759633afeae288cac2823c3d2b8cb41c22212e245e628a2340b2441d1aba19a92f20cb925f0677be3fbcd7ee
7
- data.tar.gz: 2a3735bf3b80e69c22c77d47f4e760f7613f46a2be04b50e25f97651f496608c144b1e34c805e71b6757a6b89387b4d759235aa2f59c694eacfe968de3c5d968
6
+ metadata.gz: 8b84d0f6c1b4a405ee631221a961f8e5462c7de1b0ca3eb26b0f0b2dc105e3b0e8ac19c085c8de320191a66895af313fc1b1f718f81b13505a6b31ab8a20108c
7
+ data.tar.gz: c22b944db83f5b437b9fa56e8df83da1bd69f7b81f9ef65dedb51a66d82c79714fba29701da46a6bd1b027e56ff69e54f3e5c21bddfccacddfadd3daa252e3a4
@@ -1,192 +1,165 @@
1
- #!/usr/bin/env ruby
2
- # encoding: utf-8
3
- # (c) 2011-present. Ricky Elrod <ricky@elrod.me>
4
- # Released under the MIT license.
5
- require 'rubygems'
6
- require 'cgi'
7
- require 'uri'
8
- require 'net/http'
9
- require 'net/https'
10
- require 'nokogiri'
11
- require 'json'
12
- require 'savon'
13
-
14
- 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
- class Exception < StandardError; end
20
-
21
- def initialize(subscription_key, options = {})
22
- @skip_ssl_verify = options.fetch(:skip_ssl_verify, false)
23
- @subscription_key = subscription_key
24
- end
25
-
26
- def translate(text, params = {})
27
- raise "Must provide :to." if params[:to].nil?
28
-
29
- # Important notice: param order makes sense in SOAP. Do not reorder or delete!
30
- params = {
31
- 'text' => text.to_s,
32
- 'from' => params[:from].to_s,
33
- 'to' => params[:to].to_s,
34
- 'category' => 'general',
35
- 'contentType' => params[:content_type] || 'text/plain'
36
- }
37
-
38
- result(:translate, params)
39
- end
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
55
-
56
- def translate_array2(texts, params = {})
57
- raise "Must provide :to." if params[:to].nil?
58
-
59
- # Important notice: param order makes sense in SOAP. Do not reorder or delete!
60
- params = {
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
- }
67
-
68
- array_wrap(result(:translate_array2, params)[:translate_array2_response]).map{|r| [r[:translated_text], r[:alignment]]}
69
- end
70
-
71
- def detect(text)
72
- params = {
73
- 'text' => text.to_s,
74
- 'language' => '',
75
- }
76
-
77
- if lang = result(:detect, params)
78
- lang.to_sym
79
- end
80
- end
81
-
82
- # format: 'audio/wav' [default] or 'audio/mp3'
83
- # language: valid translator language code
84
- # options: 'MinSize' [default] or 'MaxQuality'
85
- def speak(text, params = {})
86
- raise "Must provide :language" if params[:language].nil?
87
-
88
- params = {
89
- 'text' => text.to_s,
90
- 'language' => params[:language].to_s,
91
- 'format' => params[:format] || 'audio/wav',
92
- 'options' => params[:options] || 'MinSize',
93
- }
94
-
95
- uri = URI.parse(result(:speak, params))
96
-
97
- http = Net::HTTP.new(uri.host, uri.port)
98
- if uri.scheme == "https"
99
- http.use_ssl = true
100
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @skip_ssl_verify
101
- end
102
- results = http.get(uri.to_s, {'Authorization' => "Bearer #{get_access_token['access_token']}"})
103
-
104
- if results.response.code.to_i == 200
105
- results.body
106
- else
107
- html = Nokogiri::HTML(results.body)
108
- raise Exception, html.xpath("//text()").remove.map(&:to_s).join(' ')
109
- end
110
- end
111
-
112
- def supported_language_codes
113
- result(:get_languages_for_translate)[:string]
114
- end
115
-
116
- def language_names(codes, locale = 'en')
117
- response = result(:get_language_names, locale: locale, languageCodes: {'a:string' => codes}) do
118
- attributes 'xmlns:a' => 'http://schemas.microsoft.com/2003/10/Serialization/Arrays'
119
- end
120
-
121
- response[:string]
122
- end
123
-
124
- private
125
-
126
- # Get a new access token and set it internally as @access_token
127
- #
128
- # @return {hash}
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
- }
146
- end
147
-
148
- # Performs actual request to Bing Translator SOAP API
149
- def result(action, params = {}, &block)
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
153
- end
154
-
155
- # Specify SOAP namespace in tag names (see https://github.com/savonrb/savon/issues/340 )
156
- #
157
- # @return [Hash]
158
- def build_soap_message(params)
159
- Hash[params.map{|k,v| ["v2:#{k}", v]}]
160
- end
161
-
162
- # Private: Constructs SOAP client
163
- #
164
- # Construct and store new client when called first time.
165
- # Return stored client while access token is fresh.
166
- # Construct and store new client when token have been expired.
167
- def soap_client
168
- return @client if @client and @access_token and @access_token['expires_at'] and
169
- Time.now < @access_token['expires_at']
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
- )
180
- end
181
-
182
- # Private: Array#wrap based on ActiveSupport extension
183
- def array_wrap(object)
184
- if object.nil?
185
- []
186
- elsif object.respond_to?(:to_ary)
187
- object.to_ary || [object]
188
- else
189
- [object]
190
- end
191
- end
192
- end
1
+ #!/usr/bin/env ruby
2
+
3
+ # (c) 2011-present. Ricky Elrod <ricky@elrod.me>
4
+ # Released under the MIT license.
5
+ require 'rubygems'
6
+ require 'cgi'
7
+ require 'uri'
8
+ require 'net/http'
9
+ require 'net/https'
10
+ require 'json'
11
+
12
+ class BingTranslator
13
+ class Exception < StandardError; end
14
+
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
19
+
20
+ def initialize(subscription_key, skip_ssl_verify)
21
+ @subscription_key = subscription_key
22
+ @skip_ssl_verify = skip_ssl_verify
23
+ end
24
+
25
+ def get(path, params: {}, headers: {}, authorization: false)
26
+ uri = request_uri(path, params)
27
+ request = Net::HTTP::Get.new(uri.request_uri, default_headers(authorization).merge(headers))
28
+
29
+ json_response(uri, request)
30
+ end
31
+
32
+ def post(path, params: {}, headers: {}, data: {}, authorization: true)
33
+ uri = request_uri(path, params)
34
+
35
+ request = Net::HTTP::Post.new(uri.request_uri, default_headers(authorization).merge(headers))
36
+ request.body = data
37
+
38
+ json_response(uri, request)
39
+ end
40
+
41
+ private
42
+
43
+ def default_headers(authorization = true)
44
+ headers = { 'Content-Type' => 'application/json' }
45
+ headers['Authorization'] = "Bearer #{access_token}" if authorization
46
+ headers
47
+ end
48
+
49
+ def json_response(uri, request)
50
+ http = http_client(uri)
51
+
52
+ response = http.request(request)
53
+
54
+ raise Exception.new("Unsuccessful API call: Code: #{response.code}") unless response.code == '200'
55
+ JSON.parse(response.body)
56
+ end
57
+
58
+ def http_client(uri)
59
+ http = Net::HTTP.new(uri.host, 443)
60
+ http.use_ssl = true
61
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @skip_ssl_verify
62
+ http
63
+ end
64
+
65
+ def request_uri(path, params)
66
+ encoded_params = URI.encode_www_form(params.merge('api-version' => '3.0'))
67
+ uri = URI.parse("#{API_HOST}#{path}")
68
+ uri.query = encoded_params
69
+ uri
70
+ end
71
+
72
+ def access_token
73
+ if @access_token.nil? || Time.now < @access_token_expiration_time
74
+ @access_token = request_new_access_token
75
+ end
76
+ @access_token
77
+ end
78
+
79
+ def request_new_access_token
80
+ headers = {
81
+ 'Ocp-Apim-Subscription-Key' => @subscription_key
82
+ }
83
+
84
+ http = Net::HTTP.new(COGNITIVE_ACCESS_TOKEN_URI.host, COGNITIVE_ACCESS_TOKEN_URI.port)
85
+ http.use_ssl = true
86
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @skip_ssl_verify
87
+
88
+ response = http.post(COGNITIVE_ACCESS_TOKEN_URI.path, '', headers)
89
+ if response.code != '200'
90
+ raise Exception.new('Invalid credentials')
91
+ else
92
+ @access_token_expiration_time = Time.now + 480
93
+ response.body
94
+ end
95
+ end
96
+ end
97
+
98
+ def initialize(subscription_key, options = {})
99
+ skip_ssl_verify = options.fetch(:skip_ssl_verify, false)
100
+ @api_client = ApiClient.new(subscription_key, skip_ssl_verify)
101
+ end
102
+
103
+ def translate(text, params)
104
+ translate_array([text], params).first
105
+ end
106
+
107
+ def translate_array(texts, params = {})
108
+ translations = translation_request(texts, params)
109
+ translations.map do |translation|
110
+ translation['text'] if translation.is_a?(Hash)
111
+ end
112
+ end
113
+
114
+ def translate_array2(texts, params = {})
115
+ translations = translation_request(texts, params.merge('includeAlignment' => true))
116
+ translations.map do |translation|
117
+ [translation['text'], translation['alignment']['proj']] if translation.is_a?(Hash)
118
+ end
119
+ end
120
+
121
+ def detect(text)
122
+ data = [{ 'Text' => text }].to_json
123
+
124
+ response_json = api_client.post('/detect', data: data)
125
+ best_detection = response_json.sort_by { |detection| -detection['score'] }.first
126
+ best_detection['language'].to_sym
127
+ end
128
+
129
+ def speak(text, params = {})
130
+ raise Exception.new('Not supported since 3.0.0')
131
+ end
132
+
133
+ def supported_language_codes
134
+ response_json = api_client.get('/languages',
135
+ params: { scope: 'translation' },
136
+ authorization: false)
137
+ response_json['translation'].keys
138
+ end
139
+
140
+ def language_names(codes, locale = 'en')
141
+ response_json = api_client.get('/languages',
142
+ params: { scope: 'translation' },
143
+ headers: { 'Accept-Language' => locale },
144
+ authorization: false)
145
+ codes.map do |code|
146
+ response = response_json['translation'][code.to_s]
147
+ response['name'] unless response.nil?
148
+ end
149
+ end
150
+
151
+ private
152
+
153
+ attr_reader :api_client
154
+
155
+ def translation_request(texts, params)
156
+ raise Exception.new('Must provide :to.') if params[:to].nil?
157
+
158
+ data = texts.map { |text| { 'Text' => text } }.to_json
159
+ response_json = api_client.post('/translate', params: params, data: data)
160
+ response_json.map do |translation|
161
+ # There should be just one translation, but who knows...
162
+ translation['translations'].find { |result| result['to'] == params[:to].to_s }
163
+ end
164
+ end
165
+ end
metadata CHANGED
@@ -1,119 +1,23 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bing_translator
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 6.0.0.beta.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ricky Elrod
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-29 00:00:00.000000000 Z
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.6.0
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: 1.9.0
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: 1.6.0
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: 1.9.0
33
13
  - !ruby/object:Gem::Dependency
34
14
  name: json
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: 1.8.0
40
- type: :runtime
41
- prerelease: false
42
- version_requirements: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: 1.8.0
47
- - !ruby/object:Gem::Dependency
48
- name: savon
49
- requirement: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: 2.10.0
54
- type: :runtime
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: 2.10.0
61
- - !ruby/object:Gem::Dependency
62
- name: rake
63
- requirement: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: '10.0'
68
- type: :development
69
- prerelease: false
70
- version_requirements: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: '10.0'
75
- - !ruby/object:Gem::Dependency
76
- name: rspec
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - "~>"
80
- - !ruby/object:Gem::Version
81
- version: '3.0'
82
- type: :development
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - "~>"
87
- - !ruby/object:Gem::Version
88
- version: '3.0'
89
- - !ruby/object:Gem::Dependency
90
- name: rspec-html-matchers
91
- requirement: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - ">="
94
- - !ruby/object:Gem::Version
95
- version: 0.9.1
96
- - - "<"
97
- - !ruby/object:Gem::Version
98
- version: 0.10.0
99
- type: :development
100
- prerelease: false
101
- version_requirements: !ruby/object:Gem::Requirement
102
- requirements:
103
- - - ">="
104
- - !ruby/object:Gem::Version
105
- version: 0.9.1
106
- - - "<"
107
- - !ruby/object:Gem::Version
108
- version: 0.10.0
109
- - !ruby/object:Gem::Dependency
110
- name: dotenv
111
15
  requirement: !ruby/object:Gem::Requirement
112
16
  requirements:
113
17
  - - ">="
114
18
  - !ruby/object:Gem::Version
115
19
  version: '0'
116
- type: :development
20
+ type: :runtime
117
21
  prerelease: false
118
22
  version_requirements: !ruby/object:Gem::Requirement
119
23
  requirements:
@@ -143,13 +47,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
143
47
  version: '0'
144
48
  required_rubygems_version: !ruby/object:Gem::Requirement
145
49
  requirements:
146
- - - ">="
50
+ - - ">"
147
51
  - !ruby/object:Gem::Version
148
- version: '0'
52
+ version: 1.3.1
149
53
  requirements: []
150
54
  rubyforge_project:
151
- rubygems_version: 2.6.13
55
+ rubygems_version: 2.4.5.1
152
56
  signing_key:
153
57
  specification_version: 4
154
58
  summary: Translate using the Bing HTTP API
155
59
  test_files: []
60
+ has_rdoc: