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.
- checksums.yaml +4 -4
- data/.gitlab-ci.yml +38 -1
- data/CHANGELOG.md +38 -24
- data/README.md +68 -0
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/deepl-rb.gemspec +15 -3
- data/lib/deepl/constants/base_constant.rb +18 -0
- data/lib/deepl/constants/formality.rb +16 -0
- data/lib/deepl/constants/model_type.rb +14 -0
- data/lib/deepl/constants/split_sentences.rb +14 -0
- data/lib/deepl/constants/tag_handling.rb +13 -0
- data/lib/deepl/constants/tone.rb +20 -0
- data/lib/deepl/constants/writing_style.rb +20 -0
- data/lib/deepl/exceptions/authorization_failed.rb +1 -1
- data/lib/deepl/exceptions/bad_request.rb +1 -1
- data/lib/deepl/exceptions/limit_exceeded.rb +1 -1
- data/lib/deepl/exceptions/not_found.rb +1 -1
- data/lib/deepl/exceptions/quota_exceeded.rb +1 -1
- data/lib/deepl/exceptions/request_entity_too_large.rb +1 -1
- data/lib/deepl/exceptions/request_error.rb +1 -1
- data/lib/deepl/exceptions/server_error.rb +1 -1
- data/lib/deepl/requests/base.rb +38 -5
- data/lib/deepl/requests/document/upload.rb +21 -11
- data/lib/deepl/requests/rephrase.rb +55 -0
- data/lib/deepl.rb +15 -0
- data/spec/api/deepl_spec.rb +29 -0
- data/spec/constants/constants_spec.rb +158 -0
- data/spec/fixtures/vcr_cassettes/deepl_rephrase.yml +87 -0
- data/spec/fixtures/vcr_cassettes/rephrase_texts.yml +401 -0
- data/spec/fixtures/vcr_cassettes/translate_texts.yml +44 -1
- data/spec/integration_tests/document_api_spec.rb +22 -0
- data/spec/requests/rephrase_spec.rb +172 -0
- data/spec/requests/translate_spec.rb +35 -0
- data/spec/spec_helper.rb +6 -2
- metadata +14 -2
|
@@ -20,18 +20,10 @@ module DeepL
|
|
|
20
20
|
@filename = filename
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def request
|
|
23
|
+
def request
|
|
24
24
|
input_file = File.open(input_file_path, 'rb')
|
|
25
|
-
form_data =
|
|
26
|
-
|
|
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
|
data/spec/api/deepl_spec.rb
CHANGED
|
@@ -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
|