Add user content translations with configurable backends (#19218)

This commit is contained in:
Eugen Rochko 2022-09-23 23:00:12 +02:00 committed by GitHub
parent d2f7e30a28
commit 0d6b878808
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 306 additions and 11 deletions

View file

@ -0,0 +1,23 @@
# frozen_string_literal: true
class TranslationService
class Error < StandardError; end
class NotConfiguredError < Error; end
class TooManyRequestsError < Error; end
class QuotaExceededError < Error; end
class UnexpectedResponseError < Error; end
def self.configured
if ENV['DEEPL_API_KEY'].present?
TranslationService::DeepL.new(ENV.fetch('DEEPL_PLAN', 'free'), ENV['DEEPL_API_KEY'])
elsif ENV['LIBRE_TRANSLATE_ENDPOINT'].present?
TranslationService::LibreTranslate.new(ENV['LIBRE_TRANSLATE_ENDPOINT'], ENV['LIBRE_TRANSLATE_API_KEY'])
else
raise NotConfiguredError
end
end
def translate(_text, _source_language, _target_language)
raise NotImplementedError
end
end

View file

@ -0,0 +1,53 @@
# frozen_string_literal: true
class TranslationService::DeepL < TranslationService
include JsonLdHelper
def initialize(plan, api_key)
super()
@plan = plan
@api_key = api_key
end
def translate(text, source_language, target_language)
request(text, source_language, target_language).perform do |res|
case res.code
when 429
raise TooManyRequestsError
when 456
raise QuotaExceededError
when 200...300
transform_response(res.body_with_limit)
else
raise UnexpectedResponseError
end
end
end
private
def request(text, source_language, target_language)
req = Request.new(:post, endpoint_url, form: { text: text, source_lang: source_language.upcase, target_lang: target_language, tag_handling: 'html' })
req.add_headers('Authorization': "DeepL-Auth-Key #{@api_key}")
req
end
def endpoint_url
if @plan == 'free'
'https://api-free.deepl.com/v2/translate'
else
'https://api.deepl.com/v2/translate'
end
end
def transform_response(str)
json = Oj.load(str, mode: :strict)
raise UnexpectedResponseError unless json.is_a?(Hash)
Translation.new(text: json.dig('translations', 0, 'text'), detected_source_language: json.dig('translations', 0, 'detected_source_language')&.downcase)
rescue Oj::ParseError
raise UnexpectedResponseError
end
end

View file

@ -0,0 +1,43 @@
# frozen_string_literal: true
class TranslationService::LibreTranslate < TranslationService
def initialize(base_url, api_key)
super()
@base_url = base_url
@api_key = api_key
end
def translate(text, source_language, target_language)
request(text, source_language, target_language).perform do |res|
case res.code
when 429
raise TooManyRequestsError
when 403
raise QuotaExceededError
when 200...300
transform_response(res.body_with_limit, source_language)
else
raise UnexpectedResponseError
end
end
end
private
def request(text, source_language, target_language)
req = Request.new(:post, "#{@base_url}/translate", body: Oj.dump(q: text, source: source_language, target: target_language, format: 'html', api_key: @api_key))
req.add_headers('Content-Type': 'application/json')
req
end
def transform_response(str, source_language)
json = Oj.load(str, mode: :strict)
raise UnexpectedResponseError unless json.is_a?(Hash)
Translation.new(text: json['translatedText'], detected_source_language: source_language)
rescue Oj::ParseError
raise UnexpectedResponseError
end
end

View file

@ -0,0 +1,5 @@
# frozen_string_literal: true
class TranslationService::Translation < ActiveModelSerializers::Model
attributes :text, :detected_source_language
end