deepl-rb 3.9.0 → 3.10.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 (237) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +70 -52
  3. data/.rubocop.yml +26 -0
  4. data/CHANGELOG.md +429 -18
  5. data/Gemfile +4 -0
  6. data/README.md +981 -223
  7. data/Rakefile +18 -5
  8. data/VERSION +1 -1
  9. data/deepl-rb.gemspec +6 -6
  10. data/lib/deepl/admin_client.rb +183 -0
  11. data/lib/deepl/api.rb +30 -6
  12. data/lib/deepl/app_info.rb +20 -0
  13. data/lib/deepl/client.rb +969 -0
  14. data/lib/deepl/configuration.rb +56 -10
  15. data/lib/deepl/constants/admin_usage_group_by.rb +13 -0
  16. data/lib/deepl/constants/custom_tag_usage_aggregate_by.rb +13 -0
  17. data/lib/deepl/constants/document_status_code.rb +17 -0
  18. data/lib/deepl/constants/language_include.rb +17 -0
  19. data/lib/deepl/constants/language_resource_name.rb +21 -0
  20. data/lib/deepl/constants/tag_handling_version.rb +14 -0
  21. data/lib/deepl/deprecation.rb +42 -0
  22. data/lib/deepl/document_api.rb +41 -22
  23. data/lib/deepl/exceptions/bad_request.rb +1 -3
  24. data/lib/deepl/exceptions/connection_error.rb +37 -0
  25. data/lib/deepl/exceptions/document_not_ready_error.rb +17 -0
  26. data/lib/deepl/exceptions/document_translation_error.rb +17 -1
  27. data/lib/deepl/exceptions/error.rb +13 -1
  28. data/lib/deepl/exceptions/not_found.rb +8 -3
  29. data/lib/deepl/exceptions/not_supported.rb +6 -0
  30. data/lib/deepl/exceptions/request_error.rb +19 -2
  31. data/lib/deepl/exceptions/server_error.rb +7 -1
  32. data/lib/deepl/exceptions/validation_error.rb +14 -0
  33. data/lib/deepl/requests/admin/base.rb +94 -0
  34. data/lib/deepl/requests/admin/create_developer_key.rb +38 -0
  35. data/lib/deepl/requests/admin/custom_tag_usage_analytics.rb +38 -0
  36. data/lib/deepl/requests/admin/list_developer_keys.rb +28 -0
  37. data/lib/deepl/requests/admin/update_developer_key.rb +35 -0
  38. data/lib/deepl/requests/admin/usage_analytics.rb +36 -0
  39. data/lib/deepl/requests/base.rb +229 -59
  40. data/lib/deepl/requests/correct.rb +57 -0
  41. data/lib/deepl/requests/document/download.rb +11 -3
  42. data/lib/deepl/requests/document/get_status.rb +2 -2
  43. data/lib/deepl/requests/document/upload.rb +43 -10
  44. data/lib/deepl/requests/glossary/create.rb +6 -2
  45. data/lib/deepl/requests/glossary/language_pairs.rb +20 -4
  46. data/lib/deepl/requests/language_resources.rb +41 -0
  47. data/lib/deepl/requests/languages.rb +28 -4
  48. data/lib/deepl/requests/languages_for_resource.rb +54 -0
  49. data/lib/deepl/requests/multilingual_glossary/create.rb +37 -0
  50. data/lib/deepl/requests/multilingual_glossary/destroy.rb +36 -0
  51. data/lib/deepl/requests/multilingual_glossary/destroy_dictionary.rb +51 -0
  52. data/lib/deepl/requests/multilingual_glossary/entries.rb +66 -0
  53. data/lib/deepl/requests/multilingual_glossary/find.rb +35 -0
  54. data/lib/deepl/requests/multilingual_glossary/helpers.rb +63 -0
  55. data/lib/deepl/requests/multilingual_glossary/list.rb +39 -0
  56. data/lib/deepl/requests/multilingual_glossary/replace_dictionary.rb +41 -0
  57. data/lib/deepl/requests/multilingual_glossary/update.rb +40 -0
  58. data/lib/deepl/requests/rephrase.rb +5 -9
  59. data/lib/deepl/requests/spoken_terms/create.rb +37 -0
  60. data/lib/deepl/requests/spoken_terms/destroy.rb +36 -0
  61. data/lib/deepl/requests/spoken_terms/destroy_term_list.rb +41 -0
  62. data/lib/deepl/requests/spoken_terms/entries.rb +46 -0
  63. data/lib/deepl/requests/spoken_terms/find.rb +35 -0
  64. data/lib/deepl/requests/spoken_terms/helpers.rb +67 -0
  65. data/lib/deepl/requests/spoken_terms/list.rb +35 -0
  66. data/lib/deepl/requests/spoken_terms/replace_term_list.rb +41 -0
  67. data/lib/deepl/requests/spoken_terms/update.rb +40 -0
  68. data/lib/deepl/requests/style_profile/base.rb +83 -0
  69. data/lib/deepl/requests/style_profile/create.rb +35 -0
  70. data/lib/deepl/requests/style_profile/destroy.rb +34 -0
  71. data/lib/deepl/requests/style_profile/find.rb +33 -0
  72. data/lib/deepl/requests/style_profile/list.rb +43 -0
  73. data/lib/deepl/requests/style_profile/update.rb +35 -0
  74. data/lib/deepl/requests/style_rule/create.rb +1 -1
  75. data/lib/deepl/requests/style_rule/create_custom_instruction.rb +4 -2
  76. data/lib/deepl/requests/style_rule/destroy.rb +4 -2
  77. data/lib/deepl/requests/style_rule/destroy_custom_instruction.rb +5 -3
  78. data/lib/deepl/requests/style_rule/find.rb +4 -2
  79. data/lib/deepl/requests/style_rule/find_custom_instruction.rb +5 -3
  80. data/lib/deepl/requests/style_rule/helpers.rb +35 -0
  81. data/lib/deepl/requests/style_rule/list.rb +1 -1
  82. data/lib/deepl/requests/style_rule/update.rb +4 -2
  83. data/lib/deepl/requests/style_rule/update_configured_rules.rb +4 -2
  84. data/lib/deepl/requests/style_rule/update_custom_instruction.rb +5 -3
  85. data/lib/deepl/requests/translate.rb +52 -20
  86. data/lib/deepl/requests/translation_memory/base.rb +1 -4
  87. data/lib/deepl/requests/translation_memory/create_export.rb +1 -1
  88. data/lib/deepl/requests/translation_memory/create_import.rb +1 -1
  89. data/lib/deepl/requests/translation_memory/destroy.rb +1 -1
  90. data/lib/deepl/requests/translation_memory/download_export.rb +4 -0
  91. data/lib/deepl/requests/translation_memory/find.rb +1 -1
  92. data/lib/deepl/requests/translation_memory/find_job.rb +1 -1
  93. data/lib/deepl/requests/translation_memory/list.rb +2 -1
  94. data/lib/deepl/requests/translation_memory/segments.rb +3 -2
  95. data/lib/deepl/requests/translation_memory/storage_base.rb +40 -8
  96. data/lib/deepl/requests/translation_memory/upload_file.rb +1 -1
  97. data/lib/deepl/requests/usage.rb +28 -1
  98. data/lib/deepl/requests/write_improvements.rb +35 -0
  99. data/lib/deepl/resources/admin_usage_report.rb +73 -0
  100. data/lib/deepl/resources/custom_tag_usage_report.rb +59 -0
  101. data/lib/deepl/resources/developer_key.rb +57 -0
  102. data/lib/deepl/resources/document_handle.rb +24 -25
  103. data/lib/deepl/resources/document_translation_status.rb +23 -4
  104. data/lib/deepl/resources/language.rb +10 -2
  105. data/lib/deepl/resources/language_resource.rb +57 -0
  106. data/lib/deepl/resources/language_support.rb +56 -0
  107. data/lib/deepl/resources/multilingual_glossary.rb +112 -0
  108. data/lib/deepl/resources/spoken_terms.rb +61 -0
  109. data/lib/deepl/resources/style_profile.rb +44 -0
  110. data/lib/deepl/resources/style_rule.rb +3 -0
  111. data/lib/deepl/resources/text.rb +18 -2
  112. data/lib/deepl/resources/translation_memory.rb +8 -2
  113. data/lib/deepl/resources/translation_memory_import.rb +1 -1
  114. data/lib/deepl/resources/translation_memory_job.rb +23 -3
  115. data/lib/deepl/resources/translation_memory_segments.rb +8 -6
  116. data/lib/deepl/resources/usage.rb +93 -4
  117. data/lib/deepl/style_rule_api.rb +11 -0
  118. data/lib/deepl/translation_memory_api.rb +104 -24
  119. data/lib/deepl/utils/client_builder.rb +54 -0
  120. data/lib/deepl/utils/document_poller.rb +70 -0
  121. data/lib/deepl/utils/exception_builder.rb +3 -2
  122. data/lib/deepl/utils/http_client_builder.rb +79 -0
  123. data/lib/deepl/utils/number_parser.rb +34 -0
  124. data/lib/deepl/utils/term_validator.rb +33 -0
  125. data/lib/deepl/utils/time_parser.rb +14 -0
  126. data/lib/deepl.rb +166 -41
  127. data/lib/http_client_options.rb +21 -1
  128. data/lib/version.rb +1 -1
  129. data/spec/fixtures.rb +33 -0
  130. data/spec/offline/admin_client_spec.rb +551 -0
  131. data/spec/offline/client_spec.rb +273 -0
  132. data/spec/{api → offline}/configuration_spec.rb +45 -3
  133. data/spec/{constants → offline}/constants_spec.rb +23 -1
  134. data/spec/offline/correct_text_spec.rb +50 -0
  135. data/spec/offline/deprecation_spec.rb +32 -0
  136. data/spec/offline/errors_spec.rb +92 -0
  137. data/spec/offline/glossaries_spec.rb +98 -0
  138. data/spec/offline/http_client_spec.rb +319 -0
  139. data/spec/offline/languages_spec.rb +155 -0
  140. data/spec/offline/module_methods_spec.rb +13 -0
  141. data/spec/offline/multilingual_glossaries_spec.rb +358 -0
  142. data/spec/offline/offline_helper.rb +71 -0
  143. data/spec/offline/rephrase_text_spec.rb +73 -0
  144. data/spec/offline/spoken_terms_spec.rb +215 -0
  145. data/spec/offline/style_profiles_spec.rb +266 -0
  146. data/spec/offline/style_rules_spec.rb +318 -0
  147. data/spec/offline/translate_document_spec.rb +606 -0
  148. data/spec/offline/translate_text_spec.rb +286 -0
  149. data/spec/offline/translation_memories_client_spec.rb +256 -0
  150. data/spec/offline/translation_memories_spec.rb +463 -0
  151. data/spec/offline/usage_spec.rb +151 -0
  152. data/spec/real_api/admin_client_spec.rb +77 -0
  153. data/spec/real_api/correct_text_spec.rb +20 -0
  154. data/spec/real_api/glossaries_spec.rb +28 -0
  155. data/spec/real_api/languages_spec.rb +27 -0
  156. data/spec/real_api/multilingual_glossaries_spec.rb +53 -0
  157. data/spec/real_api/proxy_spec.rb +41 -0
  158. data/spec/real_api/real_api_helper.rb +65 -0
  159. data/spec/real_api/rephrase_text_spec.rb +16 -0
  160. data/spec/real_api/spoken_terms_spec.rb +56 -0
  161. data/spec/real_api/style_profiles_spec.rb +46 -0
  162. data/spec/real_api/style_rules_spec.rb +46 -0
  163. data/spec/real_api/translate_document_spec.rb +18 -0
  164. data/spec/real_api/translate_text_spec.rb +16 -0
  165. data/spec/real_api/translation_memories_spec.rb +56 -0
  166. data/spec/real_api/usage_spec.rb +20 -0
  167. data/spec/spec_helper.rb +1 -50
  168. metadata +116 -76
  169. data/.circleci/config.yml +0 -27
  170. data/.github/workflows/add_issues_to_kanban.yml +0 -16
  171. data/spec/api/api_spec.rb +0 -20
  172. data/spec/api/deepl_spec.rb +0 -262
  173. data/spec/integration_tests/document_api_spec.rb +0 -211
  174. data/spec/integration_tests/document_error_paths_spec.rb +0 -33
  175. data/spec/integration_tests/glossary_api_spec.rb +0 -114
  176. data/spec/integration_tests/glossary_error_paths_spec.rb +0 -107
  177. data/spec/integration_tests/integration_test_utils.rb +0 -178
  178. data/spec/integration_tests/languages_api_spec.rb +0 -54
  179. data/spec/integration_tests/languages_error_paths_spec.rb +0 -25
  180. data/spec/integration_tests/rephrase_api_spec.rb +0 -90
  181. data/spec/integration_tests/rephrase_error_paths_spec.rb +0 -53
  182. data/spec/integration_tests/smoke_test_spec.rb +0 -24
  183. data/spec/integration_tests/style_rule_api_spec.rb +0 -117
  184. data/spec/integration_tests/style_rule_error_paths_spec.rb +0 -45
  185. data/spec/integration_tests/translate_api_spec.rb +0 -117
  186. data/spec/integration_tests/translate_error_paths_spec.rb +0 -48
  187. data/spec/integration_tests/translation_memory_api_spec.rb +0 -244
  188. data/spec/integration_tests/translation_memory_error_paths_spec.rb +0 -87
  189. data/spec/integration_tests/usage_api_spec.rb +0 -29
  190. data/spec/integration_tests/usage_error_paths_spec.rb +0 -18
  191. data/spec/requests/document/upload_spec.rb +0 -130
  192. data/spec/requests/extra_body_parameters_types_spec.rb +0 -82
  193. data/spec/requests/glossary/create_spec.rb +0 -44
  194. data/spec/requests/glossary/destroy_spec.rb +0 -27
  195. data/spec/requests/glossary/entries_spec.rb +0 -27
  196. data/spec/requests/glossary/find_spec.rb +0 -28
  197. data/spec/requests/glossary/language_pairs_spec.rb +0 -27
  198. data/spec/requests/glossary/list_spec.rb +0 -27
  199. data/spec/requests/languages_spec.rb +0 -27
  200. data/spec/requests/rephrase_spec.rb +0 -46
  201. data/spec/requests/style_rule/create_custom_instruction_spec.rb +0 -30
  202. data/spec/requests/style_rule/create_spec.rb +0 -29
  203. data/spec/requests/style_rule/destroy_custom_instruction_spec.rb +0 -28
  204. data/spec/requests/style_rule/destroy_spec.rb +0 -27
  205. data/spec/requests/style_rule/find_custom_instruction_spec.rb +0 -29
  206. data/spec/requests/style_rule/find_spec.rb +0 -28
  207. data/spec/requests/style_rule/list_spec.rb +0 -27
  208. data/spec/requests/style_rule/update_configured_rules_spec.rb +0 -31
  209. data/spec/requests/style_rule/update_custom_instruction_spec.rb +0 -32
  210. data/spec/requests/style_rule/update_spec.rb +0 -29
  211. data/spec/requests/translate_spec.rb +0 -338
  212. data/spec/requests/translation_memory/create_export_spec.rb +0 -37
  213. data/spec/requests/translation_memory/create_import_spec.rb +0 -61
  214. data/spec/requests/translation_memory/destroy_spec.rb +0 -36
  215. data/spec/requests/translation_memory/download_export_spec.rb +0 -54
  216. data/spec/requests/translation_memory/find_job_spec.rb +0 -37
  217. data/spec/requests/translation_memory/find_spec.rb +0 -36
  218. data/spec/requests/translation_memory/list_spec.rb +0 -27
  219. data/spec/requests/translation_memory/segments_spec.rb +0 -58
  220. data/spec/requests/translation_memory/upload_file_spec.rb +0 -55
  221. data/spec/requests/usage_spec.rb +0 -27
  222. data/spec/resources/custom_instruction_spec.rb +0 -32
  223. data/spec/resources/glossary_spec.rb +0 -38
  224. data/spec/resources/language_pair_spec.rb +0 -23
  225. data/spec/resources/language_spec.rb +0 -45
  226. data/spec/resources/style_rule_spec.rb +0 -68
  227. data/spec/resources/text_spec.rb +0 -23
  228. data/spec/resources/translation_memory_export_spec.rb +0 -38
  229. data/spec/resources/translation_memory_import_spec.rb +0 -30
  230. data/spec/resources/translation_memory_job_spec.rb +0 -109
  231. data/spec/resources/translation_memory_segments_spec.rb +0 -78
  232. data/spec/resources/translation_memory_spec.rb +0 -52
  233. data/spec/resources/usage_spec.rb +0 -35
  234. data/spec/support/live_mock_server.rb +0 -12
  235. data/spec/support/managed_glossary.rb +0 -65
  236. data/spec/support/managed_style_rule.rb +0 -17
  237. data/spec/support/managed_translation_memory.rb +0 -55
