deepl-rb 3.9.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +70 -52
  3. data/.rubocop.yml +26 -0
  4. data/CHANGELOG.md +429 -18
  5. data/Gemfile +4 -0
  6. data/README.md +981 -223
  7. data/Rakefile +18 -5
  8. data/VERSION +1 -1
  9. data/deepl-rb.gemspec +6 -6
  10. data/lib/deepl/admin_client.rb +183 -0
  11. data/lib/deepl/api.rb +30 -6
  12. data/lib/deepl/app_info.rb +20 -0
  13. data/lib/deepl/client.rb +969 -0
  14. data/lib/deepl/configuration.rb +56 -10
  15. data/lib/deepl/constants/admin_usage_group_by.rb +13 -0
  16. data/lib/deepl/constants/custom_tag_usage_aggregate_by.rb +13 -0
  17. data/lib/deepl/constants/document_status_code.rb +17 -0
  18. data/lib/deepl/constants/language_include.rb +17 -0
  19. data/lib/deepl/constants/language_resource_name.rb +21 -0
  20. data/lib/deepl/constants/tag_handling_version.rb +14 -0
  21. data/lib/deepl/deprecation.rb +42 -0
  22. data/lib/deepl/document_api.rb +41 -22
  23. data/lib/deepl/exceptions/bad_request.rb +1 -3
  24. data/lib/deepl/exceptions/connection_error.rb +37 -0
  25. data/lib/deepl/exceptions/document_not_ready_error.rb +17 -0
  26. data/lib/deepl/exceptions/document_translation_error.rb +17 -1
  27. data/lib/deepl/exceptions/error.rb +13 -1
  28. data/lib/deepl/exceptions/not_found.rb +8 -3
  29. data/lib/deepl/exceptions/not_supported.rb +6 -0
  30. data/lib/deepl/exceptions/request_error.rb +19 -2
  31. data/lib/deepl/exceptions/server_error.rb +7 -1
  32. data/lib/deepl/exceptions/validation_error.rb +14 -0
  33. data/lib/deepl/requests/admin/base.rb +94 -0
  34. data/lib/deepl/requests/admin/create_developer_key.rb +38 -0
  35. data/lib/deepl/requests/admin/custom_tag_usage_analytics.rb +38 -0
  36. data/lib/deepl/requests/admin/list_developer_keys.rb +28 -0
  37. data/lib/deepl/requests/admin/update_developer_key.rb +35 -0
  38. data/lib/deepl/requests/admin/usage_analytics.rb +36 -0
  39. data/lib/deepl/requests/base.rb +229 -59
  40. data/lib/deepl/requests/correct.rb +57 -0
  41. data/lib/deepl/requests/document/download.rb +11 -3
  42. data/lib/deepl/requests/document/get_status.rb +2 -2
  43. data/lib/deepl/requests/document/upload.rb +43 -10
  44. data/lib/deepl/requests/glossary/create.rb +6 -2
  45. data/lib/deepl/requests/glossary/language_pairs.rb +20 -4
  46. data/lib/deepl/requests/language_resources.rb +41 -0
  47. data/lib/deepl/requests/languages.rb +28 -4
  48. data/lib/deepl/requests/languages_for_resource.rb +54 -0
  49. data/lib/deepl/requests/multilingual_glossary/create.rb +37 -0
  50. data/lib/deepl/requests/multilingual_glossary/destroy.rb +36 -0
  51. data/lib/deepl/requests/multilingual_glossary/destroy_dictionary.rb +51 -0
  52. data/lib/deepl/requests/multilingual_glossary/entries.rb +66 -0
  53. data/lib/deepl/requests/multilingual_glossary/find.rb +35 -0
  54. data/lib/deepl/requests/multilingual_glossary/helpers.rb +63 -0
  55. data/lib/deepl/requests/multilingual_glossary/list.rb +39 -0
  56. data/lib/deepl/requests/multilingual_glossary/replace_dictionary.rb +41 -0
  57. data/lib/deepl/requests/multilingual_glossary/update.rb +40 -0
  58. data/lib/deepl/requests/rephrase.rb +5 -9
  59. data/lib/deepl/requests/spoken_terms/create.rb +37 -0
  60. data/lib/deepl/requests/spoken_terms/destroy.rb +36 -0
  61. data/lib/deepl/requests/spoken_terms/destroy_term_list.rb +41 -0
  62. data/lib/deepl/requests/spoken_terms/entries.rb +46 -0
  63. data/lib/deepl/requests/spoken_terms/find.rb +35 -0
  64. data/lib/deepl/requests/spoken_terms/helpers.rb +67 -0
  65. data/lib/deepl/requests/spoken_terms/list.rb +35 -0
  66. data/lib/deepl/requests/spoken_terms/replace_term_list.rb +41 -0
  67. data/lib/deepl/requests/spoken_terms/update.rb +40 -0
  68. data/lib/deepl/requests/style_profile/base.rb +83 -0
  69. data/lib/deepl/requests/style_profile/create.rb +35 -0
  70. data/lib/deepl/requests/style_profile/destroy.rb +34 -0
  71. data/lib/deepl/requests/style_profile/find.rb +33 -0
  72. data/lib/deepl/requests/style_profile/list.rb +43 -0
  73. data/lib/deepl/requests/style_profile/update.rb +35 -0
  74. data/lib/deepl/requests/style_rule/create.rb +1 -1
  75. data/lib/deepl/requests/style_rule/create_custom_instruction.rb +4 -2
  76. data/lib/deepl/requests/style_rule/destroy.rb +4 -2
  77. data/lib/deepl/requests/style_rule/destroy_custom_instruction.rb +5 -3
  78. data/lib/deepl/requests/style_rule/find.rb +4 -2
  79. data/lib/deepl/requests/style_rule/find_custom_instruction.rb +5 -3
  80. data/lib/deepl/requests/style_rule/helpers.rb +35 -0
  81. data/lib/deepl/requests/style_rule/list.rb +1 -1
  82. data/lib/deepl/requests/style_rule/update.rb +4 -2
  83. data/lib/deepl/requests/style_rule/update_configured_rules.rb +4 -2
  84. data/lib/deepl/requests/style_rule/update_custom_instruction.rb +5 -3
  85. data/lib/deepl/requests/translate.rb +52 -20
  86. data/lib/deepl/requests/translation_memory/base.rb +1 -4
  87. data/lib/deepl/requests/translation_memory/create_export.rb +1 -1
  88. data/lib/deepl/requests/translation_memory/create_import.rb +1 -1
  89. data/lib/deepl/requests/translation_memory/destroy.rb +1 -1
  90. data/lib/deepl/requests/translation_memory/download_export.rb +4 -0
  91. data/lib/deepl/requests/translation_memory/find.rb +1 -1
  92. data/lib/deepl/requests/translation_memory/find_job.rb +1 -1
  93. data/lib/deepl/requests/translation_memory/list.rb +2 -1
  94. data/lib/deepl/requests/translation_memory/segments.rb +3 -2
  95. data/lib/deepl/requests/translation_memory/storage_base.rb +40 -8
  96. data/lib/deepl/requests/translation_memory/upload_file.rb +1 -1
  97. data/lib/deepl/requests/usage.rb +28 -1
  98. data/lib/deepl/requests/write_improvements.rb +35 -0
  99. data/lib/deepl/resources/admin_usage_report.rb +73 -0
  100. data/lib/deepl/resources/custom_tag_usage_report.rb +59 -0
  101. data/lib/deepl/resources/developer_key.rb +57 -0
  102. data/lib/deepl/resources/document_handle.rb +24 -25
  103. data/lib/deepl/resources/document_translation_status.rb +23 -4
  104. data/lib/deepl/resources/language.rb +10 -2
  105. data/lib/deepl/resources/language_resource.rb +57 -0
  106. data/lib/deepl/resources/language_support.rb +56 -0
  107. data/lib/deepl/resources/multilingual_glossary.rb +112 -0
  108. data/lib/deepl/resources/spoken_terms.rb +61 -0
  109. data/lib/deepl/resources/style_profile.rb +44 -0
  110. data/lib/deepl/resources/style_rule.rb +3 -0
  111. data/lib/deepl/resources/text.rb +18 -2
  112. data/lib/deepl/resources/translation_memory.rb +8 -2
  113. data/lib/deepl/resources/translation_memory_import.rb +1 -1
  114. data/lib/deepl/resources/translation_memory_job.rb +23 -3
  115. data/lib/deepl/resources/translation_memory_segments.rb +8 -6
  116. data/lib/deepl/resources/usage.rb +93 -4
  117. data/lib/deepl/style_rule_api.rb +11 -0
  118. data/lib/deepl/translation_memory_api.rb +104 -24
  119. data/lib/deepl/utils/client_builder.rb +54 -0
  120. data/lib/deepl/utils/document_poller.rb +70 -0
  121. data/lib/deepl/utils/exception_builder.rb +3 -2
  122. data/lib/deepl/utils/http_client_builder.rb +79 -0
  123. data/lib/deepl/utils/number_parser.rb +34 -0
  124. data/lib/deepl/utils/term_validator.rb +33 -0
  125. data/lib/deepl/utils/time_parser.rb +14 -0
  126. data/lib/deepl.rb +166 -41
  127. data/lib/http_client_options.rb +21 -1
  128. data/lib/version.rb +1 -1
  129. data/spec/fixtures.rb +33 -0
  130. data/spec/offline/admin_client_spec.rb +551 -0
  131. data/spec/offline/client_spec.rb +273 -0
  132. data/spec/{api → offline}/configuration_spec.rb +45 -3
  133. data/spec/{constants → offline}/constants_spec.rb +23 -1
  134. data/spec/offline/correct_text_spec.rb +50 -0
  135. data/spec/offline/deprecation_spec.rb +32 -0
  136. data/spec/offline/errors_spec.rb +92 -0
  137. data/spec/offline/glossaries_spec.rb +98 -0
  138. data/spec/offline/http_client_spec.rb +319 -0
  139. data/spec/offline/languages_spec.rb +155 -0
  140. data/spec/offline/module_methods_spec.rb +13 -0
  141. data/spec/offline/multilingual_glossaries_spec.rb +358 -0
  142. data/spec/offline/offline_helper.rb +71 -0
  143. data/spec/offline/rephrase_text_spec.rb +73 -0
  144. data/spec/offline/spoken_terms_spec.rb +215 -0
  145. data/spec/offline/style_profiles_spec.rb +266 -0
  146. data/spec/offline/style_rules_spec.rb +318 -0
  147. data/spec/offline/translate_document_spec.rb +606 -0
  148. data/spec/offline/translate_text_spec.rb +286 -0
  149. data/spec/offline/translation_memories_client_spec.rb +256 -0
  150. data/spec/offline/translation_memories_spec.rb +463 -0
  151. data/spec/offline/usage_spec.rb +151 -0
  152. data/spec/real_api/admin_client_spec.rb +77 -0
  153. data/spec/real_api/correct_text_spec.rb +20 -0
  154. data/spec/real_api/glossaries_spec.rb +28 -0
  155. data/spec/real_api/languages_spec.rb +27 -0
  156. data/spec/real_api/multilingual_glossaries_spec.rb +53 -0
  157. data/spec/real_api/proxy_spec.rb +41 -0
  158. data/spec/real_api/real_api_helper.rb +65 -0
  159. data/spec/real_api/rephrase_text_spec.rb +16 -0
  160. data/spec/real_api/spoken_terms_spec.rb +56 -0
  161. data/spec/real_api/style_profiles_spec.rb +46 -0
  162. data/spec/real_api/style_rules_spec.rb +46 -0
  163. data/spec/real_api/translate_document_spec.rb +18 -0
  164. data/spec/real_api/translate_text_spec.rb +16 -0
  165. data/spec/real_api/translation_memories_spec.rb +56 -0
  166. data/spec/real_api/usage_spec.rb +20 -0
  167. data/spec/spec_helper.rb +1 -50
  168. metadata +116 -76
  169. data/.circleci/config.yml +0 -27
  170. data/.github/workflows/add_issues_to_kanban.yml +0 -16
  171. data/spec/api/api_spec.rb +0 -20
  172. data/spec/api/deepl_spec.rb +0 -262
  173. data/spec/integration_tests/document_api_spec.rb +0 -211
  174. data/spec/integration_tests/document_error_paths_spec.rb +0 -33
  175. data/spec/integration_tests/glossary_api_spec.rb +0 -114
  176. data/spec/integration_tests/glossary_error_paths_spec.rb +0 -107
  177. data/spec/integration_tests/integration_test_utils.rb +0 -178
  178. data/spec/integration_tests/languages_api_spec.rb +0 -54
  179. data/spec/integration_tests/languages_error_paths_spec.rb +0 -25
  180. data/spec/integration_tests/rephrase_api_spec.rb +0 -90
  181. data/spec/integration_tests/rephrase_error_paths_spec.rb +0 -53
  182. data/spec/integration_tests/smoke_test_spec.rb +0 -24
  183. data/spec/integration_tests/style_rule_api_spec.rb +0 -117
  184. data/spec/integration_tests/style_rule_error_paths_spec.rb +0 -45
  185. data/spec/integration_tests/translate_api_spec.rb +0 -117
  186. data/spec/integration_tests/translate_error_paths_spec.rb +0 -48
  187. data/spec/integration_tests/translation_memory_api_spec.rb +0 -244
  188. data/spec/integration_tests/translation_memory_error_paths_spec.rb +0 -87
  189. data/spec/integration_tests/usage_api_spec.rb +0 -29
  190. data/spec/integration_tests/usage_error_paths_spec.rb +0 -18
  191. data/spec/requests/document/upload_spec.rb +0 -130
  192. data/spec/requests/extra_body_parameters_types_spec.rb +0 -82
  193. data/spec/requests/glossary/create_spec.rb +0 -44
  194. data/spec/requests/glossary/destroy_spec.rb +0 -27
  195. data/spec/requests/glossary/entries_spec.rb +0 -27
  196. data/spec/requests/glossary/find_spec.rb +0 -28
  197. data/spec/requests/glossary/language_pairs_spec.rb +0 -27
  198. data/spec/requests/glossary/list_spec.rb +0 -27
  199. data/spec/requests/languages_spec.rb +0 -27
  200. data/spec/requests/rephrase_spec.rb +0 -46
  201. data/spec/requests/style_rule/create_custom_instruction_spec.rb +0 -30
  202. data/spec/requests/style_rule/create_spec.rb +0 -29
  203. data/spec/requests/style_rule/destroy_custom_instruction_spec.rb +0 -28
  204. data/spec/requests/style_rule/destroy_spec.rb +0 -27
  205. data/spec/requests/style_rule/find_custom_instruction_spec.rb +0 -29
  206. data/spec/requests/style_rule/find_spec.rb +0 -28
  207. data/spec/requests/style_rule/list_spec.rb +0 -27
  208. data/spec/requests/style_rule/update_configured_rules_spec.rb +0 -31
  209. data/spec/requests/style_rule/update_custom_instruction_spec.rb +0 -32
  210. data/spec/requests/style_rule/update_spec.rb +0 -29
  211. data/spec/requests/translate_spec.rb +0 -338
  212. data/spec/requests/translation_memory/create_export_spec.rb +0 -37
  213. data/spec/requests/translation_memory/create_import_spec.rb +0 -61
  214. data/spec/requests/translation_memory/destroy_spec.rb +0 -36
  215. data/spec/requests/translation_memory/download_export_spec.rb +0 -54
  216. data/spec/requests/translation_memory/find_job_spec.rb +0 -37
  217. data/spec/requests/translation_memory/find_spec.rb +0 -36
  218. data/spec/requests/translation_memory/list_spec.rb +0 -27
  219. data/spec/requests/translation_memory/segments_spec.rb +0 -58
  220. data/spec/requests/translation_memory/upload_file_spec.rb +0 -55
  221. data/spec/requests/usage_spec.rb +0 -27
  222. data/spec/resources/custom_instruction_spec.rb +0 -32
  223. data/spec/resources/glossary_spec.rb +0 -38
  224. data/spec/resources/language_pair_spec.rb +0 -23
  225. data/spec/resources/language_spec.rb +0 -45
  226. data/spec/resources/style_rule_spec.rb +0 -68
  227. data/spec/resources/text_spec.rb +0 -23
  228. data/spec/resources/translation_memory_export_spec.rb +0 -38
  229. data/spec/resources/translation_memory_import_spec.rb +0 -30
  230. data/spec/resources/translation_memory_job_spec.rb +0 -109
  231. data/spec/resources/translation_memory_segments_spec.rb +0 -78
  232. data/spec/resources/translation_memory_spec.rb +0 -52
  233. data/spec/resources/usage_spec.rb +0 -35
  234. data/spec/support/live_mock_server.rb +0 -12
  235. data/spec/support/managed_glossary.rb +0 -65
  236. data/spec/support/managed_style_rule.rb +0 -17
  237. data/spec/support/managed_translation_memory.rb +0 -55
