deepl-rb 3.6.0 → 3.7.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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +27 -0
  3. data/.github/workflows/add_issues_to_kanban.yml +16 -0
  4. data/.gitlab-ci.yml +177 -0
  5. data/.rubocop.yml +51 -0
  6. data/CHANGELOG.md +40 -3
  7. data/CODE_OF_CONDUCT.md +132 -0
  8. data/CONTRIBUTING.md +37 -0
  9. data/Gemfile +24 -0
  10. data/README.md +153 -22
  11. data/Rakefile +58 -0
  12. data/SECURITY.md +58 -0
  13. data/VERSION +1 -0
  14. data/deepl-rb.gemspec +167 -0
  15. data/lib/deepl/api.rb +22 -0
  16. data/lib/deepl/configuration.rb +59 -0
  17. data/lib/deepl/constants/base_constant.rb +18 -0
  18. data/lib/deepl/constants/formality.rb +16 -0
  19. data/lib/deepl/constants/model_type.rb +14 -0
  20. data/lib/deepl/constants/split_sentences.rb +14 -0
  21. data/lib/deepl/constants/tag_handling.rb +13 -0
  22. data/lib/deepl/constants/tone.rb +20 -0
  23. data/lib/deepl/constants/writing_style.rb +20 -0
  24. data/lib/deepl/document_api.rb +121 -0
  25. data/lib/deepl/exceptions/authorization_failed.rb +14 -0
  26. data/lib/deepl/exceptions/bad_request.rb +16 -0
  27. data/lib/deepl/exceptions/document_translation_error.rb +15 -0
  28. data/lib/deepl/exceptions/error.rb +14 -0
  29. data/lib/deepl/exceptions/limit_exceeded.rb +18 -0
  30. data/lib/deepl/exceptions/not_found.rb +16 -0
  31. data/lib/deepl/exceptions/not_supported.rb +14 -0
  32. data/lib/deepl/exceptions/quota_exceeded.rb +14 -0
  33. data/lib/deepl/exceptions/request_entity_too_large.rb +15 -0
  34. data/lib/deepl/exceptions/request_error.rb +21 -0
  35. data/lib/deepl/exceptions/server_error.rb +18 -0
  36. data/lib/deepl/glossary_api.rb +38 -0
  37. data/lib/deepl/requests/base.rb +212 -0
  38. data/lib/deepl/requests/document/download.rb +44 -0
  39. data/lib/deepl/requests/document/get_status.rb +44 -0
  40. data/lib/deepl/requests/document/upload.rb +75 -0
  41. data/lib/deepl/requests/glossary/create.rb +59 -0
  42. data/lib/deepl/requests/glossary/destroy.rb +37 -0
  43. data/lib/deepl/requests/glossary/entries.rb +37 -0
  44. data/lib/deepl/requests/glossary/find.rb +38 -0
  45. data/lib/deepl/requests/glossary/language_pairs.rb +38 -0
  46. data/lib/deepl/requests/glossary/list.rb +37 -0
  47. data/lib/deepl/requests/languages.rb +37 -0
  48. data/lib/deepl/requests/rephrase.rb +55 -0
  49. data/lib/deepl/requests/style_rule/create.rb +46 -0
  50. data/lib/deepl/requests/style_rule/create_custom_instruction.rb +45 -0
  51. data/lib/deepl/requests/style_rule/destroy.rb +39 -0
  52. data/lib/deepl/requests/style_rule/destroy_custom_instruction.rb +40 -0
  53. data/lib/deepl/requests/style_rule/find.rb +40 -0
  54. data/lib/deepl/requests/style_rule/find_custom_instruction.rb +41 -0
  55. data/lib/deepl/requests/style_rule/list.rb +59 -0
  56. data/lib/deepl/requests/style_rule/update.rb +42 -0
  57. data/lib/deepl/requests/style_rule/update_configured_rules.rb +41 -0
  58. data/lib/deepl/requests/style_rule/update_custom_instruction.rb +47 -0
  59. data/lib/deepl/requests/translate.rb +106 -0
  60. data/lib/deepl/requests/translation_memory/list.rb +58 -0
  61. data/lib/deepl/requests/usage.rb +33 -0
  62. data/lib/deepl/resources/base.rb +17 -0
  63. data/lib/deepl/resources/document_handle.rb +57 -0
  64. data/lib/deepl/resources/document_translation_status.rb +54 -0
  65. data/lib/deepl/resources/glossary.rb +28 -0
  66. data/lib/deepl/resources/language.rb +30 -0
  67. data/lib/deepl/resources/language_pair.rb +23 -0
  68. data/lib/deepl/resources/style_rule.rb +86 -0
  69. data/lib/deepl/resources/text.rb +24 -0
  70. data/lib/deepl/resources/translation_memory.rb +25 -0
  71. data/lib/deepl/resources/usage.rb +27 -0
  72. data/lib/deepl/style_rule_api.rb +61 -0
  73. data/lib/deepl/translation_memory_api.rb +17 -0
  74. data/lib/deepl/utils/backoff_timer.rb +46 -0
  75. data/lib/deepl/utils/exception_builder.rb +34 -0
  76. data/lib/deepl.rb +175 -0
  77. data/lib/http_client_options.rb +22 -0
  78. data/lib/version.rb +8 -0
  79. data/spec/api/api_spec.rb +20 -0
  80. data/spec/api/configuration_spec.rb +122 -0
  81. data/spec/api/deepl_spec.rb +460 -0
  82. data/spec/constants/constants_spec.rb +158 -0
  83. data/spec/fixtures/vcr_cassettes/deepl_document.yml +95 -0
  84. data/spec/fixtures/vcr_cassettes/deepl_document_download.yml +1214 -0
  85. data/spec/fixtures/vcr_cassettes/deepl_glossaries.yml +1163 -0
  86. data/spec/fixtures/vcr_cassettes/deepl_languages.yml +54 -0
  87. data/spec/fixtures/vcr_cassettes/deepl_rephrase.yml +87 -0
  88. data/spec/fixtures/vcr_cassettes/deepl_translate.yml +358 -0
  89. data/spec/fixtures/vcr_cassettes/deepl_usage.yml +129 -0
  90. data/spec/fixtures/vcr_cassettes/glossaries.yml +1702 -0
  91. data/spec/fixtures/vcr_cassettes/languages.yml +229 -0
  92. data/spec/fixtures/vcr_cassettes/rephrase_texts.yml +401 -0
  93. data/spec/fixtures/vcr_cassettes/style_rules.yml +92 -0
  94. data/spec/fixtures/vcr_cassettes/style_rules_crud.yml +926 -0
  95. data/spec/fixtures/vcr_cassettes/translate_texts.yml +10630 -0
  96. data/spec/fixtures/vcr_cassettes/translation_memories.yml +74 -0
  97. data/spec/fixtures/vcr_cassettes/usage.yml +171 -0
  98. data/spec/integration_tests/document_api_spec.rb +178 -0
  99. data/spec/integration_tests/integration_test_utils.rb +170 -0
  100. data/spec/integration_tests/style_rule_api_spec.rb +135 -0
  101. data/spec/integration_tests/translation_memory_api_spec.rb +70 -0
  102. data/spec/requests/extra_body_parameters_types_spec.rb +82 -0
  103. data/spec/requests/glossary/create_spec.rb +65 -0
  104. data/spec/requests/glossary/destroy_spec.rb +66 -0
  105. data/spec/requests/glossary/entries_spec.rb +62 -0
  106. data/spec/requests/glossary/find_spec.rb +68 -0
  107. data/spec/requests/glossary/language_pairs_spec.rb +40 -0
  108. data/spec/requests/glossary/list_spec.rb +54 -0
  109. data/spec/requests/languages_spec.rb +68 -0
  110. data/spec/requests/rephrase_spec.rb +172 -0
  111. data/spec/requests/style_rule/create_custom_instruction_spec.rb +54 -0
  112. data/spec/requests/style_rule/create_spec.rb +45 -0
  113. data/spec/requests/style_rule/destroy_custom_instruction_spec.rb +54 -0
  114. data/spec/requests/style_rule/destroy_spec.rb +54 -0
  115. data/spec/requests/style_rule/find_custom_instruction_spec.rb +56 -0
  116. data/spec/requests/style_rule/find_spec.rb +56 -0
  117. data/spec/requests/style_rule/list_spec.rb +58 -0
  118. data/spec/requests/style_rule/update_configured_rules_spec.rb +52 -0
  119. data/spec/requests/style_rule/update_custom_instruction_spec.rb +58 -0
  120. data/spec/requests/style_rule/update_spec.rb +48 -0
  121. data/spec/requests/translate_spec.rb +492 -0
  122. data/spec/requests/translation_memory/list_spec.rb +61 -0
  123. data/spec/requests/usage_spec.rb +43 -0
  124. data/spec/resources/glossary_spec.rb +38 -0
  125. data/spec/resources/language_pair_spec.rb +23 -0
  126. data/spec/resources/language_spec.rb +45 -0
  127. data/spec/resources/text_spec.rb +23 -0
  128. data/spec/resources/translation_memory_spec.rb +35 -0
  129. data/spec/resources/usage_spec.rb +35 -0
  130. data/spec/spec_helper.rb +92 -0
  131. metadata +130 -3