data/Rakefile CHANGED
@@ -34,10 +34,13 @@ Juwelier::Tasks.new do |gem|
34
34
  'homepage_uri' => 'https://github.com/DeepLcom/deepl-rb'
35
35
  }
36
36
  gem.files.exclude '.bumpversion.toml'
37
- gem.files.exclude '.devcontainer/**/*'
38
- gem.files.exclude '.github'
39
- gem.files.exclude '.circleci'
37
+ gem.files.exclude '.github/**/*'
38
+ gem.files.exclude '.circleci/**/*'
40
39
  gem.files.exclude 'catalog-info.yaml'
40
+ gem.files.exclude '.auto-approve.yaml'
41
+ gem.files.exclude '.gitar/**/*'
42
+ gem.files.exclude '.releaserc.mjs'
43
+ gem.files.exclude 'renovate.json5'
41
44
  end
42
45
 
43
46
  Juwelier::RubygemsDotOrgTasks.new
@@ -47,9 +50,19 @@ require 'rspec/core/rake_task'
47
50
  require 'rubocop/rake_task'
48
51
 
49
52
  RuboCop::RakeTask.new
50
- RSpec::Core::RakeTask.new(:spec)
51
53
 
52
- desc 'Run all tests.'
54
+ namespace :spec do
55
+ desc 'Run the offline specs: no network, no key.'
56
+ RSpec::Core::RakeTask.new(:offline) { |task| task.pattern = 'spec/offline/**/*_spec.rb' }
57
+
58
+ desc 'Run the real-API specs: needs DEEPL_AUTH_KEY, sends real requests.'
59
+ RSpec::Core::RakeTask.new(:real_api) { |task| task.pattern = 'spec/real_api/**/*_spec.rb' }
60
+ end
61
+
62
+ desc 'Run the offline specs.'
63
+ task spec: 'spec:offline'
64
+
65
+ desc 'Run the offline specs and rubocop.'
53
66
  task :test do