@@ -0,0 +1,112 @@
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
+ class MultilingualGlossaryDictionary < Base
9
+ attr_reader :source_lang, :target_lang, :entry_count
10
+
11
+ def initialize(dictionary, *args)
12
+ super(*args)
13
+
14
+ @source_lang = dictionary['source_lang']
15
+ @target_lang = dictionary['target_lang']
16
+ @entry_count = dictionary['entry_count']
17
+ end
18
+
19
+ def to_s
20
+ "#{source_lang} -> #{target_lang} (#{entry_count})"
21
+ end
22
+ end
23
+
24
+ # The entries of one dictionary. Used as input for +create_multilingual_glossary+,
25
+ # +update_multilingual_glossary+, +update_multilingual_glossary_dictionary+ and
26
+ # +replace_multilingual_glossary_dictionary+, and returned by
27
+ # +get_multilingual_glossary_dictionary_entries+.
28
+ class MultilingualGlossaryDictionaryEntries < Base
29
+ # @return [Hash{String => String}] The target term of each source term.
30
+ attr_reader :entries
31
+ attr_reader :source_lang, :target_lang
32
+
33
+ ##
34
+ # @param [String] source_lang Source language code, for example +'en'+.
35
+ # @param [String] target_lang Target language code, for example +'de'+.
36
+ # @param [Hash{String => String}, Array<Array(String, String)>, String] entries The target
37
+ # term of each source term, as a hash, source/target pairs or a TSV string; terms are
38
+ # stripped and checked when sent.
39
+ # @raise [DeepL::Exceptions::ValidationError] If a pair or TSV line is not exactly two
40
+ # terms, or a source term appears twice.
41
+ def initialize(source_lang, target_lang, entries, request = nil, response = nil)
42
+ super(request, response)
43
+
44
+ @source_lang = source_lang
45
+ @target_lang = target_lang
46
+ pairs = entries.is_a?(String) ? self.class.tsv_to_pairs(entries) : entries.to_a
47
+ @entries = self.class.check_pairs!(pairs).to_h
48
+ end
49
+
50
+ # @api private
51
+ # Builds the entries from a dictionary of the API, whose entries are a TSV string.
52
+ def self.from_response(dictionary, request, response)
53
+ entries = (dictionary['entries'] || '').split(/\r?\n/).reject(&:empty?).to_h do |line|
54
+ source, target = line.split("\t", 2)
55
+ [source, target]
56
+ end
57
+ new(dictionary['source_lang'], dictionary['target_lang'], entries, request, response)
58
+ end
59
+
60
+ # @api private
61
+ # Raises ValidationError unless each pair is two strings and no source term, stripped as
62
+ # the term check strips it, appears twice. Returns +pairs+.
63
+ def self.check_pairs!(pairs)
64
+ seen = {}
65
+ pairs.each do |pair|
66
+ unless pair.is_a?(Array) && pair.size == 2 && pair.all?(String)
67
+ raise Exceptions::ValidationError, "Entry #{pair.inspect} must be two terms"
68
+ end
69
+
70
+ source = pair.first.strip
71
+ next seen[source] = true unless seen[source]
72
+
73
+ raise Exceptions::ValidationError, "Duplicate source term #{source.inspect}"
74
+ end
75
+ end
76
+
77
+ # @api private
78
+ # One source/target pair per non-blank line, separated by a single tab.
79
+ def self.tsv_to_pairs(tsv)
80
+ tsv.split(/\r?\n/).reject { |line| line.strip.empty? }.map do |line|
81
+ pair = line.split("\t", -1)
82
+ unless pair.size == 2
83
+ raise Exceptions::ValidationError,
84
+ "TSV entry #{line.inspect} must be a source and a target term separated by " \
85
+ 'one tab'
86
+ end
87
+
88
+ pair
89
+ end
90
+ end
91
+ end
92
+
93
+ class MultilingualGlossary < Base
94
+ attr_reader :id, :name, :creation_time, :dictionaries
95
+
96
+ def initialize(glossary, *args)
97
+ super(*args)
98
+
99
+ @id = glossary['glossary_id']
100
+ @name = glossary['name']
101
+ @creation_time = Utils::TimeParser.parse_optional_utc_time(glossary['creation_time'])
102
+ @dictionaries = (glossary['dictionaries'] || []).map do |dictionary|
103
+ MultilingualGlossaryDictionary.new(dictionary, *args)
104
+ end
105
+ end
106
+
107
+ def to_s
108
+ "#{id} - #{name}"
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,61 @@
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
+ # The term count of one language in a spoken terms collection.
9
+ class SpokenTermsList < Base
10
+ attr_reader :lang, :entry_count
11
+
12
+ def initialize(term_list, *args)
13
+ super(*args)
14
+
15
+ @lang = term_list['lang']
16
+ @entry_count = term_list['entry_count']
17
+ end
18
+
19
+ def to_s
20
+ "#{lang} (#{entry_count})"
21
+ end
22
+ end
23
+
24
+ # The terms of one language. Used as input for +create_spoken_terms+,
25
+ # +update_spoken_terms+ and +replace_spoken_terms_list+, and returned by
26
+ # +get_spoken_terms_entries+.
27
+ class SpokenTermsEntries < Base
28
+ attr_reader :lang, :entries
29
+
30
+ ##
31
+ # @param [String] lang Language code, for example +'en'+.
32
+ # @param [Array<String>] entries The terms, one per element.
33
+ def initialize(lang, entries, request = nil, response = nil)
34
+ super(request, response)
35
+
36
+ @lang = lang
37
+ @entries = entries
38
+ end
39
+ end
40
+
41
+ # A spoken terms collection.
42
+ class SpokenTerms < Base
43
+ attr_reader :spoken_terms_id, :name, :creation_time, :term_lists
44
+
45
+ def initialize(spoken_terms, *args)
46
+ super(*args)
47
+
48
+ @spoken_terms_id = spoken_terms['spoken_terms_id']
49
+ @name = spoken_terms['name']
50
+ @creation_time = Utils::TimeParser.parse_optional_utc_time(spoken_terms['creation_time'])
51
+ @term_lists = (spoken_terms['term_lists'] || []).map do |term_list|
52
+ SpokenTermsList.new(term_list, *args)
53
+ end
54
+ end
55
+
56
+ def to_s
57
+ "#{spoken_terms_id} - #{name}"
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,44 @@
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
+ # A style profile: a named set of glossaries, style rule lists and translation memories.
9
+ class StyleProfile < Base
10
+ attr_reader :style_profile_id, :name, :description, :industry, :glossary_ids, :style_rule_ids,
11
+ :translation_memory_ids, :glossary_priority_order_enabled, :creation_time,
12
+ :updated_time
13
+
14
+ def initialize(profile, *args) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
15
+ super(*args)
16
+
17
+ @style_profile_id = profile['style_profile_id']
18
+ @name = profile['name']
19
+ @description = optional_string(profile['description'])
20
+ @industry = optional_string(profile['industry'])
21
+ @glossary_ids = id_list(profile['glossary_ids'])
22
+ @style_rule_ids = id_list(profile['style_rule_ids'])
23
+ @translation_memory_ids = id_list(profile['translation_memory_ids'])
24
+ @glossary_priority_order_enabled = profile['glossary_priority_order_enabled'] == true
25
+ @creation_time = Utils::TimeParser.parse_optional_utc_time(profile['creation_time'])
26
+ @updated_time = Utils::TimeParser.parse_optional_utc_time(profile['updated_time'])
27
+ end
28
+
29
+ def to_s
30
+ "#{style_profile_id} - #{name}"
31
+ end
32
+
33
+ private
34
+
35
+ def optional_string(value)
36
+ value if value.is_a?(String)
37
+ end
38
+
39
+ def id_list(value)
40
+ value.is_a?(Array) ? value.grep(String) : []
41
+ end
42
+ end
43
+ end
44
+ end
@@ -80,6 +80,9 @@ module DeepL
80
80
  return nil unless time_string
