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/CHANGELOG.md CHANGED
@@ -6,6 +6,416 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [3.10.0] - 2026-11-02
10
+ TL;DR: new `DeepL::Client`, an instance-based client, and `DeepL::AdminClient` for the Admin API.
11
+ New APIs on `DeepL::Client`: multilingual glossaries, style profiles, spoken terms and text correction.
12
+ Read Changed and Deprecated before upgrading; no public API is removed in this release.
13
+
14
+ ### Added
15
+ - Added `DeepL::Resources::DocumentTranslationStatus#done?` (the status is `done`).
16
+ - `DeepL::Resources::DocumentHandle.new(document_id, document_key)` creates a handle from a
17
+ stored ID and key; the request and response arguments are now optional.
18
+ - `DeepL::Exceptions::DocumentTranslationError` copies `http_status_code` and `should_retry?`
19
+ from the DeepL error it wraps.
20
+ - `DeepL::Exceptions::Error.new` accepts the keywords `http_status_code:` and `should_retry:`.
21
+ - Added `DeepL::Client#get_usage`, which returns the same `DeepL::Resources::Usage` as
22
+ `DeepL.usage`. Please refer to the README for usage instructions.
23
+ - Added translation memories to `DeepL::Client`: `list_translation_memories(page: nil,
24
+ page_size: nil)`, `get_translation_memory(translation_memory)`,
25
+ `delete_translation_memory(translation_memory)`,
26
+ `list_translation_memory_segments(translation_memory, page_size: nil, page_cursor: nil,
27
+ filter_text: nil, filter_case_sensitive: nil)`, `create_translation_memory_import(file_name,
28
+ content_length, content_type: nil, display_name: nil, **options)`,
29
+ `upload_translation_memory_file(translation_memory_import, content, content_type: nil)`,
30
+ `import_translation_memory_from_filepath(input_path, display_name: nil, content_type: nil,
31
+ timeout_s: nil, **options)`, `create_translation_memory_export(translation_memory)`,
32
+ `download_translation_memory_export(job, output_path)`,
33
+ `export_translation_memory_to_filepath(translation_memory, output_path, timeout_s: nil)`,
34
+ `get_translation_memory_job(job)` and `wait_until_translation_memory_job_done(job,
35
+ timeout_s: nil)`. Keywords left `nil` are not sent; the delete and download methods return
36
+ `nil`. Please refer to the README for usage instructions. A missing input file raises
37
+ `ArgumentError` before any request; other local file failures raise
38
+ `DeepL::Exceptions::Error` with the original error as `cause`. A non-2xx response from the
39
+ pre-signed storage URL (upload or export download) raises `DeepL::Exceptions::Error` with
40
+ `http_status_code`, `should_retry?` true for 429 and 5xx.
41
+ - `DeepL.translation_memories.import_from_filepath` accepts `content_type:`, declared in the
42
+ import job and sent with the upload, and `extra_body_parameters:`.
43
+ `DeepL.translation_memories.create_import` accepts `extra_body_parameters:`.
44
+ - Added `DeepL::Resources::TranslationMemoryJob#done?` (the status is `completed`) and `#ok?`
45
+ (the status is not `failed` or `expired`, also true while the job runs). `finished?` stays
46
+ true for any terminal status.
47
+ - Added style rule lists (`/v3/style_rules`) to `DeepL::Client`:
48
+ `get_all_style_rules(page: nil, page_size: nil, detailed: nil)`,
49
+ `create_style_rule(name, language, configured_rules: nil, custom_instructions: nil,
50
+ **options)`, `get_style_rule(style_rule)`, `update_style_rule_name(style_rule, name,
51
+ **options)`, `update_style_rule_configured_rules(style_rule, configured_rules, **options)`,
52
+ `delete_style_rule(style_rule)`, `create_style_rule_custom_instruction(style_rule, label,
53
+ prompt, source_language: nil, **options)`, `get_style_rule_custom_instruction(style_rule,
54
+ instruction_id)`, `update_style_rule_custom_instruction(style_rule, instruction_id, label,
55
+ prompt, source_language: nil, **options)` and
56
+ `delete_style_rule_custom_instruction(style_rule, instruction_id)`. Keywords left `nil` are
57
+ not sent; the delete methods return `nil`. Please refer to the README for usage
58
+ instructions.
59
+ - The style rule operations, in `DeepL::Client` and in `DeepL.style_rules`, take a style rule
60
+ list as its ID or a `DeepL::Resources::StyleRule`, and a custom instruction as its ID or a
61
+ `DeepL::Resources::CustomInstruction`.
62
+ - Added the language operations to `DeepL::Client`: `get_source_languages`,
63
+ `get_target_languages`, `get_language_resources` and `get_languages_for_resource(resource,
64
+ include: nil)`. `include:` takes one value or an array and is sent as one `include` query
65
+ parameter per value; without it none is sent. Source and target languages keep their
66
+ upper-case codes; `get_languages_for_resource` returns the codes as the API sends them.
67
+ `get_languages_for_resource` returns `DeepL::Resources::LanguageSupport` objects
68
+ (source/target usability, status and per-feature support); `get_language_resources` returns
69
+ `DeepL::Resources::LanguageResource` objects with each feature's source and/or target
70
+ language requirement.
71
+ - Added `DeepL::Constants::LanguageResourceName` (`TRANSLATE_TEXT`, `TRANSLATE_DOCUMENT`,
72
+ `GLOSSARY`, `VOICE`, `WRITE`, `STYLE_RULES`, `TRANSLATION_MEMORY`) and
73
+ `DeepL::Constants::LanguageInclude` (`BETA`, `EXTERNAL`). Other strings are still accepted
74
+ and sent unchanged.
75
+ - Added document translation to `DeepL::Client`: `translate_document(input_path,
76
+ output_path, source_lang, target_lang, timeout_s: nil, **options)`,
77
+ `translate_document_upload(input_path, source_lang, target_lang, **options)`,
78
+ `translate_document_get_status(handle)`, `translate_document_wait_until_done(handle,
79
+ timeout_s: nil)` and `translate_document_download(handle, output_path)`. The file name is
80
+ the `filename:` option. `translate_document` returns the final status and wraps every
81
+ failure in `DeepL::Exceptions::DocumentTranslationError`; `translate_document_wait_until_done`
82
+ raises `DeepL::Exceptions::Error` with the API's message when the translation fails, and both
83
+ raise when `timeout_s` expires. `translate_document` and `translate_document_upload` raise
84
+ `ArgumentError` for a missing input file before any request; `translate_document` does not
85
+ wrap it in `DocumentTranslationError`.
86
+ - Added `DeepL::Exceptions::DocumentNotReadyError` (a `ServerError`), raised for HTTP 503 on a
87
+ document download.
88
+ - Added `DeepL::Exceptions::DocumentTranslationError#document_handle`, the handle of the
89
+ uploaded document (`nil` if the upload failed).
90
+ - Added `DeepL::Resources::DocumentTranslationStatus#ok?` (the translation has not failed) and
91
+ `DeepL::Constants::DocumentStatusCode` (`QUEUED`, `TRANSLATING`, `DONE`, `ERROR`).
92
+ - Added `DeepL::Client#translate_text(text, source_lang, target_lang, **options)`. It takes
93
+ the options of `DeepL.translate` as keyword arguments and returns one
94
+ `DeepL::Resources::Text` for a string and an array for an array, also for a one-element
95
+ array.
96
+ - Added `DeepL::Client#rephrase_text(text, target_lang = nil, writing_style: nil, tone: nil,
97
+ **options)` and `DeepL::Client#correct_text(text, target_lang = nil, **options)`. The
98
+ writing style and tone are keyword arguments, `extra_body_parameters:` is applied last, and
99
+ both return one `DeepL::Resources::Text` for a string and an array for an array, also for a
100
+ one-element array.
101
+ - Added `target_language` (`String`) to `DeepL::Resources::Text` for rephrase and correct
102
+ results. It is `nil` for text translation results and when the response does not contain
103
+ it or contains an unexpected value.
104
+ - Added the `glossary` option to text translation and document translation
105
+ (`DeepL.document.upload` and `translate_document`). It takes a glossary ID or a
106
+ `DeepL::Resources::Glossary` or `DeepL::Resources::MultilingualGlossary` object and is sent
107
+ as `glossary_id`.
108
+ - Added `billed_characters` (`Integer`) and `tag_handling_version` (`String`) to
109
+ `DeepL::Resources::Text` for text translation results. They are `nil` when the response
110
+ does not contain them or contains an unexpected value.
111
+ - Added `DeepL::Constants::TagHandlingVersion` (`V1`, `V2`) for the `tag_handling_version`
112
+ option.
113
+ - Added `DeepL::Client`, a client instance that owns its configuration and HTTP connection
114
+ and reads no environment variables: `DeepL::Client.new(auth_key, server_url:, headers:,
115
+ max_retries:, timeout:, proxy:, send_platform_info:, app_info:, http_client_options:,
116
+ logger:)`. Its operations follow in later changes. New public types: `DeepL::Client` and
117
+ `DeepL::AppInfo` (`app_name`, `app_version`).
118
+ - Added the client options `headers:` (sent with every DeepL API request; a header of the same
119
+ name, in any casing, replaces the library's, including `Authorization` and `User-Agent`,
120
+ except `Content-Type`, `Content-Length`, `Transfer-Encoding` and `Host`), `timeout:` (seconds
121
+ per attempt, default 10, applied to opening, reading and writing) and `proxy:` (a proxy URL,
122
+ also accepted as the `proxy` of `DeepL::HTTPClientOptions`; its user info never shows in
123
+ `inspect` output or error messages). The module configuration has `headers` and `timeout`
124
+ (default 60) attributes too.
125
+ - Added `DeepL::Client.free_account_auth_key?(key)`.
126
+ - Added `DeepL::Exceptions::ConnectionError`, raised by `DeepL::Client` and
127
+ `DeepL::AdminClient` for network failures without an HTTP response (DNS, connection, TLS,
128
+ timeout) after the retries; the original exception is its `cause`. The module-level API still
129
+ raises the original network errors.
130
+ - Added `DeepL::Exceptions::ValidationError`, a `DeepL::Exceptions::Error` raised before any
131
+ request for an invalid glossary or spoken terms term and an unsupported `DeepL.languages`
132
+ `type`.
133
+ - Added `http_status_code` (`Integer`, `nil` without a response) to
134
+ `DeepL::Exceptions::Error`. `RequestError#request` now returns the failed request, and the
135
+ message of a `RequestError` for an unmapped status contains the status and the API's
136
+ message instead of `Unknown error.`.
137
+ - Added the `style_profile` option to text translation (`DeepL.translate`) and document
138
+ translation (`DeepL.document.upload` and `translate_document`). It takes a style profile
139
+ ID string or a `DeepL::Resources::StyleProfile` object and is sent as `style_profile_id`.
140
+ - Added support for style profiles (`/v3/style_profiles`) to `DeepL::Client`:
141
+ `create_style_profile(name, **options)`, `list_style_profiles(page:, page_size:)`,
142
+ `get_style_profile(style_profile)`, `update_style_profile(style_profile, **options)` and
143
+ `delete_style_profile(style_profile)` (returns `nil`). A style profile links glossaries,
144
+ style rule lists and translation memories; it is given as its ID or a
145
+ `DeepL::Resources::StyleProfile`, whose ID is `style_profile_id`. The component lists
146
+ (`glossary_ids`, `style_rule_ids`, `translation_memory_ids`) take IDs or the
147
+ `Glossary`, `MultilingualGlossary`, `StyleRule` and `TranslationMemory` objects, which are
148
+ sent as their IDs. In create and update, keywords left `nil` are not sent; an empty string
149
+ clears `description` or `industry`, and an empty array unlinks all components of that
150
+ kind. Please refer to the README for usage instructions.
151
+ - Added support for spoken terms collections (`/v3/spoken-terms`) to `DeepL::Client`:
152
+ `create_spoken_terms(name, term_lists, **options)`, `list_spoken_terms`,
153
+ `get_spoken_terms(spoken_terms)`, `update_spoken_terms(spoken_terms, name:, term_lists:,
154
+ **options)`, `delete_spoken_terms(spoken_terms)`, `get_spoken_terms_entries(spoken_terms,
155
+ lang)`, `replace_spoken_terms_list(spoken_terms, entries, **options)` and
156
+ `delete_spoken_terms_list(spoken_terms, lang)`. A collection is given as its ID or a
157
+ `DeepL::Resources::SpokenTerms` (`spoken_terms_id`, `name`, `creation_time`, `term_lists`);
158
+ the terms of a language as a `DeepL::Resources::SpokenTermsEntries` or a hash with `:lang`
159
+ and `:entries`. Terms are arrays of strings and are stripped and checked like glossary terms
160
+ before they are sent. The delete methods return `nil`. Please refer to the README for usage
161
+ instructions. Collection IDs are URL-encoded in the request paths. `creation_time` is a UTC
162
+ `Time` when the timestamp has no offset and `nil` when it is malformed.
163
+ - Added `DeepL::AdminClient` for the Admin API. It must be created with an admin
164
+ API key, accepts the options of `DeepL::Client` and supports managing developer keys
165
+ (`create_developer_key(label: nil)`, `list_developer_keys`, `deactivate_developer_key`,
166
+ `rename_developer_key`, `set_developer_key_usage_limits`) and retrieving usage analytics
167
+ (`get_usage_analytics`, `get_custom_tag_usage_analytics`). The four methods that send a
168
+ body accept `extra_body_parameters:`, added to the request body last.
169
+ `create_developer_key` is retried only when the request was not sent (HTTP 429, DNS
170
+ resolution errors, refused connections and connection timeouts), and the raised error's
171
+ `should_retry?` is true only when the request was not sent.
172
+ `DeveloperKey#label`, `DeveloperKey#usage_limits`, `KeyUsage#api_key_label`,
173
+ `AdminUsageReport#total_usage`, `#start_date`, `#end_date` and
174
+ `AdminUsageBreakdown#speech_to_speech_minutes` and
175
+ `CustomTagUsageBreakdown#speech_to_speech_minutes` are `nil` when the response does not
176
+ contain them.
177
+ `set_developer_key_usage_limits` changes only the given limits, including
178
+ `speech_to_speech_milliseconds`. New public types: `DeepL::AdminClient`,
179
+ `DeepL::Resources::DeveloperKey`, `DeepL::Resources::DeveloperKeyUsageLimits`,
180
+ `DeepL::Resources::AdminUsageReport`, `DeepL::Resources::AdminUsageBreakdown`,
181
+ `DeepL::Resources::KeyUsage`, `DeepL::Resources::CustomTagUsageReport`,
182
+ `DeepL::Resources::CustomTagUsageBreakdown`, `DeepL::Resources::CustomTagUsage`,
183
+ `DeepL::Constants::AdminUsageGroupBy` and `DeepL::Constants::CustomTagUsageAggregateBy`.
184
+ The usage breakdown includes `speech_to_speech_minutes`.
185
+ - Added support for multilingual glossaries (`/v3/glossaries`) to `DeepL::Client`:
186
+ `create_multilingual_glossary(name, dictionaries, **options)`,
187
+ `create_multilingual_glossary_from_csv(name, source_lang, target_lang, csv, **options)`,
188
+ `list_multilingual_glossaries`, `get_multilingual_glossary(glossary)`,
189
+ `update_multilingual_glossary(glossary, name: nil, dictionaries: nil, **options)` (sends
190
+ only the keywords given, `{}` without any), `update_multilingual_glossary_name`,
191
+ `update_multilingual_glossary_dictionary(glossary, dictionary, **options)`,
192
+ `update_multilingual_glossary_dictionary_from_csv`,
193
+ `replace_multilingual_glossary_dictionary(glossary, dictionary, **options)`,
194
+ `replace_multilingual_glossary_dictionary_from_csv`,
195
+ `get_multilingual_glossary_dictionary_entries(glossary, source_lang, target_lang)`,
196
+ `delete_multilingual_glossary_dictionary(glossary, source_lang, target_lang)` and
197
+ `delete_multilingual_glossary(glossary)`. A glossary is given as its ID or a
198
+ `DeepL::Resources::MultilingualGlossary`; the ID is URL-encoded in the request paths. A
199
+ dictionary is a `DeepL::Resources::MultilingualGlossaryDictionaryEntries` (`source_lang`,
200
+ `target_lang` and `entries`, a hash of source term to target term; constructible with
201
+ `.new` from a hash, source/target pairs or a TSV string) or a hash
202
+ with the same keys. A source term that appears twice (after stripping) or a pair that is not
203
+ exactly two strings raises `DeepL::Exceptions::ValidationError` before any request.
204
+ Entries given as a TSV string are parsed into pairs, and each term is stripped and checked
205
+ like terms given as pairs; a line that is not exactly two tab-separated terms raises
206
+ `DeepL::Exceptions::ValidationError`. CSV content is sent as given.
207
+ Entries given as a hash or pairs are sent as TSV, also with `entries_format: 'csv'`.
208
+ `get_multilingual_glossary_dictionary_entries` returns one dictionary
209
+ and raises `DeepL::Exceptions::NotFound` if the glossary has none for the language pair;
210
+ the delete methods return `nil`. `creation_time` is a UTC `Time`, `nil` when malformed.
211
+ Please refer to the README for usage instructions.
212
+ - Added the `enable_watermark` option to `DeepL.document.upload` and
213
+ `DeepL.document.translate_document`. When `true`, the API adds a "Translated by DeepL"
214
+ watermark to `docx` and `pdf` output. Previously the option was silently dropped.
215
+ - `DeepL.usage` now returns each usage type as a `DeepL::Resources::Usage::Detail` (`count`,
216
+ `limit`, `limit_reached?`), like the other DeepL client libraries: `character`, `document`,
217
+ `team_document`, and for API Pro users `api_key_character`, `speech_to_text_minutes` and
218
+ `speech_to_speech_minutes`. API Pro users also get `start_time` and `end_time` (as `Time`)
219
+ and `products` (an array of `DeepL::Resources::UsageProduct`). Fields missing from the
220
+ response are `nil`. The new `any_limit_reached?` checks the character, document and team
221
+ document limits, not the API Pro ones. `character_count`, `character_limit`,
222
+ `quota_exceeded?` and `to_s` are unchanged.
223
+
224
+ ### Changed
225
+ - Text translation accepts a glossary without a source language: the `glossary_ids`
226
+ option no longer requires `source_lang`, and the API detects the source language.
227
+ Document translation with `glossary_ids` still requires `source_lang`.
228
+ - Text translation now always sends `show_billed_characters: true`, so every result has
229
+ `billed_characters`; previously it was sent only when set.
230
+ - Requests of the module-level API now time out after `config.timeout` seconds per attempt
231
+ (default 60, or the backoff interval if longer), applied to opening, reading and writing;
232
+ previously Net::HTTP's 60-second defaults applied and could not be set. Timeouts given in
233
+ `DeepL::HTTPClientOptions` still take precedence.
234
+ - `DeepL.languages` and `DeepL.glossaries.language_pairs` now use the `GET /v3/languages`
235
+ endpoint instead of the deprecated `GET /v2/languages` and `GET /v2/glossary-language-pairs`
236
+ endpoints. Public method signatures and return types are unchanged. An unsupported `type`
237
+ option now raises `DeepL::Exceptions::ValidationError` before any request; previously the
238
+ API answered with `DeepL::Exceptions::BadRequest`.
239
+ - Language lists returned by `DeepL.languages` and `DeepL.glossaries.language_pairs` may now
240
+ include additional languages that the v2 endpoints did not list.
241
+ - `DeepL.translation_memories.export_to_filepath` now checks the output directory before
242
+ creating the export job and raises `Errno::ENOENT` if it does not exist; previously the same
243
+ error came after the export finished. `DeepL::Client#export_translation_memory_to_filepath`
244
+ raises `DeepL::Exceptions::Error` instead.
245
+ - Glossary terms given as `[source, target]` pairs are now stripped of surrounding whitespace
246
+ before they are sent; previously they were sent as given.
247
+ - Every 5xx reply is now retried; previously only 429 and 500 were, and 502, 503, 504 and other
248
+ 5xx replies were raised at once.
249
+ - Query values are now encoded per RFC 3986, with a space as `%20`; previously as `+`.
250
+ - `DeepL.document.translate_document` and `DocumentHandle#wait_until_document_translation_finished`
251
+ now poll until the translation is done or failed; previously they stopped after 11 status
252
+ requests (about 50 seconds) and `translate_document` then downloaded the unfinished document.
253
+ A configured `max_doc_status_queries` still limits the requests; `translate_document` then
254
+ raises `DocumentTranslationError`.
255
+ - A malformed API response (invalid JSON, missing or mistyped fields) now raises
256
+ `DeepL::Exceptions::Error` for every request; previously `JSON::ParserError`,
257
+ `NoMethodError`, `TypeError` or `KeyError`.
258
+ - Reworked the test suite so that it no longer needs the `deepl-mock` server. The offline specs
259
+ assert the exact outgoing requests with WebMock, and one real-API spec per resource calls each
260
+ endpoint once. `webmock` and `webrick` are test dependencies again.
261
+
262
+ ### Deprecated
263
+ - `DeepL::Resources::DocumentTranslationStatus#successful?`, use `#done?` instead. It prints a
264
+ deprecation warning once per process when Ruby's deprecation warnings are enabled
265
+ (`ruby -W:deprecated`) and keeps working unchanged.
266
+ - `DeepL::Resources::Usage#character_count`, `#character_limit` and `#quota_exceeded?`, use
267
+ `character.count`, `character.limit` and `character.limit_reached?` (or `any_limit_reached?`)
268
+ instead. Each prints a deprecation warning once per process when Ruby's deprecation warnings
269
+ are enabled (`ruby -W:deprecated`) and keeps working unchanged. `DeepL.usage` now names
270
+ `DeepL::Client#get_usage` as its replacement.
271
+ - The methods of `DeepL::TranslationMemoryApi` (`DeepL.translation_memories`), use the
272
+ translation memory methods of `DeepL::Client` instead (`list` is
273
+ `list_translation_memories`, `find` is `get_translation_memory`, `segments` is
274
+ `list_translation_memory_segments`, `find_job` is `get_translation_memory_job`, and so on).
275
+ They keep working unchanged.
276
+ - The methods of `DeepL::StyleRuleApi` (`DeepL.style_rules`), use the style rule methods of
277
+ `DeepL::Client` instead (`list` is `get_all_style_rules`, `find` is `get_style_rule`,
278
+ `update_name` is `update_style_rule_name`, `destroy` is `delete_style_rule`, and so on).
279
+ They keep working unchanged.
280
+ - `DeepL::Exceptions::NotSupported` is deprecated: only the module-level API raises it, from
281
+ `DeepL::Resources::Language#supports_formality?` for a source language of `DeepL.languages`;
282
+ for a source language from `DeepL::Client`, `supports_formality?` returns `nil`. Creating one
283
+ prints a deprecation warning once per process when Ruby's deprecation warnings are enabled
284
+ (`ruby -W:deprecated`).
285
+ - The module-level API is deprecated in favour of `DeepL::Client` and will be removed in the
286
+ next major version: `DeepL.configure`, `DeepL.configuration`, `DeepL.api`, `DeepL.translate`,
287
+ `DeepL.rephrase`, `DeepL.usage`, `DeepL.languages`, `DeepL.document`, `DeepL.glossaries`,
288
+ `DeepL.style_rules`,
289
+ `DeepL.translation_memories`, `DeepL.with_session` and `DeepL.http_client`. Each prints a
290
+ deprecation warning once per process when Ruby's deprecation warnings are enabled
291
+ (`ruby -W:deprecated`) and keeps working unchanged.
292
+ - `DeepL::Configuration#version=` (the API version is part of each endpoint).
293
+ - The `glossary_id` option of text and document translation, use `glossary` instead. It prints a
294
+ deprecation warning once per process when Ruby's deprecation warnings are enabled
295
+ (`ruby -W:deprecated`) and keeps working unchanged.
296
+ - `DeepL::Resources::DocumentHandle#wait_until_document_translation_finished`, use
297
+ `DeepL::Client#translate_document_wait_until_done` instead.
298
+ - `DeepL::Configuration#max_doc_status_queries=`, use the `timeout_s:` of
299
+ `DeepL::Client#translate_document` or `translate_document_wait_until_done` instead.
300
+
301
+ ### Fixed
302
+ - The README's module-level examples for turning off platform information and for setting app
303
+ information called `DeepL.configure` with arguments, which raises `ArgumentError`. They now
304
+ use the block form.
305
+ - `get_usage` (and `DeepL.usage`), the glossary and multilingual glossary get, list, entries
306
+ and delete requests, and the style rule and custom instruction get and delete requests sent a
307
+ JSON body `{}` with `Content-Type: application/json`. GET and DELETE requests are now sent
308
+ without a body.
309
+ - A response body shorter than its `Content-Length` (the connection closed mid-body) was
310
+ returned as complete: `translate_document_download` wrote a truncated document, and JSON
311
+ calls raised a generic "Unexpected response" error. It is now retried like a reset
312
+ connection and, after the last retry, raises `DeepL::Exceptions::ConnectionError` with
313
+ `should_retry?` true (`DeepL::Client`, `DeepL::AdminClient`) or the original `EOFError`
314
+ (module-level API).
315
+ - An HTTPS proxy that refuses the `CONNECT` request (for example with 407 Proxy Authentication
316
+ Required) raised a raw `Net::HTTPClientException` or `Net::HTTPFatalError`. It now raises
317
+ `DeepL::Exceptions::ConnectionError` with `should_retry?` false (`DeepL::Client`,
318
+ `DeepL::AdminClient`) or the original proxy error (module-level API), without retrying.
319
+ - Net::HTTP re-sent a failed GET, PUT or DELETE request once on its own, so with
320
+ `max_retries: 0` such a request could still be sent twice. The library's connections now
321
+ disable Net::HTTP's own retry; only `max_retries:` applies.
322
+ - TLS failures (including certificate verification failures) and malformed HTTP responses were
323
+ retried like transient network failures. They are now raised at once, as
324
+ `DeepL::Exceptions::ConnectionError` with `should_retry?` false (`DeepL::Client`,
325
+ `DeepL::AdminClient`) or as the original TLS or protocol error (module-level API).
326
+ - `DeepL.document.translate_document` raised `NoMethodError` when the output file already
327
+ exists. It now raises `DeepL::Exceptions::DocumentTranslationError` (without a document
328
+ handle); the file is kept and nothing is uploaded, as in `DeepL::Client#translate_document`.
329
+ The doc comment of `DeepL.document.translate_document` no longer claims that an
330
+ existing file is overwritten.
331
+ - `wait_until_translation_memory_job_done` (and the file helpers) could wait up to five seconds
332
+ past `timeout_s`; the last wait is now cut to the deadline.
333
+ - Uploads to and downloads from the pre-signed translation memory storage URLs ignored the
334
+ proxy, SSL verification, certificate file and timeouts set with `proxy:` or
335
+ `http_client_options:` (or `DeepL.with_session`). They now use the same options as the API
336
+ connection, still without the `Authorization` header.
337
+ - A malformed timestamp in a translation memory, segment, import or job response raised a raw
338
+ `ArgumentError`, and a timestamp without a zone offset was read as local time. A malformed
339
+ timestamp is now `nil`, and one without an offset is read as UTC.
340
+ - `DeepL.translation_memories.list` and `segments` sent an option set to `nil` as an empty
341
+ query parameter (`page=`). Such options are no longer sent.
342
+ - Translation memory and job IDs were inserted into the request path without URL encoding:
343
+ an ID containing `/` changed the path and a space raised `URI::InvalidURIError`. They are
344
+ now URL-encoded.
345
+ - Style rule list and custom instruction IDs were inserted into the request path without
346
+ URL encoding: an ID containing `/` changed the path and a space raised
347
+ `URI::InvalidURIError`. They are now URL-encoded.
348
+ - A malformed `creation_time` or `updated_time` of a style rule list raised a raw
349
+ `ArgumentError`. It now raises `DeepL::Exceptions::Error`.
350
+ - Creating a style rule list removed `configured_rules` and `custom_instructions` from the
351
+ options hash passed in, so reusing the hash for another create sent neither. The hash is no
352
+ longer changed.
353
+ - Downloaded documents were written in text mode, which converted newlines on Windows and
354
+ failed or transcoded the bytes when `Encoding.default_internal` was set. They are now
355
+ written in binary mode.
356
+ - Document IDs are now URL-encoded in the status and download paths.
357
+ - Text translation sent options set to `nil`, and a `nil` source language, as JSON `null`.
358
+ They are now treated as unset and not sent.
359
+ - `translation_memory: nil` sent an empty `translation_memory_id`, and
360
+ `translation_memory_threshold: nil` sent `null` (text) or an empty field (documents). Both
361
+ are now treated as unset and not sent.
362
+ - Document translation (`DeepL.document.upload` and `translate_document`) treated an option
363
+ set to `nil` as set: `glossary_ids` with `glossary_id: nil` raised `ArgumentError`. Options
364
+ set to `nil` are now treated as unset and not sent, as in text translation.
365
+ - A `custom_instructions` string was split at commas into several instructions. It is now
366
+ sent as one instruction.
367
+ - `DeepL.configure { |c| c.auth_key = '...:fx' }` kept `https://api.deepl.com`, because the
368
+ default server was chosen before the block set the key. Without a configured host, the
369
+ server is now chosen from the final key.
370
+ - A Free key with surrounding whitespace (`'abc:fx '`) selected `https://api.deepl.com`
371
+ although the trimmed key was sent. The Free check now trims the key too.
372
+ - The auth key was sent with surrounding whitespace, and a whitespace-only key passed
373
+ `DeepL.configure`. The key is now trimmed, and a blank key raises
374
+ `DeepL::Exceptions::Error`.
375
+ - Refused or reset connections and `EOFError` were not retried. They are now
376
+ retried like timeouts and DNS failures. `AdminClient#create_developer_key` also retries a
377
+ refused connection, which shows that the request was not sent.
378
+ - A 400 or 404 reply with a JSON array body raised `TypeError` when its message was read.
379
+ - `extra_body_parameters` were overridden by options of the same name, and a string key next
380
+ to the SDK's symbol key sent the field twice. They are now applied last and replace the
381
+ SDK's field.
382
+ - `DeepL.with_session` turned TLS off (instead of certificate verification) when
383
+ `enable_ssl_verification` was `false`, turned it on for `http://` hosts, and used a proxy
384
+ with only an address. It now builds its connection like `DeepL::AdminClient`.
385
+ - `DeepL.with_session` opened its connection with Net::HTTP's 60 second open timeout. It now
386
+ uses the configured `timeout` unless the HTTP client options set `open_timeout`.
387
+ - `DocumentHandle#wait_until_document_translation_finished` polled through the module
388
+ configuration instead of the connection that created the handle.
389
+ - `DeepL::Configuration#inspect` showed the auth key. It now shows `[REDACTED]`.
390
+ - Glossary entries given as `[source, target]` pairs (`DeepL.glossaries.create`) were sent
391
+ unchecked, so a term with a tab or newline
392
+ silently corrupted the entries. An invalid term now raises
393
+ `DeepL::Exceptions::ValidationError` before any request: after stripping, it must not be empty
394
+ or contain C0 or C1 control characters, U+2028 or U+2029. Entries given as a TSV or CSV string
395
+ are still sent as given.
396
+ - Debug logging no longer includes the auth key in the logged request headers.
397
+ - Network errors (timeouts, `SocketError`) were retried without limit. They are now retried at
398
+ most `max_network_retries` times, like 429 and 500 replies, and then raised.
399
+ - `DeepL.with_session` without client options raised `ArgumentError`, and without a block raised
400
+ `NoMethodError`. It now runs with default options and raises `ArgumentError` without a block.
401
+ `HTTPClientOptions.new` now defaults `proxy` to `{}` and `cert_path` to `nil`, so the README
402
+ example `HTTPClientOptions.new({ 'proxy_addr' => ..., 'proxy_port' => ... })` works.
403
+ - `DeepL.with_session` left its HTTP client, including the proxy, in place after the block, so
404
+ every later call went through a closed client and the proxy. It now restores the previous
405
+ client when the block ends or raises.
406
+ - A failed `translate_document` raised `NameError` instead of
407
+ `DeepL::Exceptions::DocumentTranslationError` when nothing else had loaded `fileutils`. The
408
+ library now requires it.
409
+ - An option set to `false` with a string key (for example `'split_sentences' => false`) was read as
410
+ unset, so its conversion was skipped and `false` was sent instead of the API value `'0'`.
411
+ - Document translation (`DeepL.document.upload` and `translate_document`) with source language
412
+ `nil` raised `TypeError` before sending the request. It now omits `source_lang`, so the API
413
+ detects the language of the document.
414
+ - `style_rule: nil` sent an empty `style_id` in text and document translation. A `nil` style rule
415
+ is now treated as unset and sends nothing.
416
+ - `DeepL.rephrase` returned fewer results than texts, or `nil` for a string, when the response
417
+ contained fewer improvements than texts. It now raises `DeepL::Exceptions::Error`.
418
+
9
419
  ## [3.9.0] - 2026-08-11
