deepl-rb 3.7.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.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +4 -2
  3. data/CHANGELOG.md +39 -1
  4. data/Gemfile +0 -2
  5. data/README.md +225 -16
  6. data/VERSION +1 -1
  7. data/deepl-rb.gemspec +52 -19
  8. data/lib/deepl/document_api.rb +11 -1
  9. data/lib/deepl/requests/base.rb +55 -3
  10. data/lib/deepl/requests/document/upload.rb +50 -1
  11. data/lib/deepl/requests/rephrase.rb +3 -2
  12. data/lib/deepl/requests/translate.rb +6 -3
  13. data/lib/deepl/requests/translation_memory/base.rb +47 -0
  14. data/lib/deepl/requests/translation_memory/create_export.rb +41 -0
  15. data/lib/deepl/requests/translation_memory/create_import.rb +53 -0
  16. data/lib/deepl/requests/translation_memory/destroy.rb +37 -0
  17. data/lib/deepl/requests/translation_memory/download_export.rb +35 -0
  18. data/lib/deepl/requests/translation_memory/find.rb +38 -0
  19. data/lib/deepl/requests/translation_memory/find_job.rb +38 -0
  20. data/lib/deepl/requests/translation_memory/list.rb +5 -18
  21. data/lib/deepl/requests/translation_memory/segments.rb +46 -0
  22. data/lib/deepl/requests/translation_memory/storage_base.rb +47 -0
  23. data/lib/deepl/requests/translation_memory/upload_file.rb +47 -0
  24. data/lib/deepl/resources/translation_memory.rb +4 -1
  25. data/lib/deepl/resources/translation_memory_export.rb +37 -0
  26. data/lib/deepl/resources/translation_memory_import.rb +29 -0
  27. data/lib/deepl/resources/translation_memory_job.rb +142 -0
  28. data/lib/deepl/resources/translation_memory_segments.rb +78 -0
  29. data/lib/deepl/translation_memory_api.rb +293 -1
  30. data/lib/deepl/utils/time_parser.rb +26 -0
  31. data/lib/deepl.rb +38 -4
  32. data/lib/version.rb +1 -1
  33. data/spec/api/deepl_spec.rb +134 -332
  34. data/spec/integration_tests/document_api_spec.rb +51 -18
  35. data/spec/integration_tests/document_error_paths_spec.rb +33 -0
  36. data/spec/integration_tests/glossary_api_spec.rb +114 -0
  37. data/spec/integration_tests/glossary_error_paths_spec.rb +107 -0
  38. data/spec/integration_tests/integration_test_utils.rb +8 -0
  39. data/spec/integration_tests/languages_api_spec.rb +54 -0
  40. data/spec/integration_tests/languages_error_paths_spec.rb +25 -0
  41. data/spec/integration_tests/rephrase_api_spec.rb +90 -0
  42. data/spec/integration_tests/rephrase_error_paths_spec.rb +53 -0
  43. data/spec/integration_tests/smoke_test_spec.rb +24 -0
  44. data/spec/integration_tests/style_rule_api_spec.rb +1 -19
  45. data/spec/integration_tests/style_rule_error_paths_spec.rb +45 -0
  46. data/spec/integration_tests/translate_api_spec.rb +117 -0
  47. data/spec/integration_tests/translate_error_paths_spec.rb +48 -0
  48. data/spec/integration_tests/translation_memory_api_spec.rb +191 -17
  49. data/spec/integration_tests/translation_memory_error_paths_spec.rb +87 -0
  50. data/spec/integration_tests/usage_api_spec.rb +29 -0
  51. data/spec/integration_tests/usage_error_paths_spec.rb +18 -0
  52. data/spec/requests/document/upload_spec.rb +130 -0
  53. data/spec/requests/glossary/create_spec.rb +0 -21
  54. data/spec/requests/glossary/destroy_spec.rb +0 -39
  55. data/spec/requests/glossary/entries_spec.rb +0 -35
  56. data/spec/requests/glossary/find_spec.rb +0 -40
  57. data/spec/requests/glossary/language_pairs_spec.rb +0 -13
  58. data/spec/requests/glossary/list_spec.rb +0 -27
  59. data/spec/requests/languages_spec.rb +0 -41
  60. data/spec/requests/rephrase_spec.rb +13 -139
  61. data/spec/requests/style_rule/create_custom_instruction_spec.rb +0 -24
  62. data/spec/requests/style_rule/create_spec.rb +0 -16
  63. data/spec/requests/style_rule/destroy_custom_instruction_spec.rb +0 -26
  64. data/spec/requests/style_rule/destroy_spec.rb +0 -27
  65. data/spec/requests/style_rule/find_custom_instruction_spec.rb +0 -27
  66. data/spec/requests/style_rule/find_spec.rb +0 -28
  67. data/spec/requests/style_rule/list_spec.rb +0 -31
  68. data/spec/requests/style_rule/update_configured_rules_spec.rb +0 -21
  69. data/spec/requests/style_rule/update_custom_instruction_spec.rb +0 -26
  70. data/spec/requests/style_rule/update_spec.rb +0 -19
  71. data/spec/requests/translate_spec.rb +63 -217
  72. data/spec/requests/translation_memory/create_export_spec.rb +37 -0
  73. data/spec/requests/translation_memory/create_import_spec.rb +61 -0
  74. data/spec/requests/translation_memory/destroy_spec.rb +36 -0
  75. data/spec/requests/translation_memory/download_export_spec.rb +54 -0
  76. data/spec/requests/translation_memory/find_job_spec.rb +37 -0
  77. data/spec/requests/translation_memory/find_spec.rb +36 -0
  78. data/spec/requests/translation_memory/list_spec.rb +0 -34
  79. data/spec/requests/translation_memory/segments_spec.rb +58 -0
  80. data/spec/requests/translation_memory/upload_file_spec.rb +55 -0
  81. data/spec/requests/usage_spec.rb +0 -16
  82. data/spec/resources/custom_instruction_spec.rb +32 -0
  83. data/spec/resources/style_rule_spec.rb +68 -0
  84. data/spec/resources/translation_memory_export_spec.rb +38 -0
  85. data/spec/resources/translation_memory_import_spec.rb +30 -0
  86. data/spec/resources/translation_memory_job_spec.rb +109 -0
  87. data/spec/resources/translation_memory_segments_spec.rb +78 -0
  88. data/spec/resources/translation_memory_spec.rb +18 -1
  89. data/spec/spec_helper.rb +15 -45
  90. data/spec/support/live_mock_server.rb +12 -0
  91. data/spec/support/managed_glossary.rb +65 -0
  92. data/spec/support/managed_style_rule.rb +17 -0
  93. data/spec/support/managed_translation_memory.rb +55 -0
  94. metadata +50 -17
  95. data/spec/fixtures/vcr_cassettes/deepl_document.yml +0 -95
  96. data/spec/fixtures/vcr_cassettes/deepl_document_download.yml +0 -1214
  97. data/spec/fixtures/vcr_cassettes/deepl_glossaries.yml +0 -1163
  98. data/spec/fixtures/vcr_cassettes/deepl_languages.yml +0 -54
  99. data/spec/fixtures/vcr_cassettes/deepl_rephrase.yml +0 -87
  100. data/spec/fixtures/vcr_cassettes/deepl_translate.yml +0 -358
  101. data/spec/fixtures/vcr_cassettes/deepl_usage.yml +0 -129
  102. data/spec/fixtures/vcr_cassettes/glossaries.yml +0 -1702
  103. data/spec/fixtures/vcr_cassettes/languages.yml +0 -229
  104. data/spec/fixtures/vcr_cassettes/rephrase_texts.yml +0 -401
  105. data/spec/fixtures/vcr_cassettes/style_rules.yml +0 -92
  106. data/spec/fixtures/vcr_cassettes/style_rules_crud.yml +0 -926
  107. data/spec/fixtures/vcr_cassettes/translate_texts.yml +0 -10630
  108. data/spec/fixtures/vcr_cassettes/translation_memories.yml +0 -74
  109. data/spec/fixtures/vcr_cassettes/usage.yml +0 -171