54
67
  Rake::Task['spec'].invoke
55
68
  Rake::Task['rubocop'].invoke
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.9.0
1
+ 3.10.0
data/deepl-rb.gemspec CHANGED
@@ -2,17 +2,17 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: deepl-rb 3.9.0 ruby lib
5
+ # stub: deepl-rb 3.10.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "deepl-rb".freeze
9
- s.version = "3.9.0"
9
+ s.version = "3.10.0".freeze
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.metadata = { "bug_tracker_uri" => "https://github.com/DeepLcom/deepl-rb/issues", "changelog_uri" => "https://github.com/DeepLcom/deepl-rb/blob/main/CHANGELOG.md", "documentation_uri" => "https://github.com/DeepLcom/deepl-rb/blob/main/README.md", "homepage_uri" => "https://github.com/DeepLcom/deepl-rb" } if s.respond_to? :metadata=
13
13
  s.require_paths = ["lib".freeze]
14
14
  s.authors = ["DeepL SE".freeze]
15
- s.date = "2026-08-12"
15
+ s.date = "2026-08-11"
16
16
  s.description = "Official Ruby library for the DeepL language translation API (v2). For more information, check this: https://www.deepl.com/docs/api-reference.html".freeze
17
17
  s.email = "open-source@deepl.com".freeze
