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
data/Gemfile
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
source "https://rubygems.org"
|
|
2
|
+
|
|
3
|
+
gemspec
|
|
4
|
+
|
|
5
|
+
# Not runtime dependencies of the gem (see the gemspec) -- Configuration#
|
|
6
|
+
# redis_pool requires them lazily, and Stores::Redis/RateLimiters::Redis
|
|
7
|
+
# duck-type against whatever a caller's connection pool yields. They are
|
|
8
|
+
# only here so the test suite, which builds real pools against these
|
|
9
|
+
# classes, has them available.
|
|
10
|
+
gem "connection_pool", "~> 2.4", require: false
|
|
11
|
+
gem "redis", "~> 5.0", require: false
|
|
12
|
+
gem "redis-namespace", "~> 1.11", require: false
|
|
13
|
+
|
|
14
|
+
# Not a runtime dependency of the gem (see the gemspec) -- RateLimiters::Redis
|
|
15
|
+
# requires it lazily on the first check, so an application that configures no
|
|
16
|
+
# rate limit never needs it installed. It is only here so the test suite,
|
|
17
|
+
# which exercises the limiter against the real Ratelimit class rather than a
|
|
18
|
+
# stand-in, has it available.
|
|
19
|
+
gem "ratelimit", "~> 1.1", require: false
|
|
20
|
+
|
|
21
|
+
# Not a runtime dependency of the gem (see the gemspec) -- lib/ only ever
|
|
22
|
+
# needs CGI.escape, which cgi/escape (in Ruby's default load path) still
|
|
23
|
+
# provides. This is here because the Google provider's test decodes the
|
|
24
|
+
# query string it sent, and Ruby 4.0 removed CGI.parse from the default
|
|
25
|
+
# load path; "install cgi gem" is Ruby's own suggested fix.
|
|
26
|
+
gem "cgi", "~> 0.5", require: false
|
|
27
|
+
|
|
28
|
+
# Not a runtime dependency of the gem (see the gemspec) -- the Amazon
|
|
29
|
+
# provider requires it lazily when it signs its first request, so an
|
|
30
|
+
# application using another provider never needs it installed. It is here so
|
|
31
|
+
# the test suite, which signs against the real library rather than a
|
|
32
|
+
# stand-in, has it available.
|
|
33
|
+
gem "aws-sigv4", "~> 1.12", require: false
|
|
34
|
+
|
|
35
|
+
# Not runtime dependencies of the gem (see the gemspec) -- TranslationDiff::Stores::ActiveRecord
|
|
36
|
+
# and TranslationDiff::RateLimiters::ActiveRecord require active_record lazily on first use, so an
|
|
37
|
+
# application caching in Redis never needs it installed. They are here so the
|
|
38
|
+
# suite can exercise the stores against a real database rather than a stand-in.
|
|
39
|
+
gem "activerecord", "~> 8.1", require: false
|
|
40
|
+
gem "sqlite3", "~> 2.9", require: false
|
|
41
|
+
|
|
42
|
+
# Not a runtime dependency of the gem (see the gemspec) -- only the CI job that
|
|
43
|
+
# sets TRANSLATION_DIFF_DATABASE_URL ever opens a Postgres connection, where
|
|
44
|
+
# the concurrency the SQL store and rate limiter rest on can actually be
|
|
45
|
+
# tested. SQLite has one writer, so most of the suite never needs this gem.
|
|
46
|
+
gem "pg", "~> 1.5", require: false
|
|
47
|
+
|
|
48
|
+
# Not a runtime dependency of the gem (see the gemspec) -- only the CI job that
|
|
49
|
+
# sets TRANSLATION_DIFF_DATABASE_URL to a MySQL database ever opens one, where
|
|
50
|
+
# the missing supports_insert_conflict_target? behaviour actually bites. Trilogy
|
|
51
|
+
# over mysql2: it is a pure Ruby/C socket client with no libmysqlclient headers
|
|
52
|
+
# to install, so it builds on a bare CI runner and on this machine alike.
|
|
53
|
+
gem "trilogy", "~> 2.9", require: false
|
|
54
|
+
|
|
55
|
+
# Not a runtime dependency of the gem (see the gemspec) -- the generator under
|
|
56
|
+
# lib/generators/ is loaded only when Rails loads generators, so a non-Rails
|
|
57
|
+
# application never needs it installed. It is here so
|
|
58
|
+
# test/translation_diff/install_generator_test.rb can load it and check the
|
|
59
|
+
# generated migration against the schema the rest of the suite runs against,
|
|
60
|
+
# instead of skipping itself for want of Rails::Generators::Base.
|
|
61
|
+
gem "railties", "~> 8.1", require: false
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Islam Gagiev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# TranslationDiff
|
|
2
|
+
|
|
3
|
+
A translation cache that helps translate only changes between revisions of long texts.
|
|
4
|
+
|
|
5
|
+
## Why TranslationDiff?
|
|
6
|
+
|
|
7
|
+
Assume your project contains a significant amount of products descriptions which:
|
|
8
|
+
- Require retranslation each time user edits them.
|
|
9
|
+
- Have a lot of equal parts (like return policy).
|
|
10
|
+
- Change frequently.
|
|
11
|
+
|
|
12
|
+
If your user changes a single word within the long description, you will be charged for the retranslation of the whole text.
|
|
13
|
+
|
|
14
|
+
Much better approach is to try to translate every repeated structural element (sentence) in your texts array just once to save money. This gem helps to make it done.
|
|
15
|
+
|
|
16
|
+
## Show me the code
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
# The simplest translation
|
|
20
|
+
TranslationDiff.configure do |config|
|
|
21
|
+
config.deepl_api_key = ENV["DEEPL_API_KEY"]
|
|
22
|
+
config.redis_url = ENV["REDIS_URL"]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
TranslationDiff.translate("Привет.", from: "ru", to: "en")
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
# Nested structures come back the same shape
|
|
30
|
+
TranslationDiff.translate("test", from: "en", to: "es")
|
|
31
|
+
TranslationDiff.translate(%w[test language], from: "en", to: "es")
|
|
32
|
+
TranslationDiff.translate(
|
|
33
|
+
{ title: "test", values: { type: "frequent" } }, from: "en", to: "es"
|
|
34
|
+
)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
# HTML markup is preserved
|
|
39
|
+
TranslationDiff.translate("<b>Black</b>", from: "en", to: "es")
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
# class="notranslate" marks a span to protect (provider support varies -- see the caveats below)
|
|
44
|
+
TranslationDiff.translate(
|
|
45
|
+
'<span class="notranslate">Bold Mountain</span> is a good place.', from: "en", to: "ru"
|
|
46
|
+
)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
# Switch provider for one call, without touching the global configuration
|
|
51
|
+
TranslationDiff.translate(blog_post, from: "en", to: "de", provider: :google)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
# Any keyword other than from:, to:, provider:, config: and assume_supported: is forwarded to
|
|
56
|
+
# the provider. assume_supported: is this library's own decision, not a vendor's, so it's
|
|
57
|
+
# reserved rather than forwarded -- it must never reach a payload.
|
|
58
|
+
TranslationDiff.translate(contract, from: "en", to: "de", formality: :more)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
# An isolated context for its own configuration -- global config.deepl_api_key stays untouched
|
|
63
|
+
formal = TranslationDiff.context do |config|
|
|
64
|
+
config.provider = :deepl
|
|
65
|
+
config.deepl_api_key = ENV["DEEPL_AUTH_KEY"]
|
|
66
|
+
end
|
|
67
|
+
formal.translate(contract, from: "en", to: "de", formality: :more)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
# See what a call would send and find cached, without calling the provider or writing anything
|
|
72
|
+
preview = TranslationDiff.preview(contract, from: "en", to: "de")
|
|
73
|
+
preview.sendable_sentences # => sentences not yet cached
|
|
74
|
+
preview.cached_sentences # => sentences already cached
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
# Redis-backed cache, shared across processes
|
|
79
|
+
TranslationDiff.configure { |config| config.redis_url = ENV["REDIS_URL"] }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
# A rate limit, in characters per interval
|
|
84
|
+
TranslationDiff.configure { |config| config.rate_limit = 100_000 }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
# Instrumentation, through anything satisfying the ActiveSupport::Notifications interface
|
|
89
|
+
TranslationDiff.configure { |config| config.instrumenter = ActiveSupport::Notifications }
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Providers
|
|
93
|
+
|
|
94
|
+
Every provider declares what it can do through
|
|
95
|
+
`TranslationDiff::Capabilities` -- there is nowhere else these numbers live,
|
|
96
|
+
so this table is generated from the same source the library reads at
|
|
97
|
+
runtime:
|
|
98
|
+
|
|
99
|
+
| Provider | `config.provider` | Auth option(s) | Batch size | Request size (escaped chars) | HTML support | `notranslate` | Detects language | Reports billing |
|
|
100
|
+
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
101
|
+
| Null | `:null` | none | 1,000,000 | 1,000,000 | no | no | no | no |
|
|
102
|
+
| DeepL | `:deepl` (default) | `deepl_api_key` | 50 | 1,700 | yes (`tag_handling`) | yes | yes | yes |
|
|
103
|
+
| Google | `:google` | `google_api_key` | 128 | 5,000 | yes (`format`) | yes | yes | no |
|
|
104
|
+
| Azure | `:azure` | `azure_api_key` | 1,000 | 50,000 | yes (`textType`) | yes | yes | yes |
|
|
105
|
+
| ModernMT | `:modernmt` | `modernmt_api_key` | 128 | 5,000 | yes (`format`) | no | yes | yes |
|
|
106
|
+
| LibreTranslate | `:libretranslate` | `libretranslate_api_base` | 50 | 5,000 | yes (`format`) | no | yes | no |
|
|
107
|
+
| Amazon | `:amazon` | `amazon_access_key_id`, `amazon_secret_access_key`, `amazon_region` | 1 | 10,000 | no | no | yes | no |
|
|
108
|
+
|
|
109
|
+
**Amazon:** translates one text per call (no batch form of `TranslateText`) and has no HTML mode, so `notranslate` is not honoured.
|
|
110
|
+
|
|
111
|
+
**LibreTranslate:** preserves markup but does not honour `notranslate` either -- measured against a real instance, not assumed.
|
|
112
|
+
|
|
113
|
+
See [Providers](docs/providers.md) for configuring each one, the full capabilities explanation, and writing your own.
|
|
114
|
+
|
|
115
|
+
## Features
|
|
116
|
+
|
|
117
|
+
- **Content-hash caching:** a small edit to a long document is billed for the edit, not the whole document
|
|
118
|
+
- **Six built-in providers** -- DeepL, Google Cloud Translation, Azure AI Translator, ModernMT, LibreTranslate, Amazon Translate -- or bring your own by subclassing a small base class
|
|
119
|
+
- **HTML aware:** markup is preserved, and `class="notranslate"` can protect a span (provider support varies -- see the caveats below)
|
|
120
|
+
- **Any shape:** strings, arrays, and deep hashes go in and come back translated in the same shape
|
|
121
|
+
- **Three cache stores:** `Stores::Memory` out of the box, `Stores::Redis` once you configure `redis_url`, `Stores::ActiveRecord` to cache in your own database instead -- see [SQL cache](docs/sql-cache.md)
|
|
122
|
+
- **Isolated contexts:** `TranslationDiff.context` for multi-tenant apps and per-request provider overrides, without touching the global configuration
|
|
123
|
+
- **Pluggable sentence segmenter:** `pragmatic_segmenter` by default, with a zero-dependency `Simple` alternative
|
|
124
|
+
- **HTTP retries, timeouts, and backoff** on every REST-backed provider, via `faraday` and `faraday-retry`
|
|
125
|
+
- **One error hierarchy** under `TranslationDiff::Error`, carrying the provider name and HTTP status
|
|
126
|
+
- **Optional rate limiting and instrumentation** -- credentials and translated content never appear in a log line this gem writes (the SQL cache store is the one exception worth knowing before you adopt it -- see [SQL cache](docs/sql-cache.md#what-ends-up-in-your-log))
|
|
127
|
+
|
|
128
|
+
## Installation
|
|
129
|
+
|
|
130
|
+
Ruby 3.4 or newer is required.
|
|
131
|
+
|
|
132
|
+
Add this line to your application's Gemfile:
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
gem 'translation_diff'
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
And then execute:
|
|
139
|
+
|
|
140
|
+
$ bundle
|
|
141
|
+
|
|
142
|
+
Or install it yourself as:
|
|
143
|
+
|
|
144
|
+
$ gem install translation_diff
|
|
145
|
+
|
|
146
|
+
This gem loads `ox`, `pragmatic_segmenter`, `faraday`, and `faraday-retry` at require time. `aws-sigv4`, `activerecord`, `redis`, `connection_pool`, `redis-namespace`, and `ratelimit` are yours to add, only if you use the feature that needs them -- see [Dependencies](docs/configuration.md#dependencies).
|
|
147
|
+
|
|
148
|
+
## Documentation
|
|
149
|
+
|
|
150
|
+
[Configuration](docs/configuration.md) · [Providers](docs/providers.md) · [Languages](docs/languages.md) · [Caching](docs/caching.md) · [SQL cache](docs/sql-cache.md) · [Contracts](docs/contracts.md) · [Instrumentation](docs/instrumentation.md) · [Errors](docs/errors.md) · [How it works](docs/how-it-works.md) · [Upgrading & development](docs/development.md)
|
|
151
|
+
|
|
152
|
+
## Contributing
|
|
153
|
+
|
|
154
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Halvanhelv/translation_diff.
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
Released under the [MIT License](LICENSE.txt).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require "rake/testtask"
|
|
3
|
+
|
|
4
|
+
Rake::TestTask.new(:test) do |t|
|
|
5
|
+
t.libs << "test"
|
|
6
|
+
t.libs << "lib"
|
|
7
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
|
8
|
+
# The dependencies are noisy under -w; their warnings drown out our own.
|
|
9
|
+
t.warning = false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
task default: :test
|
|
13
|
+
|
|
14
|
+
namespace :languages do
|
|
15
|
+
desc "Re-fetch every provider's language lists from its vendor"
|
|
16
|
+
task :refresh do
|
|
17
|
+
require "translation_diff"
|
|
18
|
+
|
|
19
|
+
not_shipped = TranslationDiff::Languages::NOT_SHIPPED
|
|
20
|
+
unless not_shipped.empty?
|
|
21
|
+
puts "not shipping data for #{not_shipped.join(', ')}: a vendor credential or a private instance " \
|
|
22
|
+
"would make the file unshareable"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
skip = [:null, *not_shipped]
|
|
26
|
+
providers = TranslationDiff::Providers.names.reject { |name| skip.include?(name) }.map do |name|
|
|
27
|
+
TranslationDiff::Providers.build(name, TranslationDiff.config)
|
|
28
|
+
rescue TranslationDiff::ConfigurationError => e
|
|
29
|
+
warn "skipping #{name}: #{e.message}"
|
|
30
|
+
nil
|
|
31
|
+
end.compact
|
|
32
|
+
|
|
33
|
+
report = TranslationDiff::Languages::Refresh.call(providers: providers)
|
|
34
|
+
puts "updated: #{report[:updated].join(', ')}" unless report[:updated].empty?
|
|
35
|
+
puts "skipped: #{report[:skipped].join(', ')}" unless report[:skipped].empty?
|
|
36
|
+
report[:failed].each { |name, message| warn "failed: #{name}: #{message}" }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The task itself ships in lib/, so a host application's own `rake` can load it too -- this just reuses it here.
|
|
41
|
+
load File.expand_path("lib/translation_diff/tasks/translation_diff.rake", __dir__)
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provider": "azure",
|
|
3
|
+
"captured_at": "2026-09-10",
|
|
4
|
+
"endpoint": "https://api.cognitive.microsofttranslator.com/languages?api-version=3.0&scope=translation",
|
|
5
|
+
"source": [
|
|
6
|
+
"af",
|
|
7
|
+
"am",
|
|
8
|
+
"ar",
|
|
9
|
+
"as",
|
|
10
|
+
"az",
|
|
11
|
+
"ba",
|
|
12
|
+
"be",
|
|
13
|
+
"bg",
|
|
14
|
+
"bho",
|
|
15
|
+
"bn",
|
|
16
|
+
"bo",
|
|
17
|
+
"brx",
|
|
18
|
+
"bs",
|
|
19
|
+
"ca",
|
|
20
|
+
"cs",
|
|
21
|
+
"cy",
|
|
22
|
+
"da",
|
|
23
|
+
"de",
|
|
24
|
+
"doi",
|
|
25
|
+
"dsb",
|
|
26
|
+
"dv",
|
|
27
|
+
"el",
|
|
28
|
+
"en",
|
|
29
|
+
"es",
|
|
30
|
+
"es-mx",
|
|
31
|
+
"et",
|
|
32
|
+
"eu",
|
|
33
|
+
"fa",
|
|
34
|
+
"fi",
|
|
35
|
+
"fil",
|
|
36
|
+
"fj",
|
|
37
|
+
"fo",
|
|
38
|
+
"fr",
|
|
39
|
+
"fr-ca",
|
|
40
|
+
"ga",
|
|
41
|
+
"gl",
|
|
42
|
+
"gom",
|
|
43
|
+
"gu",
|
|
44
|
+
"ha",
|
|
45
|
+
"he",
|
|
46
|
+
"hi",
|
|
47
|
+
"hne",
|
|
48
|
+
"hr",
|
|
49
|
+
"hsb",
|
|
50
|
+
"ht",
|
|
51
|
+
"hu",
|
|
52
|
+
"hy",
|
|
53
|
+
"id",
|
|
54
|
+
"ig",
|
|
55
|
+
"ikt",
|
|
56
|
+
"is",
|
|
57
|
+
"it",
|
|
58
|
+
"iu",
|
|
59
|
+
"iu-latn",
|
|
60
|
+
"ja",
|
|
61
|
+
"ka",
|
|
62
|
+
"kk",
|
|
63
|
+
"km",
|
|
64
|
+
"kmr",
|
|
65
|
+
"kn",
|
|
66
|
+
"ko",
|
|
67
|
+
"ks",
|
|
68
|
+
"ku",
|
|
69
|
+
"ky",
|
|
70
|
+
"lb",
|
|
71
|
+
"ln",
|
|
72
|
+
"lo",
|
|
73
|
+
"lt",
|
|
74
|
+
"lug",
|
|
75
|
+
"lv",
|
|
76
|
+
"lzh",
|
|
77
|
+
"mai",
|
|
78
|
+
"mg",
|
|
79
|
+
"mi",
|
|
80
|
+
"mk",
|
|
81
|
+
"ml",
|
|
82
|
+
"mn-cyrl",
|
|
83
|
+
"mn-mong",
|
|
84
|
+
"mni",
|
|
85
|
+
"mr",
|
|
86
|
+
"ms",
|
|
87
|
+
"mt",
|
|
88
|
+
"mww",
|
|
89
|
+
"my",
|
|
90
|
+
"nb",
|
|
91
|
+
"ne",
|
|
92
|
+
"nl",
|
|
93
|
+
"nso",
|
|
94
|
+
"nya",
|
|
95
|
+
"or",
|
|
96
|
+
"otq",
|
|
97
|
+
"pa",
|
|
98
|
+
"pl",
|
|
99
|
+
"prs",
|
|
100
|
+
"ps",
|
|
101
|
+
"pt",
|
|
102
|
+
"pt-pt",
|
|
103
|
+
"ro",
|
|
104
|
+
"ru",
|
|
105
|
+
"run",
|
|
106
|
+
"rw",
|
|
107
|
+
"sd",
|
|
108
|
+
"si",
|
|
109
|
+
"sk",
|
|
110
|
+
"sl",
|
|
111
|
+
"sm",
|
|
112
|
+
"sn",
|
|
113
|
+
"so",
|
|
114
|
+
"sq",
|
|
115
|
+
"sr-cyrl",
|
|
116
|
+
"sr-latn",
|
|
117
|
+
"st",
|
|
118
|
+
"sv",
|
|
119
|
+
"sw",
|
|
120
|
+
"ta",
|
|
121
|
+
"te",
|
|
122
|
+
"th",
|
|
123
|
+
"ti",
|
|
124
|
+
"tk",
|
|
125
|
+
"tlh-latn",
|
|
126
|
+
"tlh-piqd",
|
|
127
|
+
"tn",
|
|
128
|
+
"to",
|
|
129
|
+
"tr",
|
|
130
|
+
"tt",
|
|
131
|
+
"ty",
|
|
132
|
+
"ug",
|
|
133
|
+
"uk",
|
|
134
|
+
"ur",
|
|
135
|
+
"uz",
|
|
136
|
+
"vi",
|
|
137
|
+
"xh",
|
|
138
|
+
"yo",
|
|
139
|
+
"yua",
|
|
140
|
+
"yue",
|
|
141
|
+
"zh-hans",
|
|
142
|
+
"zh-hant",
|
|
143
|
+
"zu"
|
|
144
|
+
],
|
|
145
|
+
"target": [
|
|
146
|
+
"af",
|
|
147
|
+
"am",
|
|
148
|
+
"ar",
|
|
149
|
+
"as",
|
|
150
|
+
"az",
|
|
151
|
+
"ba",
|
|
152
|
+
"be",
|
|
153
|
+
"bg",
|
|
154
|
+
"bho",
|
|
155
|
+
"bn",
|
|
156
|
+
"bo",
|
|
157
|
+
"brx",
|
|
158
|
+
"bs",
|
|
159
|
+
"ca",
|
|
160
|
+
"cs",
|
|
161
|
+
"cy",
|
|
162
|
+
"da",
|
|
163
|
+
"de",
|
|
164
|
+
"doi",
|
|
165
|
+
"dsb",
|
|
166
|
+
"dv",
|
|
167
|
+
"el",
|
|
168
|
+
"en",
|
|
169
|
+
"es",
|
|
170
|
+
"es-mx",
|
|
171
|
+
"et",
|
|
172
|
+
"eu",
|
|
173
|
+
"fa",
|
|
174
|
+
"fi",
|
|
175
|
+
"fil",
|
|
176
|
+
"fj",
|
|
177
|
+
"fo",
|
|
178
|
+
"fr",
|
|
179
|
+
"fr-ca",
|
|
180
|
+
"ga",
|
|
181
|
+
"gl",
|
|
182
|
+
"gom",
|
|
183
|
+
"gu",
|
|
184
|
+
"ha",
|
|
185
|
+
"he",
|
|
186
|
+
"hi",
|
|
187
|
+
"hne",
|
|
188
|
+
"hr",
|
|
189
|
+
"hsb",
|
|
190
|
+
"ht",
|
|
191
|
+
"hu",
|
|
192
|
+
"hy",
|
|
193
|
+
"id",
|
|
194
|
+
"ig",
|
|
195
|
+
"ikt",
|
|
196
|
+
"is",
|
|
197
|
+
"it",
|
|
198
|
+
"iu",
|
|
199
|
+
"iu-latn",
|
|
200
|
+
"ja",
|
|
201
|
+
"ka",
|
|
202
|
+
"kk",
|
|
203
|
+
"km",
|
|
204
|
+
"kmr",
|
|
205
|
+
"kn",
|
|
206
|
+
"ko",
|
|
207
|
+
"ks",
|
|
208
|
+
"ku",
|
|
209
|
+
"ky",
|
|
210
|
+
"lb",
|
|
211
|
+
"ln",
|
|
212
|
+
"lo",
|
|
213
|
+
"lt",
|
|
214
|
+
"lug",
|
|
215
|
+
"lv",
|
|
216
|
+
"lzh",
|
|
217
|
+
"mai",
|
|
218
|
+
"mg",
|
|
219
|
+
"mi",
|
|
220
|
+
"mk",
|
|
221
|
+
"ml",
|
|
222
|
+
"mn-cyrl",
|
|
223
|
+
"mn-mong",
|
|
224
|
+
"mni",
|
|
225
|
+
"mr",
|
|
226
|
+
"ms",
|
|
227
|
+
"mt",
|
|
228
|
+
"mww",
|
|
229
|
+
"my",
|
|
230
|
+
"nb",
|
|
231
|
+
"ne",
|
|
232
|
+
"nl",
|
|
233
|
+
"nso",
|
|
234
|
+
"nya",
|
|
235
|
+
"or",
|
|
236
|
+
"otq",
|
|
237
|
+
"pa",
|
|
238
|
+
"pl",
|
|
239
|
+
"prs",
|
|
240
|
+
"ps",
|
|
241
|
+
"pt",
|
|
242
|
+
"pt-pt",
|
|
243
|
+
"ro",
|
|
244
|
+
"ru",
|
|
245
|
+
"run",
|
|
246
|
+
"rw",
|
|
247
|
+
"sd",
|
|
248
|
+
"si",
|
|
249
|
+
"sk",
|
|
250
|
+
"sl",
|
|
251
|
+
"sm",
|
|
252
|
+
"sn",
|
|
253
|
+
"so",
|
|
254
|
+
"sq",
|
|
255
|
+
"sr-cyrl",
|
|
256
|
+
"sr-latn",
|
|
257
|
+
"st",
|
|
258
|
+
"sv",
|
|
259
|
+
"sw",
|
|
260
|
+
"ta",
|
|
261
|
+
"te",
|
|
262
|
+
"th",
|
|
263
|
+
"ti",
|
|
264
|
+
"tk",
|
|
265
|
+
"tlh-latn",
|
|
266
|
+
"tlh-piqd",
|
|
267
|
+
"tn",
|
|
268
|
+
"to",
|
|
269
|
+
"tr",
|
|
270
|
+
"tt",
|
|
271
|
+
"ty",
|
|
272
|
+
"ug",
|
|
273
|
+
"uk",
|
|
274
|
+
"ur",
|
|
275
|
+
"uz",
|
|
276
|
+
"vi",
|
|
277
|
+
"xh",
|
|
278
|
+
"yo",
|
|
279
|
+
"yua",
|
|
280
|
+
"yue",
|
|
281
|
+
"zh-hans",
|
|
282
|
+
"zh-hant",
|
|
283
|
+
"zu"
|
|
284
|
+
]
|
|
285
|
+
}
|