@@ -49,10 +49,59 @@ module DeepL
49
49
  ]
50
50
  filename_param = filename || File.basename(input_file_path)
51
51
  form_data.push(['filename', filename_param]) unless filename_param.nil?
52
- add_supported_options_to_form(form_data)
52
+ add_extra_options_to_form(form_data)
53
53
  form_data
54
54
  end
55
55
 
56
+ def add_extra_options_to_form(form_data)
57
+ add_supported_options_to_form(form_data)
58
+ add_glossary_ids_to_form(form_data)
59
+ add_style_rule_to_form(form_data)
60
+ add_translation_memory_to_form(form_data)
61
+ add_translation_memory_threshold_to_form(form_data)
62
+ end
63
+
64
+ # Validates and serializes the `glossary_ids` option (see
65
+ # `DeepL::Requests::Base#build_glossary_ids_param`) and appends it to the form data as a
66
+ # comma-separated `glossary_ids` field. Requires `source_lang`, cannot be combined with
67
+ # the singular `glossary_id` option, and allows at most 5 IDs.
68
+ def add_glossary_ids_to_form(form_data)
69
+ glossary_ids = build_glossary_ids_param(source_lang)
70
+ form_data.push(['glossary_ids', glossary_ids.join(',')]) unless glossary_ids.nil?
71
+ end
72
+
73
+ # Serializes the `style_rule` option and appends it to the form data as a `style_id`
74
+ # field. Mirrors text translation: accepts either a style rule ID string or a
75
+ # `DeepL::Resources::StyleRule` object.
76
+ def add_style_rule_to_form(form_data)
77
+ return unless option?(:style_rule)
78
+
79
+ rule = delete_option(:style_rule)
80
+ style_id = rule.is_a?(DeepL::Resources::StyleRule) ? rule.style_id : rule
81
+ form_data.push(['style_id', style_id.to_s])
82
+ end
83
+
84
+ # Serializes the `translation_memory` option and appends it to the form data as a
85
+ # `translation_memory_id` field. Mirrors text translation: the `translation_memory`
86
+ # option accepts either a translation memory ID string or a
87
+ # `DeepL::Resources::TranslationMemory` object.
88
+ def add_translation_memory_to_form(form_data)
89
+ return unless option?(:translation_memory)
90
+
91
+ tm = delete_option(:translation_memory)
92
+ tm_id = tm.is_a?(DeepL::Resources::TranslationMemory) ? tm.translation_memory_id : tm
93
+ form_data.push(['translation_memory_id', tm_id.to_s])
94
+ end
95
+
96
+ # Serializes the `translation_memory_threshold` option (integer 0-100) and appends it to
97
+ # the form data as a `translation_memory_threshold` field, mirroring text translation.
98
+ def add_translation_memory_threshold_to_form(form_data)
99
+ return unless option?(:translation_memory_threshold)
100
+
101
+ threshold = delete_option(:translation_memory_threshold)
102
+ form_data.push(['translation_memory_threshold', threshold.to_s])
103
+ end
104
+
56
105
  def add_supported_options_to_form(form_data)