@@ -0,0 +1,58 @@
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::StyleRule::UpdateCustomInstruction do
9
+ subject(:update_instruction) do
10
+ described_class.new(api, style_id, instruction_id, label, prompt, nil, {})
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(:style_id) { 'dca2e053-8ae5-45e6-a0d2-881156e7f4e4' }
21
+ let(:instruction_id) { 'test-instruction-id' }
22
+ let(:label) { 'Updated Label' }
23
+ let(:prompt) { 'Use casual language' }
24
+
25
+ describe '#initialize' do
26
+ context 'when building a request' do
27
+ it 'creates a request object' do
28
+ expect(update_instruction).to be_a(described_class)
29
+ end
30
+ end
31
+ end
32
+
33
+ describe '#request' do
34
+ around do |example|
35
+ VCR.use_cassette('style_rules_crud') { example.call }
36
+ end
37
+
38
+ context 'when performing a valid request' do
39
+ subject(:update_instruction) do
40
+ described_class.new(api, new_rule.style_id, new_instruction.id, label, prompt)
41
+ end
42
+
43
+ let(:new_rule) do
44
+ DeepL::Requests::StyleRule::Create.new(api, 'Update Instruction Test', 'en').request
45
+ end
46
+ let(:new_instruction) do
47
+ DeepL::Requests::StyleRule::CreateCustomInstruction.new(
48
+ api, new_rule.style_id, 'Original', 'Original prompt'
49
+ ).request
50
+ end
51
+
52
+ it 'returns a custom instruction object' do
53
+ instruction = update_instruction.request
54
+ expect(instruction).to be_a(DeepL::Resources::CustomInstruction)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,48 @@
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::StyleRule::Update do
9
+ subject(:update) { described_class.new(api, style_id, name, 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(:style_id) { 'dca2e053-8ae5-45e6-a0d2-881156e7f4e4' }
19
+ let(:name) { 'Updated Name' }
20
+ let(:options) { {} }
21
+
22
+ describe '#initialize' do
23
+ context 'when building a request' do
24
+ it 'creates a request object' do
25
+ expect(update).to be_a(described_class)
26
+ end
27
+ end
28
+ end
29
+
30
+ describe '#request' do
31
+ around do |example|
32
+ VCR.use_cassette('style_rules_crud') { example.call }
33
+ end
34
+
35
+ context 'when performing a valid request' do
36
+ subject(:update) { described_class.new(api, new_rule.style_id, 'Updated Name') }
37
+
38
+ let(:new_rule) do
39
+ DeepL::Requests::StyleRule::Create.new(api, 'Update Test', 'en').request
40
+ end
41
+
42
+ it 'returns a style rule object' do
43
+ style_rule = update.request
44
+ expect(style_rule).to be_a(DeepL::Resources::StyleRule)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,492 @@
1
+ # Copyright 2018 Daniel Herzog
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::Translate do
9
+ subject(:translate) { described_class.new(api, text, source_lang, target_lang, 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(:tags_str) { 'p,strong,span' }
18
+ let(:tags_array) { %w[p strong span] }
19
+
20
+ let(:api) { build_deepl_api }
21
+ let(:text) { 'Sample text' }
22
+ let(:source_lang) { 'EN' }
23
+ let(:target_lang) { 'ES' }
24
+ let(:options) { {} }
25
+
26
+ describe '#initialize' do
27
+ context 'when building a request' do
28
+ it 'creates a request object' do
29
+ expect(translate).to be_a(described_class)
30
+ end
31
+ end
32
+
33
+ context 'when using `splitting_tags` options' do
34
+ it 'works with a nil values' do
35
+ request = described_class.new(api, nil, nil, nil, splitting_tags: nil)
36
+ expect(request.options[:splitting_tags]).to be_nil
37
+ end
38
+
39
+ it 'works with a blank list' do
40
+ request = described_class.new(api, nil, nil, nil, splitting_tags: '')
41
+ expect(request.options[:splitting_tags]).to eq([])
42
+ end
43
+
44
+ it 'works with a comma-separated list and converts strings to an array' do
45
+ request = described_class.new(api, nil, nil, nil, splitting_tags: tags_str)
46
+ expect(request.options[:splitting_tags]).to eq(tags_array)
47
+ end
48
+
49
+ it 'works with an array of tags and leaves it as is' do
50
+ request = described_class.new(api, nil, nil, nil, splitting_tags: tags_array)
51
+ expect(request.options[:splitting_tags]).to eq(tags_array)
52
+ end
53
+ end
54
+
55
+ context 'when using `non_splitting_tags` options' do
56
+ it 'works with a nil values' do
57
+ request = described_class.new(api, nil, nil, nil, non_splitting_tags: nil)
58
+ expect(request.options[:non_splitting_tags]).to be_nil
59
+ end
60
+
61
+ it 'works with a blank list' do
62
+ request = described_class.new(api, nil, nil, nil, non_splitting_tags: '')
63
+ expect(request.options[:non_splitting_tags]).to eq([])
64
+ end
65
+
66
+ it 'works with a comma-separated list and converts strings to an array' do
67
+ request = described_class.new(api, nil, nil, nil, non_splitting_tags: tags_str)
68
+ expect(request.options[:non_splitting_tags]).to eq(tags_array)
69
+ end
70
+
71
+ it 'works with an array and leaves it as it is' do
72
+ request = described_class.new(api, nil, nil, nil, non_splitting_tags: tags_array)
73
+ expect(request.options[:non_splitting_tags]).to eq(tags_array)
74
+ end
75
+ end
76
+
77
+ context 'when using `ignore_tags` options' do
78
+ it 'works with a nil values' do
79
+ request = described_class.new(api, nil, nil, nil, ignore_tags: nil)
80
+ expect(request.options[:ignore_tags]).to be_nil
81
+ end
82
+
83
+ it 'works with a blank list' do
84
+ request = described_class.new(api, nil, nil, nil, ignore_tags: '')
85
+ expect(request.options[:ignore_tags]).to eq([])
86
+ end
87
+
88
+ it 'works with a comma-separated list and converts a string to an array' do
89
+ request = described_class.new(api, nil, nil, nil, ignore_tags: tags_str)
90
+ expect(request.options[:ignore_tags]).to eq(tags_array)
91
+ end
92
+
93
+ it 'works with an array and leaves it as it is' do
94
+ request = described_class.new(api, nil, nil, nil, ignore_tags: tags_array)
95
+ expect(request.options[:ignore_tags]).to eq(tags_array)
96
+ end
97
+ end
98
+
99
+ context 'when using `split_sentences` options' do
100
+ it 'converts `true` to `1`' do
101
+ request = described_class.new(api, nil, nil, nil, split_sentences: true)
102
+ expect(request.options[:split_sentences]).to eq('1')
103
+ end
104
+
105
+ it 'converts `false` to `0`' do
106
+ request = described_class.new(api, nil, nil, nil, split_sentences: false)
107
+ expect(request.options[:split_sentences]).to eq('0')
108
+ end
109
+
110
+ it 'leaves `0` as is' do
111
+ request = described_class.new(api, nil, nil, nil, split_sentences: '0')
112
+ expect(request.options[:split_sentences]).to eq('0')
113
+ end
114
+
115
+ it 'leaves `nonewlines` as is' do
116
+ request = described_class.new(api, nil, nil, nil, split_sentences: 'nonewlines')
117
+ expect(request.options[:split_sentences]).to eq('nonewlines')
118
+ end
119
+
120
+ it 'leaves `1` as is' do
121
+ request = described_class.new(api, nil, nil, nil, split_sentences: '1')
122
+ expect(request.options[:split_sentences]).to eq('1')
123
+ end
124
+
125
+ it 'works with provided constants' do
126
+ request = described_class.new(
127
+ api,
128
+ nil,
129
+ nil,
130
+ nil,
131
+ split_sentences: DeepL::Constants::SplitSentences::SPLIT_ON_PUNCTUATION_AND_NEWLINES
132
+ )
133
+ expect(request.options[:split_sentences]).to eq('1')
134
+ end
135
+ end
136
+
137
+ context 'when using `preserve_formatting` options' do
138
+ it 'leaves `true` as is' do
139
+ request = described_class.new(api, nil, nil, nil, preserve_formatting: true)
140
+ expect(request.options[:preserve_formatting]).to be(true)
141
+ end
142
+
143
+ it 'leaves `false` as is' do
144
+ request = described_class.new(api, nil, nil, nil, preserve_formatting: false)
145
+ expect(request.options[:preserve_formatting]).to be(false)
146
+ end
147
+
148
+ it 'converts `0` to `false`' do
149
+ request = described_class.new(api, nil, nil, nil, preserve_formatting: '0')
150
+ expect(request.options[:preserve_formatting]).to be(false)
151
+ end
152
+
153
+ it 'converts `1` to `true`' do
154
+ request = described_class.new(api, nil, nil, nil, preserve_formatting: '1')
155
+ expect(request.options[:preserve_formatting]).to be(true)
156
+ end
157
+ end
158
+
159
+ context 'when using `outline_detection` options' do
160
+ it 'leaves `true` as is' do
161
+ request = described_class.new(api, nil, nil, nil, outline_detection: true)
162
+ expect(request.options[:outline_detection]).to be(true)
163
+ end
164
+
165
+ it 'leaves `false` as is' do
166
+ request = described_class.new(api, nil, nil, nil, outline_detection: false)
167
+ expect(request.options[:outline_detection]).to be(false)
168
+ end
169
+
170
+ it 'converts `0` to `false`' do
171
+ request = described_class.new(api, nil, nil, nil, outline_detection: '0')
172
+ expect(request.options[:outline_detection]).to be(false)
173
+ end
174
+
175
+ it 'converts `1` to `true`' do
176
+ request = described_class.new(api, nil, nil, nil, outline_detection: '1')
177
+ expect(request.options[:outline_detection]).to be(true)
178
+ end
179
+ end
180
+
181
+ context 'when using `glossary_id` options' do
182
+ it 'works with a nil values' do
183
+ request = described_class.new(api, nil, nil, nil, glossary_id: nil)
184
+ expect(request.options[:glossary_id]).to be_nil
185
+ end
186
+
187
+ it 'works with a string' do
188
+ request = described_class.new(api, nil, nil, nil, glossary_id: 'sample_id')
189
+ expect(request.options[:glossary_id]).to eq('sample_id')
190
+ end
191
+ end
192
+
193
+ context 'when using `formality` options' do
194
+ it 'works with a nil values' do
195
+ request = described_class.new(api, nil, nil, nil, formality: nil)
196
+ expect(request.options[:formality]).to be_nil
197
+ end
198
+
199
+ it 'works with a string' do
200
+ request = described_class.new(api, nil, nil, nil, formality: 'more')
201
+ expect(request.options[:formality]).to eq('more')
202
+ end
203
+
204
+ it 'works with provided constants' do
205
+ request = described_class.new(api, nil, nil, nil,
206
+ formality: DeepL::Constants::Formality::MORE)
207
+ expect(request.options[:formality]).to eq('more')
208
+ end
209
+ end
210
+
211
+ context 'when using `tag_handling_version` options' do
212
+ it 'works with a nil value' do
213
+ request = described_class.new(api, nil, nil, nil, tag_handling_version: nil)
214
+ expect(request.options[:tag_handling_version]).to be_nil
215
+ end
216
+
217
+ it 'works with v1' do
218
+ request = described_class.new(api, nil, nil, nil, tag_handling_version: 'v1')
219
+ expect(request.options[:tag_handling_version]).to eq('v1')
220
+ end
221
+
222
+ it 'works with v2' do
223
+ request = described_class.new(api, nil, nil, nil, tag_handling_version: 'v2')
224
+ expect(request.options[:tag_handling_version]).to eq('v2')
225
+ end
226
+ end
227
+
228
+ context 'when using `model_type` options' do
229
+ it 'works with a nil value' do
230
+ request = described_class.new(api, nil, nil, nil, model_type: nil)
231
+ expect(request.options[:model_type]).to be_nil
232
+ end
233
+
234
+ it 'works with a string' do
235
+ request = described_class.new(api, nil, nil, nil, model_type: 'latency_optimized')
236
+ expect(request.options[:model_type]).to eq('latency_optimized')
237
+ end
238
+
239
+ it 'works with provided constants' do
240
+ request = described_class.new(api, nil, nil, nil,
241
+ model_type: DeepL::Constants::ModelType::LATENCY_OPTIMIZED)
242
+ expect(request.options[:model_type]).to eq('latency_optimized')
243
+ end
244
+ end
245
+
246
+ context 'when using `custom_instructions` options' do
247
+ it 'works with a nil value' do
248
+ request = described_class.new(api, nil, nil, nil, custom_instructions: nil)
249
+ expect(request.options[:custom_instructions]).to be_nil
250
+ end
251
+
252
+ it 'works with an array of strings' do
253
+ instructions = ['Use informal language', 'Be concise']
254
+ request = described_class.new(api, nil, nil, nil, custom_instructions: instructions)
255
+ expect(request.options[:custom_instructions]).to eq(instructions)
256
+ end
257
+
258
+ it 'works with a single string' do
259
+ request = described_class.new(api, nil, nil, nil, custom_instructions: ['Be concise'])
260
+ expect(request.options[:custom_instructions]).to eq(['Be concise'])
261
+ end
262
+
263
+ it 'works with a single string and converts it to an array' do
264
+ instructions = 'Use informal language,Be concise'
265
+ request = described_class.new(api, nil, nil, nil, custom_instructions: instructions)
266
+ expect(request.options[:custom_instructions]).to eq(['Use informal language', 'Be concise'])
267
+ end
268
+ end
269
+ end
270
+
271
+ describe '#request' do
272
+ around do |example|
273
+ VCR.use_cassette('translate_texts') { example.call }
274
+ end
275
+
276
+ context 'when performing a valid request with one text' do
277
+ it 'returns a text object' do
278
+ text = translate.request
279
+
280
+ expect(text).to be_a(DeepL::Resources::Text)
281
+ expect(text.text).to eq('Texto de muestra')
282
+ expect(text.detected_source_language).to eq('EN')
283
+ end
284
+ end
285
+
286
+ context 'when performing a valid request with multiple texts' do
287
+ let(:text) { %w[Sample Word] }
288
+
289
+ it 'returns a text object' do
290
+ texts = translate.request
291
+
292
+ expect(texts).to be_a(Array)
293
+ expect(texts.first.text).to eq('Muestra')
294
+ expect(texts.first.detected_source_language).to eq('EN')
295
+
296
+ expect(texts.last.text).to eq('Palabra')
297
+ expect(texts.last.detected_source_language).to eq('EN')
298
+ end
299
+ end
300
+
301
+ context 'when performing a valid request with tag handling' do
302
+ let(:text) { '<p>Sample text</p>' }
303
+ let(:options) { { tag_handling: 'xml' } }
304
+
305
+ it 'returns a text object' do
306
+ text = translate.request
307
+
308
+ expect(text).to be_a(DeepL::Resources::Text)
309
+ expect(text.text).to eq('<p>Texto de muestra</p>')
310
+ expect(text.detected_source_language).to eq('EN')
311
+ end
312
+ end
313
+
314
+ context 'when performing a valid request and passing a variable' do
315
+ let(:text) { 'Welcome and <code>Hello great World</code> Good Morning!' }
316
+ let(:options) { { tag_handling: 'xml', ignore_tags: %w[code span] } }
317
+
318
+ it 'returns a text object' do
319
+ text = translate.request
320
+
321
+ expect(text).to be_a(DeepL::Resources::Text)
322
+ expect(text.text).to eq('Bienvenido y <code>Hello great World</code> ¡Buenos días!')
323
+ expect(text.detected_source_language).to eq('EN')
324
+ end
325
+ end
326
+
327
+ context 'when performing a valid request with an HTML document' do
328
+ let(:text) do
329
+ <<~XML
330
+ <document>
331
+ <meta>
332
+ <title>A document's title</title>
333
+ </meta>
334
+ <content>
335
+ <par>This is the first sentence. Followed by a second one.</par>
336
+ <par>This is the third sentence.</par>
337
+ </content>
338
+ </document>
339
+ XML
340
+ end
341
+ let(:options) do
342
+ {
343
+ tag_handling: 'xml',
344
+ split_sentences: 'nonewlines',
345
+ outline_detection: false,
346
+ splitting_tags: %w[title par]
347
+ }
348
+ end
349
+
350
+ it 'returns a text object' do
351
+ text = translate.request
352
+
353
+ expect(text).to be_a(DeepL::Resources::Text)
354
+ expect(text.text).to eq(
355
+ <<~XML
356
+ <document>
357
+ <meta>
358
+ <title>Título de un documento</title>
359
+ </meta>
360
+ <content>
361
+ <par>Es la primera frase. Seguido de una segunda.</par>
362
+ <par>Esta es la tercera frase.</par>
363
+ </content>
364
+ </document>
365
+ XML
366
+ )
367
+ expect(text.detected_source_language).to eq('EN')
368
+ end
369
+ end
370
+
371
+ context 'when performing a valid request with context' do
372
+ let(:text) { 'That is hot!' }
373
+
374
+ context 'when context is empty' do
375
+ let(:options) { { context: '' } }
376
+
377
+ it 'translates correctly with empty context' do
378
+ res = translate.request
379
+ expect(res).to be_a(DeepL::Resources::Text)
380
+ expect(res.text).to eq('¡Eso está caliente!')
381
+ end
382
+ end
383
+
384
+ context 'when context is set' do
385
+ let(:options) { { context: 'He did not like the jalapenos in his meal.' } }
386
+
387
+ it 'translates correctly with context taken into account' do
388
+ res = translate.request
389
+ expect(res).to be_a(DeepL::Resources::Text)
390
+ expect(res.text).to eq('¡Eso es picante!')
391
+ end
392
+ end
393
+ end
394
+
395
+ context 'when performing a request with a model type' do
396
+ let(:target_lang) { 'DE' }
397
+
398
+ %w[quality_optimized latency_optimized prefer_quality_optimized].each do |model_type_str|
399
+ it "translates correctly with #{model_type_str} models" do
400
+ options = { model_type: model_type_str }
401
+ translate = described_class.new(api, text, source_lang, target_lang, options)
402
+ res = translate.request
403
+ expect(res).to be_a(DeepL::Resources::Text)
404
+ expect(res.model_type_used).not_to be_nil
405
+ end
406
+ end
407
+ end
408
+
409
+ context 'when performing a request with extra_body_parameters' do
410
+ it 'allows extra parameters to override standard parameters' do
411
+ extra_options = { extra_body_parameters: { target_lang: 'FR', debug: '1' } }
412
+ translate = described_class.new(api, text, source_lang, target_lang, extra_options)
413
+ res = translate.request
414
+
415
+ expect(res).to be_a(DeepL::Resources::Text)
416
+ expect(res.text).to eq('Texte modèle')
417
+ expect(res.detected_source_language).to eq('EN')
418
+ end
419
+ end
420
+
421
+ context 'when performing a request with custom_instructions' do
422
+ let(:text) { 'Hello world' }
423
+ let(:target_lang) { 'DE' }
424
+
425
+ it 'includes custom_instructions in the payload' do
426
+ options = { custom_instructions: ['Use formal language', 'Be concise'] }
427
+ translate = described_class.new(api, text, source_lang, target_lang, options)
428
+
429
+ # Verify the options are properly stored
430
+ expect(translate.options[:custom_instructions]).to eq(['Use formal language', 'Be concise'])
431
+ end
432
+ end
433
+
434
+ context 'when performing a request with tag_handling_version' do
435
+ let(:text) { '<p>Hello world</p>' }
436
+ let(:target_lang) { 'DE' }
437
+
438
+ %w[v1 v2].each do |version|
439
+ it "translates correctly with tag_handling_version #{version}" do
440
+ options = { tag_handling: 'html', tag_handling_version: version }
441
+ translate = described_class.new(api, text, source_lang, target_lang, options)
442
+ res = translate.request
443
+
444
+ expect(res).to be_a(DeepL::Resources::Text)
445
+ expect(res.text).not_to be_nil
446
+ expect(res.text).not_to be_empty
447
+ end
448
+ end
449
+ end
450
+
451
+ context 'when performing a bad request' do
452
+ context 'when using an invalid token' do
453
+ let(:api) do
454
+ api = build_deepl_api
455
+ api.configuration.auth_key = 'invalid'
456
+ api
457
+ end
458
+
459
+ it 'raises an unauthorized error' do
460
+ expect { translate.request }.to raise_error(DeepL::Exceptions::AuthorizationFailed)
461
+ end
462
+ end
463
+
464
+ context 'when using an invalid text' do
465
+ let(:text) { nil }
466
+
467
+ it 'raises a bad request error' do
468
+ message = "Invalid request: Expected 'text' parameter to be an array of strings"
469
+ expect { translate.request }.to raise_error(DeepL::Exceptions::BadRequest, message)
470
+ end
471
+ end
472
+
473
+ context 'when using an invalid target language' do
474
+ let(:target_lang) { nil }
475
+
476
+ it 'raises a bad request error' do
477
+ message = "Value for 'target_lang' not supported."
478
+ expect { translate.request }.to raise_error(DeepL::Exceptions::BadRequest, message)
479
+ end
480
+ end
481
+ end
482
+
483
+ context 'when performing a request with too many texts' do
484
+ let(:text) { Array.new(10_000) { |i| "This is the sentence number #{i}" } }
485
+
486
+ it 'raises a request entity too large error' do
487
+ expect { translate.request }.to raise_error(DeepL::Exceptions::RequestEntityTooLarge,
488
+ /request size has reached the supported limit/)
489
+ end
490
+ end
491
+ end
492
+ 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::List do
9
+ subject(:translation_memory_list) { described_class.new(api, 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(:options) { {} }
19
+
20
+ describe '#initialize' do
21
+ context 'when building a request' do
22
+ it 'creates a request object' do
23
+ expect(translation_memory_list).to be_a(described_class)
24
+ end
25
+ end
26
+ end
27
+
28
+ describe '#request' do
29
+ around do |example|
30
+ VCR.use_cassette('translation_memories') { example.call }
31
+ end
32
+
33
+ context 'when requesting a list of all translation memories' do
34
+ it 'returns an array of translation memories' do
35
+ translation_memories = translation_memory_list.request
36
+ expect(translation_memories).to be_an(Array)
37
+ expect(translation_memories).not_to be_empty
38
+ expect(translation_memories.first).to be_a(DeepL::Resources::TranslationMemory)
39
+ expect(translation_memories.first.translation_memory_id).to be_a(String)
40
+ expect(translation_memories.first.name).to be_a(String)
41
+ expect(translation_memories.first.source_language).to be_a(String)
42
+ expect(translation_memories.first.target_languages).to be_an(Array)
43
+ expect(translation_memories.first.segment_count).to be_an(Integer)
44
+ end
45
+ end
46
+
47
+ context 'when performing a bad request' do
48
+ context 'when using an invalid token' do
49
+ let(:api) do
50
+ api = build_deepl_api
51
+ api.configuration.auth_key = 'invalid'
52
+ api
53
+ end
54
+
55
+ it 'raises an authorization failed error' do
56
+ expect { translation_memory_list.request }.to raise_error(DeepL::Exceptions::AuthorizationFailed)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,43 @@
1
+ # Copyright 2018 Daniel Herzog
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::Usage do
9
+ subject(:usage_req) { described_class.new(api, 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(:options) { {} }
19
+
20
+ describe '#initialize' do
21
+ context 'when building a request' do
22
+ it 'creates a request object' do
23
+ expect(usage_req).to be_a(described_class)
24
+ end
25
+ end
26
+ end
27
+
28
+ describe '#request' do
29
+ around do |example|
30
+ VCR.use_cassette('usage') { example.call }
31
+ end
32
+
33
+ context 'when performing a valid request' do
34
+ it 'returns an usage object' do
35
+ usage = usage_req.request
36
+
37
+ expect(usage).to be_a(DeepL::Resources::Usage)
38
+ expect(usage.character_count).to be_a(Numeric)
39
+ expect(usage.character_limit).to be_a(Numeric)
40
+ end
41
+ end
42
+ end
43
+ end