18
18
  s.extra_rdoc_files = [
@@ -189,12 +189,12 @@ Gem::Specification.new do |s|
189
189
  ]
190
190
  s.homepage = "https://github.com/DeepLcom/deepl-rb".freeze
191
191
  s.licenses = ["MIT".freeze]
192
- s.rubygems_version = "3.4.10".freeze
192
+ s.rubygems_version = "3.6.2".freeze
193
193
  s.summary = "Official Ruby library for the DeepL language translation API.".freeze
194
194
 
195
195
  s.specification_version = 4
196
196
 
197
- s.add_development_dependency(%q<juwelier>.freeze, [">= 0"])
198
- s.add_development_dependency(%q<byebug>.freeze, [">= 0"])
197
+ s.add_development_dependency(%q<juwelier>.freeze, [">= 0".freeze])
198
+ s.add_development_dependency(%q<byebug>.freeze, [">= 0".freeze])
199
199
  end
200
200
 
@@ -0,0 +1,183 @@
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
+ ##
8
+ # Client for the DeepL Admin API, used to manage the developer keys of your organization and
9
+ # to retrieve usage analytics. It is independent of +DeepL.configure+ and never reads the
10
+ # +DEEPL_AUTH_KEY+ or +DEEPL_SERVER_URL+ environment variables.
11
+ #
12
+ # All methods may raise a +DeepL::Exceptions::Error+ or a subclass, for example
13
+ # +DeepL::Exceptions::AuthorizationFailed+ if the admin key is not accepted.
14
+ class AdminClient
15
+ DEEPL_SERVER_URL = 'https://api.deepl.com'
16
+ private_constant :DEEPL_SERVER_URL
17
+
18
+ ##
19
+ # @param [String] admin_key Admin API key, surrounding whitespace is removed.
20
+ # @param [String, nil] server_url Base URL of the DeepL API, defaults to
21
+ # +https://api.deepl.com+ (also for keys ending in +:fx+).
22
+ # @param [Hash] headers HTTP headers sent with every request. A header of the same name in
23
+ # any casing replaces the library's, +Authorization+ and +User-Agent+ included, except the
24
+ # technical headers +Content-Type+, +Content-Length+, +Transfer-Encoding+ and +Host+.
25
+ # @param [Integer] max_retries Number of retries after the first attempt.
26
+ # @param [Numeric] timeout Timeout of each attempt in seconds.
27
+ # @param [String, nil] proxy Proxy URL, e.g. +http://user:password@proxy.example:8080+.
28
+ # @param [Boolean] send_platform_info Whether to send basic platform information.
29
+ # @param [DeepL::AppInfo, Hash, nil] app_info Your application's +app_name+ and
30
+ # +app_version+, added to the +User-Agent+.
31
+ # @param [DeepL::HTTPClientOptions, nil] http_client_options Proxy, SSL and timeout options.
32
+ # @param [Logger, nil] logger Logger for requests and retries.
33
+ # @raise [ArgumentError] If +admin_key+ is empty.
34
+ def initialize(admin_key, server_url: nil, headers: {}, max_retries: 5, timeout: 10, # rubocop:disable Metrics/ParameterLists
35
+ proxy: nil, send_platform_info: true, app_info: nil, http_client_options: nil,
36
+ logger: nil)
37
+ @api = Utils::ClientBuilder.build_api(
38
+ admin_key, key_name: 'admin_key', server_url: server_url,
39
+ default_server_url: DEEPL_SERVER_URL, headers: headers,
40
+ max_retries: max_retries, timeout: timeout, proxy: proxy,
41
+ send_platform_info: send_platform_info, app_info: app_info,
42
+ http_client_options: http_client_options, logger: logger
43
+ )
44
+ end
45
+
46
+ def inspect
47
+ "#<#{self.class.name} server_url=#{configuration.host}>"
48
+ end
49
+
50
+ ##
51
+ # Retrieves the usage of the organization within a date range. The API requires the start date
52
+ # to be before the end date (a report covers at least two days) and the range to be at most
53
+ # 366 days.
54
+ #
55
+ # @param [Date, Time, DateTime, String] start_date Start date, as a +Date+, a +Time+, a
56
+ # +DateTime+ or a +YYYY-MM-DD+ string. Dates are UTC calendar days and both the start and
57
+ # the end day are included. For +Time+ and +DateTime+ values the UTC date of that moment is
58
+ # sent: +2026-10-20 00:00 +02:00+ is sent as +2026-10-19+.
59
+ # @param [Date, Time, DateTime, String] end_date End date, in the same format as +start_date+.
60
+ # @param [String, Symbol, nil] group_by Group the usage by key or by key and day, see
61
+ # +DeepL::Constants::AdminUsageGroupBy+ for the accepted values. It is sent as is, and the
62
+ # +group_by+ of the report is the raw string the API returns.
63
+ # @return [DeepL::Resources::AdminUsageReport]
64
+ # @raise [ArgumentError] If a date is invalid.
65
+ # @raise [DeepL::Exceptions::BadRequest] If the API rejects the date range.
66
+ def get_usage_analytics(start_date, end_date, group_by: nil)
67
+ Requests::Admin::UsageAnalytics.new(api, start_date, end_date, group_by).request
68
+ end
69
+
70
+ ##
71
+ # Retrieves the usage per custom tag within a date range, with the same limits as
72
+ # +get_usage_analytics+. Results are paginated: pass the +next_page+ of a report as +page+ to
73
+ # retrieve the following page.
74
+ #
75
+ # @param [Date, Time, DateTime, String] start_date Start date, as a +Date+, a +Time+, a
76
+ # +DateTime+ or a +YYYY-MM-DD+ string. Dates are UTC calendar days and both the start and
77
+ # the end day are included. For +Time+ and +DateTime+ values the UTC date of that moment is
78
+ # sent: +2026-10-20 00:00 +02:00+ is sent as +2026-10-19+.
79
+ # @param [Date, Time, DateTime, String] end_date End date, in the same format as +start_date+.
80
+ # @param [String, Symbol, nil] aggregate_by Aggregate the usage over the whole period or by
81
+ # day, see +DeepL::Constants::CustomTagUsageAggregateBy+. It is sent as is, and the
82
+ # +aggregate_by+ of the report is the raw string the API returns.
83
+ # @param [Integer, nil] page Page number to retrieve.
84
+ # @return [DeepL::Resources::CustomTagUsageReport]
85
+ # @raise [ArgumentError] If a date is invalid.
86
+ # @raise [DeepL::Exceptions::BadRequest] If the API rejects the date range.
87
+ def get_custom_tag_usage_analytics(start_date, end_date, aggregate_by: nil, page: nil)
88
+ Requests::Admin::CustomTagUsageAnalytics.new(api, start_date, end_date, aggregate_by,
89
+ page).request
90
+ end
91
+
92
+ ##
93
+ # Creates a developer key. The API uses "DeepL API Key" as label if none is given.
94
+ #
95
+ # Creation is retried only after failures that show the request was not processed (HTTP 429,
96
+ # DNS resolution errors, refused connections and connection timeouts), so that a retry
97
+ # cannot create a second key. The raised error has +should_retry?+ true only for these.
98
+ #
99
+ # @param [String, nil] label Label for the new key, not sent if +nil+.
100
+ # @param [Hash, nil] extra_body_parameters Added to the request body last; replaces fields
101
+ # of the same name. Values are sent as given.
102
+ # @return [DeepL::Resources::DeveloperKey]
103
+ def create_developer_key(label: nil, extra_body_parameters: nil)
104
+ Requests::Admin::CreateDeveloperKey.new(api, label, extra_body_parameters).request
105
+ end
106
+
107
+ ##
108
+ # Retrieves all developer keys of the organization, including deactivated ones.
109
+ #
110
+ # @return [Array<DeepL::Resources::DeveloperKey>]
111
+ def list_developer_keys
112
+ Requests::Admin::ListDeveloperKeys.new(api).request
113
+ end
114
+
115
+ ##
116
+ # Deactivates a developer key. Deactivation is permanent.
117
+ #
118
+ # @param [String, DeepL::Resources::DeveloperKey] key Key ID or developer key.
119
+ # @param [Hash, nil] extra_body_parameters Added to the request body last; replaces fields
120
+ # of the same name. Values are sent as given.
121
+ # @return [DeepL::Resources::DeveloperKey]
122
+ # @raise [ArgumentError] If the key ID is empty.
123
+ def deactivate_developer_key(key, extra_body_parameters: nil)
124
+ update_developer_key('deactivate', { key_id: key_id(key) }, extra_body_parameters)
125
+ end
126
+
127
+ ##
128
+ # Changes the label of a developer key.
129
+ #
130
+ # @param [String, DeepL::Resources::DeveloperKey] key Key ID or developer key.
131
+ # @param [String] label New label for the key.
132
+ # @param [Hash, nil] extra_body_parameters Added to the request body last; replaces fields
133
+ # of the same name. Values are sent as given.
134
+ # @return [DeepL::Resources::DeveloperKey]
135
+ # @raise [ArgumentError] If the key ID is empty.
136
+ def rename_developer_key(key, label, extra_body_parameters: nil)
137
+ update_developer_key('label', { key_id: key_id(key), label: label }, extra_body_parameters)
138
+ end
139
+
140
+ ##
141
+ # Changes the usage limits of a developer key. Only the given limits are changed; omitted
142
+ # limits keep their current value. A +nil+ limit means unlimited and +0+ blocks that usage.
143
+ #
144
+ # admin_client.set_developer_key_usage_limits(key, characters: 0)
145
+ # admin_client.set_developer_key_usage_limits(key, speech_to_text_milliseconds: nil)
146
+ #
147
+ # @param [String, DeepL::Resources::DeveloperKey] key Key ID or developer key.
148
+ # @param [Hash] limits Limits to change: +:characters+ (characters per usage period across
149
+ # text translation, document translation and text improvement),
150
+ # +:speech_to_text_milliseconds+ and +:speech_to_speech_milliseconds+, each an +Integer+ or
151
+ # +nil+. They are sent as is and validated by the API.
152
+ # @param [Hash, nil] extra_body_parameters Added to the request body last; replaces fields
153
+ # of the same name. Values are sent as given.
154
+ # @return [DeepL::Resources::DeveloperKey]
155
+ # @raise [ArgumentError] If the key ID is empty.
156
+ def set_developer_key_usage_limits(key, extra_body_parameters: nil, **limits)
157
+ update_developer_key('limits', limits.merge(key_id: key_id(key)), extra_body_parameters)
158
+ end
159
+
160
+ private
161
+
162
+ attr_reader :api
163
+
164
+ def configuration
165
+ api.configuration
166
+ end
167
+
168
+ def update_developer_key(action, payload, extra_body_parameters)
169
+ Requests::Admin::UpdateDeveloperKey.new(api, action, payload, extra_body_parameters).request
170
+ end
171
+
172
+ def key_id(key)
173
+ id = key.is_a?(Resources::DeveloperKey) ? key.key_id : key
174
+ raise ArgumentError, 'key ID must not be empty' if blank?(id)
175
+
176
+ id
177
+ end
178
+
179
+ def blank?(value)
180
+ value.nil? || value.to_s.strip.empty?
181
+ end
182
+ end
183
+ end
data/lib/deepl/api.rb CHANGED
@@ -5,18 +5,42 @@
5
5
 
