deepl_diff 2.0.0 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca22132db1cfc83ab8e56a9a17bd635ca3648e9d7fde32a2df13dce21991f946
4
- data.tar.gz: '018f598439a77db2e229d2c95f7b8ff0c14c0f17ba7130e19cbdf3f897b716a6'
3
+ metadata.gz: 2c71f2ab36a0f279252d4a42bdd0fc915b51668c87ab05279474598487d19e74
4
+ data.tar.gz: bf24b7de677ac350e1fbe174409442626ff45101a57a04909494e46f36c0a5d5
5
5
  SHA512:
6
- metadata.gz: 0144f0c5d615c25036d0f2f2b41238667e6ab045442714e45235c8192f49c70fdeeb92753c6e7ec5f0e1189de3064191bdbb2014108a5715942ee1455f6ae76f
7
- data.tar.gz: 71a0a99b3f5869bec1f2383f8d212c6ead9bd63f9e5eb5a9f8ad98ed2aa93f0701d5775b9de3764c21aad7ac96f11afe2a506e674056f9a97a80ed5d357c65b6
6
+ metadata.gz: 14e9475cad58ca2114b1849a442b2f01cbb958a2413bd86603c29fddc07cb5d815e8bbb342608b77dc353b1a7899f15b6d447a36a30cd0ef9c591aa726c6dd3d
7
+ data.tar.gz: 3fb543ab7c6cd3111bcd0ddf4843cc1b8d8ec6e4f0f35b18c8c85fda2be590641e2bd2ff3679a3150ca8dd3f36bcd5db47536a1bc1675ec5c6fcc6f7ac84010a
@@ -3,7 +3,7 @@
3
3
  class DeepLDiff::Chunker
4
4
  class Error < StandardError; end
5
5
 
6
- Chunk = Struct.new(:texts, :bytesize)
6
+ Chunk = Struct.new(:texts, :escaped_size)
7
7
 
8
8
  MAX_CHUNK_SIZE = 1700
9
9
  COUNT_LIMIT = 300
@@ -39,20 +39,25 @@ class DeepLDiff::Chunker
39
39
 
40
40
  def next_chunk?(tail, value)
41
41
  tail.nil? ||
42
- (size(value) + tail.bytesize > limit) ||
43
- tail.texts.size > count_limit
42
+ (escaped_size(value) + tail.escaped_size > limit) ||
43
+ tail.texts.size >= count_limit
44
44
  end
45
45
 
46
- def size(text)
46
+ # What the limit is about is the size of the request that goes over the
47
+ # wire, so every measurement here is of the escaped form. Mixing it with
48
+ # String#size lets a chunk of non-ASCII text run several times over.
49
+ def escaped_size(text)
47
50
  CGI.escape(text).size
48
51
  end
49
52
 
50
53
  def update_chunk(chunk, value)
51
54
  chunk.texts << value
52
- chunk.bytesize += value.size
55
+ chunk.escaped_size += escaped_size(value)
53
56
  end
54
57
 
55
58
  def validate_value_size(value)
56
- raise Error, "Too long part #{value.size} > #{limit}" if value.size > limit
59
+ size = escaped_size(value)
60
+
61
+ raise Error, "Too long part #{size} > #{limit}" if size > limit
57
62
  end
58
63
  end
@@ -3,18 +3,22 @@
3
3
  class DeepLDiff::Request
4
4
  extend Forwardable
5
5
 
6
+ class Error < StandardError; end
7
+
6
8
  def_delegators :DeepLDiff, :api, :cache_store, :rate_limiter
7
9
  def_delegators :"DeepLDiff::Linearizer", :linearize, :restore
8
10
 
9
11
  def initialize(values, options)
10
12
  @values = values
11
- @options = options
13
+ # #from and #to consume their keys so the rest can go to the API as-is.
14
+ # Copy first: the caller's hash is theirs, and it is often frozen.
15
+ @options = options.dup
12
16
  end
13
17
 
14
18
  def call
15
19
  validate_globals
16
20
 
17
- return values if from == to || values.empty?
21
+ return values if same_language? || nothing_to_translate?
18
22
 
19
23
  translation
20
24
  end
@@ -31,6 +35,19 @@ class DeepLDiff::Request
31
35
  @to ||= options.delete(:to) { nil }
32
36
  end
33
37
 
38
+ # A detected language arrives as a String while :to is usually a Symbol, so
39
+ # the two have to be compared on equal footing or the short circuit never
40
+ # fires and the text gets translated into its own language.
41
+ def same_language?
42
+ !to.nil? && from.to_s.casecmp?(to.to_s)
43
+ end
44
+
45
+ # Covers values holding no translatable text at all: "", nil, an empty
46
+ # collection, or a scalar the tokenizer has nothing to say about.
47
+ def nothing_to_translate?
48
+ text_tokens_texts.all?(&:empty?)
49
+ end
50
+
34
51
  def detect_language
35
52
  api.translate(text_tokens_texts.join(" ")[0..100], nil, to)
36
53
  .detected_source_language.downcase
@@ -126,7 +143,12 @@ class DeepLDiff::Request
126
143
  # Restores texts from tokens
127
144
  # [..., "<b>Horoshiy</b> Malchik", ...]
128
145
  def texts_translated
129
- @texts_translated ||= tokens_translated.map do |group|
146
+ @texts_translated ||= tokens_translated.map.with_index do |group, index|
147
+ source = texts[index]
148
+ # Only strings are rebuilt from tokens. Anything else has no tokens to
149
+ # rebuild from; nil keeps collapsing to "" the way it always has.
150
+ next source unless source.nil? || source.is_a?(String)
151
+
130
152
  group.map { |value, type| type == :text ? value : fix_ascii(value) }.join
131
153
  end
132
154
  end
@@ -138,7 +160,13 @@ class DeepLDiff::Request
138
160
 
139
161
  def call_api(values)
140
162
  check_rate_limit(values)
141
- [api.translate(values, from, to, options)].flatten.map(&:text)
163
+ translations = [api.translate(values, from, to, options)].flatten.map(&:text)
164
+ return translations if translations.size == values.size
165
+
166
+ # Letting a short response through means shifting nils into the results,
167
+ # which surfaces much later as a NoMethodError far from the cause.
168
+ raise Error,
169
+ "API returned #{translations.size} translations for #{values.size} values"
142
170
  end
143
171
 
144
172
  def cache
@@ -149,7 +149,8 @@ class DeepLDiff::Tokenizer < Ox::Sax
149
149
 
150
150
  class << self
151
151
  def tokenize(value)
152
- return [] if value.nil?
152
+ # Anything that is not a string has no markup and no sentences in it.
153
+ return [] unless value.is_a?(String)
153
154
 
154
155
  tokenizer = new(value).tap do |h|
155
156
  Ox.sax_parse(h, StringIO.new(value), HTML_OPTIONS)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeepLDiff
4
- VERSION = "2.0.0"
4
+ VERSION = "2.1.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deepl_diff
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Islam Gagiev