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,86 @@
1
+ # Copyright 2025 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ require 'time'
7
+
8
+ module DeepL
9
+ module Resources
10
+ class ConfiguredRules
11
+ attr_reader :dates_and_times, :formatting, :numbers, :punctuation,
12
+ :spelling_and_grammar, :style_and_tone, :vocabulary
13
+
14
+ def initialize(configured_rules_data)
15
+ @dates_and_times = extract_rule(configured_rules_data, 'dates_and_times')
16
+ @formatting = extract_rule(configured_rules_data, 'formatting')
17
+ @numbers = extract_rule(configured_rules_data, 'numbers')
18
+ @punctuation = extract_rule(configured_rules_data, 'punctuation')
19
+ @spelling_and_grammar = extract_rule(configured_rules_data, 'spelling_and_grammar')
20
+ @style_and_tone = extract_rule(configured_rules_data, 'style_and_tone')
21
+ @vocabulary = extract_rule(configured_rules_data, 'vocabulary')
22
+ end
23
+
24
+ private
25
+
26
+ def extract_rule(data, key)
27
+ data[key] || {}
28
+ end
29
+ end
30
+
31
+ class CustomInstruction
32
+ attr_reader :id, :label, :prompt, :source_language
33
+
34
+ def initialize(custom_instruction_data)
35
+ @id = custom_instruction_data['id']
36
+ @label = custom_instruction_data['label']
37
+ @prompt = custom_instruction_data['prompt']
38
+ @source_language = custom_instruction_data['source_language']
39
+ end
40
+ end
41
+
42
+ class StyleRule < Base
43
+ attr_reader :style_id, :name, :creation_time, :updated_time, :language, :version,
44
+ :configured_rules, :custom_instructions
45
+
46
+ def initialize(style_rule, *args)
47
+ super(*args)
48
+ extract_basic_fields(style_rule)
49
+ extract_configured_rules(style_rule)
50
+ extract_custom_instructions(style_rule)
51
+ end
52
+
53
+ private
54
+
55
+ def extract_basic_fields(style_rule)
56
+ @style_id = style_rule['style_id']
57
+ @name = style_rule['name']
58
+ @creation_time = parse_time(style_rule['creation_time'])
59
+ @updated_time = parse_time(style_rule['updated_time'])
60
+ @language = style_rule['language']
61
+ @version = style_rule['version']
62
+ end
63
+
64
+ def extract_configured_rules(style_rule)
65
+ configured_rules_data = style_rule['configured_rules']
66
+ @configured_rules = configured_rules_data ? ConfiguredRules.new(configured_rules_data) : nil
67
+ end
68
+
69
+ def extract_custom_instructions(style_rule)
70
+ @custom_instructions = style_rule['custom_instructions']&.map do |ci|
71
+ CustomInstruction.new(ci)
72
+ end
73
+ end
74
+
75
+ def to_s
76
+ "#{style_id} - #{name}"
77
+ end
78
+
79
+ def parse_time(time_string)
80
+ return nil unless time_string
81
+
82
+ Time.parse(time_string)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,24 @@
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
+ module DeepL
7
+ module Resources
8
+ class Text < Base
9
+ attr_reader :text, :detected_source_language, :model_type_used
10
+
11
+ def initialize(text, detected_source_language, model_type_used, *args)
12
+ super(*args)
13
+
14
+ @text = text
15
+ @detected_source_language = detected_source_language
16
+ @model_type_used = model_type_used
17
+ end
18
+
19
+ def to_s
20
+ text
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
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
+ class TranslationMemory < Base
9
+ attr_reader :translation_memory_id, :name, :source_language, :target_languages, :segment_count
10
+
11
+ def initialize(translation_memory, *args)
12
+ super(*args)
13
+ @translation_memory_id = translation_memory['translation_memory_id']
14
+ @name = translation_memory['name']
15
+ @source_language = translation_memory['source_language']
16
+ @target_languages = translation_memory['target_languages'] || []
17
+ @segment_count = translation_memory['segment_count'] || 0
18
+ end
19
+
20
+ def to_s
21
+ "#{translation_memory_id} - #{name}"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
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
+ module DeepL
7
+ module Resources
8
+ class Usage < Base
9
+ attr_reader :character_count, :character_limit
10
+
11
+ def initialize(character_count, character_limit, *args)
12
+ super(*args)
13
+
14
+ @character_count = character_count
15
+ @character_limit = character_limit
16
+ end
17
+
18
+ def to_s
19
+ "#{character_count} / #{character_limit}"
20
+ end
21
+
22
+ def quota_exceeded?
23
+ character_count >= character_limit
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,61 @@
1
+ # Copyright 2025 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ class StyleRuleApi
8
+ def initialize(api, options = {})
9
+ @api = api
10
+ @options = options
11
+ end
12
+
13
+ def list(options = {})
14
+ DeepL::Requests::StyleRule::List.new(@api, options).request
15
+ end
16
+
17
+ def create(name, language, options = {})
18
+ DeepL::Requests::StyleRule::Create.new(@api, name, language, options).request
19
+ end
20
+
21
+ def find(style_id, options = {})
22
+ DeepL::Requests::StyleRule::Find.new(@api, style_id, options).request
23
+ end
24
+
25
+ def update_name(style_id, name, options = {})
26
+ DeepL::Requests::StyleRule::Update.new(@api, style_id, name, options).request
27
+ end
28
+
29
+ def destroy(style_id, options = {})
30
+ DeepL::Requests::StyleRule::Destroy.new(@api, style_id, options).request
31
+ end
32
+
33
+ def update_configured_rules(style_id, configured_rules, options = {})
34
+ DeepL::Requests::StyleRule::UpdateConfiguredRules.new(@api, style_id, configured_rules,
35
+ options).request
36
+ end
37
+
38
+ def create_custom_instruction(style_id, label, prompt, source_language = nil, options = {})
39
+ DeepL::Requests::StyleRule::CreateCustomInstruction.new(@api, style_id, label, prompt,
40
+ source_language, options).request
41
+ end
42
+
43
+ def find_custom_instruction(style_id, instruction_id, options = {})
44
+ DeepL::Requests::StyleRule::FindCustomInstruction.new(@api, style_id, instruction_id,
45
+ options).request
46
+ end
47
+
48
+ def update_custom_instruction(style_id, instruction_id, label,
49
+ prompt, source_language = nil,
50
+ options = {})
51
+ DeepL::Requests::StyleRule::UpdateCustomInstruction.new(@api, style_id, instruction_id,
52
+ label, prompt, source_language,
53
+ options).request
54
+ end
55
+
56
+ def destroy_custom_instruction(style_id, instruction_id, options = {})
57
+ DeepL::Requests::StyleRule::DestroyCustomInstruction.new(@api, style_id, instruction_id,
58
+ options).request
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,17 @@
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
+ class TranslationMemoryApi
8
+ def initialize(api, options = {})
9
+ @api = api
10
+ @options = options
11
+ end
12
+
13
+ def list(options = {})
14
+ DeepL::Requests::TranslationMemory::List.new(@api, options).request
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ # Copyright 2024 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 file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Utils
8
+ class BackoffTimer
9
+ # Implements exponential-backoff strategy.
10
+ # This strategy is based on the GRPC Connection Backoff Protocol:
11
+ # https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
12
+
13
+ BACKOFF_INITIAL = 1.0
14
+ BACKOFF_MAX = 120.0
15
+ BACKOFF_JITTER = 0.23
16
+ BACKOFF_MULTIPLIER = 1.6
17
+
18
+ attr_reader :num_retries
19
+
20
+ def initialize(min_connection_timeout = 10.0)
21
+ @num_retries = 0
22
+ @backoff = BACKOFF_INITIAL
23
+ @deadline = Time.now.to_f + @backoff
24
+ @min_connection_timeout = min_connection_timeout
25
+ end
26
+
27
+ def current_request_timeout
28
+ [time_until_deadline, @min_connection_timeout].max
29
+ end
30
+
31
+ def time_until_deadline
32
+ [@deadline - Time.now.to_f, 0.0].max
33
+ end
34
+
35
+ def sleep_until_deadline
36
+ sleep(time_until_deadline)
37
+ # Apply multiplier to current backoff time
38
+ @backoff = [@backoff * BACKOFF_MULTIPLIER, BACKOFF_MAX].min
39
+ # Get deadline by applying jitter as a proportion of backoff:
40
+ # if jitter is 0.1, then multiply backoff by random value in [0.9, 1.1]
41
+ @deadline = Time.now.to_f + (@backoff * (1 + (BACKOFF_JITTER * rand(-1.0..1.0))))
42
+ @num_retries += 1
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,34 @@
1
+ # Copyright 2022 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
+ module DeepL
7
+ module Utils
8
+ class ExceptionBuilder
9
+ attr_reader :request, :response
10
+
11
+ def self.error_class_from_response_code(code) # rubocop:disable Metrics/CyclomaticComplexity
12
+ case code
13
+ when 400 then Exceptions::BadRequest
14
+ when 401, 403 then Exceptions::AuthorizationFailed
15
+ when 404 then Exceptions::NotFound
16
+ when 413 then Exceptions::RequestEntityTooLarge
17
+ when 429 then Exceptions::LimitExceeded
18
+ when 456 then Exceptions::QuotaExceeded
19
+ when 500..599 then Exceptions::ServerError
20
+ else Exceptions::RequestError
21
+ end
22
+ end
23
+
24
+ def initialize(response)
25
+ @response = response
26
+ end
27
+
28
+ def build
29
+ error_class = self.class.error_class_from_response_code(response.code.to_i)
30
+ error_class.new(response)
31
+ end
32
+ end
33
+ end
34
+ end
data/lib/deepl.rb ADDED
@@ -0,0 +1,175 @@
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
+ # -- Dependencies
7
+ require 'json'
8
+ require 'net/http'
9
+
10
+ # -- Exceptions
11
+ require_relative 'deepl/exceptions/error'
12
+ require_relative 'deepl/exceptions/request_error'
13
+ require_relative 'deepl/exceptions/authorization_failed'
14
+ require_relative 'deepl/exceptions/bad_request'
15
+ require_relative 'deepl/exceptions/limit_exceeded'
16
+ require_relative 'deepl/exceptions/quota_exceeded'
17
+ require_relative 'deepl/exceptions/not_found'
18
+ require_relative 'deepl/exceptions/not_supported'
19
+ require_relative 'deepl/exceptions/request_entity_too_large'
20
+ require_relative 'deepl/exceptions/document_translation_error'
21
+ require_relative 'deepl/exceptions/server_error'
22
+
23
+ # -- Requests
24
+ require_relative 'deepl/requests/base'
25
+ require_relative 'deepl/requests/document/download'
26
+ require_relative 'deepl/requests/document/get_status'
27
+ require_relative 'deepl/requests/document/upload'
28
+ require_relative 'deepl/requests/glossary/create'
29
+ require_relative 'deepl/requests/glossary/destroy'
30
+ require_relative 'deepl/requests/glossary/entries'
31
+ require_relative 'deepl/requests/glossary/find'
32
+ require_relative 'deepl/requests/glossary/language_pairs'
33
+ require_relative 'deepl/requests/glossary/list'
34
+ require_relative 'deepl/requests/style_rule/list'
35
+ require_relative 'deepl/requests/style_rule/create'
36
+ require_relative 'deepl/requests/style_rule/find'
37
+ require_relative 'deepl/requests/style_rule/update'
38
+ require_relative 'deepl/requests/style_rule/destroy'
39
+ require_relative 'deepl/requests/style_rule/update_configured_rules'
40
+ require_relative 'deepl/requests/style_rule/create_custom_instruction'
41
+ require_relative 'deepl/requests/style_rule/find_custom_instruction'
42
+ require_relative 'deepl/requests/style_rule/update_custom_instruction'
43
+ require_relative 'deepl/requests/style_rule/destroy_custom_instruction'
44
+ require_relative 'deepl/requests/translation_memory/list'
45
+ require_relative 'deepl/requests/languages'
46
+ require_relative 'deepl/requests/translate'
47
+ require_relative 'deepl/requests/usage'
48
+ require_relative 'deepl/requests/rephrase'
49
+
50
+ # -- Responses and resources
51
+ require_relative 'deepl/resources/base'
52
+ require_relative 'deepl/resources/document_handle'
53
+ require_relative 'deepl/resources/document_translation_status'
54
+ require_relative 'deepl/resources/glossary'
55
+ require_relative 'deepl/resources/style_rule'
56
+ require_relative 'deepl/resources/translation_memory'
57
+ require_relative 'deepl/resources/language'
58
+ require_relative 'deepl/resources/language_pair'
59
+ require_relative 'deepl/resources/text'
60
+ require_relative 'deepl/resources/usage'
61
+
62
+ # -- Utils
63
+ require_relative 'deepl/utils/exception_builder'
64
+ require_relative 'deepl/utils/backoff_timer'
65
+
66
+ # -- Constants
67
+ require_relative 'deepl/constants/base_constant'
68
+ require_relative 'deepl/constants/formality'
69
+ require_relative 'deepl/constants/model_type'
70
+ require_relative 'deepl/constants/split_sentences'
71
+ require_relative 'deepl/constants/tag_handling'
72
+ require_relative 'deepl/constants/tone'
73
+ require_relative 'deepl/constants/writing_style'
74
+
75
+ # -- HTTP Utils
76
+ require_relative 'http_client_options'
77
+
78
+ # -- Version
79
+ require_relative 'version'
80
+
81
+ # -- Other wrappers
82
+ require_relative 'deepl/api'
83
+ require_relative 'deepl/configuration'
84
+ require_relative 'deepl/document_api'
85
+ require_relative 'deepl/glossary_api'
86
+ require_relative 'deepl/style_rule_api'
87
+ require_relative 'deepl/translation_memory_api'
88
+
89
+ # -- Gem interface
90
+ module DeepL
91
+ extend self
92
+
93
+ ## -- API shortcuts
94
+
95
+ def api
96
+ @api ||= API.new(configuration)
97
+ end
98
+
99
+ def languages(options = {})
100
+ Requests::Languages.new(api, options).request
101
+ end
102
+
103
+ def translate(text, source_lang, target_lang, options = {})
104
+ configure if @configuration.nil?
105
+ Requests::Translate.new(api, text, source_lang, target_lang, options).request
106
+ end
107
+
108
+ def document(options = {})
109
+ configure if @configuration.nil?
110
+ DocumentApi.new(api, options)
111
+ end
112
+
113
+ def glossaries(options = {})
114
+ configure if @configuration.nil?
115
+ GlossaryApi.new(api, options)
116
+ end
117
+
118
+ def style_rules(options = {})
119
+ configure if @configuration.nil?
120
+ StyleRuleApi.new(api, options)
121
+ end
122
+
123
+ def translation_memories(options = {})
124
+ configure if @configuration.nil?
125
+ TranslationMemoryApi.new(api, options)
126
+ end
127
+
128
+ def rephrase(text, target_lang = nil, writing_style = nil, tone = nil, options = {}) # rubocop:disable Metrics/ParameterLists
129
+ configure if @configuration.nil?
130
+ Requests::Rephrase.new(api, text, target_lang, writing_style, tone, options).request
131
+ end
132
+
133
+ def usage(options = {})
134
+ configure if @configuration.nil?
135
+ Requests::Usage.new(api, options).request
136
+ end
137
+
138
+ def http_client
139
+ @http_client
140
+ end
141
+
142
+ def with_session(client_options = HTTPClientOptions.new()) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
143
+ raise ArgumentError 'This method requires a block to be passed in which contains the actual API calls, see README for example usage.' unless block_given? # rubocop:disable Layout/LineLength
144
+
145
+ has_proxy = client_options.proxy.key?('proxy_addr') and client_options.proxy.key?('proxy_port')
146
+ begin
147
+ uri = URI(configuration.host)
148
+ http = Net::HTTP.new(uri.host, uri.port, has_proxy ? client_options.proxy['proxy_addr'] : nil,
149
+ has_proxy ? client_options.proxy['proxy_port'] : nil)
150
+ http.use_ssl = client_options.enable_ssl_verification
151
+ http.ca_file = client_options.cert_path if client_options.cert_path
152
+ http.open_timeout = client_options.open_timeout unless client_options.open_timeout.nil?
153
+ http.read_timeout = client_options.read_timeout unless client_options.read_timeout.nil?
154
+ http.write_timeout = client_options.write_timeout unless client_options.write_timeout.nil?
155
+ http.ssl_timeout = client_options.ssl_timeout unless client_options.ssl_timeout.nil?
156
+ http.start
157
+ @http_client = http
158
+ api.update_http_client(http)
159
+ yield
160
+ ensure
161
+ http.finish
162
+ end
163
+ end
164
+
165
+ # -- Configuration
166
+
167
+ def configuration
168
+ @configuration ||= Configuration.new
169
+ end
170
+
171
+ def configure
172
+ yield configuration if block_given?
173
+ configuration.validate!
174
+ end
175
+ end
@@ -0,0 +1,22 @@
1
+ # Copyright 2024 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 file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ class HTTPClientOptions
8
+ attr_reader :proxy, :send_platform_info, :enable_ssl_verification, :cert_path, :open_timeout,
9
+ :read_timeout, :write_timeout, :ssl_timeout
10
+
11
+ def initialize(proxy, cert_path, enable_ssl_verification: true, open_timeout: nil, # rubocop:disable Metrics/ParameterLists
12
+ read_timeout: nil, write_timeout: nil, ssl_timeout: nil)
13
+ @proxy = proxy
14
+ @enable_ssl_verification = enable_ssl_verification
15
+ @cert_path = cert_path
16
+ @open_timeout = open_timeout
17
+ @read_timeout = read_timeout
18
+ @write_timeout = write_timeout
19
+ @ssl_timeout = ssl_timeout
20
+ end
21
+ end
22
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,8 @@
1
+ # Copyright 2025 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ VERSION = '3.7.0'
8
+ end
@@ -0,0 +1,20 @@
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::API do
9
+ subject(:api) { described_class.new(configuration) }
10
+
11
+ let(:configuration) { DeepL::Configuration.new }
12
+
13
+ describe '#initialize' do
14
+ context 'when building an API object' do
15
+ it 'saves the configuration' do
16
+ expect(api.configuration).to be(configuration)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,122 @@
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 'logger'
7
+ require 'spec_helper'
8
+
9
+ describe DeepL::Configuration do
10
+ subject(:config) { described_class.new(attributes) }
11
+
12
+ let(:attributes) { {} }
13
+
14
+ around do |tests|
15
+ tmp_env = ENV.to_hash
16
+ ENV.clear
17
+ tests.call
18
+ ENV.replace(tmp_env)
19
+ end
20
+
21
+ describe '#initialize' do
22
+ context 'when using default configuration attributes' do
23
+ it 'uses default attributes' do
24
+ expect(config.auth_key).to eq(ENV.fetch('DEEPL_AUTH_KEY', nil))
25
+ expect(config.host).to eq('https://api.deepl.com')
26
+ expect(config.max_doc_status_queries).to be_nil
27
+ expect(config.version).to eq('v2')
28
+ end
29
+
30
+ it 'sends platform information' do
31
+ expect(config.user_agent).to include('deepl-ruby/')
32
+ expect(config.user_agent).to include('(')
33
+ expect(config.user_agent).to include(')')
34
+ expect(config.user_agent).to include(' ruby/')
35
+ end
36
+ end
37
+
38
+ context 'when using custom configuration attributes' do
39
+ let(:logger) { Logger.new($stdout) }
40
+ let(:attributes) { { auth_key: 'SAMPLE', host: 'https://api-free.deepl.com', logger: logger, max_doc_status_queries: 42, max_network_retries: 43, version: 'v1' } }
41
+
42
+ it 'uses custom attributes' do
43
+ expect(config.auth_key).to eq(attributes[:auth_key])
44
+ expect(config.host).to eq(attributes[:host])
45
+ expect(config.max_doc_status_queries).to eq(attributes[:max_doc_status_queries])
46
+ expect(config.max_network_retries).to eq(attributes[:max_network_retries])
47
+ expect(config.version).to eq(attributes[:version])
48
+ end
49
+ end
50
+
51
+ context 'when using a free key' do
52
+ let(:attributes) { { auth_key: '123e4567-e89b-12d3-a456-426614174000:fx' } }
53
+
54
+ it 'uses the free API URL' do
55
+ expect(config.host).to eq('https://api-free.deepl.com')
56
+ end
57
+ end
58
+
59
+ context 'when using a pro key' do
60
+ let(:attributes) { { auth_key: '123e4567-e89b-12d3-a456-426614174000' } }
61
+
62
+ it 'uses the pro API URL' do
63
+ expect(config.host).to eq('https://api.deepl.com')
64
+ end
65
+ end
66
+
67
+ context 'when opting out of sending platform info' do
68
+ subject(:config) { described_class.new(attributes, nil, nil, false) }
69
+
70
+ it 'does not send platform information' do
71
+ expect(config.user_agent).to include('deepl-ruby/')
72
+ expect(config.user_agent).not_to include('(')
73
+ expect(config.user_agent).not_to include(')')
74
+ expect(config.user_agent).not_to include(' ruby/')
75
+ end
76
+ end
77
+
78
+ context 'when registering an app and sending platform info' do
79
+ subject(:config) { described_class.new(attributes, 'MyTestApp', '0.1.3', true) }
80
+
81
+ it 'sends platform and app info' do
82
+ expect(config.user_agent).to include('deepl-ruby/')
83
+ expect(config.user_agent).to include('(')
84
+ expect(config.user_agent).to include(')')
85
+ expect(config.user_agent).to include(' ruby/')
86
+ expect(config.user_agent).to include('MyTestApp/0.1.3')
87
+ end
88
+ end
89
+
90
+ context 'when registering an app and not sending platform info' do
91
+ subject(:config) { described_class.new(attributes, 'MyTestApp', '0.1.3', false) }
92
+
93
+ it 'sends app, but not platform info' do
94
+ expect(config.user_agent).to include('deepl-ruby/')
95
+ expect(config.user_agent).not_to include('(')
96
+ expect(config.user_agent).not_to include(')')
97
+ expect(config.user_agent).not_to include(' ruby/')
98
+ expect(config.user_agent).to include('MyTestApp/0.1.3')
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '#validate!' do
104
+ let(:auth_message) { 'auth_key not provided' }
105
+
106
+ context 'when providing a valid auth key' do
107
+ let(:attributes) { { auth_key: '' } }
108
+
109
+ it 'raises an error' do
110
+ expect { config.validate! }.to raise_error(DeepL::Exceptions::Error, auth_message)
111
+ end
112
+ end
113
+
114
+ context 'when providing an invalid auth key' do
115
+ let(:attributes) { { auth_key: 'not-empty' } }
116
+
117
+ it 'does not raise an error' do
118
+ expect { config.validate! }.not_to raise_error
119
+ end
120
+ end
121
+ end
122
+ end