57
106
  SUPPORTED_OPTIONS.each do |option_name|
58
107
  option_value = option(option_name)
@@ -8,8 +8,9 @@ module DeepL
8
8
  class Rephrase < Base
9
9
  attr_reader :text, :target_lang, :writing_style, :tone
10
10
 
11
- def initialize(api, text, target_lang = nil, writing_style = nil, tone = nil, options = {}) # rubocop:disable Metrics/ParameterLists
12
- super(api, options)
11
+ def initialize(api, text, target_lang = nil, writing_style = nil, tone = nil, options = {}, # rubocop:disable Metrics/ParameterLists
12
+ additional_headers = {})
13
+ super(api, options, additional_headers)
13
14
  @text = text
14
15
  @target_lang = target_lang
15
16
  @writing_style = writing_style
@@ -30,8 +30,8 @@ module DeepL
30
30
  attr_reader :text, :source_lang, :target_lang, :ignore_tags, :splitting_tags,
31
31
  :non_splitting_tags, :model_type, :custom_instructions, :tag_handling_version
32
32
 
33
- def initialize(api, text, source_lang, target_lang, options = {})
34
- super(api, options)
33
+ def initialize(api, text, source_lang, target_lang, options = {}, additional_headers = {})
34
+ super(api, options, additional_headers)
35
35
  @text = text
