deepl.rb 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. # frozen_string_literal: true
  2. class TranslationService::DeepL < TranslationService
  3. include JsonLdHelper
  4. def initialize(plan, api_key)
  5. super()
  6. @plan = plan
  7. @api_key = api_key
  8. end
  9. def translate(texts, source_language, target_language)
  10. form = { text: texts, source_lang: source_language&.upcase, target_lang: target_language, tag_handling: 'html' }
  11. request(:post, '/v2/translate', form: form) do |res|
  12. transform_response(res.body_with_limit)
  13. end
  14. end
  15. def languages
  16. source_languages = [nil] + fetch_languages('source')
  17. # In DeepL, EN and PT are deprecated in favor of EN-GB/EN-US and PT-BR/PT-PT, so
  18. # they are supported but not returned by the API.
  19. target_languages = %w(en pt) + fetch_languages('target')
  20. source_languages.index_with { |language| target_languages.without(nil, language) }
  21. end
  22. private
  23. def fetch_languages(type)
  24. request(:get, "/v2/languages?type=#{type}") do |res|
  25. Oj.load(res.body_with_limit).map { |language| normalize_language(language['language']) }
  26. end
  27. end
  28. def normalize_language(language)
  29. subtags = language.split(/[_-]/)
  30. subtags[0].downcase!
  31. subtags[1]&.upcase!
  32. subtags.join('-')
  33. end
  34. def request(verb, path, **options)
  35. req = Request.new(verb, "#{base_url}#{path}", **options)
  36. req.add_headers(Authorization: "DeepL-Auth-Key #{@api_key}")
  37. req.perform do |res|
  38. case res.code
  39. when 429
  40. raise TooManyRequestsError
  41. when 456
  42. raise QuotaExceededError
  43. when 200...300
  44. yield res
  45. else
  46. raise UnexpectedResponseError
  47. end
  48. end
  49. end
  50. def base_url
  51. if @plan == 'free'
  52. 'https://api-free.deepl.com'
  53. else
  54. 'https://api.deepl.com'
  55. end
  56. end
  57. def transform_response(json)
  58. data = Oj.load(json, mode: :strict)
  59. raise UnexpectedResponseError unless data.is_a?(Hash)
  60. data['translations'].map do |translation|
  61. Translation.new(
  62. text: translation['text'],
  63. detected_source_language: translation['detected_source_language']&.downcase,
  64. provider: 'DeepL.com'
  65. )
  66. end
  67. rescue Oj::ParseError
  68. raise UnexpectedResponseError
  69. end
  70. end