deepl-rb 3.8.0 → 3.9.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 +1 -1
- data/CHANGELOG.md +23 -1
- data/README.md +213 -4
- data/VERSION +1 -1
- data/deepl-rb.gemspec +34 -6
- data/lib/deepl/document_api.rb +11 -1
- data/lib/deepl/requests/base.rb +55 -3
- data/lib/deepl/requests/document/upload.rb +50 -1
- data/lib/deepl/requests/translate.rb +4 -1
- data/lib/deepl/requests/translation_memory/base.rb +47 -0
- data/lib/deepl/requests/translation_memory/create_export.rb +41 -0
- data/lib/deepl/requests/translation_memory/create_import.rb +53 -0
- data/lib/deepl/requests/translation_memory/destroy.rb +37 -0
- data/lib/deepl/requests/translation_memory/download_export.rb +35 -0
- data/lib/deepl/requests/translation_memory/find.rb +38 -0
- data/lib/deepl/requests/translation_memory/find_job.rb +38 -0
- data/lib/deepl/requests/translation_memory/list.rb +5 -18
- data/lib/deepl/requests/translation_memory/segments.rb +46 -0
- data/lib/deepl/requests/translation_memory/storage_base.rb +47 -0
- data/lib/deepl/requests/translation_memory/upload_file.rb +47 -0
- data/lib/deepl/resources/translation_memory.rb +4 -1
- data/lib/deepl/resources/translation_memory_export.rb +37 -0
- data/lib/deepl/resources/translation_memory_import.rb +29 -0
- data/lib/deepl/resources/translation_memory_job.rb +142 -0
- data/lib/deepl/resources/translation_memory_segments.rb +78 -0
- data/lib/deepl/translation_memory_api.rb +293 -1
- data/lib/deepl/utils/time_parser.rb +26 -0
- data/lib/deepl.rb +31 -0
- data/lib/version.rb +1 -1
- data/spec/integration_tests/document_api_spec.rb +47 -0
- data/spec/integration_tests/integration_test_utils.rb +8 -0
- data/spec/integration_tests/translate_api_spec.rb +19 -0
- data/spec/integration_tests/translation_memory_api_spec.rb +192 -2
- data/spec/integration_tests/translation_memory_error_paths_spec.rb +68 -0
- data/spec/requests/document/upload_spec.rb +130 -0
- data/spec/requests/translate_spec.rb +55 -0
- data/spec/requests/translation_memory/create_export_spec.rb +37 -0
- data/spec/requests/translation_memory/create_import_spec.rb +61 -0
- data/spec/requests/translation_memory/destroy_spec.rb +36 -0
- data/spec/requests/translation_memory/download_export_spec.rb +54 -0
- data/spec/requests/translation_memory/find_job_spec.rb +37 -0
- data/spec/requests/translation_memory/find_spec.rb +36 -0
- data/spec/requests/translation_memory/segments_spec.rb +58 -0
- data/spec/requests/translation_memory/upload_file_spec.rb +55 -0
- data/spec/resources/translation_memory_export_spec.rb +38 -0
- data/spec/resources/translation_memory_import_spec.rb +30 -0
- data/spec/resources/translation_memory_job_spec.rb +109 -0
- data/spec/resources/translation_memory_segments_spec.rb +78 -0
- data/spec/resources/translation_memory_spec.rb +18 -1
- data/spec/support/managed_glossary.rb +48 -0
- data/spec/support/managed_translation_memory.rb +48 -0
- metadata +34 -3
|
@@ -6,15 +6,20 @@
|
|
|
6
6
|
require 'spec_helper'
|
|
7
7
|
|
|
8
8
|
describe DeepL::TranslationMemoryApi, :mock_server_only do
|
|
9
|
+
include_context 'with temp dir'
|
|
10
|
+
|
|
11
|
+
let(:default_tm_id) { 'a74d88fb-ed2a-4943-a664-a4512398b994' }
|
|
12
|
+
let(:tmx_file_path) { write_example_tmx(File.join(temp_dir, 'example.tmx')) }
|
|
13
|
+
let(:translation_memories) { DeepL.translation_memories }
|
|
14
|
+
|
|
9
15
|
describe '#translate_with_translation_memory' do
|
|
10
16
|
it 'when performing a request with translation_memory_id' do
|
|
11
17
|
source_lang = 'DE'
|
|
12
18
|
target_lang = 'EN'
|
|
13
19
|
text = 'Protonenstrahl'
|
|
14
|
-
translation_memory_id = 'a74d88fb-ed2a-4943-a664-a4512398b994'
|
|
15
20
|
|
|
16
21
|
result = DeepL.translate(text, source_lang, target_lang,
|
|
17
|
-
{ translation_memory:
|
|
22
|
+
{ translation_memory: default_tm_id })
|
|
18
23
|
expect(result).to be_a(DeepL::Resources::Text)
|
|
19
24
|
end
|
|
20
25
|
|
|
@@ -41,6 +46,191 @@ describe DeepL::TranslationMemoryApi, :mock_server_only do
|
|
|
41
46
|
end
|
|
42
47
|
end
|
|
43
48
|
|
|
49
|
+
describe '#find' do
|
|
50
|
+
it 'when requesting a single translation memory by ID' do
|
|
51
|
+
translation_memory = DeepL.translation_memories.find(default_tm_id)
|
|
52
|
+
expect(translation_memory).to be_a(DeepL::Resources::TranslationMemory)
|
|
53
|
+
expect(translation_memory.translation_memory_id).to eq(default_tm_id)
|
|
54
|
+
expect(translation_memory.name).to eq('Default Translation Memory')
|
|
55
|
+
expect(translation_memory.source_language).to eq('de')
|
|
56
|
+
expect(translation_memory.target_languages).to eq(%w[en es fr])
|
|
57
|
+
expect(translation_memory.segment_count).to eq(12)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'when requesting a single translation memory by resource object' do
|
|
61
|
+
translation_memory = DeepL.translation_memories.find(build_test_translation_memory)
|
|
62
|
+
expect(translation_memory.translation_memory_id).to eq(default_tm_id)
|
|
63
|
+
expect(translation_memory.creation_time).to be_a(Time)
|
|
64
|
+
expect(translation_memory.updated_time).to be_a(Time)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe '#segments' do
|
|
69
|
+
it 'when requesting the segments of a translation memory' do
|
|
70
|
+
page = DeepL.translation_memories.segments(default_tm_id)
|
|
71
|
+
expect(page).to be_a(DeepL::Resources::TranslationMemorySegments)
|
|
72
|
+
expect(page.segments.length).to eq(12)
|
|
73
|
+
expect(page.segment_count).to eq(12)
|
|
74
|
+
expect(page.next_page?).to be(false)
|
|
75
|
+
expect(page.segments.first.source_text).to eq('Quelltext Nummer 0')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'when reading segment timestamps' do
|
|
79
|
+
# The API does not return creation_time/updated_time on segments at all, and returns
|
|
80
|
+
# last_used_time only for segments that have been used.
|
|
81
|
+
page = DeepL.translation_memories.segments(default_tm_id)
|
|
82
|
+
expect(page.segments.first.creation_time).to be_nil
|
|
83
|
+
expect(page.segments.first.updated_time).to be_nil
|
|
84
|
+
expect(page.segments.first.last_used_time).to be_a(Time)
|
|
85
|
+
expect(page.segments[1].last_used_time).to be_nil
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'when reading the target segments of a source segment' do
|
|
89
|
+
page = DeepL.translation_memories.segments(default_tm_id, page_size: 1)
|
|
90
|
+
segment = page.segments.first
|
|
91
|
+
expect(segment.targets.map(&:target_language)).to eq(%w[en es fr])
|
|
92
|
+
expect(segment.targets.first.target_text).to eq('Target text number 0 (de->en)')
|
|
93
|
+
expect(segment.targets.first.target_segment_id).not_to be_nil
|
|
94
|
+
expect(segment.targets.first.last_used_time).to be_a(Time)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'when paginating through the segments with the page cursor' do
|
|
98
|
+
first_page = DeepL.translation_memories.segments(default_tm_id, page_size: 5)
|
|
99
|
+
second_page = next_segments_page(first_page)
|
|
100
|
+
last_page = next_segments_page(second_page)
|
|
101
|
+
|
|
102
|
+
expect(first_page.segments.length).to eq(5)
|
|
103
|
+
expect(first_page.next_page?).to be(true)
|
|
104
|
+
expect(second_page.segments.first.source_text).to eq('Quelltext Nummer 5')
|
|
105
|
+
expect(last_page.segments.length).to eq(2)
|
|
106
|
+
expect(last_page.next_page?).to be(false)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'when filtering the segments by a text containing a space' do
|
|
110
|
+
page = DeepL.translation_memories.segments(default_tm_id, filter_text: 'Nummer 1')
|
|
111
|
+
expect(page.segments.map(&:source_text))
|
|
112
|
+
.to eq(['Quelltext Nummer 1', 'Quelltext Nummer 10', 'Quelltext Nummer 11'])
|
|
113
|
+
# segment_count is the number of segments stored in the translation memory, a text filter
|
|
114
|
+
# does not reduce it.
|
|
115
|
+
expect(page.segment_count).to eq(12)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'when filtering the segments case-sensitively' do
|
|
119
|
+
page = DeepL.translation_memories.segments(default_tm_id, filter_text: 'quelltext',
|
|
120
|
+
filter_case_sensitive: true)
|
|
121
|
+
expect(page.segments).to be_empty
|
|
122
|
+
expect(page.segment_count).to eq(12)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
describe '#import_from_filepath' do
|
|
127
|
+
it 'when importing a translation memory from a TMX file' do
|
|
128
|
+
with_managed_translation_memory(tmx_file_path, display_name: 'Imported TM') do |job|
|
|
129
|
+
expect(job).to be_a(DeepL::Resources::TranslationMemoryJob)
|
|
130
|
+
expect(job.operation).to eq('import')
|
|
131
|
+
expect(job.display_name).to eq('Imported TM')
|
|
132
|
+
expect(job.status).to eq('completed')
|
|
133
|
+
expect(job.finished?).to be(true)
|
|
134
|
+
expect(job.result.translation_memory_id).not_to be_nil
|
|
135
|
+
expect(job.result.skipped_segment_count).to eq(0)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it 'when the imported translation memory can be retrieved afterwards' do
|
|
140
|
+
with_managed_translation_memory(tmx_file_path) do |job|
|
|
141
|
+
translation_memory = DeepL.translation_memories.find(job.result.translation_memory_id)
|
|
142
|
+
expect(translation_memory.name).to eq('example.tmx')
|
|
143
|
+
expect(translation_memory.source_language).to eq('de')
|
|
144
|
+
expect(job.source_content_type).to eq('application/xml')
|
|
145
|
+
expect(job.source_content_length).to eq(File.size(tmx_file_path))
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
describe '#create_import' do
|
|
151
|
+
it 'when the import job is still awaiting the file upload' do
|
|
152
|
+
created = DeepL.translation_memories.create_import('awaiting.tmx', 128,
|
|
153
|
+
display_name: 'Awaiting Upload')
|
|
154
|
+
expect(created).to be_a(DeepL::Resources::TranslationMemoryImport)
|
|
155
|
+
expect(created.upload_url).to start_with('http')
|
|
156
|
+
expect(created.expires_at).to be_a(Time)
|
|
157
|
+
|
|
158
|
+
job = DeepL.translation_memories.find_job(created.job_id)
|
|
159
|
+
expect(job.awaiting_input?).to be(true)
|
|
160
|
+
expect(job.status).to eq('awaiting_input')
|
|
161
|
+
expect(job.result.required_action).not_to be_nil
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# The API detects the upload asynchronously, so an uploaded job keeps reporting
|
|
165
|
+
# `awaiting_input` for a while. The wait loop must poll through that status.
|
|
166
|
+
it 'when waiting on an uploaded job it polls through the awaiting_input status' do
|
|
167
|
+
created = create_uploaded_import(translation_memories, tmx_file_path, processing_polls: 1)
|
|
168
|
+
polled_statuses = []
|
|
169
|
+
allow(translation_memories).to receive(:find_job).and_wrap_original do |original, *args|
|
|
170
|
+
original.call(*args).tap { |status| polled_statuses << status.status }
|
|
171
|
+
end
|
|
172
|
+
expect(translation_memories).to receive(:sleep).once
|
|
173
|
+
|
|
174
|
+
job = translation_memories.wait_until_job_done(created.job_id)
|
|
175
|
+
|
|
176
|
+
expect(polled_statuses).to eq(%w[awaiting_input completed])
|
|
177
|
+
expect(job.status).to eq('completed')
|
|
178
|
+
DeepL.translation_memories.destroy(job.result.translation_memory_id)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it 'when the file is never uploaded it waits until the timeout of the caller' do
|
|
182
|
+
created = translation_memories.create_import('never-uploaded.tmx', 128)
|
|
183
|
+
# Such a job does not finish on its own, only the timeout ends the wait.
|
|
184
|
+
expect(translation_memories).not_to receive(:sleep)
|
|
185
|
+
|
|
186
|
+
expect { translation_memories.wait_until_job_done(created.job_id, timeout_s: 0) }
|
|
187
|
+
.to raise_error(DeepL::Exceptions::Error, /Manual timeout of 0s exceeded/)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
describe '#export_to_filepath' do
|
|
192
|
+
let(:output_path) { File.join(temp_dir, 'export.tmx') }
|
|
193
|
+
|
|
194
|
+
it 'when exporting a translation memory to a TMX file' do
|
|
195
|
+
job = DeepL.translation_memories.export_to_filepath(default_tm_id, output_path)
|
|
196
|
+
expect(job).to be_a(DeepL::Resources::TranslationMemoryJob)
|
|
197
|
+
expect(job.operation).to eq('export')
|
|
198
|
+
expect(job.status).to eq('completed')
|
|
199
|
+
expect(job.result.download_url).to start_with('http')
|
|
200
|
+
|
|
201
|
+
contents = File.read(output_path)
|
|
202
|
+
expect(contents).to include('<tmx version="1.4">')
|
|
203
|
+
expect(contents).to include('<seg>Quelltext Nummer 0</seg>')
|
|
204
|
+
expect(contents).to include('<tuv xml:lang="en"><seg>Target text number 0 (de->en)</seg>')
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it 'when re-exporting an unchanged translation memory it reuses the completed job' do
|
|
208
|
+
job = DeepL.translation_memories.export_to_filepath(default_tm_id, output_path)
|
|
209
|
+
reused = DeepL.translation_memories.create_export(default_tm_id)
|
|
210
|
+
expect(reused).to be_a(DeepL::Resources::TranslationMemoryExport)
|
|
211
|
+
expect(reused.reused_existing?).to be(true)
|
|
212
|
+
expect(reused.job_id).to eq(job.job_id)
|
|
213
|
+
expect(reused.translation_memory_id).to eq(default_tm_id)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
describe '#destroy' do
|
|
218
|
+
it 'when deleting a translation memory it can no longer be found' do
|
|
219
|
+
job = DeepL.translation_memories.import_from_filepath(tmx_file_path)
|
|
220
|
+
translation_memory_id = job.result.translation_memory_id
|
|
221
|
+
|
|
222
|
+
expect(DeepL.translation_memories.destroy(translation_memory_id))
|
|
223
|
+
.to eq(translation_memory_id)
|
|
224
|
+
expect { DeepL.translation_memories.find(translation_memory_id) }
|
|
225
|
+
.to raise_error(DeepL::Exceptions::NotFound)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def next_segments_page(page)
|
|
230
|
+
DeepL.translation_memories.segments(default_tm_id, page_size: 5,
|
|
231
|
+
page_cursor: page.next_page_cursor)
|
|
232
|
+
end
|
|
233
|
+
|
|
44
234
|
def build_test_translation_memory
|
|
45
235
|
tm_data = {
|
|
46
236
|
'translation_memory_id' => 'a74d88fb-ed2a-4943-a664-a4512398b994',
|
|
@@ -8,6 +8,9 @@ require 'spec_helper'
|
|
|
8
8
|
describe DeepL::TranslationMemoryApi, :mock_server_only do # rubocop:disable RSpec/SpecFilePathFormat
|
|
9
9
|
include_context 'with a live mock server'
|
|
10
10
|
|
|
11
|
+
let(:default_tm_id) { 'a74d88fb-ed2a-4943-a664-a4512398b994' }
|
|
12
|
+
let(:missing_uuid) { '00000000-0000-0000-0000-000000000000' }
|
|
13
|
+
|
|
11
14
|
describe 'authorization failures' do
|
|
12
15
|
let(:unauthorized_translation_memories) { described_class.new(unauthorized_api) }
|
|
13
16
|
|
|
@@ -15,5 +18,70 @@ describe DeepL::TranslationMemoryApi, :mock_server_only do # rubocop:disable RSp
|
|
|
15
18
|
expect { unauthorized_translation_memories.list }
|
|
16
19
|
.to raise_error(DeepL::Exceptions::AuthorizationFailed)
|
|
17
20
|
end
|
|
21
|
+
|
|
22
|
+
it 'raises AuthorizationFailed on a read operation (find)' do
|
|
23
|
+
expect { unauthorized_translation_memories.find(default_tm_id) }
|
|
24
|
+
.to raise_error(DeepL::Exceptions::AuthorizationFailed)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'raises AuthorizationFailed on a write operation (create_export)' do
|
|
28
|
+
expect { unauthorized_translation_memories.create_export(default_tm_id) }
|
|
29
|
+
.to raise_error(DeepL::Exceptions::AuthorizationFailed)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe 'not-found failures' do
|
|
34
|
+
it 'raises NotFound when #find is called with a missing UUID' do
|
|
35
|
+
expect { DeepL.translation_memories.find(missing_uuid) }
|
|
36
|
+
.to raise_error(DeepL::Exceptions::NotFound)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'raises NotFound when #segments is called with a missing UUID' do
|
|
40
|
+
expect { DeepL.translation_memories.segments(missing_uuid) }
|
|
41
|
+
.to raise_error(DeepL::Exceptions::NotFound)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'raises NotFound when #find_job is called with a missing UUID' do
|
|
45
|
+
expect { DeepL.translation_memories.find_job(missing_uuid) }
|
|
46
|
+
.to raise_error(DeepL::Exceptions::NotFound)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe 'bad-request failures' do
|
|
51
|
+
it 'raises BadRequest when #segments is called with a too short text filter' do
|
|
52
|
+
expect { DeepL.translation_memories.segments(default_tm_id, filter_text: 'a') }
|
|
53
|
+
.to raise_error(DeepL::Exceptions::BadRequest)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'raises an error when #destroy is called with a malformed UUID' do
|
|
57
|
+
expect { DeepL.translation_memories.destroy('invalid-uuid') }
|
|
58
|
+
.to raise_error(DeepL::Exceptions::Error)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe 'client-side validation failures' do
|
|
63
|
+
it 'raises an error when #find is called with an empty ID' do
|
|
64
|
+
expect { DeepL.translation_memories.find('') }
|
|
65
|
+
.to raise_error(DeepL::Exceptions::Error, /must not be empty/)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'raises an error when #find_job is called with an empty job ID' do
|
|
69
|
+
expect { DeepL.translation_memories.find_job(nil) }
|
|
70
|
+
.to raise_error(DeepL::Exceptions::Error, /must not be empty/)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'raises an error when #import_from_filepath is given a missing file' do
|
|
74
|
+
expect { DeepL.translation_memories.import_from_filepath('/no/such/file.tmx') }
|
|
75
|
+
.to raise_error(DeepL::Exceptions::Error, /No file found/)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'raises an error when #download_export is given an unfinished job' do
|
|
79
|
+
created = DeepL.translation_memories.create_export(default_tm_id)
|
|
80
|
+
job = DeepL::Resources::TranslationMemoryJob.new(
|
|
81
|
+
{ 'job_id' => created.job_id, 'results' => [{ 'status' => 'processing' }] }, nil, nil
|
|
82
|
+
)
|
|
83
|
+
expect { DeepL.translation_memories.download_export(job, '/tmp/unused.tmx') }
|
|
84
|
+
.to raise_error(DeepL::Exceptions::Error, /no download URL/)
|
|
85
|
+
end
|
|
18
86
|
end
|
|
19
87
|
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Copyright 2026 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::Requests::Document::Upload do
|
|
9
|
+
subject(:upload) do
|
|
10
|
+
described_class.new(api, input_file_path, source_lang, target_lang, filename, options)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
around do |tests|
|
|
14
|
+
tmp_env = replace_env_preserving_deepl_vars_except_mock_server
|
|
15
|
+
tests.call
|
|
16
|
+
ENV.replace(tmp_env)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let(:api) { build_deepl_api }
|
|
20
|
+
let(:input_file_path) { '/tmp/example.txt' }
|
|
21
|
+
let(:source_lang) { 'EN' }
|
|
22
|
+
let(:target_lang) { 'DE' }
|
|
23
|
+
let(:filename) { 'example.txt' }
|
|
24
|
+
let(:options) { {} }
|
|
25
|
+
let(:input_file) { instance_double(File) }
|
|
26
|
+
|
|
27
|
+
# Builds the multipart form data without performing any network or filesystem IO.
|
|
28
|
+
def form_data
|
|
29
|
+
upload.send(:build_base_form_data, input_file)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def form_value(form, key)
|
|
33
|
+
field = form.find { |name, _| name == key }
|
|
34
|
+
field&.last
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe '#initialize' do
|
|
38
|
+
it 'creates a request object' do
|
|
39
|
+
expect(upload).to be_a(described_class)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe 'building the upload form data' do
|
|
44
|
+
context 'when using the `style_rule` option' do
|
|
45
|
+
it 'does not append a `style_id` field when not provided' do
|
|
46
|
+
expect(form_value(form_data, 'style_id')).to be_nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'appends a `style_id` field from a style rule ID string' do
|
|
50
|
+
options[:style_rule] = 'style-123'
|
|
51
|
+
expect(form_value(form_data, 'style_id')).to eq('style-123')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'appends a `style_id` field from a StyleRule object' do
|
|
55
|
+
options[:style_rule] =
|
|
56
|
+
DeepL::Resources::StyleRule.new({ 'style_id' => 'style-obj' }, nil, nil)
|
|
57
|
+
expect(form_value(form_data, 'style_id')).to eq('style-obj')
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
context 'when using the `translation_memory` option' do
|
|
62
|
+
it 'does not append a `translation_memory_id` field when not provided' do
|
|
63
|
+
expect(form_value(form_data, 'translation_memory_id')).to be_nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'appends a `translation_memory_id` field from a translation memory ID string' do
|
|
67
|
+
options[:translation_memory] = 'tm-123'
|
|
68
|
+
expect(form_value(form_data, 'translation_memory_id')).to eq('tm-123')
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'appends a `translation_memory_id` field from a TranslationMemory object' do
|
|
72
|
+
options[:translation_memory] =
|
|
73
|
+
DeepL::Resources::TranslationMemory.new({ 'translation_memory_id' => 'tm-obj' }, nil, nil)
|
|
74
|
+
expect(form_value(form_data, 'translation_memory_id')).to eq('tm-obj')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'appends a `translation_memory_threshold` field serialized to a string' do
|
|
78
|
+
options[:translation_memory] = 'tm-123'
|
|
79
|
+
options[:translation_memory_threshold] = 80
|
|
80
|
+
form = form_data
|
|
81
|
+
expect(form_value(form, 'translation_memory_id')).to eq('tm-123')
|
|
82
|
+
expect(form_value(form, 'translation_memory_threshold')).to eq('80')
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'does not append a `translation_memory_threshold` field when not provided' do
|
|
86
|
+
options[:translation_memory] = 'tm-123'
|
|
87
|
+
expect(form_value(form_data, 'translation_memory_threshold')).to be_nil
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
context 'when using the `glossary_ids` option' do
|
|
92
|
+
it 'does not append a `glossary_ids` field when not provided' do
|
|
93
|
+
expect(form_value(form_data, 'glossary_ids')).to be_nil
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'appends a comma-separated `glossary_ids` field from an array of IDs' do
|
|
97
|
+
options[:glossary_ids] = %w[gid1 gid2 gid3]
|
|
98
|
+
expect(form_value(form_data, 'glossary_ids')).to eq('gid1,gid2,gid3')
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'appends `glossary_ids` from Glossary objects using their IDs' do
|
|
102
|
+
options[:glossary_ids] = [
|
|
103
|
+
DeepL::Resources::Glossary.new({ 'glossary_id' => 'gid-obj' }, nil, nil),
|
|
104
|
+
'gid-str'
|
|
105
|
+
]
|
|
106
|
+
expect(form_value(form_data, 'glossary_ids')).to eq('gid-obj,gid-str')
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'raises when combined with the singular `glossary_id` option' do
|
|
110
|
+
options[:glossary_id] = 'single'
|
|
111
|
+
options[:glossary_ids] = %w[gid1]
|
|
112
|
+
expect { form_data }.to raise_error(ArgumentError, /cannot be used together/)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it 'raises when more than 5 glossary IDs are provided' do
|
|
116
|
+
options[:glossary_ids] = %w[a b c d e f]
|
|
117
|
+
expect { form_data }.to raise_error(ArgumentError, /maximum of 5 glossary IDs/)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
context 'when `source_lang` is not set' do
|
|
121
|
+
let(:source_lang) { nil }
|
|
122
|
+
|
|
123
|
+
it 'raises because `glossary_ids` requires `source_lang`' do
|
|
124
|
+
options[:glossary_ids] = %w[gid1]
|
|
125
|
+
expect { form_data }.to raise_error(ArgumentError, /requires `source_lang`/)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -190,6 +190,61 @@ describe DeepL::Requests::Translate do
|
|
|
190
190
|
end
|
|
191
191
|
end
|
|
192
192
|
|
|
193
|
+
context 'when using `glossary_ids` options' do
|
|
194
|
+
it 'works with a nil value' do
|
|
195
|
+
request = described_class.new(api, nil, nil, nil, glossary_ids: nil)
|
|
196
|
+
expect(request.options[:glossary_ids]).to be_nil
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it 'works with an array of strings' do
|
|
200
|
+
request = described_class.new(api, nil, nil, nil, glossary_ids: %w[id1 id2])
|
|
201
|
+
expect(request.options[:glossary_ids]).to eq(%w[id1 id2])
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
context 'when building the `glossary_ids` request parameter' do
|
|
206
|
+
def glossary_ids_param(source_lang, options)
|
|
207
|
+
described_class.new(api, text, source_lang, target_lang, options)
|
|
208
|
+
.send(:build_glossary_ids_param, source_lang)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it 'returns nil when `glossary_ids` is not provided' do
|
|
212
|
+
expect(glossary_ids_param('EN', {})).to be_nil
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it 'returns nil when `glossary_ids` is nil' do
|
|
216
|
+
expect(glossary_ids_param('EN', { glossary_ids: nil })).to be_nil
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it 'returns nil when `glossary_ids` is an empty array' do
|
|
220
|
+
expect(glossary_ids_param('EN', { glossary_ids: [] })).to be_nil
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
it 'resolves an array of IDs to an array of strings' do
|
|
224
|
+
expect(glossary_ids_param('EN', { glossary_ids: %w[id1 id2 id3] })).to eq(%w[id1 id2 id3])
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it 'resolves glossary objects to their IDs' do
|
|
228
|
+
glossary = DeepL::Resources::Glossary.new({ 'glossary_id' => 'obj_id' }, nil, nil)
|
|
229
|
+
expect(glossary_ids_param('EN', { glossary_ids: [glossary, 'id2'] })).to eq(%w[obj_id id2])
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
it 'raises when `source_lang` is not set' do
|
|
233
|
+
expect { glossary_ids_param(nil, { glossary_ids: %w[id1] }) }
|
|
234
|
+
.to raise_error(ArgumentError, /requires `source_lang`/)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
it 'raises when combined with the singular `glossary_id` option' do
|
|
238
|
+
expect { glossary_ids_param('EN', { glossary_id: 'single', glossary_ids: %w[id1] }) }
|
|
239
|
+
.to raise_error(ArgumentError, /cannot be used together with the `glossary_id`/)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it 'raises when more than 5 IDs are provided' do
|
|
243
|
+
expect { glossary_ids_param('EN', { glossary_ids: %w[id1 id2 id3 id4 id5 id6] }) }
|
|
244
|
+
.to raise_error(ArgumentError, /maximum of 5 glossary IDs/)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
193
248
|
context 'when using `formality` options' do
|
|
194
249
|
it 'works with a nil values' do
|
|
195
250
|
request = described_class.new(api, nil, nil, nil, formality: nil)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Copyright 2026 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::Requests::TranslationMemory::CreateExport do
|
|
9
|
+
subject(:translation_memory_create_export) { described_class.new(api, id, options) }
|
|
10
|
+
|
|
11
|
+
around do |tests|
|
|
12
|
+
tmp_env = replace_env_preserving_deepl_vars_except_mock_server
|
|
13
|
+
tests.call
|
|
14
|
+
ENV.replace(tmp_env)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
let(:api) { build_deepl_api }
|
|
18
|
+
let(:id) { 'a74d88fb-ed2a-4943-a664-a4512398b994' }
|
|
19
|
+
let(:options) { {} }
|
|
20
|
+
|
|
21
|
+
describe '#initialize' do
|
|
22
|
+
context 'when building a request' do
|
|
23
|
+
it 'creates a request object' do
|
|
24
|
+
expect(translation_memory_create_export).to be_a(described_class)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe '#to_s' do
|
|
30
|
+
context 'when building a request' do
|
|
31
|
+
it 'posts to the export endpoint below the v3 prefix' do
|
|
32
|
+
expect(translation_memory_create_export.to_s)
|
|
33
|
+
.to eq("POST /v3/translation_memories/#{id}/export")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Copyright 2026 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::Requests::TranslationMemory::CreateImport do
|
|
9
|
+
subject(:translation_memory_create_import) do
|
|
10
|
+
described_class.new(api, file_name, content_length, options)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
around do |tests|
|
|
14
|
+
tmp_env = replace_env_preserving_deepl_vars_except_mock_server
|
|
15
|
+
tests.call
|
|
16
|
+
ENV.replace(tmp_env)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let(:api) { build_deepl_api }
|
|
20
|
+
let(:file_name) { 'legal.tmx' }
|
|
21
|
+
let(:content_length) { 128 }
|
|
22
|
+
let(:options) { {} }
|
|
23
|
+
|
|
24
|
+
describe '#initialize' do
|
|
25
|
+
context 'when building a request' do
|
|
26
|
+
it 'creates a request object' do
|
|
27
|
+
expect(translation_memory_create_import).to be_a(described_class)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe '#to_s' do
|
|
33
|
+
context 'when building a request' do
|
|
34
|
+
it 'posts to the import endpoint below the v3 prefix' do
|
|
35
|
+
expect(translation_memory_create_import.to_s)
|
|
36
|
+
.to eq('POST /v3/translation_memories/import')
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '#details' do
|
|
42
|
+
context 'when only the required parameters are given' do
|
|
43
|
+
it 'declares the source file without the optional fields' do
|
|
44
|
+
expect(translation_memory_create_import.details).to include('legal.tmx', '128')
|
|
45
|
+
expect(translation_memory_create_import.details).not_to include('content_type')
|
|
46
|
+
expect(translation_memory_create_import.details).not_to include('parameters')
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
context 'when the optional parameters are given' do
|
|
51
|
+
let(:options) { { content_type: 'text/xml', display_name: 'Legal' } }
|
|
52
|
+
|
|
53
|
+
it 'declares the content type and the display name' do
|
|
54
|
+
expect(translation_memory_create_import.details)
|
|
55
|
+
.to include('content_type', 'text/xml')
|
|
56
|
+
expect(translation_memory_create_import.details)
|
|
57
|
+
.to include('parameters', 'display_name', 'Legal')
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Copyright 2026 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::Requests::TranslationMemory::Destroy do
|
|
9
|
+
subject(:translation_memory_destroy) { described_class.new(api, id, options) }
|
|
10
|
+
|
|
11
|
+
around do |tests|
|
|
12
|
+
tmp_env = replace_env_preserving_deepl_vars_except_mock_server
|
|
13
|
+
tests.call
|
|
14
|
+
ENV.replace(tmp_env)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
let(:api) { build_deepl_api }
|
|
18
|
+
let(:id) { 'a74d88fb-ed2a-4943-a664-a4512398b994' }
|
|
19
|
+
let(:options) { {} }
|
|
20
|
+
|
|
21
|
+
describe '#initialize' do
|
|
22
|
+
context 'when building a request' do
|
|
23
|
+
it 'creates a request object' do
|
|
24
|
+
expect(translation_memory_destroy).to be_a(described_class)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe '#to_s' do
|
|
30
|
+
context 'when building a request' do
|
|
31
|
+
it 'deletes the translation memory below the v3 prefix' do
|
|
32
|
+
expect(translation_memory_destroy.to_s).to eq("DELETE /v3/translation_memories/#{id}")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Copyright 2026 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::Requests::TranslationMemory::DownloadExport do
|
|
9
|
+
subject(:download_export) { described_class.new(api, download_url, output_path) }
|
|
10
|
+
|
|
11
|
+
around do |tests|
|
|
12
|
+
tmp_env = replace_env_preserving_deepl_vars_except_mock_server
|
|
13
|
+
tests.call
|
|
14
|
+
ENV.replace(tmp_env)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
let(:api) { build_deepl_api }
|
|
18
|
+
let(:download_url) { 'https://storage.example.com/download/tm?signature=abc' }
|
|
19
|
+
let(:output_path) { '/tmp/export.tmx' }
|
|
20
|
+
|
|
21
|
+
describe '#initialize' do
|
|
22
|
+
context 'when building a request' do
|
|
23
|
+
it 'creates a request object' do
|
|
24
|
+
expect(download_export).to be_a(described_class)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context 'when no download URL is given' do
|
|
29
|
+
let(:download_url) { nil }
|
|
30
|
+
|
|
31
|
+
it 'raises an error' do
|
|
32
|
+
expect { download_export }.to raise_error(DeepL::Exceptions::Error, /must not be empty/)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe '#to_s' do
|
|
38
|
+
context 'when building a request' do
|
|
39
|
+
it 'gets from the pre-signed storage URL' do
|
|
40
|
+
expect(download_export.to_s).to eq('GET /download/tm?signature=abc')
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe '#details' do
|
|
46
|
+
context 'when building a request' do
|
|
47
|
+
# The download URL points outside of the DeepL API, so the auth key must not be sent there.
|
|
48
|
+
it 'does not send the DeepL authorization header' do
|
|
49
|
+
expect(download_export.details).not_to include('Authorization')
|
|
50
|
+
expect(download_export.details).to include('User-Agent')
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|