36
36
  @source_lang = source_lang
37
37
  @target_lang = target_lang
@@ -39,7 +39,7 @@ module DeepL
39
39
  tweak_parameters!
40
40
  end
41
41
 
42
- def request # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
42
+ def request # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
43
43
  text_arrayified = text.is_a?(Array) ? text : [text]
44
44
  payload = { text: text_arrayified, source_lang: source_lang, target_lang: target_lang }
45
45
 
@@ -65,6 +65,9 @@ module DeepL
65
65
  payload[:translation_memory_threshold] = delete_option(:translation_memory_threshold)
66
66
  end
67
67
 
68
+ glossary_ids = build_glossary_ids_param(source_lang)
69
+ payload[:glossary_ids] = glossary_ids unless glossary_ids.nil?
70
+
68
71
  build_texts(*execute_request_with_retries(post_request(payload)))
69
72
  end
70
73
 
@@ -0,0 +1,47 @@
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
+ module DeepL
7
+ module Requests
8
+ module TranslationMemory
9
+ ##
10
+ # Common behaviour of the translation memory endpoints: they are served under the `v3`
11
+ # prefix instead of the configured API version, and take their parameters in the query
12
+ # string rather than in the request body.
13
+
14
+ class Base < DeepL::Requests::Base
15
+ private
16
+
17
+ def uri
18
+ @uri ||= begin
19
+ base_uri = URI("#{host}/v3/#{path}")
20
+ query_string = build_query_string
21
+ base_uri.query = query_string unless query_string.empty?
22
+ base_uri
23
+ end
24
+ end
25
+
26
+ def build_query_string
27
+ # encode_www_form encodes a space as `+`, which is correct for a form body but not for
28
+ # a URI query string. A literal `+` is already escaped as %2B, so every remaining `+`
29
+ # is a space.
30
+ URI.encode_www_form(query_params).gsub('+', '%20')
31
+ end
32
+
33
+ def query_params
34
+ {}
35
+ end
36
+
37
+ def get_request # rubocop:disable Naming/AccessorMethodName
38
+ Net::HTTP::Get.new(uri.request_uri, add_json_content_type(headers))
39
+ end
40
+
41
+ def delete_request
42
+ Net::HTTP::Delete.new(uri.request_uri, add_json_content_type(headers))
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,41 @@
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
+ module DeepL
7
+ module Requests
8
+ module TranslationMemory
9
+ class CreateExport < Base
10
+ attr_reader :translation_memory_id
11
+
12
+ def initialize(api, translation_memory_id, options = {})
13
+ super(api, options)
14
+ @translation_memory_id = translation_memory_id
15
+ end
16
+
17
+ def request
18
+ build_translation_memory_export(*execute_request_with_retries(post_request({})))
19
+ end
20
+
21
+ def to_s
22
+ "POST #{uri.request_uri}"
23
+ end
24
+
25
+ private
26
+
27
+ def build_translation_memory_export(request, response)
28
+ data = JSON.parse(response.body)
29
+ # The API answers 200 when it reused a previously completed export, and 202 when it
30
+ # started a new one.
31
+ reused_existing = response.code.to_i == 200
32
+ Resources::TranslationMemoryExport.new(data, reused_existing, request, response)
33
+ end
34
+
35
+ def path
36
+ "translation_memories/#{translation_memory_id}/export"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,53 @@
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
+ module DeepL
7
+ module Requests
8
+ module TranslationMemory
9
+ class CreateImport < Base
10
+ attr_reader :file_name, :content_length
11
+
12
+ def initialize(api, file_name, content_length, options = {}, additional_headers = {})
13
+ super(api, options, additional_headers)
14
+ @file_name = file_name
15
+ @content_length = content_length
16
+ @content_type = delete_option(:content_type)
17
+ @display_name = delete_option(:display_name)
18
+ end
19
+
20
+ def request
21
+ build_translation_memory_import(*execute_request_with_retries(post_request(payload)))
22
+ end
23
+
24
+ def details
25
+ "HTTP Headers: #{headers}\nPayload #{payload}"
26
+ end
27
+
28
+ def to_s
29
+ "POST #{uri.request_uri}"
30
+ end
31
+
32
+ private
33
+
34
+ def payload
35
+ source_file = { file_name: file_name, content_length: content_length }
36
+ source_file[:content_type] = @content_type if @content_type
37
+ payload = { source_file: source_file }
38
+ payload[:parameters] = { display_name: @display_name } if @display_name
39
+ payload
40
+ end
41
+
42
+ def build_translation_memory_import(request, response)
43
+ data = JSON.parse(response.body)
44
+ Resources::TranslationMemoryImport.new(data, request, response)
45
+ end
46
+
47
+ def path
48
+ 'translation_memories/import'
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -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
+ module DeepL
7
+ module Requests
8
+ module TranslationMemory
9
+ class Destroy < Base
10
+ attr_reader :translation_memory_id
11
+
12
+ def initialize(api, translation_memory_id, options = {})
13
+ super(api, options)
14
+ @translation_memory_id = translation_memory_id
15
+ end
16
+
17
+ def request
18
+ build_response(*execute_request_with_retries(delete_request))
19
+ end
20
+
21
+ def to_s
22
+ "DELETE #{uri.request_uri}"
23
+ end
24
+
25
+ private
26
+
27
+ def build_response(_, _)
28
+ translation_memory_id
29
+ end
30
+
31
+ def path
32
+ "translation_memories/#{translation_memory_id}"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,35 @@
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
+ module DeepL
7
+ module Requests
8
+ module TranslationMemory
9
+ class DownloadExport < StorageBase
10
+ def initialize(api, download_url, output_file)
11
+ super(api, download_url)
12
+ @output_file = output_file
13
+ end
14
+
15
+ def request
16
+ extract_file(*execute_request_with_retries(get_request))
17
+ end
18
+
19
+ def to_s
20
+ "GET #{uri.request_uri}"
21
+ end
22
+
23
+ private
24
+
25
+ def get_request # rubocop:disable Naming/AccessorMethodName
26
+ Net::HTTP::Get.new(uri.request_uri, headers)
27
+ end
28
+
29
+ def extract_file(_request, response)
30
+ File.binwrite(@output_file, response.body)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,38 @@
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
+ module DeepL
7
+ module Requests
8
+ module TranslationMemory
9
+ class Find < Base
10
+ attr_reader :translation_memory_id
11
+
12
+ def initialize(api, translation_memory_id, options = {})
13
+ super(api, options)
14
+ @translation_memory_id = translation_memory_id
15
+ end
16
+
17
+ def request
18
+ build_translation_memory(*execute_request_with_retries(get_request))
19
+ end
20
+
21
+ def to_s
22
+ "GET #{uri.request_uri}"
23
+ end
24
+
25
+ private
26
+
27
+ def build_translation_memory(request, response)
28
+ data = JSON.parse(response.body)
29
+ Resources::TranslationMemory.new(data, request, response)
30
+ end
31
+
32
+ def path
33
+ "translation_memories/#{translation_memory_id}"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
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
+ module DeepL
7
+ module Requests
8
+ module TranslationMemory
9
+ class FindJob < Base
10
+ attr_reader :job_id
11
+
12
+ def initialize(api, job_id, options = {})
13
+ super(api, options)
14
+ @job_id = job_id
15
+ end
16
+
17
+ def request
18
+ build_job(*execute_request_with_retries(get_request))
19
+ end
20
+
21
+ def to_s
22
+ "GET #{uri.request_uri}"
23
+ end
24
+
25
+ private
26
+
27
+ def build_job(request, response)
28
+ data = JSON.parse(response.body)
29
+ Resources::TranslationMemoryJob.new(data, request, response)
30
+ end
31
+
32
+ def path
33
+ "translation_memories/jobs/#{job_id}"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -7,6 +7,8 @@ module DeepL
7
7
  module Requests
