translation_diff 1.0.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 +7 -0
- data/.github/workflows/ci.yml +84 -0
- data/.github/workflows/release.yml +28 -0
- data/.gitignore +11 -0
- data/.rubocop.yml +39 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +723 -0
- data/Gemfile +61 -0
- data/LICENSE.txt +21 -0
- data/README.md +158 -0
- data/Rakefile +41 -0
- data/data/languages/azure.json +285 -0
- data/data/languages/deepl.json +220 -0
- data/data/languages/google.json +399 -0
- data/data/languages/modernmt.json +413 -0
- data/docs/caching.md +185 -0
- data/docs/configuration.md +205 -0
- data/docs/contracts.md +187 -0
- data/docs/development.md +34 -0
- data/docs/errors.md +92 -0
- data/docs/how-it-works.md +145 -0
- data/docs/instrumentation.md +96 -0
- data/docs/languages.md +94 -0
- data/docs/providers.md +379 -0
- data/docs/sql-cache.md +366 -0
- data/lib/generators/translation_diff/install_generator.rb +15 -0
- data/lib/generators/translation_diff/templates/create_translation_diff_tables.rb.erb +24 -0
- data/lib/translation_diff/active_record/support.rb +34 -0
- data/lib/translation_diff/active_record.rb +3 -0
- data/lib/translation_diff/batch.rb +98 -0
- data/lib/translation_diff/call_preparation.rb +47 -0
- data/lib/translation_diff/capabilities.rb +9 -0
- data/lib/translation_diff/configuration/cache_guard_options.rb +39 -0
- data/lib/translation_diff/configuration/cache_ttl_option.rb +30 -0
- data/lib/translation_diff/configuration/option_table.rb +38 -0
- data/lib/translation_diff/configuration/provider_option_owners.rb +40 -0
- data/lib/translation_diff/configuration.rb +135 -0
- data/lib/translation_diff/context.rb +23 -0
- data/lib/translation_diff/dispatcher.rb +57 -0
- data/lib/translation_diff/document.rb +23 -0
- data/lib/translation_diff/errors.rb +44 -0
- data/lib/translation_diff/fragment.rb +33 -0
- data/lib/translation_diff/http_provider.rb +128 -0
- data/lib/translation_diff/instrumentation.rb +25 -0
- data/lib/translation_diff/languages/refresh.rb +70 -0
- data/lib/translation_diff/languages/set.rb +53 -0
- data/lib/translation_diff/languages.rb +30 -0
- data/lib/translation_diff/leaves.rb +22 -0
- data/lib/translation_diff/markup.rb +85 -0
- data/lib/translation_diff/passage.rb +149 -0
- data/lib/translation_diff/preview.rb +3 -0
- data/lib/translation_diff/previewer.rb +78 -0
- data/lib/translation_diff/provider.rb +91 -0
- data/lib/translation_diff/providers/amazon.rb +126 -0
- data/lib/translation_diff/providers/azure.rb +78 -0
- data/lib/translation_diff/providers/deepl.rb +88 -0
- data/lib/translation_diff/providers/google.rb +64 -0
- data/lib/translation_diff/providers/libretranslate.rb +65 -0
- data/lib/translation_diff/providers/modernmt.rb +74 -0
- data/lib/translation_diff/providers/null.rb +20 -0
- data/lib/translation_diff/providers.rb +69 -0
- data/lib/translation_diff/railtie.rb +12 -0
- data/lib/translation_diff/rate_limiters/active_record.rb +92 -0
- data/lib/translation_diff/rate_limiters/redis.rb +59 -0
- data/lib/translation_diff/rate_limiters.rb +9 -0
- data/lib/translation_diff/redaction.rb +45 -0
- data/lib/translation_diff/registry.rb +31 -0
- data/lib/translation_diff/segment.rb +32 -0
- data/lib/translation_diff/segmenters/pragmatic.rb +102 -0
- data/lib/translation_diff/segmenters/simple.rb +122 -0
- data/lib/translation_diff/segmenters.rb +4 -0
- data/lib/translation_diff/sentence_cache.rb +76 -0
- data/lib/translation_diff/stores/active_record.rb +106 -0
- data/lib/translation_diff/stores/memory.rb +34 -0
- data/lib/translation_diff/stores/redis.rb +49 -0
- data/lib/translation_diff/stores.rb +9 -0
- data/lib/translation_diff/tasks/translation_diff.rake +21 -0
- data/lib/translation_diff/translation/request.rb +8 -0
- data/lib/translation_diff/translation/response.rb +35 -0
- data/lib/translation_diff/translation/usage.rb +8 -0
- data/lib/translation_diff/translator.rb +103 -0
- data/lib/translation_diff/version.rb +3 -0
- data/lib/translation_diff.rb +93 -0
- data/translation_diff.gemspec +56 -0
- metadata +243 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# One translation, coordinated: each step hands its value to the next, and a translation rides home on its own segment.
|
|
2
|
+
class TranslationDiff::Translator
|
|
3
|
+
# Its own class, so rescuing a mis-driven translator cannot also swallow a provider or a cache failure.
|
|
4
|
+
class Error < TranslationDiff::Error; end
|
|
5
|
+
|
|
6
|
+
include TranslationDiff::Instrumentation
|
|
7
|
+
include TranslationDiff::CallPreparation
|
|
8
|
+
|
|
9
|
+
attr_reader :config
|
|
10
|
+
|
|
11
|
+
# `provider:`, `config:` and `assume_supported:` are reserved; every other keyword reaches the provider untouched.
|
|
12
|
+
def initialize(values, from: nil, to: nil, provider: nil, config: nil, assume_supported: false, **options)
|
|
13
|
+
raise ArgumentError, "a translation needs a target language: pass `to:` a language code." if to.nil?
|
|
14
|
+
|
|
15
|
+
@values = values
|
|
16
|
+
@from = from
|
|
17
|
+
@to = to
|
|
18
|
+
@options = options
|
|
19
|
+
@config = config || TranslationDiff.config
|
|
20
|
+
@requested_provider = provider
|
|
21
|
+
@assume_supported = assume_supported
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Hands back the caller's value untouched unless something in it was actually translated.
|
|
25
|
+
def call
|
|
26
|
+
document = TranslationDiff::Document.new(TranslationDiff::Leaves.collapse_nils(@values))
|
|
27
|
+
passages = document.strings.map { |string| passage(string) }
|
|
28
|
+
segments = passages.flat_map(&:segments).reject(&:empty?)
|
|
29
|
+
return @values if segments.empty?
|
|
30
|
+
return @values if same_language?(@from)
|
|
31
|
+
|
|
32
|
+
provider = resolve_provider
|
|
33
|
+
from = resolve_source_language(provider, segments)
|
|
34
|
+
return @values if same_language?(from)
|
|
35
|
+
|
|
36
|
+
translated(document, passages, segments, provider, from)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# `from:` given means the whole pair is already known, so it is validated once, up front. `from:` nil means
|
|
42
|
+
# the target alone is checked before a possibly billed #detect runs, and the full pair only once it answers.
|
|
43
|
+
def resolve_source_language(provider, segments)
|
|
44
|
+
return @from.tap { |from| ensure_supported!(provider, from) } unless @from.nil?
|
|
45
|
+
|
|
46
|
+
ensure_supported!(provider, nil)
|
|
47
|
+
source_language(provider, segments).tap { |from| ensure_supported!(provider, from) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The `translate` event wraps everything a call that reaches a provider does, and nothing an early return does.
|
|
51
|
+
def translated(document, passages, segments, provider, from)
|
|
52
|
+
values = TranslationDiff::Leaves.count(@values)
|
|
53
|
+
characters = segments.sum { |segment| segment.core.size }
|
|
54
|
+
payload = { call_id: call_id, from: from.to_s, to: @to.to_s, provider: provider.cache_key,
|
|
55
|
+
values: values, characters: characters }
|
|
56
|
+
instrument("translate", payload) do
|
|
57
|
+
fill(provider, segments, from)
|
|
58
|
+
rebuild(document, passages)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Opaque and short: a correlation key for this call's own events, generated once, never derived from the text.
|
|
63
|
+
def call_id = @call_id ||= SecureRandom.hex(6)
|
|
64
|
+
|
|
65
|
+
# Resolved at first use, never in the constructor: a value with nothing to translate needs no provider at all.
|
|
66
|
+
def resolve_provider
|
|
67
|
+
TranslationDiff::Providers.resolve(@requested_provider, config).tap { |provider| log("provider #{provider.class}") }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# The strings walk and the map walk visit the same leaves in the same order, and the value itself was never touched.
|
|
71
|
+
def rebuild(document, passages)
|
|
72
|
+
rendered = passages.map(&:render)
|
|
73
|
+
document.map { rendered.shift }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Detection is attempted only where it is declared: every provider inherits a #detect that raises.
|
|
77
|
+
def source_language(provider, segments)
|
|
78
|
+
return @from unless @from.nil?
|
|
79
|
+
|
|
80
|
+
ensure_detects_language!(provider, Error, "translating")
|
|
81
|
+
provider.detect(segments.first.core)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# The cache answers for what it has, the provider for the rest, and only what came back is written home.
|
|
85
|
+
def fill(provider, segments, from)
|
|
86
|
+
cache = cache_for(provider, from)
|
|
87
|
+
misses = cache.fill(segments)
|
|
88
|
+
id = call_id
|
|
89
|
+
instrument("cache", call_id: id, provider: provider.cache_key,
|
|
90
|
+
hits: segments.size - misses.size, misses: misses.size)
|
|
91
|
+
TranslationDiff::Dispatcher.new(provider: provider, from: from, to: @to, options: @options,
|
|
92
|
+
config: config, call_id: id).dispatch(misses)
|
|
93
|
+
store(cache, misses, provider)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# A translation already paid for at the provider must reach the caller even if writing it back never does.
|
|
97
|
+
def store(cache, misses, provider)
|
|
98
|
+
cache.store(misses)
|
|
99
|
+
rescue StandardError => e
|
|
100
|
+
warn_log("cache write failed (#{e.class}), the translation is returned uncached")
|
|
101
|
+
instrument("cache_error", call_id: call_id, provider: provider.cache_key, error: e.class.to_s)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require "cgi/escape"
|
|
2
|
+
require "digest/md5"
|
|
3
|
+
require "digest/sha2"
|
|
4
|
+
require "forwardable"
|
|
5
|
+
require "securerandom"
|
|
6
|
+
require "stringio"
|
|
7
|
+
|
|
8
|
+
require "ox"
|
|
9
|
+
|
|
10
|
+
require "translation_diff/version"
|
|
11
|
+
require "translation_diff/errors"
|
|
12
|
+
require "translation_diff/redaction"
|
|
13
|
+
require "translation_diff/capabilities"
|
|
14
|
+
require "json"
|
|
15
|
+
require "translation_diff/languages"
|
|
16
|
+
require "translation_diff/languages/set"
|
|
17
|
+
require "translation_diff/languages/refresh"
|
|
18
|
+
require "translation_diff/translation/usage"
|
|
19
|
+
require "translation_diff/translation/request"
|
|
20
|
+
require "translation_diff/translation/response"
|
|
21
|
+
require "translation_diff/registry"
|
|
22
|
+
require "translation_diff/document"
|
|
23
|
+
require "translation_diff/leaves"
|
|
24
|
+
require "translation_diff/markup"
|
|
25
|
+
require "translation_diff/segment"
|
|
26
|
+
require "translation_diff/batch"
|
|
27
|
+
require "translation_diff/fragment"
|
|
28
|
+
require "translation_diff/passage"
|
|
29
|
+
require "translation_diff/sentence_cache"
|
|
30
|
+
require "translation_diff/configuration"
|
|
31
|
+
require "translation_diff/configuration/provider_option_owners"
|
|
32
|
+
|
|
33
|
+
require "translation_diff/provider"
|
|
34
|
+
require "translation_diff/http_provider"
|
|
35
|
+
require "translation_diff/providers"
|
|
36
|
+
require "translation_diff/providers/null"
|
|
37
|
+
|
|
38
|
+
require "translation_diff/providers/deepl"
|
|
39
|
+
require "translation_diff/providers/google"
|
|
40
|
+
require "translation_diff/providers/azure"
|
|
41
|
+
require "translation_diff/providers/modernmt"
|
|
42
|
+
require "translation_diff/providers/libretranslate"
|
|
43
|
+
require "translation_diff/providers/amazon"
|
|
44
|
+
|
|
45
|
+
require "translation_diff/segmenters"
|
|
46
|
+
require "translation_diff/segmenters/simple"
|
|
47
|
+
require "translation_diff/segmenters/pragmatic"
|
|
48
|
+
require "translation_diff/stores"
|
|
49
|
+
require "translation_diff/stores/memory"
|
|
50
|
+
require "translation_diff/stores/redis"
|
|
51
|
+
require "translation_diff/active_record"
|
|
52
|
+
require "translation_diff/active_record/support"
|
|
53
|
+
require "translation_diff/stores/active_record"
|
|
54
|
+
require "translation_diff/rate_limiters"
|
|
55
|
+
require "translation_diff/rate_limiters/redis"
|
|
56
|
+
require "translation_diff/rate_limiters/active_record"
|
|
57
|
+
require "translation_diff/instrumentation"
|
|
58
|
+
require "translation_diff/call_preparation"
|
|
59
|
+
require "translation_diff/dispatcher"
|
|
60
|
+
require "translation_diff/translator"
|
|
61
|
+
require "translation_diff/preview"
|
|
62
|
+
require "translation_diff/previewer"
|
|
63
|
+
require "translation_diff/context"
|
|
64
|
+
|
|
65
|
+
# Only when a host application has already loaded Rails -- never required unconditionally, so a non-Rails
|
|
66
|
+
# application never pays for it, and the gem's own suite exercises this same guarded require, not a shortcut.
|
|
67
|
+
require "translation_diff/railtie" if defined?(Rails::Railtie)
|
|
68
|
+
|
|
69
|
+
module TranslationDiff
|
|
70
|
+
class << self
|
|
71
|
+
def config = @config ||= Configuration.new
|
|
72
|
+
|
|
73
|
+
def configure = yield(config)
|
|
74
|
+
|
|
75
|
+
# Without this, one test's configuration leaks into every test that runs after it.
|
|
76
|
+
def reset! = @config = nil
|
|
77
|
+
|
|
78
|
+
# An isolated copy of the configuration with the same entry point, for per-tenant settings.
|
|
79
|
+
def context(&) = Context.new(config.copy.tap(&))
|
|
80
|
+
|
|
81
|
+
# `provider:`, `config:` and `assume_supported:` are reserved; every other keyword is forwarded to the provider.
|
|
82
|
+
def translate(values, from: nil, to: nil, provider: nil, assume_supported: false, **)
|
|
83
|
+
Translator.new(values, from: from, to: to, provider: provider, config: config,
|
|
84
|
+
assume_supported: assume_supported, **).call
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Answers what `translate` would do to `values`, without calling a provider or writing anything.
|
|
88
|
+
def preview(values, from: nil, to: nil, provider: nil, assume_supported: false, **)
|
|
89
|
+
Previewer.new(values, from: from, to: to, provider: provider, config: config,
|
|
90
|
+
assume_supported: assume_supported, **).call
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require "translation_diff/version"
|
|
4
|
+
|
|
5
|
+
# rubocop:disable-next Metrics/BlockLength
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "translation_diff"
|
|
8
|
+
spec.version = TranslationDiff::VERSION
|
|
9
|
+
spec.authors = ["Islam Gagiev"]
|
|
10
|
+
spec.email = ["omniacinis@gmail.com"]
|
|
11
|
+
|
|
12
|
+
spec.summary = "A translation cache that sends only new or changed sentences to your provider."
|
|
13
|
+
spec.description = %(
|
|
14
|
+
TranslationDiff extracts translatable text from HTML, splits it into
|
|
15
|
+
sentences, and caches each sentence by content hash. Only the sentences
|
|
16
|
+
missing from the cache are sent to whichever translation provider you plug
|
|
17
|
+
in through a small provider contract, so re-translating a long text after a
|
|
18
|
+
small edit costs the price of the edit, not the whole text.
|
|
19
|
+
)
|
|
20
|
+
spec.homepage = "https://github.com/Halvanhelv/translation_diff"
|
|
21
|
+
spec.license = "MIT"
|
|
22
|
+
spec.required_ruby_version = ">= 3.4"
|
|
23
|
+
|
|
24
|
+
if spec.respond_to?(:metadata)
|
|
25
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
26
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
27
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
28
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
29
|
+
else
|
|
30
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
|
31
|
+
"public gem pushes."
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
35
|
+
f.match(%r{^(test|spec|features)/})
|
|
36
|
+
end
|
|
37
|
+
spec.bindir = "exe"
|
|
38
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
39
|
+
spec.require_paths = ["lib"]
|
|
40
|
+
|
|
41
|
+
spec.add_development_dependency "minitest", "~> 6.0"
|
|
42
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
43
|
+
spec.add_development_dependency "rubocop", "~> 1.90"
|
|
44
|
+
spec.add_development_dependency "simplecov", "~> 1.2"
|
|
45
|
+
|
|
46
|
+
# `ox` walks HTML; `pragmatic_segmenter` backs the default sentence
|
|
47
|
+
# segmenter and has zero dependencies of its own; `faraday` and
|
|
48
|
+
# `faraday-retry` are the HTTP transport every REST provider inherits.
|
|
49
|
+
# Everything else -- the connection pool, and whatever backs the cache and
|
|
50
|
+
# the rate limiter -- is supplied by the application and duck typed, so it
|
|
51
|
+
# stays out of the gemspec.
|
|
52
|
+
spec.add_dependency "faraday", "~> 2.9"
|
|
53
|
+
spec.add_dependency "faraday-retry", "~> 2.2"
|
|
54
|
+
spec.add_dependency "ox", "~> 2.14"
|
|
55
|
+
spec.add_dependency "pragmatic_segmenter", "~> 0.3"
|
|
56
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: translation_diff
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Islam Gagiev
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: minitest
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '6.0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '6.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rake
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '13.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '13.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rubocop
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.90'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.90'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: simplecov
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.2'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '1.2'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: faraday
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '2.9'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '2.9'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: faraday-retry
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '2.2'
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '2.2'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: ox
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '2.14'
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - "~>"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '2.14'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: pragmatic_segmenter
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - "~>"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0.3'
|
|
117
|
+
type: :runtime
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - "~>"
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0.3'
|
|
124
|
+
description: "\nTranslationDiff extracts translatable text from HTML, splits it into\nsentences,
|
|
125
|
+
and caches each sentence by content hash. Only the sentences\nmissing from the cache
|
|
126
|
+
are sent to whichever translation provider you plug\nin through a small provider
|
|
127
|
+
contract, so re-translating a long text after a\nsmall edit costs the price of the
|
|
128
|
+
edit, not the whole text.\n "
|
|
129
|
+
email:
|
|
130
|
+
- omniacinis@gmail.com
|
|
131
|
+
executables: []
|
|
132
|
+
extensions: []
|
|
133
|
+
extra_rdoc_files: []
|
|
134
|
+
files:
|
|
135
|
+
- ".github/workflows/ci.yml"
|
|
136
|
+
- ".github/workflows/release.yml"
|
|
137
|
+
- ".gitignore"
|
|
138
|
+
- ".rubocop.yml"
|
|
139
|
+
- ".ruby-version"
|
|
140
|
+
- CHANGELOG.md
|
|
141
|
+
- Gemfile
|
|
142
|
+
- LICENSE.txt
|
|
143
|
+
- README.md
|
|
144
|
+
- Rakefile
|
|
145
|
+
- data/languages/azure.json
|
|
146
|
+
- data/languages/deepl.json
|
|
147
|
+
- data/languages/google.json
|
|
148
|
+
- data/languages/modernmt.json
|
|
149
|
+
- docs/caching.md
|
|
150
|
+
- docs/configuration.md
|
|
151
|
+
- docs/contracts.md
|
|
152
|
+
- docs/development.md
|
|
153
|
+
- docs/errors.md
|
|
154
|
+
- docs/how-it-works.md
|
|
155
|
+
- docs/instrumentation.md
|
|
156
|
+
- docs/languages.md
|
|
157
|
+
- docs/providers.md
|
|
158
|
+
- docs/sql-cache.md
|
|
159
|
+
- lib/generators/translation_diff/install_generator.rb
|
|
160
|
+
- lib/generators/translation_diff/templates/create_translation_diff_tables.rb.erb
|
|
161
|
+
- lib/translation_diff.rb
|
|
162
|
+
- lib/translation_diff/active_record.rb
|
|
163
|
+
- lib/translation_diff/active_record/support.rb
|
|
164
|
+
- lib/translation_diff/batch.rb
|
|
165
|
+
- lib/translation_diff/call_preparation.rb
|
|
166
|
+
- lib/translation_diff/capabilities.rb
|
|
167
|
+
- lib/translation_diff/configuration.rb
|
|
168
|
+
- lib/translation_diff/configuration/cache_guard_options.rb
|
|
169
|
+
- lib/translation_diff/configuration/cache_ttl_option.rb
|
|
170
|
+
- lib/translation_diff/configuration/option_table.rb
|
|
171
|
+
- lib/translation_diff/configuration/provider_option_owners.rb
|
|
172
|
+
- lib/translation_diff/context.rb
|
|
173
|
+
- lib/translation_diff/dispatcher.rb
|
|
174
|
+
- lib/translation_diff/document.rb
|
|
175
|
+
- lib/translation_diff/errors.rb
|
|
176
|
+
- lib/translation_diff/fragment.rb
|
|
177
|
+
- lib/translation_diff/http_provider.rb
|
|
178
|
+
- lib/translation_diff/instrumentation.rb
|
|
179
|
+
- lib/translation_diff/languages.rb
|
|
180
|
+
- lib/translation_diff/languages/refresh.rb
|
|
181
|
+
- lib/translation_diff/languages/set.rb
|
|
182
|
+
- lib/translation_diff/leaves.rb
|
|
183
|
+
- lib/translation_diff/markup.rb
|
|
184
|
+
- lib/translation_diff/passage.rb
|
|
185
|
+
- lib/translation_diff/preview.rb
|
|
186
|
+
- lib/translation_diff/previewer.rb
|
|
187
|
+
- lib/translation_diff/provider.rb
|
|
188
|
+
- lib/translation_diff/providers.rb
|
|
189
|
+
- lib/translation_diff/providers/amazon.rb
|
|
190
|
+
- lib/translation_diff/providers/azure.rb
|
|
191
|
+
- lib/translation_diff/providers/deepl.rb
|
|
192
|
+
- lib/translation_diff/providers/google.rb
|
|
193
|
+
- lib/translation_diff/providers/libretranslate.rb
|
|
194
|
+
- lib/translation_diff/providers/modernmt.rb
|
|
195
|
+
- lib/translation_diff/providers/null.rb
|
|
196
|
+
- lib/translation_diff/railtie.rb
|
|
197
|
+
- lib/translation_diff/rate_limiters.rb
|
|
198
|
+
- lib/translation_diff/rate_limiters/active_record.rb
|
|
199
|
+
- lib/translation_diff/rate_limiters/redis.rb
|
|
200
|
+
- lib/translation_diff/redaction.rb
|
|
201
|
+
- lib/translation_diff/registry.rb
|
|
202
|
+
- lib/translation_diff/segment.rb
|
|
203
|
+
- lib/translation_diff/segmenters.rb
|
|
204
|
+
- lib/translation_diff/segmenters/pragmatic.rb
|
|
205
|
+
- lib/translation_diff/segmenters/simple.rb
|
|
206
|
+
- lib/translation_diff/sentence_cache.rb
|
|
207
|
+
- lib/translation_diff/stores.rb
|
|
208
|
+
- lib/translation_diff/stores/active_record.rb
|
|
209
|
+
- lib/translation_diff/stores/memory.rb
|
|
210
|
+
- lib/translation_diff/stores/redis.rb
|
|
211
|
+
- lib/translation_diff/tasks/translation_diff.rake
|
|
212
|
+
- lib/translation_diff/translation/request.rb
|
|
213
|
+
- lib/translation_diff/translation/response.rb
|
|
214
|
+
- lib/translation_diff/translation/usage.rb
|
|
215
|
+
- lib/translation_diff/translator.rb
|
|
216
|
+
- lib/translation_diff/version.rb
|
|
217
|
+
- translation_diff.gemspec
|
|
218
|
+
homepage: https://github.com/Halvanhelv/translation_diff
|
|
219
|
+
licenses:
|
|
220
|
+
- MIT
|
|
221
|
+
metadata:
|
|
222
|
+
allowed_push_host: https://rubygems.org
|
|
223
|
+
homepage_uri: https://github.com/Halvanhelv/translation_diff
|
|
224
|
+
source_code_uri: https://github.com/Halvanhelv/translation_diff
|
|
225
|
+
rubygems_mfa_required: 'true'
|
|
226
|
+
rdoc_options: []
|
|
227
|
+
require_paths:
|
|
228
|
+
- lib
|
|
229
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
230
|
+
requirements:
|
|
231
|
+
- - ">="
|
|
232
|
+
- !ruby/object:Gem::Version
|
|
233
|
+
version: '3.4'
|
|
234
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
235
|
+
requirements:
|
|
236
|
+
- - ">="
|
|
237
|
+
- !ruby/object:Gem::Version
|
|
238
|
+
version: '0'
|
|
239
|
+
requirements: []
|
|
240
|
+
rubygems_version: 3.6.9
|
|
241
|
+
specification_version: 4
|
|
242
|
+
summary: A translation cache that sends only new or changed sentences to your provider.
|
|
243
|
+
test_files: []
|