deepl-rb 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +38 -1
  3. data/CHANGELOG.md +38 -24
  4. data/README.md +68 -0
  5. data/Rakefile +1 -0
  6. data/VERSION +1 -1
  7. data/deepl-rb.gemspec +15 -3
  8. data/lib/deepl/constants/base_constant.rb +18 -0
  9. data/lib/deepl/constants/formality.rb +16 -0
  10. data/lib/deepl/constants/model_type.rb +14 -0
  11. data/lib/deepl/constants/split_sentences.rb +14 -0
  12. data/lib/deepl/constants/tag_handling.rb +13 -0
  13. data/lib/deepl/constants/tone.rb +20 -0
  14. data/lib/deepl/constants/writing_style.rb +20 -0
  15. data/lib/deepl/exceptions/authorization_failed.rb +1 -1
  16. data/lib/deepl/exceptions/bad_request.rb +1 -1
  17. data/lib/deepl/exceptions/limit_exceeded.rb +1 -1
  18. data/lib/deepl/exceptions/not_found.rb +1 -1
  19. data/lib/deepl/exceptions/quota_exceeded.rb +1 -1
  20. data/lib/deepl/exceptions/request_entity_too_large.rb +1 -1
  21. data/lib/deepl/exceptions/request_error.rb +1 -1
  22. data/lib/deepl/exceptions/server_error.rb +1 -1
  23. data/lib/deepl/requests/base.rb +38 -5
  24. data/lib/deepl/requests/document/upload.rb +21 -11
  25. data/lib/deepl/requests/rephrase.rb +55 -0
  26. data/lib/deepl.rb +15 -0
  27. data/spec/api/deepl_spec.rb +29 -0
  28. data/spec/constants/constants_spec.rb +158 -0
  29. data/spec/fixtures/vcr_cassettes/deepl_rephrase.yml +87 -0
  30. data/spec/fixtures/vcr_cassettes/rephrase_texts.yml +401 -0
  31. data/spec/fixtures/vcr_cassettes/translate_texts.yml +44 -1
  32. data/spec/integration_tests/document_api_spec.rb +22 -0
  33. data/spec/requests/rephrase_spec.rb +172 -0
  34. data/spec/requests/translate_spec.rb +35 -0
  35. data/spec/spec_helper.rb +6 -2
  36. metadata +14 -2
@@ -20,18 +20,10 @@ module DeepL
20
20
  @filename = filename
21
21
  end
22
22
 
23
- def request # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
23
+ def request
24
24
  input_file = File.open(input_file_path, 'rb')
25
- form_data = [
26
- ['file', input_file], ['source_lang', source_lang],
27
- ['target_lang', target_lang]
28
- ]
29
- filename_param = filename || File.basename(input_file_path)
30
- form_data.push(['filename', filename_param]) unless filename_param.nil?
31
- # Manually add options due to multipart/form-data request
32
- SUPPORTED_OPTIONS.each do |option|
33
- form_data.push([option, options[option]]) unless options[option].nil?
34
- end
25
+ form_data = build_base_form_data(input_file)
26
+ apply_extra_body_parameters_to_form(form_data)
35
27
  build_doc_handle(*execute_request_with_retries(post_request_with_file(form_data),
36
28
  [input_file]))
37
29
  end
@@ -49,6 +41,24 @@ module DeepL
49
41
 
50
42
  private
51
43
 
44
+ def build_base_form_data(input_file)
45
+ form_data = [
46
+ ['file', input_file], ['source_lang', source_lang],
47
+ ['target_lang', target_lang]
48
+ ]
49
+ filename_param = filename || File.basename(input_file_path)
50
+ form_data.push(['filename', filename_param]) unless filename_param.nil?
51
+ add_supported_options_to_form(form_data)
52
+ form_data
53
+ end
54
+
55
+ def add_supported_options_to_form(form_data)
56
+ SUPPORTED_OPTIONS.each do |option_name|
57
+ option_value = option(option_name)
58
+ form_data.push([option_name, option_value]) unless option_value.nil?
59
+ end
60
+ end
61
+
52
62
  def build_doc_handle(request, response)
53
63
  parsed_response = JSON.parse(response.body)
