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.
- checksums.yaml +4 -4
- data/.gitlab-ci.yml +70 -52
- data/.rubocop.yml +26 -0
- data/CHANGELOG.md +429 -18
- data/Gemfile +4 -0
- data/README.md +981 -223
- data/Rakefile +18 -5
- data/VERSION +1 -1
- data/deepl-rb.gemspec +6 -6
- data/lib/deepl/admin_client.rb +183 -0
- data/lib/deepl/api.rb +30 -6
- data/lib/deepl/app_info.rb +20 -0
- data/lib/deepl/client.rb +969 -0
- data/lib/deepl/configuration.rb +56 -10
- data/lib/deepl/constants/admin_usage_group_by.rb +13 -0
- data/lib/deepl/constants/custom_tag_usage_aggregate_by.rb +13 -0
- data/lib/deepl/constants/document_status_code.rb +17 -0
- data/lib/deepl/constants/language_include.rb +17 -0
- data/lib/deepl/constants/language_resource_name.rb +21 -0
- data/lib/deepl/constants/tag_handling_version.rb +14 -0
- data/lib/deepl/deprecation.rb +42 -0
- data/lib/deepl/document_api.rb +41 -22
- data/lib/deepl/exceptions/bad_request.rb +1 -3
- data/lib/deepl/exceptions/connection_error.rb +37 -0
- data/lib/deepl/exceptions/document_not_ready_error.rb +17 -0
- data/lib/deepl/exceptions/document_translation_error.rb +17 -1
- data/lib/deepl/exceptions/error.rb +13 -1
- data/lib/deepl/exceptions/not_found.rb +8 -3
- data/lib/deepl/exceptions/not_supported.rb +6 -0
- data/lib/deepl/exceptions/request_error.rb +19 -2
- data/lib/deepl/exceptions/server_error.rb +7 -1
- data/lib/deepl/exceptions/validation_error.rb +14 -0
- data/lib/deepl/requests/admin/base.rb +94 -0
- data/lib/deepl/requests/admin/create_developer_key.rb +38 -0
- data/lib/deepl/requests/admin/custom_tag_usage_analytics.rb +38 -0
- data/lib/deepl/requests/admin/list_developer_keys.rb +28 -0
- data/lib/deepl/requests/admin/update_developer_key.rb +35 -0
- data/lib/deepl/requests/admin/usage_analytics.rb +36 -0
- data/lib/deepl/requests/base.rb +229 -59
- data/lib/deepl/requests/correct.rb +57 -0
- data/lib/deepl/requests/document/download.rb +11 -3
- data/lib/deepl/requests/document/get_status.rb +2 -2
- data/lib/deepl/requests/document/upload.rb +43 -10
- data/lib/deepl/requests/glossary/create.rb +6 -2
- data/lib/deepl/requests/glossary/language_pairs.rb +20 -4
- data/lib/deepl/requests/language_resources.rb +41 -0
- data/lib/deepl/requests/languages.rb +28 -4
- data/lib/deepl/requests/languages_for_resource.rb +54 -0
- data/lib/deepl/requests/multilingual_glossary/create.rb +37 -0
- data/lib/deepl/requests/multilingual_glossary/destroy.rb +36 -0
- data/lib/deepl/requests/multilingual_glossary/destroy_dictionary.rb +51 -0
- data/lib/deepl/requests/multilingual_glossary/entries.rb +66 -0
- data/lib/deepl/requests/multilingual_glossary/find.rb +35 -0
- data/lib/deepl/requests/multilingual_glossary/helpers.rb +63 -0
- data/lib/deepl/requests/multilingual_glossary/list.rb +39 -0
- data/lib/deepl/requests/multilingual_glossary/replace_dictionary.rb +41 -0
- data/lib/deepl/requests/multilingual_glossary/update.rb +40 -0
- data/lib/deepl/requests/rephrase.rb +5 -9
- data/lib/deepl/requests/spoken_terms/create.rb +37 -0
- data/lib/deepl/requests/spoken_terms/destroy.rb +36 -0
- data/lib/deepl/requests/spoken_terms/destroy_term_list.rb +41 -0
- data/lib/deepl/requests/spoken_terms/entries.rb +46 -0
- data/lib/deepl/requests/spoken_terms/find.rb +35 -0
- data/lib/deepl/requests/spoken_terms/helpers.rb +67 -0
- data/lib/deepl/requests/spoken_terms/list.rb +35 -0
- data/lib/deepl/requests/spoken_terms/replace_term_list.rb +41 -0
- data/lib/deepl/requests/spoken_terms/update.rb +40 -0
- data/lib/deepl/requests/style_profile/base.rb +83 -0
- data/lib/deepl/requests/style_profile/create.rb +35 -0
- data/lib/deepl/requests/style_profile/destroy.rb +34 -0
- data/lib/deepl/requests/style_profile/find.rb +33 -0
- data/lib/deepl/requests/style_profile/list.rb +43 -0
- data/lib/deepl/requests/style_profile/update.rb +35 -0
- data/lib/deepl/requests/style_rule/create.rb +1 -1
- data/lib/deepl/requests/style_rule/create_custom_instruction.rb +4 -2
- data/lib/deepl/requests/style_rule/destroy.rb +4 -2
- data/lib/deepl/requests/style_rule/destroy_custom_instruction.rb +5 -3
- data/lib/deepl/requests/style_rule/find.rb +4 -2
- data/lib/deepl/requests/style_rule/find_custom_instruction.rb +5 -3
- data/lib/deepl/requests/style_rule/helpers.rb +35 -0
- data/lib/deepl/requests/style_rule/list.rb +1 -1
- data/lib/deepl/requests/style_rule/update.rb +4 -2
- data/lib/deepl/requests/style_rule/update_configured_rules.rb +4 -2
- data/lib/deepl/requests/style_rule/update_custom_instruction.rb +5 -3
- data/lib/deepl/requests/translate.rb +52 -20
- data/lib/deepl/requests/translation_memory/base.rb +1 -4
- data/lib/deepl/requests/translation_memory/create_export.rb +1 -1
- data/lib/deepl/requests/translation_memory/create_import.rb +1 -1
- data/lib/deepl/requests/translation_memory/destroy.rb +1 -1
- data/lib/deepl/requests/translation_memory/download_export.rb +4 -0
- data/lib/deepl/requests/translation_memory/find.rb +1 -1
- data/lib/deepl/requests/translation_memory/find_job.rb +1 -1
- data/lib/deepl/requests/translation_memory/list.rb +2 -1
- data/lib/deepl/requests/translation_memory/segments.rb +3 -2
- data/lib/deepl/requests/translation_memory/storage_base.rb +40 -8
- data/lib/deepl/requests/translation_memory/upload_file.rb +1 -1
- data/lib/deepl/requests/usage.rb +28 -1
- data/lib/deepl/requests/write_improvements.rb +35 -0
- data/lib/deepl/resources/admin_usage_report.rb +73 -0
- data/lib/deepl/resources/custom_tag_usage_report.rb +59 -0
- data/lib/deepl/resources/developer_key.rb +57 -0
- data/lib/deepl/resources/document_handle.rb +24 -25
- data/lib/deepl/resources/document_translation_status.rb +23 -4
- data/lib/deepl/resources/language.rb +10 -2
- data/lib/deepl/resources/language_resource.rb +57 -0
- data/lib/deepl/resources/language_support.rb +56 -0
- data/lib/deepl/resources/multilingual_glossary.rb +112 -0
- data/lib/deepl/resources/spoken_terms.rb +61 -0
- data/lib/deepl/resources/style_profile.rb +44 -0
- data/lib/deepl/resources/style_rule.rb +3 -0
- data/lib/deepl/resources/text.rb +18 -2
- data/lib/deepl/resources/translation_memory.rb +8 -2
- data/lib/deepl/resources/translation_memory_import.rb +1 -1
- data/lib/deepl/resources/translation_memory_job.rb +23 -3
- data/lib/deepl/resources/translation_memory_segments.rb +8 -6
- data/lib/deepl/resources/usage.rb +93 -4
- data/lib/deepl/style_rule_api.rb +11 -0
- data/lib/deepl/translation_memory_api.rb +104 -24
- data/lib/deepl/utils/client_builder.rb +54 -0
- data/lib/deepl/utils/document_poller.rb +70 -0
- data/lib/deepl/utils/exception_builder.rb +3 -2
- data/lib/deepl/utils/http_client_builder.rb +79 -0
- data/lib/deepl/utils/number_parser.rb +34 -0
- data/lib/deepl/utils/term_validator.rb +33 -0
- data/lib/deepl/utils/time_parser.rb +14 -0
- data/lib/deepl.rb +166 -41
- data/lib/http_client_options.rb +21 -1
- data/lib/version.rb +1 -1
- data/spec/fixtures.rb +33 -0
- data/spec/offline/admin_client_spec.rb +551 -0
- data/spec/offline/client_spec.rb +273 -0
- data/spec/{api → offline}/configuration_spec.rb +45 -3
- data/spec/{constants → offline}/constants_spec.rb +23 -1
- data/spec/offline/correct_text_spec.rb +50 -0
- data/spec/offline/deprecation_spec.rb +32 -0
- data/spec/offline/errors_spec.rb +92 -0
- data/spec/offline/glossaries_spec.rb +98 -0
- data/spec/offline/http_client_spec.rb +319 -0
- data/spec/offline/languages_spec.rb +155 -0
- data/spec/offline/module_methods_spec.rb +13 -0
- data/spec/offline/multilingual_glossaries_spec.rb +358 -0
- data/spec/offline/offline_helper.rb +71 -0
- data/spec/offline/rephrase_text_spec.rb +73 -0
- data/spec/offline/spoken_terms_spec.rb +215 -0
- data/spec/offline/style_profiles_spec.rb +266 -0
- data/spec/offline/style_rules_spec.rb +318 -0
- data/spec/offline/translate_document_spec.rb +606 -0
- data/spec/offline/translate_text_spec.rb +286 -0
- data/spec/offline/translation_memories_client_spec.rb +256 -0
- data/spec/offline/translation_memories_spec.rb +463 -0
- data/spec/offline/usage_spec.rb +151 -0
- data/spec/real_api/admin_client_spec.rb +77 -0
- data/spec/real_api/correct_text_spec.rb +20 -0
- data/spec/real_api/glossaries_spec.rb +28 -0
- data/spec/real_api/languages_spec.rb +27 -0
- data/spec/real_api/multilingual_glossaries_spec.rb +53 -0
- data/spec/real_api/proxy_spec.rb +41 -0
- data/spec/real_api/real_api_helper.rb +65 -0
- data/spec/real_api/rephrase_text_spec.rb +16 -0
- data/spec/real_api/spoken_terms_spec.rb +56 -0
- data/spec/real_api/style_profiles_spec.rb +46 -0
- data/spec/real_api/style_rules_spec.rb +46 -0
- data/spec/real_api/translate_document_spec.rb +18 -0
- data/spec/real_api/translate_text_spec.rb +16 -0
- data/spec/real_api/translation_memories_spec.rb +56 -0
- data/spec/real_api/usage_spec.rb +20 -0
- data/spec/spec_helper.rb +1 -50
- metadata +116 -76
- data/.circleci/config.yml +0 -27
- data/.github/workflows/add_issues_to_kanban.yml +0 -16
- data/spec/api/api_spec.rb +0 -20
- data/spec/api/deepl_spec.rb +0 -262
- data/spec/integration_tests/document_api_spec.rb +0 -211
- data/spec/integration_tests/document_error_paths_spec.rb +0 -33
- data/spec/integration_tests/glossary_api_spec.rb +0 -114
- data/spec/integration_tests/glossary_error_paths_spec.rb +0 -107
- data/spec/integration_tests/integration_test_utils.rb +0 -178
- data/spec/integration_tests/languages_api_spec.rb +0 -54
- data/spec/integration_tests/languages_error_paths_spec.rb +0 -25
- data/spec/integration_tests/rephrase_api_spec.rb +0 -90
- data/spec/integration_tests/rephrase_error_paths_spec.rb +0 -53
- data/spec/integration_tests/smoke_test_spec.rb +0 -24
- data/spec/integration_tests/style_rule_api_spec.rb +0 -117
- data/spec/integration_tests/style_rule_error_paths_spec.rb +0 -45
- data/spec/integration_tests/translate_api_spec.rb +0 -117
- data/spec/integration_tests/translate_error_paths_spec.rb +0 -48
- data/spec/integration_tests/translation_memory_api_spec.rb +0 -244
- data/spec/integration_tests/translation_memory_error_paths_spec.rb +0 -87
- data/spec/integration_tests/usage_api_spec.rb +0 -29
- data/spec/integration_tests/usage_error_paths_spec.rb +0 -18
- data/spec/requests/document/upload_spec.rb +0 -130
- data/spec/requests/extra_body_parameters_types_spec.rb +0 -82
- data/spec/requests/glossary/create_spec.rb +0 -44
- data/spec/requests/glossary/destroy_spec.rb +0 -27
- data/spec/requests/glossary/entries_spec.rb +0 -27
- data/spec/requests/glossary/find_spec.rb +0 -28
- data/spec/requests/glossary/language_pairs_spec.rb +0 -27
- data/spec/requests/glossary/list_spec.rb +0 -27
- data/spec/requests/languages_spec.rb +0 -27
- data/spec/requests/rephrase_spec.rb +0 -46
- data/spec/requests/style_rule/create_custom_instruction_spec.rb +0 -30
- data/spec/requests/style_rule/create_spec.rb +0 -29
- data/spec/requests/style_rule/destroy_custom_instruction_spec.rb +0 -28
- data/spec/requests/style_rule/destroy_spec.rb +0 -27
- data/spec/requests/style_rule/find_custom_instruction_spec.rb +0 -29
- data/spec/requests/style_rule/find_spec.rb +0 -28
- data/spec/requests/style_rule/list_spec.rb +0 -27
- data/spec/requests/style_rule/update_configured_rules_spec.rb +0 -31
- data/spec/requests/style_rule/update_custom_instruction_spec.rb +0 -32
- data/spec/requests/style_rule/update_spec.rb +0 -29
- data/spec/requests/translate_spec.rb +0 -338
- data/spec/requests/translation_memory/create_export_spec.rb +0 -37
- data/spec/requests/translation_memory/create_import_spec.rb +0 -61
- data/spec/requests/translation_memory/destroy_spec.rb +0 -36
- data/spec/requests/translation_memory/download_export_spec.rb +0 -54
- data/spec/requests/translation_memory/find_job_spec.rb +0 -37
- data/spec/requests/translation_memory/find_spec.rb +0 -36
- data/spec/requests/translation_memory/list_spec.rb +0 -27
- data/spec/requests/translation_memory/segments_spec.rb +0 -58
- data/spec/requests/translation_memory/upload_file_spec.rb +0 -55
- data/spec/requests/usage_spec.rb +0 -27
- data/spec/resources/custom_instruction_spec.rb +0 -32
- data/spec/resources/glossary_spec.rb +0 -38
- data/spec/resources/language_pair_spec.rb +0 -23
- data/spec/resources/language_spec.rb +0 -45
- data/spec/resources/style_rule_spec.rb +0 -68
- data/spec/resources/text_spec.rb +0 -23
- data/spec/resources/translation_memory_export_spec.rb +0 -38
- data/spec/resources/translation_memory_import_spec.rb +0 -30
- data/spec/resources/translation_memory_job_spec.rb +0 -109
- data/spec/resources/translation_memory_segments_spec.rb +0 -78
- data/spec/resources/translation_memory_spec.rb +0 -52
- data/spec/resources/usage_spec.rb +0 -35
- data/spec/support/live_mock_server.rb +0 -12
- data/spec/support/managed_glossary.rb +0 -65
- data/spec/support/managed_style_rule.rb +0 -17
- data/spec/support/managed_translation_memory.rb +0 -55
data/lib/deepl/configuration.rb
CHANGED
|
@@ -6,31 +6,69 @@
|
|
|
6
6
|
module DeepL
|
|
7
7
|
class Configuration
|
|
8
8
|
ATTRIBUTES = %i[auth_key host logger max_doc_status_queries max_network_retries user_agent
|
|
9
|
-
version].freeze
|
|
9
|
+
version headers timeout].freeze
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
# max_doc_status_queries= is defined below, with its deprecation warning.
|
|
12
|
+
attr_accessor(*(ATTRIBUTES - %i[max_doc_status_queries]))
|
|
13
|
+
attr_reader :max_doc_status_queries
|
|
12
14
|
|
|
13
15
|
DEEPL_SERVER_URL = 'https://api.deepl.com'
|
|
14
16
|
DEEPL_SERVER_URL_FREE = 'https://api-free.deepl.com'
|
|
15
|
-
|
|
17
|
+
# Net::HTTP's 60-second default, kept for the module-level API; 10 in the next major version.
|
|
18
|
+
DEFAULT_TIMEOUT = 60.0
|
|
19
|
+
private_constant :DEEPL_SERVER_URL, :DEEPL_SERVER_URL_FREE, :DEFAULT_TIMEOUT
|
|
16
20
|
|
|
17
21
|
def initialize(data = {}, app_info_name = nil, app_info_version = nil, send_platform_info = true) # rubocop:disable all
|
|
18
22
|
data.each { |key, value| send("#{key}=", value) }
|
|
19
23
|
@auth_key ||= ENV.fetch('DEEPL_AUTH_KEY', nil)
|
|
20
24
|
@host ||= ENV.fetch('DEEPL_SERVER_URL', nil)
|
|
21
|
-
@host ||= if self.class.free_account_auth_key?(@auth_key)
|
|
22
|
-
DEEPL_SERVER_URL_FREE
|
|
23
|
-
else
|
|
24
|
-
DEEPL_SERVER_URL
|
|
25
|
-
end
|
|
26
25
|
@version ||= 'v2'
|
|
27
26
|
@user_agent ||= construct_user_agent(send_platform_info, app_info_name, app_info_version)
|
|
28
27
|
@max_network_retries ||= 5
|
|
29
28
|
@logger ||= nil
|
|
29
|
+
@headers ||= {}
|
|
30
|
+
@timeout ||= DEFAULT_TIMEOUT
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# The host is resolved when it is read, so that an auth key set after the configuration was
|
|
34
|
+
# created (e.g. in a +DeepL.configure+ block) still selects the Free API.
|
|
35
|
+
def host
|
|
36
|
+
return @host unless @host.nil?
|
|
37
|
+
|
|
38
|
+
self.class.free_account_auth_key?(auth_key) ? DEEPL_SERVER_URL_FREE : DEEPL_SERVER_URL
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
##
|
|
42
|
+
# @deprecated Use the +timeout_s:+ of DeepL::Client#translate_document or
|
|
43
|
+
# DeepL::Client#translate_document_wait_until_done instead.
|
|
44
|
+
def max_doc_status_queries=(value)
|
|
45
|
+
unless value.nil?
|
|
46
|
+
DeepL::Deprecation.warn_once_at_caller(
|
|
47
|
+
'DeepL::Configuration#max_doc_status_queries=',
|
|
48
|
+
'[DEPRECATION] DeepL::Configuration#max_doc_status_queries= is deprecated, use the ' \
|
|
49
|
+
'timeout_s: of DeepL::Client#translate_document instead.'
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
@max_doc_status_queries = value
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# @deprecated The API version is part of each endpoint; this setting will be removed in the
|
|
57
|
+
# next major version.
|
|
58
|
+
def version=(value)
|
|
59
|
+
unless value.nil? || value == 'v2'
|
|
60
|
+
DeepL::Deprecation.warn_once('DeepL::Configuration#version=',
|
|
61
|
+
'[DEPRECATION] DeepL::Configuration#version= is deprecated ' \
|
|
62
|
+
'and will be removed in the next major version.')
|
|
63
|
+
end
|
|
64
|
+
@version = value
|
|
30
65
|
end
|
|
31
66
|
|
|
32
67
|
def validate!
|
|
33
|
-
|
|
68
|
+
return unless auth_key.nil? || auth_key.to_s.strip.empty?
|
|
69
|
+
|
|
70
|
+
raise Exceptions::Error,
|
|
71
|
+
'auth_key not provided'
|
|
34
72
|
end
|
|
35
73
|
|
|
36
74
|
def attributes
|
|
@@ -41,8 +79,16 @@ module DeepL
|
|
|
41
79
|
attributes == other.attributes
|
|
42
80
|
end
|
|
43
81
|
|
|
82
|
+
# Redacts the auth key, so that it never shows in the inspect output of objects holding the
|
|
83
|
+
# configuration.
|
|
84
|
+
def inspect
|
|
85
|
+
shown = attributes.merge(auth_key: auth_key.nil? ? nil : '[REDACTED]')
|
|
86
|
+
"#<#{self.class.name} #{shown.map { |name, value| "#{name}=#{value.inspect}" }.join(', ')}>"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# The key is trimmed like the one sent in the Authorization header.
|
|
44
90
|
def self.free_account_auth_key?(key)
|
|
45
|
-
key
|
|
91
|
+
key.to_s.strip.end_with?(':fx')
|
|
46
92
|
end
|
|
47
93
|
|
|
48
94
|
def construct_user_agent(send_platform_info, app_info_name, app_info_version)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright 2026 DeepL SE (https://www.deepl.com)
|
|
2
|
+
# Use of this source code is governed by an MIT
|
|
3
|
+
# license that can be found in the LICENSE.md file.
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
module DeepL
|
|
7
|
+
module Constants
|
|
8
|
+
class AdminUsageGroupBy < BaseConstant
|
|
9
|
+
KEY = 'key'
|
|
10
|
+
KEY_AND_DAY = 'key_and_day'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright 2026 DeepL SE (https://www.deepl.com)
|
|
2
|
+
# Use of this source code is governed by an MIT
|
|
3
|
+
# license that can be found in the LICENSE.md file.
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
module DeepL
|
|
7
|
+
module Constants
|
|
8
|
+
class CustomTagUsageAggregateBy < BaseConstant
|
|
9
|
+
PERIOD = 'period'
|
|
10
|
+
DAY = 'day'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Copyright 2026 DeepL SE (https://www.deepl.com)
|
|
2
|
+
# Use of this source code is governed by an MIT
|
|
3
|
+
# license that can be found in the LICENSE.md file.
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
module DeepL
|
|
7
|
+
module Constants
|
|
8
|
+
# Values of +DeepL::Resources::DocumentTranslationStatus#status+. The status stays a plain
|
|
9
|
+
# string, so a value the API adds later is kept as it is.
|
|
10
|
+
class DocumentStatusCode < BaseConstant
|
|
11
|
+
QUEUED = 'queued'
|
|
12
|
+
TRANSLATING = 'translating'
|
|
13
|
+
DONE = 'done'
|
|
14
|
+
ERROR = 'error'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Copyright 2026 DeepL SE (https://www.deepl.com)
|
|
2
|
+
# Use of this source code is governed by an MIT
|
|
3
|
+
# license that can be found in the LICENSE.md file.
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
module DeepL
|
|
7
|
+
module Constants
|
|
8
|
+
# Values of the +include+ option of +DeepL::Client#get_languages_for_resource+. Other strings
|
|
9
|
+
# are accepted and sent unchanged.
|
|
10
|
+
class LanguageInclude < BaseConstant
|
|
11
|
+
# Also return beta languages and features.
|
|
12
|
+
BETA = 'beta'
|
|
13
|
+
# Also return features that rely on third-party providers.
|
|
14
|
+
EXTERNAL = 'external'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Copyright 2026 DeepL SE (https://www.deepl.com)
|
|
2
|
+
# Use of this source code is governed by an MIT
|
|
3
|
+
# license that can be found in the LICENSE.md file.
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
module DeepL
|
|
7
|
+
module Constants
|
|
8
|
+
# Values of the +resource+ argument of +DeepL::Client#get_languages_for_resource+ and of
|
|
9
|
+
# +DeepL::Resources::LanguageResource#name+. Other strings are accepted and sent unchanged, so
|
|
10
|
+
# a resource the API adds later can be queried before it is listed here.
|
|
11
|
+
class LanguageResourceName < BaseConstant
|
|
12
|
+
TRANSLATE_TEXT = 'translate_text'
|
|
13
|
+
TRANSLATE_DOCUMENT = 'translate_document'
|
|
14
|
+
GLOSSARY = 'glossary'
|
|
15
|
+
VOICE = 'voice'
|
|
16
|
+
WRITE = 'write'
|
|
17
|
+
STYLE_RULES = 'style_rules'
|
|
18
|
+
TRANSLATION_MEMORY = 'translation_memory'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Copyright 2026 DeepL SE (https://www.deepl.com)
|
|
2
|
+
# Use of this source code is governed by an MIT
|
|
3
|
+
# license that can be found in the LICENSE.md file.
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
module DeepL
|
|
7
|
+
module Constants
|
|
8
|
+
# Values of the +tag_handling_version+ option. Any other string is sent as given.
|
|
9
|
+
class TagHandlingVersion < BaseConstant
|
|
10
|
+
V1 = 'v1'
|
|
11
|
+
V2 = 'v2'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
# @api private
|
|
8
|
+
module Deprecation
|
|
9
|
+
@warned = {}
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
|
|
12
|
+
# Prints +message+ once per process for +key+, only while Ruby's deprecation warnings are on
|
|
13
|
+
# (+ruby -W:deprecated+). +uplevel+ 2 points the warning at the caller of the deprecated
|
|
14
|
+
# method.
|
|
15
|
+
def self.warn_once(key, message, uplevel: 2)
|
|
16
|
+
return unless enabled?
|
|
17
|
+
|
|
18
|
+
first = @mutex.synchronize { @warned[key] ? false : (@warned[key] = true) }
|
|
19
|
+
return unless first
|
|
20
|
+
|
|
21
|
+
if RUBY_VERSION >= '3.0'
|
|
22
|
+
Kernel.warn(message, uplevel: uplevel, category: :deprecated)
|
|
23
|
+
else
|
|
24
|
+
Kernel.warn(message, uplevel: uplevel)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Ruby 2.6 has no Warning.[] and prints no deprecation warnings.
|
|
29
|
+
def self.enabled?
|
|
30
|
+
Warning.respond_to?(:[]) && Warning[:deprecated]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Like +warn_once+, but points the warning at the first caller outside the library, for
|
|
34
|
+
# deprecated options that are read deep inside a request.
|
|
35
|
+
def self.warn_once_at_caller(key, message)
|
|
36
|
+
lib_dir = File.expand_path('..', __dir__)
|
|
37
|
+
locations = caller_locations(1) || []
|
|
38
|
+
index = locations.index { |loc| !loc.absolute_path.to_s.start_with?(lib_dir) }
|
|
39
|
+
warn_once(key, message, uplevel: (index || 0) + 2)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/deepl/document_api.rb
CHANGED
|
@@ -26,16 +26,23 @@ module DeepL
|
|
|
26
26
|
# the different kinds of documents (PDFs, etc). If nil, will use
|
|
27
27
|
# the filename of +input_file_path+.
|
|
28
28
|
# @param [Hash] options Additional (body) options for the upload. Notable glossary options:
|
|
29
|
-
# +:
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
29
|
+
# +:glossary+ (a glossary ID, `DeepL::Resources::Glossary` or
|
|
30
|
+
# `DeepL::Resources::MultilingualGlossary`; replaces the deprecated
|
|
31
|
+
# +:glossary_id+) or +:glossary_ids+ (an array of up to 5 glossary IDs
|
|
32
|
+
# or objects).
|
|
33
|
+
# +:glossary_ids+ requires +source_lang+ to be set, cannot be combined
|
|
34
|
+
# with +:glossary+ or +:glossary_id+, and raises `ArgumentError` if
|
|
35
|
+
# these rules are violated or more than 5 IDs are provided. Notable
|
|
36
|
+
# style/translation memory options (mirroring text translation):
|
|
37
|
+
# +:style_rule+ (a style rule ID string or a
|
|
38
|
+
# `DeepL::Resources::StyleRule` object),
|
|
39
|
+
# +:style_profile+ (a style profile ID string or a
|
|
40
|
+
# `DeepL::Resources::StyleProfile` object),
|
|
36
41
|
# +:translation_memory+ (a translation memory ID string or a
|
|
37
42
|
# `DeepL::Resources::TranslationMemory` object), and
|
|
38
43
|
# +:translation_memory_threshold+ (integer 0-100, recommended minimum 75).
|
|
44
|
+
# +:enable_watermark+ (boolean) adds a "Translated by DeepL" watermark;
|
|
45
|
+
# the API applies it to +docx+ and +pdf+ output only.
|
|
39
46
|
# @param [Hash] additional_headers Additional HTTP headers for the upload.
|
|
40
47
|
# @return [DeepL::Resources::DocumentHandle] Document handle for the uploaded document.
|
|
41
48
|
|
|
@@ -76,16 +83,17 @@ module DeepL
|
|
|
76
83
|
end
|
|
77
84
|
|
|
78
85
|
##
|
|
79
|
-
# Translates a document with the DeepL API, `sleep`ing during waiting periods
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
86
|
+
# Translates a document with the DeepL API, `sleep`ing during waiting periods, and returns
|
|
87
|
+
# the number of bytes written once the translated document is downloaded. Polls until the
|
|
88
|
+
# translation is done or failed; the deprecated `max_doc_status_queries` limits the number of
|
|
89
|
+
# status requests if it is configured.
|
|
83
90
|
#
|
|
84
|
-
# @raise [DocumentTranslationError] If any error occurs during the process
|
|
91
|
+
# @raise [DocumentTranslationError] If any error occurs during the process, also when the
|
|
92
|
+
# translation fails or is not done after `max_doc_status_queries`.
|
|
85
93
|
#
|
|
86
94
|
# @param [String] input_file Path to the file to be translated
|
|
87
|
-
# @param [String] output_file Path to the file to write to.
|
|
88
|
-
#
|
|
95
|
+
# @param [String] output_file Path to the file to write to. Must not exist; if it does,
|
|
96
|
+
# DocumentTranslationError is raised and the file is kept.
|
|
89
97
|
# @param [String, nil] source_lang Source language to use for the translation. `nil` will cause
|
|
90
98
|
# automatic source langauge detection to be used. Must be
|
|
91
99
|
# formatted as ISO 639-1, 2-letter language codes.
|
|
@@ -97,11 +105,13 @@ module DeepL
|
|
|
97
105
|
# the filename of +input_file_path+.
|
|
98
106
|
# @param [Hash] options Additional options for the upload.
|
|
99
107
|
# @param [Hash] additional_headers Additional headers for the upload.
|
|
100
|
-
# @return [
|
|
108
|
+
# @return [Integer] Number of bytes written
|
|
101
109
|
|
|
102
110
|
def translate_document(input_file, output_file, source_lang, target_lang, # rubocop:disable Metrics/MethodLength,Metrics/ParameterLists
|
|
103
111
|
filename = nil, options = {}, additional_headers = {})
|
|
104
|
-
|
|
112
|
+
if File.exist?(output_file)
|
|
113
|
+
raise Exceptions::DocumentTranslationError.new('File already exists at output path', nil)
|
|
114
|
+
end
|
|
105
115
|
|
|
106
116
|
begin
|
|
107
117
|
handle = upload(input_file, source_lang, target_lang, filename, options,
|
|
@@ -110,7 +120,7 @@ module DeepL
|
|
|
110
120
|
rescue StandardError => e
|
|
111
121
|
FileUtils.rm_f(output_file)
|
|
112
122
|
raise Exceptions::DocumentTranslationError.new(
|
|
113
|
-
"Error occurred during document translation: #{e.message}", handle
|
|
123
|
+
"Error occurred during document translation: #{e.message}", handle, e
|
|
114
124
|
)
|
|
115
125
|
end
|
|
116
126
|
end
|
|
@@ -118,13 +128,22 @@ module DeepL
|
|
|
118
128
|
private
|
|
119
129
|
|
|
120
130
|
def translate_document_wait_and_download(document_handle, output_file)
|
|
121
|
-
doc_status =
|
|
131
|
+
doc_status = Utils::DocumentPoller.wait_until_finished(
|
|
132
|
+
@api, document_handle, max_queries: @api.configuration.max_doc_status_queries
|
|
133
|
+
)
|
|
134
|
+
unless doc_status.done?
|
|
135
|
+
raise Exceptions::DocumentTranslationError.new(not_done_message(doc_status),
|
|
136
|
+
document_handle)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
download(document_handle, output_file)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def not_done_message(doc_status)
|
|
122
143
|
if doc_status.error?
|
|
123
|
-
|
|
124
|
-
"Exception when querying document status #{doc_status.error_message}", document_handle
|
|
125
|
-
)
|
|
144
|
+
"Exception when querying document status #{doc_status.error_message}"
|
|
126
145
|
else
|
|
127
|
-
|
|
146
|
+
"Document translation not done after max_doc_status_queries (#{doc_status.status})"
|
|
128
147
|
end
|
|
129
148
|
end
|
|
130
149
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Copyright 2026 DeepL SE (https://www.deepl.com)
|
|
2
|
+
# Use of this source code is governed by an MIT
|
|
3
|
+
# license that can be found in the LICENSE.md file.
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
module DeepL
|
|
7
|
+
module Exceptions
|
|
8
|
+
##
|
|
9
|
+
# Raised by DeepL::Client and DeepL::AdminClient for a network failure without a complete
|
|
10
|
+
# HTTP response from the API (DNS, connection, TLS, proxy, timeout or a body cut off
|
|
11
|
+
# mid-read), after the retries are used up. Transient failures (DNS, connection, timeout,
|
|
12
|
+
# cut-off body) are retried and have +should_retry?+ true; TLS failures, proxy failures and
|
|
13
|
+
# malformed responses are raised at once and have +should_retry?+ false. The original
|
|
14
|
+
# exception is available as +cause+. The module-level API raises the original exception.
|
|
15
|
+
class ConnectionError < Error
|
|
16
|
+
# Raised from a +rescue+ block, so the original exception becomes +cause+.
|
|
17
|
+
# +should_retry: false+ marks a request that is unsafe to repeat once sent.
|
|
18
|
+
def self.from_network_error(error, should_retry: true)
|
|
19
|
+
new("Connection to the DeepL API failed: #{error.message}",
|
|
20
|
+
should_retry: should_retry && transient?(error))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.transient?(error)
|
|
24
|
+
Requests::Base::RETRYABLE_NETWORK_ERRORS.any? { |type| error.is_a?(type) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize(message = nil, should_retry: true)
|
|
28
|
+
super(message)
|
|
29
|
+
@should_retry = should_retry
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def should_retry?
|
|
33
|
+
@should_retry
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Copyright 2026 DeepL SE (https://www.deepl.com)
|
|
2
|
+
# Use of this source code is governed by an MIT
|
|
3
|
+
# license that can be found in the LICENSE file.
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
module DeepL
|
|
7
|
+
module Exceptions
|
|
8
|
+
# HTTP 503 on a document download: the translated document is not ready yet. Raised after
|
|
9
|
+
# the retries.
|
|
10
|
+
class DocumentNotReadyError < ServerError
|
|
11
|
+
def to_s
|
|
12
|
+
message = body_message
|
|
13
|
+
message ? "Document not ready: #{message}" : 'Document not ready.'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -6,9 +6,25 @@
|
|
|
6
6
|
module DeepL
|
|
7
7
|
module Exceptions
|
|
8
8
|
class DocumentTranslationError < Error
|
|
9
|
-
|
|
9
|
+
# @return [DeepL::Resources::DocumentHandle, nil] Handle of the uploaded document, +nil+ if
|
|
10
|
+
# the upload failed. Use it to query the status or download the document later.
|
|
11
|
+
attr_reader :document_handle
|
|
12
|
+
|
|
13
|
+
# +error+ is the failure being wrapped; the HTTP status and retryability of a DeepL error
|
|
14
|
+
# are copied from it.
|
|
15
|
+
def initialize(message, handle, error = nil)
|
|
10
16
|
super(message)
|
|
11
17
|
@handle = handle
|
|
18
|
+
@document_handle = handle
|
|
19
|
+
return unless error.is_a?(Error)
|
|
20
|
+
|
|
21
|
+
@http_status_code = error.http_status_code
|
|
22
|
+
@should_retry = error.should_retry?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @return [Boolean] +should_retry?+ of the wrapped DeepL error, +false+ without one.
|
|
26
|
+
def should_retry?
|
|
27
|
+
@should_retry || false
|
|
12
28
|
end
|
|
13
29
|
end
|
|
14
30
|
end
|
|
@@ -6,8 +6,20 @@
|
|
|
6
6
|
module DeepL
|
|
7
7
|
module Exceptions
|
|
8
8
|
class Error < StandardError
|
|
9
|
+
# @return [Integer, nil] HTTP status of the failed response, +nil+ if there was none.
|
|
10
|
+
attr_reader :http_status_code
|
|
11
|
+
|
|
12
|
+
# @param [String, nil] message
|
|
13
|
+
# @param [Integer, nil] http_status_code HTTP status of the failed response.
|
|
14
|
+
# @param [Boolean] should_retry Whether repeating the request may succeed.
|
|
15
|
+
def initialize(message = nil, http_status_code: nil, should_retry: false)
|
|
16
|
+
super(message)
|
|
17
|
+
@http_status_code = http_status_code
|
|
18
|
+
@should_retry = should_retry
|
|
19
|
+
end
|
|
20
|
+
|
|
9
21
|
def should_retry?
|
|
10
|
-
false
|
|
22
|
+
@should_retry || false
|
|
11
23
|
end
|
|
12
24
|
end
|
|
13
25
|
end
|
|
@@ -6,10 +6,15 @@
|
|
|
6
6
|
module DeepL
|
|
7
7
|
module Exceptions
|
|
8
8
|
class NotFound < RequestError
|
|
9
|
+
# @param [String, nil] message Replaces the API's message, for a not-found condition the
|
|
10
|
+
# SDK detects in a successful response.
|
|
11
|
+
def initialize(response, request = nil, message = nil)
|
|
12
|
+
super(response, request)
|
|
13
|
+
@message = message
|
|
14
|
+
end
|
|
15
|
+
|
|
9
16
|
def to_s
|
|
10
|
-
|
|
11
|
-
rescue JSON::ParserError
|
|
12
|
-
response.body
|
|
17
|
+
(@message || body_message).to_s
|
|
13
18
|
end
|
|
14
19
|
end
|
|
15
20
|
end
|
|
@@ -5,8 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
module DeepL
|
|
7
7
|
module Exceptions
|
|
8
|
+
# @deprecated Only the module-level API raises it, from
|
|
9
|
+
# +DeepL::Resources::Language#supports_formality?+ for a source language; DeepL::Client
|
|
10
|
+
# returns +nil+ instead.
|
|
8
11
|
class NotSupported < Error
|
|
9
12
|
def initialize(msg = 'The feature is not supported')
|
|
13
|
+
Deprecation.warn_once('DeepL::Exceptions::NotSupported',
|
|
14
|
+
'[DEPRECATION] DeepL::Exceptions::NotSupported is deprecated, ' \
|
|
15
|
+
'only the module-level API raises it.')
|
|
10
16
|
super
|
|
11
17
|
end
|
|
12
18
|
end
|
|
@@ -8,13 +8,30 @@ module DeepL
|
|
|
8
8
|
class RequestError < Error
|
|
9
9
|
attr_reader :request, :response
|
|
10
10
|
|
|
11
|
-
def initialize(response)
|
|
11
|
+
def initialize(response, request = nil)
|
|
12
12
|
super()
|
|
13
13
|
@response = response
|
|
14
|
+
@request = request
|
|
15
|
+
@http_status_code = response.code.to_i if response.respond_to?(:code)
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
def to_s
|
|
17
|
-
|
|
19
|
+
message = body_message
|
|
20
|
+
status = "HTTP status #{http_status_code}"
|
|
21
|
+
message ? "Request failed with #{status}: #{message}" : "Request failed with #{status}."
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
# The +message+ of a JSON object body, else the raw body; +nil+ for an empty body.
|
|
27
|
+
def body_message
|
|
28
|
+
body = response&.body
|
|
29
|
+
return nil if body.nil? || body.to_s.strip.empty?
|
|
30
|
+
|
|
31
|
+
data = JSON.parse(body)
|
|
32
|
+
data.is_a?(Hash) && data['message'].is_a?(String) ? data['message'] : body
|
|
33
|
+
rescue JSON::ParserError
|
|
34
|
+
body
|
|
18
35
|
end
|
|
19
36
|
end
|
|
20
37
|
end
|
|
@@ -6,12 +6,18 @@
|
|
|
6
6
|
module DeepL
|
|
7
7
|
module Exceptions
|
|
8
8
|
class ServerError < RequestError
|
|
9
|
+
# @param [Boolean] should_retry False for a request that is unsafe to repeat once sent.
|
|
10
|
+
def initialize(response, request = nil, should_retry: true)
|
|
11
|
+
super(response, request)
|
|
12
|
+
@should_retry = should_retry
|
|
13
|
+
end
|
|
14
|
+
|
|
9
15
|
def to_s
|
|
10
16
|
'An internal server error occured. Try again after waiting a short period.'
|
|
11
17
|
end
|
|
12
18
|
|
|
13
19
|
def should_retry?
|
|
14
|
-
|
|
20
|
+
@should_retry
|
|
15
21
|
end
|
|
16
22
|
end
|
|
17
23
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Copyright 2026 DeepL SE (https://www.deepl.com)
|
|
2
|
+
# Use of this source code is governed by an MIT
|
|
3
|
+
# license that can be found in the LICENSE.md file.
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
module DeepL
|
|
7
|
+
module Exceptions
|
|
8
|
+
##
|
|
9
|
+
# An invalid argument detected before any request is sent: an invalid glossary or spoken
|
|
10
|
+
# terms term, a malformed glossary TSV line, or an unsupported +DeepL.languages+ type.
|
|
11
|
+
class ValidationError < Error
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|