8
8
  module TranslationMemory
9
9
  class List < Base
10
+ SUPPORTED_OPTIONS = %w[page page_size].freeze
11
+
10
12
  def initialize(api, options = {})
11
13
  super
12
14
  end
@@ -21,11 +23,6 @@ module DeepL
21
23
 
22
24
  private
23
25
 
24
- def get_request # rubocop:disable Naming/AccessorMethodName
25
- http_headers = add_json_content_type(headers)
26
- Net::HTTP::Get.new(uri.request_uri, http_headers)
27
- end
28
-
29
26
  def build_translation_memory_list(request, response)
30
27
  data = JSON.parse(response.body)
31
28
  data['translation_memories'].map do |tm|
@@ -33,22 +30,12 @@ module DeepL
33
30
  end
34
31
  end
35
32
 
36
- def uri
37
- @uri ||= begin
38
- base_uri = URI("#{host}/v3/#{path}")
39
- query_string = build_query_string
40
- base_uri.query = query_string unless query_string.empty?
41
- base_uri
33
+ def query_params
34
+ SUPPORTED_OPTIONS.each_with_object({}) do |option_name, params|
35
+ params[option_name] = option(option_name).to_s if option?(option_name)
42
36
  end
43
37
  end
44
38
 
45
- def build_query_string
46
- params = {}
47
- params['page'] = option(:page).to_s if option?(:page)
48
- params['page_size'] = option(:page_size).to_s if option?(:page_size)
49
- URI.encode_www_form(params)
50
- end
51
-
52
39
  def path