54
64
  Resources::DocumentHandle.new(parsed_response['document_id'],
@@ -0,0 +1,55 @@
1
+ # Copyright 2025 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Requests
8
+ class Rephrase < Base
9
+ attr_reader :text, :target_lang, :writing_style, :tone
10
+
11
+ def initialize(api, text, target_lang = nil, writing_style = nil, tone = nil, options = {}) # rubocop:disable Metrics/ParameterLists
12
+ super(api, options)
13
+ @text = text
14
+ @target_lang = target_lang
15
+ @writing_style = writing_style
16
+ @tone = tone
17
+ end
18
+
19
+ def request # rubocop:disable Metrics/AbcSize
20
+ text_arrayified = text.is_a?(Array) ? text : [text]
21
+ payload = { text: text_arrayified }
22
+ payload[:target_lang] = target_lang unless target_lang.nil?
23
+ payload[:writing_style] = writing_style unless writing_style.nil?
24
+ payload[:tone] = tone unless tone.nil?
25
+ build_texts(*execute_request_with_retries(post_request(payload)))
26
+ end
27
+
28
+ def details
29
+ "HTTP Headers: #{headers}\nPayload #{{ text: text, target_lang: target_lang,
30
+ writing_style: writing_style, tone: tone }}"
31
+ end
32
+
33
+ def to_s
34
+ "POST #{uri.request_uri}"
35
+ end
36
+
37
+ private
38
+
39
+ def build_texts(request, response)
40
+ data = JSON.parse(response.body)
41
+
42
+ texts = data['improvements'].map do |improvement|
43
+ Resources::Text.new(improvement['text'], improvement['detected_source_language'], nil,
44
+ request, response)
45
+ end
46
+
47
+ texts.size == 1 ? texts.first : texts
48
+ end
49
+
50
+ def path
51
+ 'write/rephrase'
52
+ end
53
+ end
54
+ end
55
+ end
data/lib/deepl.rb CHANGED
@@ -34,6 +34,7 @@ require 'deepl/requests/glossary/list'
34
34
  require 'deepl/requests/languages'
35
35
  require 'deepl/requests/translate'
36
36
  require 'deepl/requests/usage'
37
+ require 'deepl/requests/rephrase'
37
38
 
38
39
  # -- Responses and resources
39
40
  require 'deepl/resources/base'
@@ -49,6 +50,15 @@ require 'deepl/resources/usage'
49
50
  require 'deepl/utils/exception_builder'
50
51
  require 'deepl/utils/backoff_timer'
51
52
 
53
+ # -- Constants
54
+ require 'deepl/constants/base_constant'
55
+ require 'deepl/constants/formality'
56
+ require 'deepl/constants/model_type'
57
+ require 'deepl/constants/split_sentences'
58
+ require 'deepl/constants/tag_handling'
59
+ require 'deepl/constants/tone'
60
+ require 'deepl/constants/writing_style'
61
+
52
62
  # -- HTTP Utils
53
63
  require 'http_client_options'
54
64
 
@@ -87,6 +97,11 @@ module DeepL
87
97
  GlossaryApi.new(api, options)
88
98
  end
89
99
 
100
+ def rephrase(text, target_lang = nil, writing_style = nil, tone = nil, options = {}) # rubocop:disable Metrics/ParameterLists
101
+ configure if @configuration.nil?
102
+ Requests::Rephrase.new(api, text, target_lang, writing_style, tone, options).request
103
+ end
104
+
90
105
  def usage(options = {})
91
106
  configure if @configuration.nil?
92
107
  Requests::Usage.new(api, options).request
@@ -428,4 +428,33 @@ describe DeepL do
428
428
  end
429
429
  end
430
430
  end
431
+
432
+ describe '#rephrase' do
433
+ let(:text) { 'Ih bin ei beispielsatz.' }
434
+ let(:target_lang) { 'DE' }
435
+ let(:options) { {} }
436
+
437
+ around do |example|
438
+ deepl.configure
439
+ VCR.use_cassette('deepl_rephrase') { example.call }
440
+ end
441
+
442
+ context 'when rephrasing text' do
443
+ it 'creates and call a request object' do
444
+ expect(DeepL::Requests::Rephrase).to receive(:new)
445
+ .with(deepl.api, text, nil, nil, nil, {}).and_call_original
446
+
447
+ rephrased_text = deepl.rephrase(text)
448
+ expect(rephrased_text).to be_a(DeepL::Resources::Text)
449
+ end
450
+
451
+ it 'creates and call a request object when passing options' do
452
+ expect(DeepL::Requests::Rephrase).to receive(:new)
453
+ .with(deepl.api, text, target_lang, nil, nil, options).and_call_original
454
+
455
+ rephrased_text = deepl.rephrase(text, target_lang, nil, nil, options)
456
+ expect(rephrased_text).to be_a(DeepL::Resources::Text)
457
+ end
458
+ end
459
+ end
431
460
  end
