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
@@ -0,0 +1,969 @@
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 API. Every client owns its configuration and HTTP connection: two
9
+ # clients never share options, and nothing is read from environment variables or from the
10
+ # deprecated module-level configuration (+DeepL.configure+).
11
+ #
12
+ # client = DeepL::Client.new(ENV.fetch('DEEPL_AUTH_KEY'), app_info: { app_name: 'my-app',
13
+ # app_version: '1.2.3' })
14
+ #
15
+ # All methods may raise a +DeepL::Exceptions::Error+ or a subclass.
16
+ class Client # rubocop:disable Metrics/ClassLength
17
+ DEEPL_SERVER_URL = 'https://api.deepl.com'
18
+ DEEPL_SERVER_URL_FREE = 'https://api-free.deepl.com'
19
+ private_constant :DEEPL_SERVER_URL, :DEEPL_SERVER_URL_FREE
20
+
21
+ ##
22
+ # Returns true if +auth_key+ is a DeepL API Free key (it ends with +:fx+).
23
+ #
24
+ # @param [String, nil] auth_key Authentication key.
25
+ # @return [Boolean]
26
+ def self.free_account_auth_key?(auth_key)
27
+ auth_key.to_s.strip.end_with?(':fx')
28
+ end
29
+
30
+ ##
31
+ # @param [String] auth_key Authentication key, surrounding whitespace is removed.
32
+ # @param [String, nil] server_url Base URL of the DeepL API, a path prefix is kept. Defaults
33
+ # to +https://api-free.deepl.com+ for Free keys (ending in +:fx+), else
34
+ # +https://api.deepl.com+.
35
+ # @param [Hash] headers HTTP headers sent with every DeepL API request (pre-signed storage
36
+ # URLs get only a +User-Agent+). A header of the same name in any casing replaces the
37
+ # library's, +Authorization+ and +User-Agent+ included, except the technical headers
38
+ # +Content-Type+, +Content-Length+, +Transfer-Encoding+ and +Host+.
39
+ # @param [Integer] max_retries Number of retries after the first attempt, for HTTP 429, any
40
+ # 5xx and transient network failures (not TLS failures or malformed responses).
41
+ # @param [Numeric] timeout Timeout of each attempt in seconds, applied to opening the
42
+ # connection, reading and writing. A longer backoff interval takes precedence.
43
+ # @param [String, nil] proxy Proxy URL, e.g. +http://user:password@proxy.example:8080+.
44
+ # @param [Boolean] send_platform_info Whether to add the OS and Ruby versions to the
45
+ # +User-Agent+.
46
+ # @param [DeepL::AppInfo, Hash, nil] app_info Your application's +app_name+ and
47
+ # +app_version+, added to the +User-Agent+.
48
+ # @param [DeepL::HTTPClientOptions, nil] http_client_options Proxy, certificate, SSL
49
+ # verification and per-phase timeouts. Its timeouts take precedence over +timeout+.
50
+ # @param [Logger, nil] logger Logger for requests and retries.
51
+ # @raise [ArgumentError] If +auth_key+ is empty.
52
+ def initialize(auth_key, server_url: nil, headers: {}, max_retries: 5, timeout: 10, # rubocop:disable Metrics/ParameterLists
53
+ proxy: nil, send_platform_info: true, app_info: nil, http_client_options: nil,
54
+ logger: nil)
55
+ free = self.class.free_account_auth_key?(auth_key)
56
+ default_server_url = free ? DEEPL_SERVER_URL_FREE : DEEPL_SERVER_URL
57
+ @api = Utils::ClientBuilder.build_api(
58
+ auth_key, key_name: 'auth_key', server_url: server_url,
59
+ default_server_url: default_server_url, headers: headers,
60
+ max_retries: max_retries, timeout: timeout, proxy: proxy,
61
+ send_platform_info: send_platform_info, app_info: app_info,
62
+ http_client_options: http_client_options, logger: logger
63
+ )
64
+ end
65
+
66
+ ##
67
+ # Translates +text+ from +source_lang+ into +target_lang+. Always requests the billed
68
+ # characters; options set to +nil+ are not sent.
69
+ #
70
+ # @param [String, Array<String>] text Text or texts to translate.
71
+ # @param [String, nil] source_lang Source language, +nil+ to detect it.
72
+ # @param [String] target_lang Target language.
73
+ # @param [Hash] options Translation options, sent as given unless listed here:
74
+ # * +:glossary+ - A glossary ID, `DeepL::Resources::Glossary` or
75
+ # `DeepL::Resources::MultilingualGlossary`, sent as +glossary_id+.
76
+ # * +:glossary_ids+ - Up to 5 glossaries (IDs or objects). Cannot be combined with
77
+ # +:glossary+. Without +source_lang+, the API detects it.
78
+ # * +:style_rule+, +:style_profile+, +:translation_memory+ - An ID or the resource object.
79
+ # * +:split_sentences+ - +true+/+false+ are sent as +'1'+/+'0'+.
80
+ # * +:custom_instructions+ - An array of instructions; a string is one instruction.
81
+ # * +:extra_body_parameters+ - A hash added to the request body last; replaces fields of
82
+ # the same name.
83
+ # Other options (+:formality+, +:tag_handling+, +:tag_handling_version+, +:context+,
84
+ # +:model_type+, ...) are sent unchanged.
85
+ # @return [DeepL::Resources::Text, Array<DeepL::Resources::Text>] One result for a string,
86
+ # an array for an array (also for a one-element array).
87
+ # @raise [ArgumentError] If the glossary options are combined or more than 5 are given.
88
+ def translate_text(text, source_lang, target_lang, **options)
89
+ result = Requests::Translate.new(api, text, source_lang, target_lang, options).request
90
+ text.is_a?(Array) && !result.is_a?(Array) ? [result] : result
91
+ end
92
+
93
+ ##
94
+ # Translates the document at +input_path+ and writes the result to +output_path+: uploads
95
+ # it, waits until the translation is done and downloads it.
96
+ #
97
+ # @param [String] input_path Path of the document to translate.
98
+ # @param [String] output_path Path to write the translated document to. Must not exist.
99
+ # @param [String, nil] source_lang Source language, +nil+ to detect it.
100
+ # @param [String] target_lang Target language.
101
+ # @param [Numeric, nil] timeout_s Maximum time in seconds to wait for the translation after
102
+ # the upload, +nil+ to wait until it is done.
103
+ # @param [Hash] options The options of #translate_document_upload, including +:filename+.
104
+ # @return [DeepL::Resources::DocumentTranslationStatus] The final status, +done+.
105
+ # @raise [ArgumentError] If +input_path+ does not exist; nothing is uploaded.
106
+ # @raise [DeepL::Exceptions::DocumentTranslationError] For any other failure, also an
107
+ # existing +output_path+ (nothing is uploaded and the file is kept), a failed translation
108
+ # or an expired +timeout_s+. Its +document_handle+ is set once the upload succeeded, so the
109
+ # document can still be queried and downloaded. +http_status_code+ and +should_retry?+ are
110
+ # those of the DeepL error that caused it, if any.
111
+ def translate_document(input_path, output_path, source_lang, target_lang, timeout_s: nil, # rubocop:disable Metrics/MethodLength
112
+ **options)
113
+ raise ArgumentError, "Input file does not exist: #{input_path}" unless File.exist?(input_path)
114
+ if File.exist?(output_path)
115
+ raise Exceptions::DocumentTranslationError.new('File already exists at output path', nil)
116
+ end
117
+
118
+ handle = nil
119
+ begin
120
+ handle = translate_document_upload(input_path, source_lang, target_lang, **options)
121
+ status = translate_document_wait_until_done(handle, timeout_s: timeout_s)
122
+ translate_document_download(handle, output_path)
123
+ status
124
+ rescue StandardError => e
125
+ FileUtils.rm_f(output_path)
126
+ raise Exceptions::DocumentTranslationError.new(
127
+ "Error occurred during document translation: #{e.message}", handle, e
128
+ )
129
+ end
130
+ end
131
+
132
+ ##
133
+ # Uploads the document at +input_path+ for translation. Options set to +nil+ are not sent.
134
+ #
135
+ # @param [String] input_path Path of the document to translate.
136
+ # @param [String, nil] source_lang Source language, +nil+ to detect it.
137
+ # @param [String] target_lang Target language.
138
+ # @param [Hash] options Document translation options:
139
+ # * +:filename+ - File name sent with the upload, including its extension. Defaults to the
140
+ # base name of +input_path+.
141
+ # * +:formality+, +:output_format+ - Sent unchanged.
142
+ # * +:enable_watermark+ - +true+ or +false+.
143
+ # * +:glossary+ - A glossary ID, `DeepL::Resources::Glossary` or
144
+ # `DeepL::Resources::MultilingualGlossary`, sent as +glossary_id+.
145
+ # * +:glossary_ids+ - Up to 5 glossaries (IDs or objects). Requires +source_lang+ and
146
+ # cannot be combined with +:glossary+.
147
+ # * +:style_rule+, +:style_profile+, +:translation_memory+ - An ID or the resource object.
148
+ # * +:translation_memory_threshold+ - Integer 0-100.
149
+ # * +:extra_body_parameters+ - A hash added to the form last; replaces fields of the same
150
+ # name.
151
+ # @return [DeepL::Resources::DocumentHandle] Handle of the uploaded document.
152
+ # @raise [ArgumentError] If +input_path+ does not exist (nothing is uploaded), or the glossary
153
+ # options are combined or more than 5 are given.
154
+ def translate_document_upload(input_path, source_lang, target_lang, **options)
155
+ raise ArgumentError, "Input file does not exist: #{input_path}" unless File.exist?(input_path)
156
+
157
+ filename = options.delete(:filename)
158
+ Requests::Document::Upload.new(api, input_path, source_lang, target_lang, filename,
159
+ options).request
160
+ end
161
+
162
+ ##
163
+ # Queries the status of a document translation once.
164
+ #
165
+ # @param [DeepL::Resources::DocumentHandle] handle Handle returned by the upload.
166
+ # @return [DeepL::Resources::DocumentTranslationStatus]
167
+ def translate_document_get_status(handle)
168
+ Requests::Document::GetStatus.new(api, handle.document_id, handle.document_key).request
169
+ end
170
+
171
+ ##
172
+ # Polls the status of a document translation every 5 seconds until it is done.
173
+ #
174
+ # @param [DeepL::Resources::DocumentHandle] handle Handle returned by the upload.
175
+ # @param [Numeric, nil] timeout_s Maximum time to wait in seconds, +nil+ to wait until the
176
+ # translation is done or failed.
177
+ # @return [DeepL::Resources::DocumentTranslationStatus] The final status, +done+.
178
+ # @raise [DeepL::Exceptions::Error] If the translation fails (with the API's error message)
179
+ # or +timeout_s+ expires.
180
+ def translate_document_wait_until_done(handle, timeout_s: nil)
181
+ Utils::DocumentPoller.wait_until_done(api, handle, timeout_s: timeout_s)
182
+ end
183
+
184
+ ##
185
+ # Downloads the translated document to +output_path+, overwriting an existing file.
186
+ #
187
+ # @param [DeepL::Resources::DocumentHandle] handle Handle returned by the upload.
188
+ # @param [String] output_path Path to write the translated document to.
189
+ # @return [nil]
190
+ # @raise [DeepL::Exceptions::DocumentNotReadyError] If the translation is not done yet.
191
+ def translate_document_download(handle, output_path)
192
+ Requests::Document::Download.new(api, handle.document_id, handle.document_key,
193
+ output_path).request
194
+ nil
195
+ end
196
+
197
+ ##
198
+ # Rephrases +text+, optionally into another variant or language and with a writing style
199
+ # or tone. Options set to +nil+ are not sent.
200
+ #
201
+ # @param [String, Array<String>] text Text or texts to rephrase.
202
+ # @param [String, nil] target_lang Target language, +nil+ to keep the language of the text.
203
+ # @param [String, nil] writing_style Writing style, see +DeepL::Constants::WritingStyle+.
204
+ # Cannot be combined with +tone+.
205
+ # @param [String, nil] tone Tone, see +DeepL::Constants::Tone+.
206
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last;
207
+ # replaces fields of the same name. Other options are sent unchanged.
208
+ # @return [DeepL::Resources::Text, Array<DeepL::Resources::Text>] One result for a string,
209
+ # an array for an array (also for a one-element array).
210
+ # @raise [DeepL::Exceptions::Error] If the response has fewer improvements than texts.
211
+ def rephrase_text(text, target_lang = nil, writing_style: nil, tone: nil, **options)
212
+ result = Requests::Rephrase.new(api, text, target_lang, writing_style, tone,
213
+ options.compact).request
214
+ text.is_a?(Array) && !result.is_a?(Array) ? [result] : result
215
+ end
216
+
217
+ ##
218
+ # Corrects the grammar, spelling and punctuation of +text+. Options set to +nil+ are not
219
+ # sent.
220
+ #
221
+ # @param [String, Array<String>] text Text or texts to correct.
222
+ # @param [String, nil] target_lang Target language, +nil+ to keep the language of the text.
223
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last;
224
+ # replaces fields of the same name. Other options are sent unchanged.
225
+ # @return [DeepL::Resources::Text, Array<DeepL::Resources::Text>] One result for a string,
226
+ # an array for an array (also for a one-element array).
227
+ # @raise [ArgumentError] If +text+ is empty or contains an empty string.
228
+ # @raise [DeepL::Exceptions::Error] If the response has fewer improvements than texts.
229
+ def correct_text(text, target_lang = nil, **options)
230
+ Requests::Correct.new(api, text, target_lang, options.compact).request
231
+ end
232
+
233
+ ##
234
+ # Retrieves the languages that can be translated from, with upper-case codes (+EN+, +DE+).
235
+ #
236
+ # @return [Array<DeepL::Resources::Language>] The source languages;
237
+ # +supports_formality?+ is +nil+ for each.
238
+ def get_source_languages # rubocop:disable Naming/AccessorMethodName
239
+ Requests::Languages.new(api, type: :source).request
240
+ end
241
+
242
+ ##
243
+ # Retrieves the languages that can be translated into, with upper-case codes (+EN-GB+, +DE+).
244
+ #
245
+ # @return [Array<DeepL::Resources::Language>] The target languages, each with
246
+ # +supports_formality?+ set to +true+ or +false+.
247
+ def get_target_languages # rubocop:disable Naming/AccessorMethodName
248
+ Requests::Languages.new(api, type: :target).request
249
+ end
250
+
251
+ ##
252
+ # Retrieves the resources supported by the DeepL API with their features, and whether each
253
+ # feature requires support from the source and/or target language.
254
+ #
255
+ # @return [Array<DeepL::Resources::LanguageResource>] One entry per resource.
256
+ def get_language_resources # rubocop:disable Naming/AccessorMethodName
257
+ Requests::LanguageResources.new(api).request
258
+ end
259
+
260
+ ##
261
+ # Retrieves the languages supported by +resource+, each with its per-feature support. The
262
+ # language codes are returned as the API sends them.
263
+ #
264
+ # @param [String, Symbol] resource Resource to query, see
265
+ # +DeepL::Constants::LanguageResourceName+. Other values are sent unchanged.
266
+ # @param [String, Array<String>, nil] include Also return beta languages and features or
267
+ # features relying on third-party providers, see +DeepL::Constants::LanguageInclude+. Sent
268
+ # as one +include+ query parameter per value; +nil+ sends none, which returns stable
269
+ # languages and features only.
270
+ # @return [Array<DeepL::Resources::LanguageSupport>] The supported languages.
271
+ # @raise [ArgumentError] If +resource+ is empty.
272
+ def get_languages_for_resource(resource, include: nil)
273
+ Requests::LanguagesForResource.new(api, resource, { include: include }.compact).request
274
+ end
275
+
276
+ ##
277
+ # Retrieves the usage and limits of the account and, for API Pro, of this API key.
278
+ #
279
+ # @return [DeepL::Resources::Usage] Usage per type; a type absent from the response is +nil+.
280
+ def get_usage # rubocop:disable Naming/AccessorMethodName
281
+ Requests::Usage.new(api).request
282
+ end
283
+
284
+ ##
285
+ # Creates a multilingual glossary with one dictionary per language pair.
286
+ #
287
+ # @param [String] name Name of the glossary.
288
+ # @param [Array<DeepL::Resources::MultilingualGlossaryDictionaryEntries, Hash>] dictionaries
289
+ # One entry per language pair. A hash needs the keys +:source_lang+, +:target_lang+ and
290
+ # +:entries+ (a hash, source/target pairs or a TSV string; +:entries_format+ +'csv'+ for a
291
+ # CSV string, which is sent as given). Entries other than a string are always sent as TSV.
292
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
293
+ # @return [DeepL::Resources::MultilingualGlossary] The created glossary.
294
+ # @raise [DeepL::Exceptions::ValidationError] If a term is empty after stripping surrounding
295
+ # whitespace or contains a control character, or a TSV line is not exactly two terms.
296
+ def create_multilingual_glossary(name, dictionaries, **options)
297
+ Requests::MultilingualGlossary::Create.new(api, name, dictionaries, options).request
298
+ end
299
+
300
+ ##
301
+ # Creates a multilingual glossary with one dictionary from CSV content.
302
+ #
303
+ # @param [String] name Name of the glossary.
304
+ # @param [String] source_lang Source language of the dictionary.
305
+ # @param [String] target_lang Target language of the dictionary.
306
+ # @param [String, IO] csv CSV content, or an IO to read it from. Sent as given.
307
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
308
+ # @return [DeepL::Resources::MultilingualGlossary] The created glossary.
309
+ def create_multilingual_glossary_from_csv(name, source_lang, target_lang, csv, **options)
310
+ dictionary = csv_dictionary(source_lang, target_lang, csv)
311
+ Requests::MultilingualGlossary::Create.new(api, name, [dictionary], options).request
312
+ end
313
+
314
+ ##
315
+ # Lists all multilingual glossaries.
316
+ #
317
+ # @return [Array<DeepL::Resources::MultilingualGlossary>]
318
+ def list_multilingual_glossaries
319
+ Requests::MultilingualGlossary::List.new(api).request
320
+ end
321
+
322
+ ##
323
+ # Retrieves a multilingual glossary, without its entries.
324
+ #
325
+ # @param [String, DeepL::Resources::MultilingualGlossary] glossary ID or glossary.
326
+ # @return [DeepL::Resources::MultilingualGlossary]
327
+ # @raise [DeepL::Exceptions::NotFound] If the glossary does not exist.
328
+ def get_multilingual_glossary(glossary)
329
+ Requests::MultilingualGlossary::Find.new(api, glossary).request
330
+ end
331
+
332
+ ##
333
+ # Renames a glossary and/or merges entries into its dictionaries. Arguments left +nil+ are
334
+ # not sent; without any, an empty object is sent.
335
+ #
336
+ # @param [String, DeepL::Resources::MultilingualGlossary] glossary ID or glossary.
337
+ # @param [String, nil] name New name.
338
+ # @param [Array<DeepL::Resources::MultilingualGlossaryDictionaryEntries, Hash>, nil]
339
+ # dictionaries Entries to merge into the dictionary of each language pair; a new language
340
+ # pair adds a dictionary.
341
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
342
+ # @return [DeepL::Resources::MultilingualGlossary] The updated glossary.
343
+ # @raise [DeepL::Exceptions::ValidationError] If a term is empty after stripping or contains a
344
+ # control character.
345
+ def update_multilingual_glossary(glossary, name: nil, dictionaries: nil, **options)
346
+ Requests::MultilingualGlossary::Update.new(api, glossary, name, dictionaries,
347
+ options).request
348
+ end
349
+
350
+ ##
351
+ # Renames a multilingual glossary.
352
+ #
353
+ # @param [String, DeepL::Resources::MultilingualGlossary] glossary ID or glossary.
354
+ # @param [String] name New name.
355
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
356
+ # @return [DeepL::Resources::MultilingualGlossary] The updated glossary.
357
+ def update_multilingual_glossary_name(glossary, name, **options)
358
+ Requests::MultilingualGlossary::Update.new(api, glossary, name, nil, options).request
359
+ end
360
+
361
+ ##
362
+ # Merges entries into the dictionary of one language pair, or adds the dictionary if the
363
+ # language pair is new.
364
+ #
365
+ # @param [String, DeepL::Resources::MultilingualGlossary] glossary ID or glossary.
366
+ # @param [DeepL::Resources::MultilingualGlossaryDictionaryEntries, Hash] dictionary The
367
+ # language pair and its entries, see #create_multilingual_glossary.
368
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
369
+ # @return [DeepL::Resources::MultilingualGlossary] The updated glossary.
370
+ # @raise [DeepL::Exceptions::ValidationError] If a term is empty after stripping or contains a
371
+ # control character.
372
+ def update_multilingual_glossary_dictionary(glossary, dictionary, **options)
373
+ Requests::MultilingualGlossary::Update.new(api, glossary, nil, [dictionary],
374
+ options).request
375
+ end
376
+
377
+ ##
378
+ # Merges CSV entries into the dictionary of one language pair, or adds the dictionary if
379
+ # the language pair is new.
380
+ #
381
+ # @param [String, DeepL::Resources::MultilingualGlossary] glossary ID or glossary.
382
+ # @param [String] source_lang Source language of the dictionary.
383
+ # @param [String] target_lang Target language of the dictionary.
384
+ # @param [String, IO] csv CSV content, or an IO to read it from. Sent as given.
385
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
386
+ # @return [DeepL::Resources::MultilingualGlossary] The updated glossary.
387
+ def update_multilingual_glossary_dictionary_from_csv(glossary, source_lang, target_lang, csv,
388
+ **options)
389
+ dictionary = csv_dictionary(source_lang, target_lang, csv)
390
+ Requests::MultilingualGlossary::Update.new(api, glossary, nil, [dictionary],
391
+ options).request
392
+ end
393
+
394
+ ##
395
+ # Replaces the entries of the dictionary of one language pair, or adds the dictionary if
396
+ # the language pair is new.
397
+ #
398
+ # @param [String, DeepL::Resources::MultilingualGlossary] glossary ID or glossary.
399
+ # @param [DeepL::Resources::MultilingualGlossaryDictionaryEntries, Hash] dictionary The
400
+ # language pair and its entries, see #create_multilingual_glossary.
401
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
402
+ # @return [DeepL::Resources::MultilingualGlossaryDictionary] The replaced dictionary.
403
+ # @raise [DeepL::Exceptions::ValidationError] If a term is empty after stripping or contains a
404
+ # control character.
405
+ def replace_multilingual_glossary_dictionary(glossary, dictionary, **options)
406
+ Requests::MultilingualGlossary::ReplaceDictionary.new(api, glossary, dictionary,
407
+ options).request
408
+ end
409
+
410
+ ##
411
+ # Replaces the entries of the dictionary of one language pair with CSV content.
412
+ #
413
+ # @param [String, DeepL::Resources::MultilingualGlossary] glossary ID or glossary.
414
+ # @param [String] source_lang Source language of the dictionary.
415
+ # @param [String] target_lang Target language of the dictionary.
416
+ # @param [String, IO] csv CSV content, or an IO to read it from. Sent as given.
417
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
418
+ # @return [DeepL::Resources::MultilingualGlossaryDictionary] The replaced dictionary.
419
+ def replace_multilingual_glossary_dictionary_from_csv(glossary, source_lang, target_lang, csv,
420
+ **options)
421
+ dictionary = csv_dictionary(source_lang, target_lang, csv)
422
+ Requests::MultilingualGlossary::ReplaceDictionary.new(api, glossary, dictionary,
423
+ options).request
424
+ end
425
+
426
+ ##
427
+ # Retrieves the entries of the dictionary of one language pair.
428
+ #
429
+ # @param [String, DeepL::Resources::MultilingualGlossary] glossary ID or glossary.
430
+ # @param [String] source_lang Source language of the dictionary.
431
+ # @param [String] target_lang Target language of the dictionary.
432
+ # @return [DeepL::Resources::MultilingualGlossaryDictionaryEntries] The entries as a hash
433
+ # of source term to target term.
434
+ # @raise [DeepL::Exceptions::NotFound] If the glossary does not exist or has no dictionary
435
+ # for the language pair.
436
+ def get_multilingual_glossary_dictionary_entries(glossary, source_lang, target_lang)
437
+ Requests::MultilingualGlossary::Entries.new(api, glossary, source_lang, target_lang).request
438
+ end
439
+
440
+ ##
441
+ # Deletes the dictionary of one language pair from a multilingual glossary.
442
+ #
443
+ # @param [String, DeepL::Resources::MultilingualGlossary] glossary ID or glossary.
444
+ # @param [String] source_lang Source language of the dictionary.
445
+ # @param [String] target_lang Target language of the dictionary.
446
+ # @return [nil]
447
+ def delete_multilingual_glossary_dictionary(glossary, source_lang, target_lang)
448
+ Requests::MultilingualGlossary::DestroyDictionary.new(api, glossary, source_lang,
449
+ target_lang).request
450
+ end
451
+
452
+ ##
453
+ # Deletes a multilingual glossary.
454
+ #
455
+ # @param [String, DeepL::Resources::MultilingualGlossary] glossary ID or glossary.
456
+ # @return [nil]
457
+ def delete_multilingual_glossary(glossary)
458
+ Requests::MultilingualGlossary::Destroy.new(api, glossary).request
459
+ end
460
+
461
+ ##
462
+ # Creates a spoken terms collection, which improves the transcription of the given terms.
463
+ #
464
+ # @param [String] name Name of the collection.
465
+ # @param [Array<DeepL::Resources::SpokenTermsEntries, Hash>] term_lists One entry per
466
+ # language. A hash needs the keys +:lang+ and +:entries+ (an array of terms).
467
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
468
+ # @return [DeepL::Resources::SpokenTerms] The created collection.
469
+ # @raise [DeepL::Exceptions::ValidationError] If a term is empty after stripping surrounding
470
+ # whitespace or contains a control character.
471
+ def create_spoken_terms(name, term_lists, **options)
472
+ Requests::SpokenTerms::Create.new(api, name, term_lists, options).request
473
+ end
474
+
475
+ ##
476
+ # Lists all spoken terms collections.
477
+ #
478
+ # @return [Array<DeepL::Resources::SpokenTerms>]
479
+ def list_spoken_terms
480
+ Requests::SpokenTerms::List.new(api).request
481
+ end
482
+
483
+ ##
484
+ # Retrieves a spoken terms collection.
485
+ #
486
+ # @param [String, DeepL::Resources::SpokenTerms] spoken_terms ID or collection.
487
+ # @return [DeepL::Resources::SpokenTerms]
488
+ # @raise [DeepL::Exceptions::NotFound] If the collection does not exist.
489
+ def get_spoken_terms(spoken_terms)
490
+ Requests::SpokenTerms::Find.new(api, spoken_terms).request
491
+ end
492
+
493
+ ##
494
+ # Renames a collection and/or merges terms into its lists. Arguments left +nil+ are not
495
+ # sent; without any, an empty object is sent.
496
+ #
497
+ # @param [String, DeepL::Resources::SpokenTerms] spoken_terms ID or collection.
498
+ # @param [String, nil] name New name.
499
+ # @param [Array<DeepL::Resources::SpokenTermsEntries, Hash>, nil] term_lists Terms to merge
500
+ # into the list of each language; a new language adds a list.
501
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
502
+ # @return [DeepL::Resources::SpokenTerms] The updated collection.
503
+ # @raise [DeepL::Exceptions::ValidationError] If a term is empty after stripping or contains a
504
+ # control character.
505
+ def update_spoken_terms(spoken_terms, name: nil, term_lists: nil, **options)
506
+ Requests::SpokenTerms::Update.new(api, spoken_terms, name, term_lists, options).request
507
+ end
508
+
509
+ ##
510
+ # Deletes a spoken terms collection.
511
+ #
512
+ # @param [String, DeepL::Resources::SpokenTerms] spoken_terms ID or collection.
513
+ # @return [nil]
514
+ def delete_spoken_terms(spoken_terms)
515
+ Requests::SpokenTerms::Destroy.new(api, spoken_terms).request
516
+ end
517
+
518
+ ##
519
+ # Retrieves the terms of one language of a collection.
520
+ #
521
+ # @param [String, DeepL::Resources::SpokenTerms] spoken_terms ID or collection.
522
+ # @param [String] lang Language code, for example +'en'+.
523
+ # @return [DeepL::Resources::SpokenTermsEntries]
524
+ def get_spoken_terms_entries(spoken_terms, lang)
525
+ Requests::SpokenTerms::Entries.new(api, spoken_terms, lang).request
526
+ end
527
+
528
+ ##
529
+ # Replaces the terms of one language, or adds the list if the language is new.
530
+ #
531
+ # @param [String, DeepL::Resources::SpokenTerms] spoken_terms ID or collection.
532
+ # @param [DeepL::Resources::SpokenTermsEntries, Hash] entries The language and its terms; a
533
+ # hash needs the keys +:lang+ and +:entries+.
534
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
535
+ # @return [DeepL::Resources::SpokenTermsList]
536
+ # @raise [DeepL::Exceptions::ValidationError] If a term is empty after stripping or contains a
537
+ # control character.
538
+ def replace_spoken_terms_list(spoken_terms, entries, **options)
539
+ Requests::SpokenTerms::ReplaceTermList.new(api, spoken_terms, entries, options).request
540
+ end
541
+
542
+ ##
543
+ # Deletes the term list of one language of a collection.
544
+ #
545
+ # @param [String, DeepL::Resources::SpokenTerms] spoken_terms ID or collection.
546
+ # @param [String] lang Language code, for example +'en'+.
547
+ # @return [nil]
548
+ def delete_spoken_terms_list(spoken_terms, lang)
549
+ Requests::SpokenTerms::DestroyTermList.new(api, spoken_terms, lang).request
550
+ end
551
+
552
+ ##
553
+ # Lists style rule lists, one page at a time. Arguments left +nil+ are not sent.
554
+ #
555
+ # @param [Integer, nil] page Index of the page, starting at 0 (API default: 0).
556
+ # @param [Integer, nil] page_size Style rule lists per page (API default: 10).
557
+ # @param [Boolean, nil] detailed Whether to include +configured_rules+ and
558
+ # +custom_instructions+.
559
+ # @return [Array<DeepL::Resources::StyleRule>]
560
+ def get_all_style_rules(page: nil, page_size: nil, detailed: nil)
561
+ options = { page: page, page_size: page_size, detailed: detailed }.compact
562
+ Requests::StyleRule::List.new(api, options).request
563
+ end
564
+
565
+ ##
566
+ # Creates a style rule list. Keywords left +nil+ are not sent.
567
+ #
568
+ # @param [String] name Name of the style rule list.
569
+ # @param [String] language Language the rules apply to, for example +'en'+.
570
+ # @param [Hash, nil] configured_rules Category -> rule -> value, sent as given.
571
+ # @param [Array<Hash>, nil] custom_instructions Instructions in the wire shape
572
+ # (+label+, +prompt+ and optionally +source_language+), sent as given.
573
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
574
+ # @return [DeepL::Resources::StyleRule] The created style rule list.
575
+ def create_style_rule(name, language, configured_rules: nil, custom_instructions: nil,
576
+ **options)
577
+ options = options.merge(configured_rules: configured_rules,
578
+ custom_instructions: custom_instructions).compact
579
+ Requests::StyleRule::Create.new(api, name, language, options).request
580
+ end
581
+
582
+ ##
583
+ # Retrieves a style rule list, including its configured rules and custom instructions.
584
+ #
585
+ # @param [String, DeepL::Resources::StyleRule] style_rule ID or style rule list.
586
+ # @return [DeepL::Resources::StyleRule]
587
+ # @raise [DeepL::Exceptions::NotFound] If the style rule list does not exist.
588
+ def get_style_rule(style_rule)
589
+ Requests::StyleRule::Find.new(api, style_rule).request
590
+ end
591
+
592
+ ##
593
+ # Renames a style rule list.
594
+ #
595
+ # @param [String, DeepL::Resources::StyleRule] style_rule ID or style rule list.
596
+ # @param [String] name New name.
597
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
598
+ # @return [DeepL::Resources::StyleRule] The updated style rule list.
599
+ def update_style_rule_name(style_rule, name, **options)
600
+ Requests::StyleRule::Update.new(api, style_rule, name, options).request
601
+ end
602
+
603
+ ##
604
+ # Replaces the configured rules of a style rule list.
605
+ #
606
+ # @param [String, DeepL::Resources::StyleRule] style_rule ID or style rule list.
607
+ # @param [Hash] configured_rules Category -> rule -> value, sent as the request body.
608
+ # @param [Hash] options +:extra_body_parameters+, added to the body as further keys.
609
+ # @return [DeepL::Resources::StyleRule] The updated style rule list.
610
+ def update_style_rule_configured_rules(style_rule, configured_rules, **options)
611
+ Requests::StyleRule::UpdateConfiguredRules.new(api, style_rule, configured_rules,
612
+ options).request
613
+ end
614
+
615
+ ##
616
+ # Deletes a style rule list.
617
+ #
618
+ # @param [String, DeepL::Resources::StyleRule] style_rule ID or style rule list.
619
+ # @return [nil]
620
+ def delete_style_rule(style_rule)
621
+ Requests::StyleRule::Destroy.new(api, style_rule).request
622
+ nil
623
+ end
624
+
625
+ ##
626
+ # Adds a custom instruction to a style rule list. +source_language+ is not sent if +nil+.
627
+ #
628
+ # @param [String, DeepL::Resources::StyleRule] style_rule ID or style rule list.
629
+ # @param [String] label Label of the instruction.
630
+ # @param [String] prompt The instruction.
631
+ # @param [String, nil] source_language Source language the instruction applies to.
632
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
633
+ # @return [DeepL::Resources::CustomInstruction] The created instruction.
634
+ def create_style_rule_custom_instruction(style_rule, label, prompt, source_language: nil,
635
+ **options)
636
+ Requests::StyleRule::CreateCustomInstruction.new(api, style_rule, label, prompt,
637
+ source_language, options).request
638
+ end
639
+
640
+ ##
641
+ # Retrieves a custom instruction of a style rule list.
642
+ #
643
+ # @param [String, DeepL::Resources::StyleRule] style_rule ID or style rule list.
644
+ # @param [String, DeepL::Resources::CustomInstruction] instruction_id ID or instruction.
645
+ # @return [DeepL::Resources::CustomInstruction]
646
+ # @raise [DeepL::Exceptions::NotFound] If the instruction does not exist.
647
+ def get_style_rule_custom_instruction(style_rule, instruction_id)
648
+ Requests::StyleRule::FindCustomInstruction.new(api, style_rule, instruction_id).request
649
+ end
650
+
651
+ ##
652
+ # Replaces a custom instruction of a style rule list. +source_language+ is not sent if
653
+ # +nil+.
654
+ #
655
+ # @param [String, DeepL::Resources::StyleRule] style_rule ID or style rule list.
656
+ # @param [String, DeepL::Resources::CustomInstruction] instruction_id ID or instruction.
657
+ # @param [String] label Label of the instruction.
658
+ # @param [String] prompt The instruction.
659
+ # @param [String, nil] source_language Source language the instruction applies to.
660
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
661
+ # @return [DeepL::Resources::CustomInstruction] The updated instruction.
662
+ def update_style_rule_custom_instruction(style_rule, instruction_id, label, prompt,
663
+ source_language: nil, **options)
664
+ Requests::StyleRule::UpdateCustomInstruction.new(api, style_rule, instruction_id, label,
665
+ prompt, source_language,
666
+ options).request
667
+ end
668
+
669
+ ##
670
+ # Deletes a custom instruction of a style rule list.
671
+ #
672
+ # @param [String, DeepL::Resources::StyleRule] style_rule ID or style rule list.
673
+ # @param [String, DeepL::Resources::CustomInstruction] instruction_id ID or instruction.
674
+ # @return [nil]
675
+ def delete_style_rule_custom_instruction(style_rule, instruction_id)
676
+ Requests::StyleRule::DestroyCustomInstruction.new(api, style_rule, instruction_id).request
677
+ nil
678
+ end
679
+
680
+ ##
681
+ # Lists style profiles, one page at a time. Arguments left +nil+ are not sent.
682
+ #
683
+ # @param [Integer, nil] page Index of the page, starting at 0 (API default: 0).
684
+ # @param [Integer, nil] page_size Style profiles per page (API default: 10, at most 100).
685
+ # @return [Array<DeepL::Resources::StyleProfile>]
686
+ def list_style_profiles(page: nil, page_size: nil)
687
+ Requests::StyleProfile::List.new(api, { page: page, page_size: page_size }.compact).request
688
+ end
689
+
690
+ ##
691
+ # Creates a style profile, which links glossaries, style rule lists and translation
692
+ # memories. Keywords left +nil+ are not sent. If a component cannot be used, the request
693
+ # fails and no style profile is created.
694
+ #
695
+ # @param [String] name Name of the style profile.
696
+ # @param [String, nil] description Free-text description.
697
+ # @param [String, nil] industry The industry the style profile is written for.
698
+ # @param [Array<String, DeepL::Resources::Glossary, DeepL::Resources::MultilingualGlossary>,
699
+ # nil] glossary_ids Glossaries to link (IDs or objects), highest priority first.
700
+ # @param [Array<String, DeepL::Resources::StyleRule>, nil] style_rule_ids Style rule lists.
701
+ # @param [Array<String, DeepL::Resources::TranslationMemory>, nil] translation_memory_ids
702
+ # Translation memories.
703
+ # @param [Boolean, nil] glossary_priority_order_enabled Whether the order of +glossary_ids+
704
+ # sets priority.
705
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
706
+ # @return [DeepL::Resources::StyleProfile] The created style profile.
707
+ def create_style_profile(name, description: nil, industry: nil, glossary_ids: nil, # rubocop:disable Metrics/ParameterLists
708
+ style_rule_ids: nil, translation_memory_ids: nil,
709
+ glossary_priority_order_enabled: nil, **options)
710
+ fields = { description: description, industry: industry, glossary_ids: glossary_ids,
711
+ style_rule_ids: style_rule_ids, translation_memory_ids: translation_memory_ids,
712
+ glossary_priority_order_enabled: glossary_priority_order_enabled }
713
+ Requests::StyleProfile::Create.new(api, name, fields, options).request
714
+ end
715
+
716
+ ##
717
+ # Retrieves a style profile.
718
+ #
719
+ # @param [String, DeepL::Resources::StyleProfile] style_profile ID or style profile.
720
+ # @return [DeepL::Resources::StyleProfile]
721
+ # @raise [ArgumentError] If the ID is empty.
722
+ # @raise [DeepL::Exceptions::NotFound] If the style profile does not exist.
723
+ def get_style_profile(style_profile)
724
+ Requests::StyleProfile::Find.new(api, style_profile).request
725
+ end
726
+
727
+ ##
728
+ # Updates a style profile. Keywords left +nil+ are not sent and leave the field unchanged;
729
+ # without any, an empty object is sent. An empty string clears +description+ or +industry+.
730
+ # A component list replaces the linked list in full; an empty array unlinks all components
731
+ # of that kind.
732
+ #
733
+ # @param [String, DeepL::Resources::StyleProfile] style_profile ID or style profile.
734
+ # @param [String, nil] name New name.
735
+ # @param [Hash] options The keywords of #create_style_profile, including
736
+ # +:extra_body_parameters+.
737
+ # @return [DeepL::Resources::StyleProfile] The updated style profile.
738
+ # @raise [ArgumentError] If the ID is empty.
739
+ def update_style_profile(style_profile, name: nil, description: nil, industry: nil, # rubocop:disable Metrics/ParameterLists
740
+ glossary_ids: nil, style_rule_ids: nil, translation_memory_ids: nil,
741
+ glossary_priority_order_enabled: nil, **options)
742
+ fields = { name: name, description: description, industry: industry,
743
+ glossary_ids: glossary_ids, style_rule_ids: style_rule_ids,
744
+ translation_memory_ids: translation_memory_ids,
745
+ glossary_priority_order_enabled: glossary_priority_order_enabled }
746
+ Requests::StyleProfile::Update.new(api, style_profile, fields, options).request
747
+ end
748
+
749
+ ##
750
+ # Deletes a style profile.
751
+ #
752
+ # @param [String, DeepL::Resources::StyleProfile] style_profile ID or style profile.
753
+ # @return [nil]
754
+ # @raise [ArgumentError] If the ID is empty.
755
+ def delete_style_profile(style_profile)
756
+ Requests::StyleProfile::Destroy.new(api, style_profile).request
757
+ nil
758
+ end
759
+
760
+ ##
761
+ # Lists translation memories, one page at a time. Arguments left +nil+ are not sent.
762
+ #
763
+ # @param [Integer, nil] page Index of the page, starting at 0 (API default: 0).
764
+ # @param [Integer, nil] page_size Translation memories per page (API default: 10).
765
+ # @return [Array<DeepL::Resources::TranslationMemory>]
766
+ def list_translation_memories(page: nil, page_size: nil)
767
+ translation_memories.list({ page: page, page_size: page_size }.compact)
768
+ end
769
+
770
+ ##
771
+ # Retrieves a translation memory.
772
+ #
773
+ # @param [String, DeepL::Resources::TranslationMemory] translation_memory ID or translation
774
+ # memory.
775
+ # @return [DeepL::Resources::TranslationMemory]
776
+ # @raise [DeepL::Exceptions::NotFound] If the translation memory does not exist.
777
+ def get_translation_memory(translation_memory)
778
+ translation_memories.find(translation_memory)
779
+ end
780
+
781
+ ##
782
+ # Deletes a translation memory.
783
+ #
784
+ # @param [String, DeepL::Resources::TranslationMemory] translation_memory ID or translation
785
+ # memory.
786
+ # @return [nil]
787
+ def delete_translation_memory(translation_memory)
788
+ translation_memories.destroy(translation_memory)
789
+ nil
790
+ end
791
+
792
+ ##
793
+ # Retrieves one page of the segments of a translation memory. Pagination is cursor-based:
794
+ # omit +page_cursor+ on the first call, then pass the +next_page_cursor+ of the previous page
795
+ # until it is +nil+. Arguments left +nil+ are not sent.
796
+ #
797
+ # @param [String, DeepL::Resources::TranslationMemory] translation_memory ID or translation
798
+ # memory.
799
+ # @param [Integer, nil] page_size Segments per page, 1-100 (API default: 50).
800
+ # @param [String, nil] page_cursor +next_page_cursor+ of the previous page.
801
+ # @param [String, nil] filter_text Substring matched against source and target texts, at
802
+ # least 2 characters.
803
+ # @param [Boolean, nil] filter_case_sensitive Whether +filter_text+ is case-sensitive (API
804
+ # default: false).
805
+ # @return [DeepL::Resources::TranslationMemorySegments]
806
+ def list_translation_memory_segments(translation_memory, page_size: nil, page_cursor: nil,
807
+ filter_text: nil, filter_case_sensitive: nil)
808
+ options = { page_size: page_size, page_cursor: page_cursor, filter_text: filter_text,
809
+ filter_case_sensitive: filter_case_sensitive }.compact
810
+ translation_memories.segments(translation_memory, options)
811
+ end
812
+
813
+ ##
814
+ # Creates an import job for a new translation memory. Upload the TMX file to the returned
815
+ # upload URL with #upload_translation_memory_file, then wait for the job with
816
+ # #wait_until_translation_memory_job_done. Keywords left +nil+ are not sent.
817
+ #
818
+ # @param [String] file_name Name of the TMX file, for example +'legal.tmx'+.
819
+ # @param [Integer] content_length Size of the file in bytes.
820
+ # @param [String, nil] content_type MIME type of the file (API default: +application/xml+).
821
+ # @param [String, nil] display_name Name of the translation memory (API default: the file
822
+ # name).
823
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the request body last.
824
+ # @return [DeepL::Resources::TranslationMemoryImport] The job ID and the upload URL.
825
+ def create_translation_memory_import(file_name, content_length, content_type: nil,
826
+ display_name: nil, **options)
827
+ translation_memories.create_import(file_name, content_length,
828
+ content_type: content_type, display_name: display_name,
829
+ extra_body_parameters: options[:extra_body_parameters])
830
+ end
831
+
832
+ ##
833
+ # Uploads a TMX file to the upload URL of an import job, which starts the processing. The
834
+ # upload URL is a pre-signed storage URL: no +Authorization+ header and no configured
835
+ # +headers+ are sent to it.
836
+ #
837
+ # @param [DeepL::Resources::TranslationMemoryImport, String] translation_memory_import The
838
+ # import, or its upload URL.
839
+ # @param [String] content Content of the TMX file.
840
+ # @param [String, nil] content_type MIME type of the file, must match the one of the import
841
+ # job. +nil+ sends +application/xml+.
842
+ # @return [nil]
843
+ # @raise [DeepL::Exceptions::Error] If the storage URL replies with a non-2xx status; the error
844
+ # carries +http_status_code+, and +should_retry?+ is true for 429 and 5xx.
845
+ def upload_translation_memory_file(translation_memory_import, content, content_type: nil)
846
+ translation_memories.upload_file(translation_memory_import, content,
847
+ content_type: content_type)
848
+ end
849
+
850
+ ##
851
+ # Imports a TMX file as a new translation memory: creates the import job, uploads the file
852
+ # and waits until the job is completed.
853
+ #
854
+ # @param [String] input_path Path of the TMX file.
855
+ # @param [String, nil] display_name Name of the translation memory (API default: the file
856
+ # name).
857
+ # @param [String, nil] content_type MIME type of the file, declared in the import job and
858
+ # sent with the upload. +nil+ uses +application/xml+.
859
+ # @param [Numeric, nil] timeout_s Maximum time in seconds to wait for the job, +nil+ to wait
860
+ # until it is completed. The API reports +awaiting_input+ for a while after the upload.
861
+ # @param [Hash] options +:extra_body_parameters+, a hash added to the body of the import job
862
+ # request last.
863
+ # @return [DeepL::Resources::TranslationMemoryJob] The completed job; its result carries the
864
+ # ID of the new translation memory.
865
+ # @raise [ArgumentError] If the file does not exist, before any request is sent.
866
+ # @raise [DeepL::Exceptions::Error] If the file cannot be read, the upload fails, the job fails
867
+ # or expires (with the API's error message), or +timeout_s+ expires.
868
+ def import_translation_memory_from_filepath(input_path, display_name: nil, content_type: nil,
869
+ timeout_s: nil, **options)
870
+ translation_memories.import_from_filepath(
871
+ input_path, display_name: display_name, content_type: content_type, timeout_s: timeout_s,
872
+ extra_body_parameters: options[:extra_body_parameters]
873
+ )
874
+ end
875
+
876
+ ##
877
+ # Creates an export job for a translation memory. Wait for it with
878
+ # #wait_until_translation_memory_job_done, then download the TMX file with
879
+ # #download_translation_memory_export.
880
+ #
881
+ # @param [String, DeepL::Resources::TranslationMemory] translation_memory ID or translation
882
+ # memory.
883
+ # @return [DeepL::Resources::TranslationMemoryExport] The job ID, and whether the API reused
884
+ # a previously completed export.
885
+ def create_translation_memory_export(translation_memory)
886
+ translation_memories.create_export(translation_memory)
887
+ end
888
+
889
+ ##
890
+ # Downloads the TMX file of a completed export job to +output_path+, overwriting an existing
891
+ # file. The download URL is a pre-signed storage URL: no +Authorization+ header and no
892
+ # configured +headers+ are sent to it.
893
+ #
894
+ # @param [DeepL::Resources::TranslationMemoryJob, String] job The completed export job, or
895
+ # its download URL.
896
+ # @param [String] output_path Path to write the file to.
897
+ # @return [nil]
898
+ # @raise [DeepL::Exceptions::Error] If the job has no download URL, for example because it
899
+ # is not completed yet, the download fails or the file cannot be written.
900
+ def download_translation_memory_export(job, output_path)
901
+ translation_memories.download_export(job, output_path)
902
+ nil
903
+ end
904
+
905
+ ##
906
+ # Exports a translation memory to a TMX file: creates the export job, waits until it is
907
+ # completed and writes the file to +output_path+, overwriting an existing file.
908
+ #
909
+ # @param [String, DeepL::Resources::TranslationMemory] translation_memory ID or translation
910
+ # memory.
911
+ # @param [String] output_path Path to write the file to.
912
+ # @param [Numeric, nil] timeout_s Maximum time in seconds to wait for the job, +nil+ to wait
913
+ # until it is completed.
914
+ # @return [DeepL::Resources::TranslationMemoryJob] The completed job.
915
+ # @raise [DeepL::Exceptions::Error] If the directory of +output_path+ does not exist (checked
916
+ # before the job is created), the job fails or expires (with the API's error message),
917
+ # +timeout_s+ expires, the download fails or the file cannot be written.
918
+ def export_translation_memory_to_filepath(translation_memory, output_path, timeout_s: nil)
919
+ translation_memories.export_to_filepath(translation_memory, output_path,
920
+ timeout_s: timeout_s)
921
+ end
922
+
923
+ ##
924
+ # Retrieves the status of a translation memory import or export job once.
925
+ #
926
+ # @param [String, DeepL::Resources::TranslationMemoryJob] job ID or job.
927
+ # @return [DeepL::Resources::TranslationMemoryJob]
928
+ def get_translation_memory_job(job)
929
+ translation_memories.find_job(job)
930
+ end
931
+
932
+ ##
933
+ # Polls a translation memory import or export job every 5 seconds until it is completed.
934
+ #
935
+ # @param [String, DeepL::Resources::TranslationMemoryJob] job ID or job.
936
+ # @param [Numeric, nil] timeout_s Maximum time to wait in seconds, +nil+ to wait until the
937
+ # job is completed, failed or expired. The last wait is cut so that the call never waits
938
+ # past +timeout_s+. A job whose file is never uploaded does not finish.
939
+ # @return [DeepL::Resources::TranslationMemoryJob] The completed job.
940
+ # @raise [DeepL::Exceptions::Error] If the job fails or expires (with the API's error
941
+ # message), or +timeout_s+ expires.
942
+ def wait_until_translation_memory_job_done(job, timeout_s: nil)
943
+ translation_memories.wait_until_job_done(job, timeout_s: timeout_s)
944
+ end
945
+
946
+ def inspect
947
+ "#<#{self.class.name} server_url=#{configuration.host}>"
948
+ end
949
+
950
+ private
951
+
952
+ attr_reader :api
953
+
954
+ # The translation memory methods share the request, polling and ID handling of
955
+ # TranslationMemoryApi.
956
+ def translation_memories
957
+ @translation_memories ||= TranslationMemoryApi.new(api)
958
+ end
959
+
960
+ def configuration
961
+ api.configuration
962
+ end
963
+
964
+ def csv_dictionary(source_lang, target_lang, csv)
965
+ csv = csv.read if csv.respond_to?(:read)
966
+ { source_lang: source_lang, target_lang: target_lang, entries: csv, entries_format: 'csv' }
967
+ end
968
+ end
969
+ end