class BingTranslator

Constants

ACCESS_TOKEN_URI
DATASETS_URI
NAMESPACE_URI
WSDL_URI

Public Class Methods

new(client_id, client_secret, skip_ssl_verify = false, account_key = nil) click to toggle source
# File lib/bing_translator.rb, line 23
def initialize(client_id, client_secret, skip_ssl_verify = false, account_key = nil)
  @client_id = client_id
  @client_secret = client_secret
  @account_key = account_key
  @skip_ssl_verify = skip_ssl_verify

  @access_token_uri = URI.parse ACCESS_TOKEN_URI
  @datasets_uri = URI.parse DATASETS_URI
end

Public Instance Methods

balance() click to toggle source
# File lib/bing_translator.rb, line 114
def balance
  datasets["d"]["results"].each do |result|
    return result["ResourceBalance"] if result["ProviderName"] == "Microsoft Translator"
  end
end
detect(text) click to toggle source
# File lib/bing_translator.rb, line 63
def detect(text)
  params = {
    'text'     => text.to_s,
    'language' => '',
  }

  result(:detect, params).to_sym
end
get_access_token() click to toggle source

Get a new access token and set it internally as @access_token

Microsoft changed up how you get access to the Translate API. This gets a new token if it’s required. We call this internally before any request we make to the Translate API.

@return {hash} Returns existing @access_token if we don’t need a new token yet, or returns the one just obtained.

# File lib/bing_translator.rb, line 129
def get_access_token
  return @access_token if @access_token and
    Time.now < @access_token['expires_at']

  params = {
    'client_id' => CGI.escape(@client_id),
    'client_secret' => CGI.escape(@client_secret),
    'scope' => CGI.escape('http://api.microsofttranslator.com'),
    'grant_type' => 'client_credentials'
  }

  http = Net::HTTP.new(@access_token_uri.host, @access_token_uri.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @skip_ssl_verify

  response = http.post(@access_token_uri.path, prepare_param_string(params))
  @access_token = JSON.parse(response.body)
  raise AuthenticationException, @access_token['error'] if @access_token["error"]
  @access_token['expires_at'] = Time.now + @access_token['expires_in'].to_i
  @access_token
end
language_names(codes, locale = 'en') click to toggle source
# File lib/bing_translator.rb, line 106
def language_names(codes, locale = 'en')
  response = result(:get_language_names, locale: locale, languageCodes: {'a:string' => codes}) do
    attributes 'xmlns:a' => 'http://schemas.microsoft.com/2003/10/Serialization/Arrays'
  end

  response[:string]
end
speak(text, params = {}) click to toggle source

format: ‘audio/wav’ [default] or ‘audio/mp3’ language: valid translator language code options: ‘MinSize’ [default] or ‘MaxQuality’

# File lib/bing_translator.rb, line 75
def speak(text, params = {})
  raise "Must provide :language" if params[:language].nil?

  params = {
    'text'     => text.to_s,
    'language' => params[:language].to_s,
    'format'   => params[:format] || 'audio/wav',
    'options'  => params[:options] || 'MinSize',
  }

  uri = URI.parse(result(:speak, params))

  http = Net::HTTP.new(uri.host, uri.port)
  if uri.scheme == "https"
    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @skip_ssl_verify
  end
  results = http.get(uri.to_s, {'Authorization' => "Bearer #{get_access_token['access_token']}"})

  if results.response.code.to_i == 200
    results.body
  else
    html = Nokogiri::HTML(results.body)
    raise Exception, html.xpath("//text()").remove.map(&:to_s).join(' ')
  end
end
supported_language_codes() click to toggle source
# File lib/bing_translator.rb, line 102
def supported_language_codes
  result(:get_languages_for_translate)[:string]
end
translate(text, params = {}) click to toggle source
# File lib/bing_translator.rb, line 33
def translate(text, params = {})
  raise "Must provide :to." if params[:to].nil?

  # Important notice: param order makes sense in SOAP. Do not reorder or delete!
  params = {
    'text'        => text.to_s,
    'from'        => params[:from].to_s,
    'to'          => params[:to].to_s,
    'category'    => 'general',
    'contentType' => params[:content_type] || 'text/plain'
  }

  result(:translate, params)
end
translate_array(texts, params = {}) click to toggle source
# File lib/bing_translator.rb, line 48
def translate_array(texts, params = {})
  raise "Must provide :to." if params[:to].nil?

  # Important notice: param order makes sense in SOAP. Do not reorder or delete!
  params = {
    'texts'       => { 'arr:string' => texts },
    'from'        => params[:from].to_s,
    'to'          => params[:to].to_s,
    'category'    => 'general',
    'contentType' => params[:content_type] || 'text/plain'
  }

  array_wrap(result(:translate_array, params)[:translate_array_response]).map{|r| r[:translated_text]}
end

Private Instance Methods

array_wrap(object) click to toggle source

Private: Array#wrap based on ActiveSupport extension

# File lib/bing_translator.rb, line 204
def array_wrap(object)
  if object.nil?
    []
  elsif object.respond_to?(:to_ary)
    object.to_ary || [object]
  else
    [object]
  end
end
datasets() click to toggle source
# File lib/bing_translator.rb, line 152
def datasets
  raise AuthenticationException, "Must provide account key" if @account_key.nil?

  http = Net::HTTP.new(@datasets_uri.host, @datasets_uri.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  request = Net::HTTP::Get.new(@datasets_uri.request_uri)
  request.basic_auth("", @account_key)
  response = http.request(request)

  JSON.parse response.body
end
prepare_param_string(params) click to toggle source
# File lib/bing_translator.rb, line 165
def prepare_param_string(params)
  params.map { |key, value| "#{key}=#{value}" }.join '&'
end
result(action, params = {}, &block) click to toggle source

Public: performs actual request to Bing Translator SOAP API

# File lib/bing_translator.rb, line 170
def result(action, params = {}, &block)
  # Specify SOAP namespace in tag names (see https://github.com/savonrb/savon/issues/340 )
  params = Hash[params.map{|k,v| ["v2:#{k}", v]}]
  begin
    soap_client.call(action, message: params, &block).body[:"#{action}_response"][:"#{action}_result"]
  rescue AuthenticationException
    raise
  rescue StandardError => e
    # Keep old behaviour: raise only internal Exception class
    raise Exception, e.message
  end
end
soap_client() click to toggle source

Private: Constructs SOAP client

Construct and store new client when called first time. Return stored client while access token is fresh. Construct and store new client when token have been expired.

# File lib/bing_translator.rb, line 188
def soap_client
  return @client if @client and @access_token and
    Time.now < @access_token['expires_at']

  @client = Savon.client(
    wsdl: WSDL_URI,
    namespace: NAMESPACE_URI,
    namespace_identifier: :v2,
    namespaces: {
      'xmlns:arr' =>  'http://schemas.microsoft.com/2003/10/Serialization/Arrays'
    },
    headers: {'Authorization' => "Bearer #{get_access_token['access_token']}"},
  )
end