81
81
 
82
82
  Time.parse(time_string)
83
+ rescue ArgumentError, TypeError
84
+ raise Exceptions::Error,
85
+ "Unexpected response from the DeepL API: invalid time #{time_string.inspect}"
83
86
  end
84
87
  end
85
88
  end
@@ -6,14 +6,30 @@
6
6
  module DeepL
7
7
  module Resources
8
8
  class Text < Base
9
+ # @return [Integer, nil] Number of characters billed for the text, +nil+ if the response
10
+ # does not contain it.
11
+ attr_reader :billed_characters
12
+ # @return [String, nil] Tag handling algorithm version used (see
13
+ # +DeepL::Constants::TagHandlingVersion+), +nil+ if the response does not contain it.
14
+ attr_reader :tag_handling_version
15
+ # @return [String, nil] Target language of a rephrase or correct result, +nil+ for text
16
+ # translation results and if the response does not contain it.
17
+ attr_reader :target_language
9
18
  attr_reader :text, :detected_source_language, :model_type_used
10
19
 
11
- def initialize(text, detected_source_language, model_type_used, *args)
12
- super(*args)
20
+ # Positional, not keyword, parameters: Ruby 2.7 would read the response (it has +to_hash+)
21
+ # as keywords.
22
+ def initialize(text, detected_source_language, model_type_used, request = nil, # rubocop:disable Metrics/ParameterLists
23
+ response = nil, billed_characters = nil, tag_handling_version = nil,
24
+ target_language = nil)
25
+ super(request, response)
13
26
 