@@ -0,0 +1,158 @@
1
+ # Copyright 2025 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ require 'spec_helper'
7
+
8
+ describe 'DeepL::Constants' do
9
+ describe DeepL::Constants::Tone do
10
+ subject(:tone) { described_class }
11
+
12
+ it 'includes all expected tone values' do # rubocop:disable RSpec/ExampleLength
13
+ expect(tone.options).to contain_exactly(
14
+ 'confident',
15
+ 'default',
16
+ 'diplomatic',
17
+ 'enthusiastic',
18
+ 'friendly',
19
+ 'prefer_confident',
20
+ 'prefer_diplomatic',
21
+ 'prefer_enthusiastic',
22
+ 'prefer_friendly'
23
+ )
24
+ end
25
+
26
+ it 'validates correct tone values' do
27
+ expect(tone.valid?('enthusiastic')).to be true
28
+ expect(tone.valid?('friendly')).to be true
29
+ expect(tone.valid?('confident')).to be true
30
+ expect(tone.valid?('diplomatic')).to be true
31
+ expect(tone.valid?('default')).to be true
32
+ end
33
+
34
+ it 'invalidates incorrect tone values' do
35
+ expect(tone.valid?('angry')).to be false
36
+ expect(tone.valid?('')).to be false
37
+ expect(tone.valid?(nil)).to be false
38
+ end
39
+ end
40
+
41
+ describe DeepL::Constants::WritingStyle do
42
+ subject(:writing_style) { described_class }
43
+
44
+ it 'includes all expected writing style values' do # rubocop:disable RSpec/ExampleLength
45
+ expect(writing_style.options).to contain_exactly(
46
+ 'default',
47
+ 'simple',
48
+ 'business',
49
+ 'academic',
50
+ 'casual',
51
+ 'prefer_academic',
52
+ 'prefer_business',
53
+ 'prefer_casual',
54
+ 'prefer_simple'
55
+ )
56
+ end
57
+
58
+ it 'validates correct writing style values' do
59
+ expect(writing_style.valid?('simple')).to be true
60
+ expect(writing_style.valid?('business')).to be true
61
+ expect(writing_style.valid?('academic')).to be true
62
+ expect(writing_style.valid?('casual')).to be true
63
+ expect(writing_style.valid?('default')).to be true
64
+ end
65
+
66
+ it 'invalidates incorrect writing style values' do
67
+ expect(writing_style.valid?('wordy')).to be false
68
+ expect(writing_style.valid?('')).to be false
69
+ expect(writing_style.valid?(nil)).to be false
70
+ end
71
+ end
72
+
73
+ describe DeepL::Constants::TagHandling do
74
+ subject(:tag_handling) { described_class }
75
+
76
+ it 'includes all expected tag handling values' do
77
+ expect(tag_handling.options).to contain_exactly('xml', 'html')
78
+ end
79
+
80
+ it 'validates correct tag handling values' do
81
+ expect(tag_handling.valid?('xml')).to be true
82
+ expect(tag_handling.valid?('html')).to be true
83
+ end
84
+
85
+ it 'invalidates incorrect tag handling values' do
86
+ expect(tag_handling.valid?('json')).to be false
87
+ expect(tag_handling.valid?('')).to be false
88
+ expect(tag_handling.valid?(nil)).to be false
89
+ end
90
+ end
91
+
92
+ describe DeepL::Constants::SplitSentences do
93
+ subject(:split_sentences) { described_class }
94
+
95
+ it 'includes all expected split sentences values' do
96
+ expect(split_sentences.options).to contain_exactly('0', '1', 'nonewlines')
97
+ end
98
+
99
+ it 'validates correct split sentences values' do
100
+ expect(split_sentences.valid?('0')).to be true
101
+ expect(split_sentences.valid?('1')).to be true
102
+ expect(split_sentences.valid?('nonewlines')).to be true
103
+ end
104
+
105
+ it 'invalidates incorrect split sentences values' do
106
+ expect(split_sentences.valid?('2')).to be false
107
+ expect(split_sentences.valid?('')).to be false
108
+ expect(split_sentences.valid?(nil)).to be false
109
+ end
110
+ end
111
+
112
+ describe DeepL::Constants::ModelType do
113
+ subject(:model_type) { described_class }
114
+
115
+ it 'includes all expected model type values' do
116
+ expect(model_type.options).to contain_exactly(
117
+ 'quality_optimized',
118
+ 'prefer_quality_optimized',
119
+ 'latency_optimized'
120
+ )
121
+ end
122
+
123
+ it 'validates correct model type values' do
124
+ expect(model_type.valid?('quality_optimized')).to be true
125
+ expect(model_type.valid?('prefer_quality_optimized')).to be true
126
+ expect(model_type.valid?('latency_optimized')).to be true
127
+ end
128
+
129
+ it 'invalidates incorrect model type values' do
130
+ expect(model_type.valid?('speed_optimized')).to be false
131
+ expect(model_type.valid?('')).to be false
132
+ expect(model_type.valid?(nil)).to be false
133
+ end
134
+ end
135
+
136
+ describe DeepL::Constants::Formality do
137
+ subject(:formality) { described_class }
138
+
139
+ it 'includes all expected formality values' do
140
+ expect(formality.options).to contain_exactly('default', 'more', 'less', 'prefer_more',
141
+ 'prefer_less')
142
+ end
143
+
144
+ it 'validates correct formality values' do
145
+ expect(formality.valid?('default')).to be true
146
+ expect(formality.valid?('more')).to be true
147
+ expect(formality.valid?('less')).to be true
148
+ expect(formality.valid?('prefer_more')).to be true
149
+ expect(formality.valid?('prefer_less')).to be true
150
+ end
151
+
152
+ it 'invalidates incorrect formality values' do
153
+ expect(formality.valid?('neutral')).to be false
154
+ expect(formality.valid?('')).to be false
155
+ expect(formality.valid?(nil)).to be false
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,87 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://api.deepl.com/v2/write/rephrase
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"text":["Ih bin ei beispielsatz."]}'
9
+ headers:
10
+ Authorization:
11
+ - DeepL-Auth-Key VALID_TOKEN
12
+ User-Agent:
13
+ - deepl-ruby/3.0.2 (darwin22) ruby/3.2.1
14
+ Content-Type:
15
+ - application/json
16
+ Accept-Encoding:
17
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
18
+ Accept:
19
+ - "*/*"
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: OK
24
+ headers:
25
+ Content-Type:
26
+ - application/json; charset=utf-8
27
+ Date:
28
+ - Tue, 04 Nov 2025 09:49:46 GMT
29
+ X-Trace-Id:
30
+ - 55e4d15b3b5f43d5911ddd20e43e4853
31
+ Access-Control-Expose-Headers:
32
+ - Server-Timing, X-Trace-ID
33
+ Server-Timing:
34
+ - l7_lb_tls;dur=39, l7_lb_idle;dur=0, l7_lb_receive;dur=0, l7_lb_total;dur=122
35
+ Strict-Transport-Security:
36
+ - max-age=63072000; includeSubDomains; preload
37
+ Transfer-Encoding:
38
+ - chunked
39
+ Vary:
40
+ - Accept-Encoding
41
+ body:
42
+ encoding: ASCII-8BIT
43
+ string: '{"improvements":[{"text":"Ich bin ein Beispiel.","detected_source_language":"de","target_language":"de"}]}'
44
+ recorded_at: Tue, 04 Nov 2025 09:49:47 GMT
45
+ - request:
46
+ method: post
47
+ uri: https://api.deepl.com/v2/write/rephrase
48
+ body:
49
+ encoding: UTF-8
50
+ string: '{"text":["Ih bin ei beispielsatz."],"target_lang":"DE"}'
51
+ headers:
52
+ Authorization:
53
+ - DeepL-Auth-Key VALID_TOKEN
54
+ User-Agent:
55
+ - deepl-ruby/3.0.2 (darwin22) ruby/3.2.1
56
+ Content-Type:
57
+ - application/json
58
+ Accept-Encoding:
59
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
60
+ Accept:
61
+ - "*/*"
62
+ response:
63
+ status:
64
+ code: 200
65
+ message: OK
66
+ headers:
67
+ Content-Type:
68
+ - application/json; charset=utf-8
69
+ Date:
70
+ - Tue, 04 Nov 2025 09:49:47 GMT
71
+ X-Trace-Id:
72
+ - bdeb8592514b40b8a16068b3cff1a4ed
73
+ Access-Control-Expose-Headers:
74
+ - Server-Timing, X-Trace-ID
75
+ Server-Timing:
76
+ - l7_lb_tls;dur=40, l7_lb_idle;dur=0, l7_lb_receive;dur=0, l7_lb_total;dur=138
77
+ Strict-Transport-Security:
78
+ - max-age=63072000; includeSubDomains; preload
79
+ Transfer-Encoding:
80
+ - chunked
81
+ Vary:
82
+ - Accept-Encoding
83
+ body:
84
+ encoding: ASCII-8BIT
85
+ string: '{"improvements":[{"text":"Ich bin ein Beispiel.","detected_source_language":"de","target_language":"de"}]}'
86
+ recorded_at: Tue, 04 Nov 2025 09:49:47 GMT
87
+ recorded_with: VCR 6.2.0