53
40
  'translation_memories'
54
41
  end
@@ -0,0 +1,46 @@
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
+ module DeepL
7
+ module Requests
8
+ module TranslationMemory
9
+ class Segments < Base
10
+ SUPPORTED_OPTIONS = %w[page_size page_cursor filter_text filter_case_sensitive].freeze
11
+
12
+ attr_reader :translation_memory_id
13
+
14
+ def initialize(api, translation_memory_id, options = {})
15
+ super(api, options)
16
+ @translation_memory_id = translation_memory_id
17
+ end
18
+
19
+ def request
20
+ build_segments(*execute_request_with_retries(get_request))
21
+ end
22
+
23
+ def to_s
24
+ "GET #{uri.request_uri}"
25
+ end
26
+
27
+ private
28
+
29
+ def build_segments(request, response)
30
+ data = JSON.parse(response.body)
31
+ Resources::TranslationMemorySegments.new(data, request, response)
32
+ end
33
+
34
+ def query_params
35
+ SUPPORTED_OPTIONS.each_with_object({}) do |option_name, params|
36
+ params[option_name] = option(option_name).to_s if option?(option_name)
37
+ end
38
+ end
39
+
40
+ def path
41
+ "translation_memories/#{translation_memory_id}/segments"
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,47 @@
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
+ module DeepL
7
+ module Requests
8
+ module TranslationMemory
9
+ ##
10
+ # Common behaviour of the requests to a pre-signed storage URL handed out by the API, such
11
+ # as a translation memory upload or download URL. Those URLs point outside of the DeepL
12
+ # API, so they are requested over their own connection and the DeepL `Authorization` header
13
+ # is deliberately not sent.
14
+
15
+ class StorageBase < DeepL::Requests::Base
16
+ attr_reader :storage_url
17
+
18
+ def initialize(api, storage_url, options = {}, additional_headers = {})
19
+ super(api, options, additional_headers)
20
+ if storage_url.nil? || storage_url.empty?
21
+ raise Exceptions::Error, 'Storage URL must not be empty'
22
+ end
23
+
24
+ @storage_url = storage_url
25
+ end
26
+
27
+ private
28
+
29
+ def http_client
30
+ @http_client ||= begin
31
+ client = Net::HTTP.new(uri.host, uri.port)
32
+ client.use_ssl = uri.scheme == 'https'
33
+ client
34
+ end
35
+ end
36
+
37
+ def uri
38
+ @uri ||= URI(storage_url)
39
+ end
40
+
41
+ def headers
42
+ { 'User-Agent' => api.configuration.user_agent }.merge(@additional_headers)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,47 @@
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
+ module DeepL
7
+ module Requests
8
+ module TranslationMemory
9
+ class UploadFile < StorageBase
10
+ DEFAULT_CONTENT_TYPE = 'application/xml'
11
+
12
+ attr_reader :file_content, :content_type
13
+
14
+ def initialize(api, upload_url, file_content, content_type = DEFAULT_CONTENT_TYPE)
15
+ super(api, upload_url)
16
+ @file_content = file_content
17
+ @content_type = content_type || DEFAULT_CONTENT_TYPE
18
+ end
19
+
20
+ def request
21
+ build_response(*execute_request_with_retries(put_request))
22
+ end
23
+
24
+ def details
25
+ "HTTP Headers: #{headers}\nPayload #{file_content.bytesize} byte(s)"
26
+ end
27
+
28
+ def to_s
29
+ "PUT #{uri.request_uri}"
30
+ end
31
+
32
+ private
33
+
34
+ def put_request
35
+ put_req = Net::HTTP::Put.new(uri.request_uri,
36
+ headers.merge('Content-Type' => content_type))
37
+ put_req.body = file_content
38
+ put_req
39
+ end
40
+
41
+ def build_response(_, _)
42
+ nil
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -6,7 +6,8 @@
6
6
  module DeepL