6
6
  module DeepL
7
7
  class API
8
- attr_reader :configuration, :http_client
8
+ attr_reader :configuration, :http_client, :http_client_options
9
9
 
10
- def initialize(configuration)
10
+ # +client:+ is true for the API of a DeepL::Client or DeepL::AdminClient, whose errors differ
11
+ # from those of the module-level API until the next major version. @api private
12
+ def initialize(configuration, http_client_options = nil, client: false)
11
13
  @configuration = configuration
14
+ @client = client
12
15
  configuration.validate!
13
- uri = URI(configuration.host)
14
- @http_client = Net::HTTP.new(uri.host, uri.port)
15
- @http_client.use_ssl = uri.scheme == 'https'
16
+ @http_client_options = http_client_options
17
+ @http_client = if http_client_options
18
+ Utils::HTTPClientBuilder.build(configuration.host, http_client_options)
19
+ else
20
+ default_http_client(configuration.host)
21
+ end
16
22
  end
17
23
 
18
- def update_http_client(client)
24
+ # @api private
25
+ def client?
26
+ @client
27
+ end
28
+
29
+ # +options+ are the HTTPClientOptions the client was built with; their timeouts take
30
+ # precedence over the per-attempt timeout.
31
+ def update_http_client(client, options = nil)
19
32
  @http_client = client
33
+ @http_client_options = options
34
+ end
35
+
36
+ private
37
+
38
+ # Without explicit options Net::HTTP applies the proxy from the environment (+http_proxy+).
39
+ def default_http_client(host)
40
+ uri = URI(host)
41
+ client = Net::HTTP.new(uri.host, uri.port)
42
+ client.use_ssl = uri.scheme == 'https'
43
+ Utils::HTTPClientBuilder.disable_lenient_defaults(client)
20
44
  end
21
45
  end
22
46
  end
@@ -0,0 +1,20 @@
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
+ ##
8
+ # Identifies your application in the +User-Agent+ header as +<app_name>/<app_version>+.
9
+ AppInfo = Struct.new(:app_name, :app_version, keyword_init: true) do
10
+ # Accepts an AppInfo or a hash with +:app_name+ and +:app_version+.
11
+ def self.from(value)
12
+ return nil if value.nil?
13
+ return value if value.is_a?(AppInfo)
14
+ raise ArgumentError, 'app_info must be a DeepL::AppInfo or a Hash' unless value.is_a?(Hash)
15
+
16
+ new(app_name: value[:app_name] || value['app_name'],
17
+ app_version: value[:app_version] || value['app_version'])
18
+ end
19
+ end
20
+ end