Class: TranslationDiff::Providers::Azure

Inherits:
HTTPProvider
  • Object
show all
Defined in:
lib/translation_diff/providers/azure.rb

Overview

Azure AI Translator, REST v3.0: cheapest per character, most generous per request (1,000 strings/50,000 chars).

Constant Summary collapse

HOST =
"https://api.cognitive.microsofttranslator.com".freeze
API_VERSION =
"3.0".freeze
DEFAULT_TEXT_TYPE =

Azure spells HTML handling textType, and under it honours class=notranslate like DeepL and Google do.

"html".freeze

Constants inherited from HTTPProvider

HTTPProvider::RETRY_STATUSES, HTTPProvider::TRANSPORT_FAILURES

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from HTTPProvider

#connection

Class Method Details

.capabilities ⇒ Object



9
10
11
12
13
14
# File 'lib/translation_diff/providers/azure.rb', line 9

def self.capabilities
  TranslationDiff::Capabilities.new(
    max_request_size: 50_000, max_batch_size: 1_000, max_text_size: 50_000,
    html: :textType, notranslate: true, detects_language: true, reports_billing: true
  )
end

.configuration_options ⇒ Object



16
# File 'lib/translation_diff/providers/azure.rb', line 16

def self.configuration_options = %i[azure_api_key azure_region azure_api_base]

.configuration_requirements ⇒ Object



17
# File 'lib/translation_diff/providers/azure.rb', line 17

def self.configuration_requirements = %i[azure_api_key]

Instance Method Details

#api_base ⇒ Object



19
# File 'lib/translation_diff/providers/azure.rb', line 19

def api_base = config.azure_api_base || HOST

#detect(text) ⇒ Object



51
52
53
54
# File 'lib/translation_diff/providers/azure.rb', line 51

def detect(text)
  response = post("detect?api-version=#{API_VERSION}", [{ Text: text }])
  response.body.dig(0, "language")&.downcase
end

#headers ⇒ Object

A multi-service resource needs the region header; a single-service one rejects nothing without it.



22
23
24
25
# File 'lib/translation_diff/providers/azure.rb', line 22

def headers
  { "Ocp-Apim-Subscription-Key" => config.azure_api_key.to_s }
    .tap { |h| h["Ocp-Apim-Subscription-Region"] = config.azure_region if config.azure_region }
end

#languages ⇒ Object

Azure's own docs mark this endpoint public, but this provider still requires a key to be built at all.



57
58
59
60
61
# File 'lib/translation_diff/providers/azure.rb', line 57

def languages
  codes = Array(get("languages?api-version=#{API_VERSION}&scope=translation").body["translation"]&.keys)

  { source: codes, target: codes }
end

#languages_endpoint ⇒ Object



63
# File 'lib/translation_diff/providers/azure.rb', line 63

def languages_endpoint = "#{api_base}/languages?api-version=#{API_VERSION}&scope=translation"

#parse_translate_response(body, headers, request) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/translation_diff/providers/azure.rb', line 37

def parse_translate_response(body, headers, request)
  results = Array(body)

  TranslationDiff::Translation::Response.build(
    request: request,
    texts: results.map { |result| result.dig("translations", 0, "text") },
    detected_source: results.first&.dig("detectedLanguage", "language")&.downcase,
    usage: TranslationDiff::Translation::Usage.new(
      characters: request.texts.sum(&:size),
      billed_characters: headers["x-metered-usage"]&.to_i
    )
  )
end

#render_translate_payload(request) ⇒ Object



35
# File 'lib/translation_diff/providers/azure.rb', line 35

def render_translate_payload(request) = request.texts.map { |text| { Text: text } }

#translate(request) ⇒ Object



30
31
32
33
# File 'lib/translation_diff/providers/azure.rb', line 30

def translate(request)
  response = post(url_for(request), render_translate_payload(request))
  parse_translate_response(response.body, response.headers, request)
end

#translate_url ⇒ Object

Azure takes the language pair in the query string, so the URL is built per request, not a constant.



28
# File 'lib/translation_diff/providers/azure.rb', line 28

def translate_url = "translate"