14
27
  @text = text
15
28
  @detected_source_language = detected_source_language
16
29
  @model_type_used = model_type_used
30
+ @billed_characters = billed_characters
31
+ @tag_handling_version = tag_handling_version
32
+ @target_language = target_language
17
33
  end
18
34
 
19
35
  def to_s
@@ -16,13 +16,19 @@ module DeepL
16
16
  @source_language = translation_memory['source_language']
17
17
  @target_languages = translation_memory['target_languages'] || []
18
18
  @segment_count = translation_memory['segment_count'] || 0
19
- @creation_time = Utils::TimeParser.parse_optional_time(translation_memory['creation_time'])
20
- @updated_time = Utils::TimeParser.parse_optional_time(translation_memory['updated_time'])
19
+ @creation_time = parse_time(translation_memory['creation_time'])
20
+ @updated_time = parse_time(translation_memory['updated_time'])
21
21
  end
22
22
 
23
23
  def to_s
24
24
  "#{translation_memory_id} - #{name}"
25
25
  end
26
+
27
+ private
28
+
29
+ def parse_time(value)
30
+ Utils::TimeParser.parse_optional_utc_time(value)
31
+ end
26
32
  end
27
33
  end
28
34
  end
@@ -16,7 +16,7 @@ module DeepL
16
16
  super(*args)