7
7
  module Resources
8
8
  class TranslationMemory < Base
9
- attr_reader :translation_memory_id, :name, :source_language, :target_languages, :segment_count
9
+ attr_reader :translation_memory_id, :name, :source_language, :target_languages,
10
+ :segment_count, :creation_time, :updated_time
10
11
 
11
12
  def initialize(translation_memory, *args)
12
13
  super(*args)
@@ -15,6 +16,8 @@ module DeepL
15
16
  @source_language = translation_memory['source_language']
16
17
  @target_languages = translation_memory['target_languages'] || []
17
18
  @segment_count = translation_memory['segment_count'] || 0
19
+ @creation_time = Utils::TimeParser.parse_optional_time(translation_memory['creation_time'])
20
+ @updated_time = Utils::TimeParser.parse_optional_time(translation_memory['updated_time'])
18
21
  end
19
22
 
20
23
  def to_s
@@ -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
+ module DeepL
7
+ module Resources
8
+ ##
9
+ # A translation memory export job. Poll the job until it is finished to obtain the download
10
+ # URL of the exported TMX file.
11
+
12
+ class TranslationMemoryExport < Base
13
+ attr_reader :job_id, :translation_memory_id
14
+
15
+ def initialize(translation_memory_export, reused_existing, *args)
16
+ super(*args)
17
+ parameters = translation_memory_export['parameters'] || {}
18
+ @job_id = translation_memory_export['job_id']
19
+ @translation_memory_id = parameters['translation_memory_id']
20
+ @reused_existing = reused_existing
21
+ end
22
+
23
+ ##
24
+ # Checks if the API reused a previously completed export instead of starting a new one.
25
+ #
26
+ # @return [true] if so
27
+
28
+ def reused_existing?
29
+ @reused_existing
30
+ end
31
+
32
+ def to_s
33
+ "TranslationMemoryExport: ID: #{job_id}"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,29 @@
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
+ module DeepL
7
+ module Resources
8
+ ##
9
+ # A newly created translation memory import job. The TMX file must be uploaded to
10
+ # `upload_url` before `expires_at`, processing starts once the upload is detected.
11
+
12
+ class TranslationMemoryImport < Base
13
+ attr_reader :job_id, :upload_url, :expires_at
14
+
15
+ def initialize(translation_memory_import, *args)
16
+ super(*args)
17
+ @job_id = translation_memory_import['job_id']
18
+ @upload_url = translation_memory_import['upload_url']
19
+ @expires_at = Utils::TimeParser.parse_optional_time(
20
+ translation_memory_import['expires_at']
21
+ )
22
+ end
23
+
24
+ def to_s
25
+ "TranslationMemoryImport: ID: #{job_id}"
26
+ end
27
+ end
28
+ end
29
+ end