10
420
  ### Added
11
421
  - Added support for using multiple glossaries in text and document translation
@@ -103,7 +513,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
103
513
  ### Added
104
514
  - Added `extra_body_parameters` option to text and document translation methods to pass arbitrary parameters in the request body. This can be used to access beta features or override built-in parameters (such as `target_lang`, `source_lang`, etc.).
105
515
  - Added `rephrase` functionality to the top-level `DeepL` module for convenience.
106
- * Thanks to [341bms](https://github.com/341bms) in [#14](https://github.com/DeepLcom/deepl-rb/issues/14) for the report!
516
+ * Thanks to [341bms](https://github.com/341bms) in [#14](https://github.com/DeepL/deepl-rb/issues/14) for the report!
107
517
 
108
518
  ## [3.2.0] - 2025-01-15
109
519
  ### Added
@@ -155,20 +565,21 @@ The change in major version is only due to the change in maintainership, there i
155
565
  ### Fixed
156
566
  - Make RequestEntityTooLarge error message more clear
157
567
 
158
- [Unreleased]: https://github.com/DeepLcom/deepl-rb/compare/v3.9.0...HEAD
159
- [3.9.0]: https://github.com/DeepLcom/deepl-rb/compare/v3.8.0...v3.9.0
160
- [3.8.0]: https://github.com/DeepLcom/deepl-rb/compare/v3.7.0...v3.8.0
161
- [3.7.0]: https://github.com/DeepLcom/deepl-rb/compare/v3.6.1...v3.7.0
162
- [3.6.1]: https://github.com/DeepLcom/deepl-rb/compare/v3.6.0...v3.6.1
163
- [3.6.0]: https://github.com/DeepLcom/deepl-rb/compare/v3.5.1...v3.6.0
164
- [3.5.1]: https://github.com/DeepLcom/deepl-rb/compare/v3.5.0...v3.5.1
165
- [3.5.0]: https://github.com/DeepLcom/deepl-rb/compare/v3.4.1...v3.5.0
166
- [3.4.1]: https://github.com/DeepLcom/deepl-rb/compare/v3.4.0...v3.4.1
167
- [3.4.0]: https://github.com/DeepLcom/deepl-rb/compare/v3.3.0...v3.4.0
168
- [3.3.0]: https://github.com/DeepLcom/deepl-rb/compare/v3.2.0...v3.3.0
169
- [3.2.0]: https://github.com/DeepLcom/deepl-rb/compare/v3.1.0...v3.2.0
170
- [3.1.0]: https://github.com/DeepLcom/deepl-rb/compare/v3.0.2...v3.1.0
171
- [3.0.2]: https://github.com/DeepLcom/deepl-rb/compare/v3.0.1...v3.0.2
172
- [3.0.1]: https://github.com/DeepLcom/deepl-rb/compare/v3.0.0...v3.0.1
173
- [3.0.0]: https://github.com/DeepLcom/deepl-rb/compare/v2.5.3...v3.0.0
174
- [2.5.3]: https://github.com/DeepLcom/deepl-rb/releases/tag/v2.5.3
568
+ [Unreleased]: https://github.com/DeepL/deepl-rb/compare/v3.10.0...HEAD
569
+ [3.10.0]: https://github.com/DeepL/deepl-rb/compare/v3.9.0...v3.10.0
570
+ [3.9.0]: https://github.com/DeepL/deepl-rb/compare/v3.8.0...v3.9.0
571
+ [3.8.0]: https://github.com/DeepL/deepl-rb/compare/v3.7.0...v3.8.0
572
+ [3.7.0]: https://github.com/DeepL/deepl-rb/compare/v3.6.1...v3.7.0
573
+ [3.6.1]: https://github.com/DeepL/deepl-rb/compare/v3.6.0...v3.6.1
574
+ [3.6.0]: https://github.com/DeepL/deepl-rb/compare/v3.5.1...v3.6.0
575
+ [3.5.1]: https://github.com/DeepL/deepl-rb/compare/v3.5.0...v3.5.1
576
+ [3.5.0]: https://github.com/DeepL/deepl-rb/compare/v3.4.1...v3.5.0
577
+ [3.4.1]: https://github.com/DeepL/deepl-rb/compare/v3.4.0...v3.4.1
578
+ [3.4.0]: https://github.com/DeepL/deepl-rb/compare/v3.3.0...v3.4.0
579
+ [3.3.0]: https://github.com/DeepL/deepl-rb/compare/v3.2.0...v3.3.0
580
+ [3.2.0]: https://github.com/DeepL/deepl-rb/compare/v3.1.0...v3.2.0
581
+ [3.1.0]: https://github.com/DeepL/deepl-rb/compare/v3.0.2...v3.1.0
582
+ [3.0.2]: https://github.com/DeepL/deepl-rb/compare/v3.0.1...v3.0.2
583
+ [3.0.1]: https://github.com/DeepL/deepl-rb/compare/v3.0.0...v3.0.1
584
+ [3.0.0]: https://github.com/DeepL/deepl-rb/compare/v2.5.3...v3.0.0
585
+ [2.5.3]: https://github.com/DeepL/deepl-rb/releases/tag/v2.5.3
data/Gemfile CHANGED
@@ -5,6 +5,8 @@ source 'https://rubygems.org'
5
5
  group :development do
6
6
  # Gem management
7
7
  gem 'juwelier'
8
+ # juwelier 2.4.9 calls Git::Repository#lib, which git 6 removed
9
+ gem 'git', '< 6'
8
10
  end
9
11
 
10
12
  group :test, :development do
@@ -19,4 +21,6 @@ group :test do
19
21
  gem 'rubocop-rspec'
20
22
  gem 'simplecov'
21
23
  gem 'simplecov-cobertura'
24
+ gem 'webmock', '~> 3.34'
25
+ gem 'webrick'
22
26
  end