17
17
  @job_id = translation_memory_import['job_id']
18
18
  @upload_url = translation_memory_import['upload_url']
19
- @expires_at = Utils::TimeParser.parse_optional_time(
19
+ @expires_at = Utils::TimeParser.parse_optional_utc_time(
20
20
  translation_memory_import['expires_at']
21
21
  )
22
22
  end
@@ -26,7 +26,7 @@ module DeepL
26
26
  @status = result['status']
27
27
  @required_action = status_metadata['required_action']
28
28
  @download_url = result['download_url']
29
- @expires_at = Utils::TimeParser.parse_optional_time(result['expires_at'])
29
+ @expires_at = Utils::TimeParser.parse_optional_utc_time(result['expires_at'])
30
30
  @error_message = error['message']
31
31
  @translation_memory_id = result['translation_memory_id']
32
32
  @skipped_segment_count = result['skipped_segment_count']
@@ -110,6 +110,26 @@ module DeepL
110
110
  result ? result.error? : false
111
111
  end
112
112
 
113
+ ##
114
+ # Checks if the job succeeded: its status is +completed+. Unlike +finished?+, this is
115
+ # false for a failed or expired job.
116
+ #
117
+ # @return [Boolean]
118
+
119
+ def done?
120
+ status == TranslationMemoryJobResult::STATUS_COMPLETED
121
+ end
122
+
123
+ ##
124
+ # Checks if the job has not failed: its status is not +failed+ or +expired+. Also true
125
+ # while the job is still running.
126
+ #
127
+ # @return [Boolean]
128
+
129
+ def ok?
130
+ !error?
131
+ end
132
+
113
133
  def error_message
114
134
  result&.error_message
115
135
  end
@@ -124,8 +144,8 @@ module DeepL
124
144
  @job_id = job['job_id']
125
145
  @product = job['product']
126
146
  @operation = job['operation']
127
- @creation_time = Utils::TimeParser.parse_optional_time(job['creation_time'])
128
- @updated_time = Utils::TimeParser.parse_optional_time(job['updated_time'])
147
+ @creation_time = Utils::TimeParser.parse_optional_utc_time(job['creation_time'])
148
+ @updated_time = Utils::TimeParser.parse_optional_utc_time(job['updated_time'])
129
149
  end
130
150
 
131
151
  def extract_parameters(job)
@@ -13,9 +13,11 @@ module DeepL
13
13
  @target_segment_id = target_segment['target_segment_id']
14
14
  @target_language = target_segment['target_language']
15
15
  @target_text = target_segment['target_text']
16
- @creation_time = Utils::TimeParser.parse_optional_time(target_segment['creation_time'])
17
- @updated_time = Utils::TimeParser.parse_optional_time(target_segment['updated_time'])
18
- @last_used_time = Utils::TimeParser.parse_optional_time(target_segment['last_used_time'])
16
+ @creation_time = Utils::TimeParser.parse_optional_utc_time(target_segment['creation_time'])
17
+ @updated_time = Utils::TimeParser.parse_optional_utc_time(target_segment['updated_time'])
18
+ @last_used_time = Utils::TimeParser.parse_optional_utc_time(
19
+ target_segment['last_used_time']
20
+ )
19
21
  end
20
22
 
21
23
  def to_s
@@ -33,9 +35,9 @@ module DeepL
33
35
  @targets = (segment['targets'] || []).map do |target|
34
36
  TranslationMemoryTargetSegment.new(target)
35
37
  end
36
- @creation_time = Utils::TimeParser.parse_optional_time(segment['creation_time'])
37
- @updated_time = Utils::TimeParser.parse_optional_time(segment['updated_time'])
38
- @last_used_time = Utils::TimeParser.parse_optional_time(segment['last_used_time'])
38
+ @creation_time = Utils::TimeParser.parse_optional_utc_time(segment['creation_time'])
39
+ @updated_time = Utils::TimeParser.parse_optional_utc_time(segment['updated_time'])
40
+ @last_used_time = Utils::TimeParser.parse_optional_utc_time(segment['last_used_time'])
39
41
  end
40
42
 
41
43
  def to_s
@@ -6,21 +6,110 @@
6
6
  module DeepL
7
7
  module Resources
8
8
  class Usage < Base
9
- attr_reader :character_count, :character_limit
9
+ ##
10
+ # The amount used and the maximum amount allowed for one usage type.
11
+ class Detail
12
+ attr_reader :count, :limit
10
13
 
11
- def initialize(character_count, character_limit, *args)
14
+ def initialize(count, limit)
15
+ @count = count
16
+ @limit = limit
17
+ end
18
+
19
+ def limit_reached?
20
+ count >= limit
21
+ end
22
+
23
+ def to_s
24
+ "#{count} of #{limit}"
25
+ end
26
+ end
27
+
28
+ DETAIL_TYPES = %i[document team_document api_key_character speech_to_text_minutes
29
+ speech_to_speech_minutes].freeze
30
+ KEYWORD_FIELDS = (DETAIL_TYPES + %i[products start_time end_time]).freeze
31
+
32
+ attr_reader :character, *KEYWORD_FIELDS
33
+
34
+ ##
35
+ # Each keyword argument is nil when the response does not contain the field.
36
+ #
37
+ # @param [Detail, nil] document Document usage, for accounts with a document limit.
38
+ # @param [Detail, nil] team_document Document usage of the whole team.
39
+ # @param [Detail, nil] api_key_character Character usage of this API key (API Pro).
40
+ # @param [Detail, nil] speech_to_text_minutes Speech-to-text usage in minutes (API Pro).
41
+ # @param [Detail, nil] speech_to_speech_minutes Speech-to-speech usage in minutes (API Pro).
42
+ # @param [Array<UsageProduct>, nil] products Per-product usage (API Pro).
43
+ # @param [Time, nil] start_time Start of the current billing period (API Pro).
44
+ # @param [Time, nil] end_time End of the current billing period (API Pro).
45
+
46
+ def initialize(character_count, character_limit, *args, **fields)
12
47
  super(*args)
13
48
 
49
+ unknown = fields.keys - KEYWORD_FIELDS
50
+ raise ArgumentError, "unknown keywords: #{unknown.join(', ')}" unless unknown.empty?
51
+
14
52
  @character_count = character_count
15
53
  @character_limit = character_limit
54
+ @character = Detail.new(character_count, character_limit) unless
55
+ character_count.nil? || character_limit.nil?
56
+ KEYWORD_FIELDS.each { |field| instance_variable_set("@#{field}", fields[field]) }
57
+ end
58
+
59
+ ##
60
+ # @deprecated Use +character.count+ instead.
61
+ def character_count
62
+ deprecated(:character_count, 'character.count')
63
+ @character_count
64
+ end
65
+
66
+ ##
67
+ # @deprecated Use +character.limit+ instead.
68
+ def character_limit
69
+ deprecated(:character_limit, 'character.limit')
70
+ @character_limit
16
71
  end
17
72
 
18
73
  def to_s
19
- "#{character_count} / #{character_limit}"
74
+ "#{@character_count} / #{@character_limit}"
20
75
  end
21
76
 
77
+ ##
78
+ # @deprecated Use +character.limit_reached?+ or +any_limit_reached?+ instead.
22
79
  def quota_exceeded?
23
- character_count >= character_limit
80
+ deprecated(:quota_exceeded?, 'character.limit_reached? or any_limit_reached?')
81
+ @character_count >= @character_limit
82
+ end
83
+
84
+ ##
85
+ # True if the character, document or team document limit has been reached. The API Pro
86
+ # usage types (api_key_character and the speech minutes) are not considered; call
87
+ # limit_reached? on them directly.
88
+ def any_limit_reached?
89
+ [character, document, team_document].compact.any?(&:limit_reached?)
90
+ end
91
+
92
+ private
93
+
94
+ def deprecated(name, replacement)
95
+ Deprecation.warn_once("DeepL::Resources::Usage##{name}",
96
+ "[DEPRECATION] DeepL::Resources::Usage##{name} is deprecated, " \
97
+ "use #{replacement} instead.", uplevel: 3)
98
+ end
99
+ end
100
+
101
+ class UsageProduct
102
+ attr_reader :product_type, :billing_unit, :api_key_unit_count, :account_unit_count
103
+
104
+ def initialize(product)
105
+ @product_type = product['product_type']
106
+ @billing_unit = product['billing_unit']
107
+ @api_key_unit_count = product['api_key_unit_count']
108
+ @account_unit_count = product['account_unit_count']
109
+ end
110
+
111
+ def to_s
112
+ "#{product_type}: #{api_key_unit_count} / #{account_unit_count} #{billing_unit}"
24
113
  end
25
114
  end
26
115
  end
@@ -4,47 +4,57 @@
4
4
  # frozen_string_literal: true
5
5
 
6
6
  module DeepL
7
+ # @deprecated Use the style rule methods of DeepL::Client instead.
7
8
  class StyleRuleApi
8
9
  def initialize(api, options = {})
9
10
  @api = api
10
11
  @options = options
11
12
  end
12
13
 
14
+ # @deprecated Use DeepL::Client#get_all_style_rules instead.
13
15
  def list(options = {})
14
16
  DeepL::Requests::StyleRule::List.new(@api, options).request
15
17
  end
16
18
 
19
+ # @deprecated Use DeepL::Client#create_style_rule instead.
17
20
  def create(name, language, options = {})
18
21
  DeepL::Requests::StyleRule::Create.new(@api, name, language, options).request
19
22
  end
20
23
 
24
+ # @deprecated Use DeepL::Client#get_style_rule instead.
21
25
  def find(style_id, options = {})
22
26
  DeepL::Requests::StyleRule::Find.new(@api, style_id, options).request
23
27
  end
24
28
 
29
+ # @deprecated Use DeepL::Client#update_style_rule_name instead.
25
30
  def update_name(style_id, name, options = {})
26
31
  DeepL::Requests::StyleRule::Update.new(@api, style_id, name, options).request
27
32
  end
28
33
 
34
+ # @deprecated Use DeepL::Client#delete_style_rule instead.
29
35
  def destroy(style_id, options = {})
30
36
  DeepL::Requests::StyleRule::Destroy.new(@api, style_id, options).request
31
37
  end
32
38
 
39
+ # @deprecated Use DeepL::Client#update_style_rule_configured_rules instead.
33
40
  def update_configured_rules(style_id, configured_rules, options = {})
34
41
  DeepL::Requests::StyleRule::UpdateConfiguredRules.new(@api, style_id, configured_rules,
35
42
  options).request
36
43
  end
37
44
 
45
+ # @deprecated Use DeepL::Client#create_style_rule_custom_instruction instead.
38
46
  def create_custom_instruction(style_id, label, prompt, source_language = nil, options = {})
39
47
  DeepL::Requests::StyleRule::CreateCustomInstruction.new(@api, style_id, label, prompt,
40
48
  source_language, options).request
41
49
  end
42
50
 
51
+ # @deprecated Use DeepL::Client#get_style_rule_custom_instruction instead.
43
52
  def find_custom_instruction(style_id, instruction_id, options = {})
44
53
  DeepL::Requests::StyleRule::FindCustomInstruction.new(@api, style_id, instruction_id,
45
54
  options).request
46
55
  end
47
56
 
57
+ # @deprecated Use DeepL::Client#update_style_rule_custom_instruction instead.
48
58
  def update_custom_instruction(style_id, instruction_id, label,
49
59
  prompt, source_language = nil,
50
60
  options = {})
@@ -53,6 +63,7 @@ module DeepL
53
63
  options).request
54
64
  end
55
65
 
66
+ # @deprecated Use DeepL::Client#delete_style_rule_custom_instruction instead.
56
67
  def destroy_custom_instruction(style_id, instruction_id, options = {})
57
68
  DeepL::Requests::StyleRule::DestroyCustomInstruction.new(@api, style_id, instruction_id,
58
69
  options).request