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
|
@@ -9,8 +9,11 @@ module DeepL
|
|
|
9
9
|
##
|
|
10
10
|
# Common behaviour of the requests to a pre-signed storage URL handed out by the API, such
|
|
11
11
|
# as a translation memory upload or download URL. Those URLs point outside of the DeepL
|
|
12
|
-
# API, so they are requested over their own connection and the
|
|
13
|
-
#
|
|
12
|
+
# API, so they are requested over their own connection and send only the User-Agent: neither
|
|
13
|
+
# the DeepL `Authorization` header nor other caller headers. That connection uses the same
|
|
14
|
+
# proxy, SSL and timeout options as the API connection. A non-2xx response raises the API
|
|
15
|
+
# error class for its status (e.g. DeepL::Exceptions::AuthorizationFailed for 401/403) in the
|
|
16
|
+
# module-level API and a plain DeepL::Exceptions::Error in DeepL::Client.
|
|
14
17
|
|
|
15
18
|
class StorageBase < DeepL::Requests::Base
|
|
16
19
|
attr_reader :storage_url
|
|
@@ -27,19 +30,48 @@ module DeepL
|
|
|
27
30
|
private
|
|
28
31
|
|
|
29
32
|
def http_client
|
|
30
|
-
@http_client ||=
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
@http_client ||= if api.http_client_options
|
|
34
|
+
Utils::HTTPClientBuilder.build(storage_url, api.http_client_options)
|
|
35
|
+
else
|
|
36
|
+
default_http_client
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def http_client_options_of(_client)
|
|
41
|
+
api.http_client_options
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def default_http_client
|
|
45
|
+
client = Net::HTTP.new(uri.host, uri.port)
|
|
46
|
+
client.use_ssl = uri.scheme == 'https'
|
|
47
|
+
Utils::HTTPClientBuilder.disable_lenient_defaults(client)
|
|
35
48
|
end
|
|
36
49
|
|
|
37
50
|
def uri
|
|
38
51
|
@uri ||= URI(storage_url)
|
|
39
52
|
end
|
|
40
53
|
|
|
54
|
+
# Storage responses are not DeepL API responses, so their statuses carry none of the API's
|
|
55
|
+
# meanings: an expired URL replies 403, which is no authorization failure. DeepL::Client
|
|
56
|
+
# raises a plain error; until the next major version, the module-level API raises the API
|
|
57
|
+
# error classes it has released.
|
|
58
|
+
def validate_response!(response, request = nil)
|
|
59
|
+
return if response.is_a?(Net::HTTPSuccess)
|
|
60
|
+
raise Utils::ExceptionBuilder.new(response, request).build unless api.client?
|
|
61
|
+
|
|
62
|
+
status = response.code.to_i
|
|
63
|
+
raise Exceptions::Error.new(
|
|
64
|
+
"Request to the storage URL failed with HTTP status #{status}",
|
|
65
|
+
http_status_code: status, should_retry: status == 429 || (500..599).cover?(status)
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Only the User-Agent: the caller's from the configured +headers+, else the library's.
|
|
41
70
|
def headers
|
|
42
|
-
|
|
71
|
+
caller_agent = (api.configuration.headers || {}).find do |name, _|
|
|
72
|
+
name.to_s.casecmp?('user-agent')
|
|
73
|
+
end
|
|
74
|
+
{ 'User-Agent' => caller_agent ? caller_agent.last : api.configuration.user_agent }
|
|
43
75
|
end
|
|
44
76
|
end
|
|
45
77
|
end
|
data/lib/deepl/requests/usage.rb
CHANGED
|
@@ -22,7 +22,34 @@ module DeepL
|
|
|
22
22
|
|
|
23
23
|
def build_usage(request, response)
|
|
24
24
|
data = JSON.parse(response.body)
|
|
25
|
-
Resources::Usage.new(data['character_count'], data['character_limit'], request, response
|
|
25
|
+
Resources::Usage.new(data['character_count'], data['character_limit'], request, response,
|
|
26
|
+
**usage_fields(data))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def usage_fields(data)
|
|
30
|
+
details = Resources::Usage::DETAIL_TYPES.to_h { |type| [type, detail(data, type)] }
|
|
31
|
+
details.merge(products: products(data['products']),
|
|
32
|
+
start_time: optional_time(data['start_time']),
|
|
33
|
+
end_time: optional_time(data['end_time']))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def detail(data, type)
|
|
37
|
+
count = data["#{type}_count"]
|
|
38
|
+
limit = data["#{type}_limit"]
|
|
39
|
+
Resources::Usage::Detail.new(count, limit) unless count.nil? || limit.nil?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def products(entries)
|
|
43
|
+
return nil unless entries.is_a?(Array)
|
|
44
|
+
|
|
45
|
+
entries.grep(Hash).map { |product| Resources::UsageProduct.new(product) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# An unexpected billing-period format must not fail the whole usage response.
|
|
49
|
+
def optional_time(value)
|
|
50
|
+
Utils::TimeParser.parse_optional_time(value) if value.is_a?(String)
|
|
51
|
+
rescue ArgumentError
|
|
52
|
+
nil
|
|
26
53
|
end
|
|
27
54
|
|
|
28
55
|
def path
|
|
@@ -0,0 +1,35 @@
|
|
|
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 Requests
|
|
8
|
+
# @api private
|
|
9
|
+
# Builds the results of the write endpoints (rephrase, correct) from their response.
|
|
10
|
+
module WriteImprovements
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
# Returns one DeepL::Resources::Text per improvement. Raises DeepL::Exceptions::Error if
|
|
14
|
+
# the response has fewer improvements than +input_count+ input texts.
|
|
15
|
+
def build_improvements(input_count, request, response)
|
|
16
|
+
improvements = JSON.parse(response.body)['improvements']
|
|
17
|
+
validate_improvement_count!(improvements, input_count)
|
|
18
|
+
improvements.map do |improvement|
|
|
19
|
+
target_language = improvement['target_language']
|
|
20
|
+
Resources::Text.new(improvement['text'], improvement['detected_source_language'], nil,
|
|
21
|
+
request, response, nil, nil,
|
|
22
|
+
target_language.is_a?(String) ? target_language : nil)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def validate_improvement_count!(improvements, input_count)
|
|
27
|
+
return if improvements.is_a?(Array) && improvements.size >= input_count
|
|
28
|
+
|
|
29
|
+
count = improvements.is_a?(Array) ? improvements.size : 'no'
|
|
30
|
+
raise Exceptions::Error, 'Unexpected response from the DeepL API: expected ' \
|
|
31
|
+
"#{input_count} improvements, got #{count}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Copyright 2026 DeepL SE (https://www.deepl.com)
|
|
2
|
+
# Use of this source code is governed by an MIT
|
|
3
|
+
# license that can be found in the LICENSE.md file.
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
module DeepL
|
|
7
|
+
module Resources
|
|
8
|
+
##
|
|
9
|
+
# Usage breakdown of an admin usage report. +speech_to_speech_minutes+ is +nil+ when the
|
|
10
|
+
# response does not contain it.
|
|
11
|
+
class AdminUsageBreakdown
|
|
12
|
+
attr_reader :total_characters, :text_translation_characters,
|
|
13
|
+
:document_translation_characters, :text_improvement_characters,
|
|
14
|
+
:speech_to_text_minutes, :speech_to_speech_minutes
|
|
15
|
+
|
|
16
|
+
def initialize(usage)
|
|
17
|
+
usage = {} unless usage.is_a?(Hash)
|
|
18
|
+
@total_characters = Utils::NumberParser.count(usage['total_characters'])
|
|
19
|
+
@text_translation_characters = Utils::NumberParser.count(usage['text_translation_characters'])
|
|
20
|
+
@document_translation_characters =
|
|
21
|
+
Utils::NumberParser.count(usage['document_translation_characters'])
|
|
22
|
+
@text_improvement_characters = Utils::NumberParser.count(usage['text_improvement_characters'])
|
|
23
|
+
@speech_to_text_minutes = Utils::NumberParser.minutes(usage['speech_to_text_minutes'])
|
|
24
|
+
@speech_to_speech_minutes =
|
|
25
|
+
Utils::NumberParser.optional_minutes(usage['speech_to_speech_minutes'])
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Usage of a single API key. +usage_date+ is only set when grouped by key and day;
|
|
31
|
+
# +api_key_label+ is +nil+ when the response does not contain it.
|
|
32
|
+
class KeyUsage
|
|
33
|
+
attr_reader :api_key, :api_key_label, :usage, :usage_date
|
|
34
|
+
|
|
35
|
+
def initialize(key_usage)
|
|
36
|
+
@api_key = key_usage['api_key'].is_a?(String) ? key_usage['api_key'] : ''
|
|
37
|
+
@api_key_label = key_usage['api_key_label'].is_a?(String) ? key_usage['api_key_label'] : nil
|
|
38
|
+
@usage = AdminUsageBreakdown.new(key_usage['usage'])
|
|
39
|
+
@usage_date = Utils::TimeParser.parse_optional_utc_time(key_usage['usage_date'])
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
##
|
|
44
|
+
# Admin usage analytics report. +key_usages+ is only filled when grouped by key and
|
|
45
|
+
# +key_and_day_usages+ only when grouped by key and day; otherwise they are empty.
|
|
46
|
+
# +total_usage+, +start_date+ and +end_date+ are +nil+ when the response does not contain them.
|
|
47
|
+
class AdminUsageReport < Base
|
|
48
|
+
attr_reader :total_usage, :start_date, :end_date, :group_by, :key_usages,
|
|
49
|
+
:key_and_day_usages
|
|
50
|
+
|
|
51
|
+
def initialize(data, *args) # rubocop:disable Metrics/AbcSize
|
|
52
|
+
super(*args)
|
|
53
|
+
report = data.is_a?(Hash) ? data['usage_report'] : nil
|
|
54
|
+
report = {} unless report.is_a?(Hash)
|
|
55
|
+
total_usage = report['total_usage']
|
|
56
|
+
@total_usage = total_usage.is_a?(Hash) ? AdminUsageBreakdown.new(total_usage) : nil
|
|
57
|
+
@start_date = Utils::TimeParser.parse_optional_utc_time(report['start_date'])
|
|
58
|
+
@end_date = Utils::TimeParser.parse_optional_utc_time(report['end_date'])
|
|
59
|
+
@group_by = report['group_by'].is_a?(String) ? report['group_by'] : nil
|
|
60
|
+
@key_usages = parse_key_usages(report['key_usages'])
|
|
61
|
+
@key_and_day_usages = parse_key_usages(report['key_and_day_usages'])
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def parse_key_usages(key_usages)
|
|
67
|
+
return [] unless key_usages.is_a?(Array)
|
|
68
|
+
|
|
69
|
+
key_usages.grep(Hash).map { |usage| KeyUsage.new(usage) }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Copyright 2026 DeepL SE (https://www.deepl.com)
|
|
2
|
+
# Use of this source code is governed by an MIT
|
|
3
|
+
# license that can be found in the LICENSE.md file.
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
module DeepL
|
|
7
|
+
module Resources
|
|
8
|
+
##
|
|
9
|
+
# Usage breakdown of a custom tag. +speech_to_speech_minutes+ is +nil+ when the response
|
|
10
|
+
# does not contain it.
|
|
11
|
+
class CustomTagUsageBreakdown
|
|
12
|
+
attr_reader :total_characters, :text_translation_characters,
|
|
13
|
+
:text_improvement_characters, :speech_to_text_minutes,
|
|
14
|
+
:speech_to_speech_minutes
|
|
15
|
+
|
|
16
|
+
def initialize(breakdown)
|
|
17
|
+
breakdown = {} unless breakdown.is_a?(Hash)
|
|
18
|
+
@total_characters = Utils::NumberParser.count(breakdown['total_characters'])
|
|
19
|
+
@text_translation_characters =
|
|
20
|
+
Utils::NumberParser.count(breakdown['text_translation_characters'])
|
|
21
|
+
@text_improvement_characters =
|
|
22
|
+
Utils::NumberParser.count(breakdown['text_improvement_characters'])
|
|
23
|
+
@speech_to_text_minutes = Utils::NumberParser.minutes(breakdown['speech_to_text_minutes'])
|
|
24
|
+
@speech_to_speech_minutes =
|
|
25
|
+
Utils::NumberParser.optional_minutes(breakdown['speech_to_speech_minutes'])
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Usage of a single custom tag. +usage_date+ is only set when aggregated by day.
|
|
31
|
+
class CustomTagUsage
|
|
32
|
+
attr_reader :custom_tag, :breakdown, :usage_date
|
|
33
|
+
|
|
34
|
+
def initialize(usage)
|
|
35
|
+
@custom_tag = usage['custom_tag'].is_a?(String) ? usage['custom_tag'] : ''
|
|
36
|
+
@breakdown = CustomTagUsageBreakdown.new(usage['breakdown'])
|
|
37
|
+
@usage_date = Utils::TimeParser.parse_optional_utc_time(usage['usage_date'])
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
##
|
|
42
|
+
# Custom tag usage analytics report. +next_page+ is +nil+ on the last page.
|
|
43
|
+
class CustomTagUsageReport < Base
|
|
44
|
+
attr_reader :aggregate_by, :start_date, :end_date, :next_page, :usage
|
|
45
|
+
|
|
46
|
+
def initialize(data, *args) # rubocop:disable Metrics/AbcSize
|
|
47
|
+
super(*args)
|
|
48
|
+
report = data.is_a?(Hash) ? data['custom_tag_usage_report'] : nil
|
|
49
|
+
report = {} unless report.is_a?(Hash)
|
|
50
|
+
@aggregate_by = report['aggregate_by'].is_a?(String) ? report['aggregate_by'] : nil
|
|
51
|
+
@start_date = Utils::TimeParser.parse_optional_utc_time(report['start_date'])
|
|
52
|
+
@end_date = Utils::TimeParser.parse_optional_utc_time(report['end_date'])
|
|
53
|
+
@next_page = report['next_page'].is_a?(Integer) ? report['next_page'] : nil
|
|
54
|
+
usages = report['usage'].is_a?(Array) ? report['usage'] : []
|
|
55
|
+
@usage = usages.grep(Hash).map { |item| CustomTagUsage.new(item) }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Copyright 2026 DeepL SE (https://www.deepl.com)
|
|
2
|
+
# Use of this source code is governed by an MIT
|
|
3
|
+
# license that can be found in the LICENSE.md file.
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
module DeepL
|
|
7
|
+
module Resources
|
|
8
|
+
##
|
|
9
|
+
# Usage limits of a developer key. A +nil+ limit means unlimited.
|
|
10
|
+
class DeveloperKeyUsageLimits
|
|
11
|
+
attr_reader :characters, :speech_to_text_milliseconds, :speech_to_speech_milliseconds
|
|
12
|
+
|
|
13
|
+
def initialize(usage_limits)
|
|
14
|
+
usage_limits = {} unless usage_limits.is_a?(Hash)
|
|
15
|
+
@characters = Utils::NumberParser.optional_count(usage_limits['characters'])
|
|
16
|
+
@speech_to_text_milliseconds =
|
|
17
|
+
Utils::NumberParser.optional_count(usage_limits['speech_to_text_milliseconds'])
|
|
18
|
+
@speech_to_speech_milliseconds =
|
|
19
|
+
Utils::NumberParser.optional_count(usage_limits['speech_to_speech_milliseconds'])
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# A developer key. +label+ and +usage_limits+ are +nil+ when the response does not contain
|
|
25
|
+
# them.
|
|
26
|
+
class DeveloperKey < Base
|
|
27
|
+
attr_reader :key_id, :label, :creation_time, :deactivated_time, :is_deactivated,
|
|
28
|
+
:usage_limits
|
|
29
|
+
|
|
30
|
+
def initialize(developer_key, *args)
|
|
31
|
+
super(*args)
|
|
32
|
+
developer_key = {} unless developer_key.is_a?(Hash)
|
|
33
|
+
@key_id = developer_key['key_id'].is_a?(String) ? developer_key['key_id'] : ''
|
|
34
|
+
@label = developer_key['label'].is_a?(String) ? developer_key['label'] : nil
|
|
35
|
+
@creation_time = Utils::TimeParser.parse_optional_utc_time(developer_key['creation_time'])
|
|
36
|
+
@deactivated_time =
|
|
37
|
+
Utils::TimeParser.parse_optional_utc_time(developer_key['deactivated_time'])
|
|
38
|
+
@is_deactivated = developer_key['is_deactivated'] == true
|
|
39
|
+
@usage_limits = parse_usage_limits(developer_key['usage_limits'])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def deactivated?
|
|
43
|
+
is_deactivated
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def to_s
|
|
47
|
+
label.nil? ? key_id : "#{label} (#{key_id})"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def parse_usage_limits(usage_limits)
|
|
53
|
+
usage_limits.is_a?(Hash) ? DeveloperKeyUsageLimits.new(usage_limits) : nil
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -8,49 +8,48 @@ module DeepL
|
|
|
8
8
|
class DocumentHandle < Base
|
|
9
9
|
attr_reader :document_id, :document_key
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
# +api+ is the connection the handle was created with; status queries go through it.
|
|
12
|
+
# A handle for a stored ID and key needs only those two: +DocumentHandle.new(id, key)+.
|
|
13
|
+
def initialize(document_id, document_key, request = nil, response = nil, api: nil)
|
|
14
|
+
super(request, response)
|
|
13
15
|
|
|
14
16
|
@document_id = document_id
|
|
15
17
|
@document_key = document_key
|
|
18
|
+
@api = api
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
def to_s
|
|
19
22
|
"DocumentHandle: ID: #{document_id} - Key: #{document_key}"
|
|
20
23
|
end
|
|
21
24
|
|
|
25
|
+
# Leaves out the connection, which holds the auth key.
|
|
26
|
+
def inspect
|
|
27
|
+
"#<#{self.class.name} document_id=#{document_id.inspect}>"
|
|
28
|
+
end
|
|
29
|
+
|
|
22
30
|
##
|
|
23
|
-
# For this DocumentHandle, waits until the document translation is finished
|
|
24
|
-
# final status of the document.
|
|
31
|
+
# For this DocumentHandle, waits until the document translation is finished (done or
|
|
32
|
+
# failed) and returns the final status of the document. Stops early only if the
|
|
33
|
+
# deprecated +max_doc_status_queries+ is configured.
|
|
25
34
|
#
|
|
35
|
+
# @deprecated Use DeepL::Client#translate_document_wait_until_done instead.
|
|
26
36
|
# @return [DeepL::Resources::DocumentTranslationStatus] Final status of the document
|
|
27
37
|
# translation.
|
|
28
|
-
|
|
29
38
|
def wait_until_document_translation_finished
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
break if doc_status.finished? || num_tries > max_tries
|
|
38
|
-
end
|
|
39
|
-
doc_status
|
|
39
|
+
Deprecation.warn_once('DocumentHandle#wait_until_document_translation_finished',
|
|
40
|
+
'[DEPRECATION] DeepL::Resources::DocumentHandle#' \
|
|
41
|
+
'wait_until_document_translation_finished is deprecated, use ' \
|
|
42
|
+
'DeepL::Client#translate_document_wait_until_done instead.')
|
|
43
|
+
Utils::DocumentPoller.wait_until_finished(
|
|
44
|
+
api, self, max_queries: api.configuration.max_doc_status_queries
|
|
45
|
+
)
|
|
40
46
|
end
|
|
41
47
|
|
|
42
48
|
private
|
|
43
49
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def max_doc_status_queries
|
|
50
|
-
configured_value = DeepL.configuration.max_doc_status_queries
|
|
51
|
-
return configured_value unless configured_value.nil?
|
|
52
|
-
|
|
53
|
-
10
|
|
50
|
+
# Handles created without a connection fall back to the deprecated module configuration.
|
|
51
|
+
def api
|
|
52
|
+
@api || DeepL.send(:module_api)
|
|
54
53
|
end
|
|
55
54
|
end
|
|
56
55
|
end
|
|
@@ -23,13 +23,24 @@ module DeepL
|
|
|
23
23
|
"- Error message: #{error_message}"
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
##
|
|
27
|
+
# Checks if the translation is done, so the document can be downloaded.
|
|
28
|
+
#
|
|
29
|
+
# @return [Boolean] +true+ if the status is +done+.
|
|
30
|
+
def done?
|
|
31
|
+
status == Constants::DocumentStatusCode::DONE
|
|
32
|
+
end
|
|
33
|
+
|
|
26
34
|
##
|
|
27
35
|
# Checks if the translation finished successfully
|
|
28
36
|
#
|
|
37
|
+
# @deprecated Use #done? instead.
|
|
29
38
|
# @return [true] if so
|
|
30
|
-
|
|
31
39
|
def successful?
|
|
32
|
-
|
|
40
|
+
Deprecation.warn_once('DeepL::Resources::DocumentTranslationStatus#successful?',
|
|
41
|
+
'[DEPRECATION] DeepL::Resources::DocumentTranslationStatus#' \
|
|
42
|
+
'successful? is deprecated, use #done? instead.')
|
|
43
|
+
done?
|
|
33
44
|
end
|
|
34
45
|
|
|
35
46
|
##
|
|
@@ -38,7 +49,15 @@ module DeepL
|
|
|
38
49
|
# @return [true] if so
|
|
39
50
|
|
|
40
51
|
def error?
|
|
41
|
-
status ==
|
|
52
|
+
status == Constants::DocumentStatusCode::ERROR
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# Checks that the translation has not failed. Also true while it is queued or translating.
|
|
57
|
+
#
|
|
58
|
+
# @return [Boolean] +true+ unless the status is +error+.
|
|
59
|
+
def ok?
|
|
60
|
+
!error?
|
|
42
61
|
end
|
|
43
62
|
|
|
44
63
|
##
|
|
@@ -47,7 +66,7 @@ module DeepL
|
|
|
47
66
|
#
|
|
48
67
|
# @return [true] if so
|
|
49
68
|
def finished?
|
|
50
|
-
|
|
69
|
+
done? || error?
|
|
51
70
|
end
|
|
52
71
|
end
|
|
53
72
|
end
|
|
@@ -8,20 +8,28 @@ module DeepL
|
|
|
8
8
|
class Language < Base
|
|
9
9
|
attr_reader :code, :name
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
# +client:+ is true for a language returned by DeepL::Client. @api private
|
|
12
|
+
def initialize(code, name, supports_formality, *args, client: false)
|
|
12
13
|
super(*args)
|
|
13
14
|
|
|
14
15
|
@code = code
|
|
15
16
|
@name = name
|
|
16
17
|
@supports_formality = supports_formality
|
|
18
|
+
@client = client
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def to_s
|
|
20
22
|
"#{code} - #{name}"
|
|
21
23
|
end
|
|
22
24
|
|
|
25
|
+
# Whether the target language supports the +formality+ option.
|
|
26
|
+
#
|
|
27
|
+
# @return [Boolean, nil] +true+ or +false+ for a target language; for a source language
|
|
28
|
+
# +nil+ if it came from DeepL::Client.
|
|
29
|
+
# @raise [DeepL::Exceptions::NotSupported] For a source language from the module-level
|
|
30
|
+
# +DeepL.languages+, until the next major version.
|
|
23
31
|
def supports_formality?
|
|
24
|
-
return @supports_formality unless @supports_formality.nil?
|
|
32
|
+
return @supports_formality unless @supports_formality.nil? && !@client
|
|
25
33
|
|
|
26
34
|
raise Exceptions::NotSupported, 'Support formality is only available on target languages'
|
|
27
35
|
end
|
|
@@ -0,0 +1,57 @@
|
|
|
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 Resources
|
|
8
|
+
# A feature supported by a DeepL API resource, as returned by `DeepL::Client#get_language_resources`.
|
|
9
|
+
class ResourceFeature
|
|
10
|
+
attr_reader :name
|
|
11
|
+
|
|
12
|
+
def initialize(feature)
|
|
13
|
+
@name = feature['name']
|
|
14
|
+
@needs_source_support = feature['needs_source_support'] ? true : false
|
|
15
|
+
@needs_target_support = feature['needs_target_support'] ? true : false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_s
|
|
19
|
+
name
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# True if the source language must support this feature for it to be available.
|
|
23
|
+
def needs_source_support?
|
|
24
|
+
@needs_source_support
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# True if the target language must support this feature for it to be available.
|
|
28
|
+
def needs_target_support?
|
|
29
|
+
@needs_target_support
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# A DeepL API resource and the features it supports, as returned by `DeepL::Client#get_language_resources`.
|
|
34
|
+
class LanguageResource < Base
|
|
35
|
+
attr_reader :name, :features
|
|
36
|
+
|
|
37
|
+
def initialize(resource, *args)
|
|
38
|
+
super(*args)
|
|
39
|
+
|
|
40
|
+
@name = resource['name']
|
|
41
|
+
@features = (resource['features'] || []).to_h do |feature|
|
|
42
|
+
resource_feature = ResourceFeature.new(feature)
|
|
43
|
+
[resource_feature.name, resource_feature]
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_s
|
|
48
|
+
name
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns true if this resource exposes the given feature (e.g. 'glossary').
|
|
52
|
+
def supports?(feature)
|
|
53
|
+
features.key?(feature.to_s)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
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 Resources
|
|
8
|
+
# Support level of a single feature of a language for a resource.
|
|
9
|
+
class LanguageFeature
|
|
10
|
+
attr_reader :status
|
|
11
|
+
|
|
12
|
+
def initialize(status)
|
|
13
|
+
@status = status
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_s
|
|
17
|
+
status.to_s
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# A language supported by a DeepL API resource, as returned by `DeepL::Client#get_languages_for_resource`.
|
|
22
|
+
class LanguageSupport < Base
|
|
23
|
+
attr_reader :code, :name, :status, :features
|
|
24
|
+
|
|
25
|
+
def initialize(language, *args)
|
|
26
|
+
super(*args)
|
|
27
|
+
|
|
28
|
+
@code = language['lang']
|
|
29
|
+
@name = language['name']
|
|
30
|
+
@status = language['status']
|
|
31
|
+
@usable_as_source = language['usable_as_source'] ? true : false
|
|
32
|
+
@usable_as_target = language['usable_as_target'] ? true : false
|
|
33
|
+
@features = (language['features'] || {}).to_h.transform_values do |feature|
|
|
34
|
+
LanguageFeature.new(feature.to_h['status'])
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def to_s
|
|
39
|
+
"#{name} (#{code})"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def usable_as_source?
|
|
43
|
+
@usable_as_source
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def usable_as_target?
|
|
47
|
+
@usable_as_target
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Returns true if this language supports the given feature (e.g. 'formality').
|
|
51
|
+
def supports?(feature)
|
|
52
|
+
features.key?(feature.to_s)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|