lit 0.4.0.pre.alpha.5 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ab075e2f02a95653a68d3c581adc623792638f8c
4
- data.tar.gz: 3c685b7518c2a055f5592472d679c47ac24fb5a4
2
+ SHA256:
3
+ metadata.gz: ab6fb4274b54171f64b3e24002ee6f2d23645ffa88cb9fb6d9bfe919b95d27bc
4
+ data.tar.gz: 52a3826a8da2dae3c37c08383ebd58e2547a7214e6b2eae4b83f22cfd027c7c8
5
5
  SHA512:
6
- metadata.gz: 91e16c79a8b7f7cef32b20cdbc9ccb880ee3405b08c9ef8afc61adf576d7ccb3572f4c74e4f7ebc9388203f697cd150cb56cac2d49271652d695842d70601ab9
7
- data.tar.gz: 11d656414f64d6ff0c9bddb02d1e4f38a2e8ae2599ef9674e7b12e205437d9f9dd47b9680f172554d5ec43429f680326f94ca7ee62cf169c5e627e9e3570a56d
6
+ metadata.gz: becbc8639368bad9748d0815adf579031a6beb4f79610ae9920f896e5a6d9ae6c39bd071363756d1f937578b75745eed80b4c80805caf58e221f1cdee630481d
7
+ data.tar.gz: 01a66a00855eba89bbfe2057d8792d978e1435ea3a79e986463d0948d771304a96e9ebaf048000a2f2e748a529d5f56b931a04d6413d942ef327fc09bfe8097c
data/README.md CHANGED
@@ -7,7 +7,7 @@ translating app by non-technicals.
7
7
 
8
8
  Highly inspired by Copycopter by thoughtbot.
9
9
 
10
- ![travis status](https://travis-ci.org/prograils/lit.svg)
10
+ [![travis status](https://travis-ci.org/prograils/lit.svg)](https://travis-ci.org/prograils/lit)
11
11
 
12
12
  ### Features
13
13
 
@@ -39,7 +39,7 @@ gem 'lit'
39
39
 
40
40
  3. run installation generator `bundle exec rails g lit:install`
41
41
  (for production/staging environment `redis` is suggested as key value engine. `hash` will not work in multi process environment)
42
-
42
+
43
43
  4. Add `config.i18n.available_locales = [...]` to `application.rb` - it's required to precompile appropriate language flags in lit backend.
44
44
 
45
45
  5. After doing above and restarting app, point your browser to `http://app/lit`
@@ -107,6 +107,79 @@ Keys marked as deleted (i.e. still existing but deleted from the Lit UI) are *no
107
107
 
108
108
  Deleted keys whose translations are encountered during import are restored automatically.
109
109
 
110
+ ### Cloud translation services
111
+
112
+ Lit can use external translation services such as [Google Cloud Translation API](https://cloud.google.com/translate/) and [Yandex.Translate API](https://translate.yandex.com/developers) to tentatively translate localizations to a given language.
113
+ Currently, Google and Yandex translation providers are supported, but extending it to any other translation provider of your choice is as easy as subclassing `Lit::CloudTranslation::Providers::Base`; see classes in `lib/lit/cloud_translation/providers` for reference.
114
+
115
+ #### Usage
116
+
117
+ Configure your translation provider using one of routines described below. When a translation provider is configured, each localization in Lit web UI will have a "Translate using _Provider Name_" button next to it, which by default translates to the localization's language from the localization currently saved for the app's `I18n.default_locale`.
118
+ Next to the button, there is a dropdown that allows translating from the key's localization in a language different than the default one.
119
+
120
+ #### Google Cloud Translation API
121
+
122
+ Insert this into your Lit initializer:
123
+ ```
124
+ require 'lit/cloud_translation/providers/google'
125
+
126
+ Lit::CloudTranslation.provider = Lit::CloudTranslation::Providers::Google
127
+ ```
128
+
129
+ ...and make sure you have this in your Gemfile:
130
+ ```
131
+ gem 'google-cloud-translate'
132
+ ```
133
+
134
+ To use translation via Google, you need to obtain a [service account key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys) containing all the credentials required by the API.
135
+
136
+ These credentials can be given in three ways:
137
+ * via a `.json` keyfile, the path to which should be stored in the `GOOGLE_TRANSLATE_API_KEYFILE` environment variable,
138
+ * programmatically, in the initializer - be sure to use secrets in all the sensitive fields so you don't expose private credentials in the code:
139
+ ```
140
+ Lit::CloudTranslation.configure do |config|
141
+ config.keyfile_hash = {
142
+ 'type' => 'service_account',
143
+ 'project_id' => 'foo',
144
+ 'private_key_id' => 'keyid',
145
+ ... # see Google docs link above for reference
146
+ }
147
+ end
148
+ ```
149
+ * directly via `GOOGLE_TRANSLATE_API_<element>` environment variables, where e.g. the `GOOGLE_TRANSLATE_API_PROJECT_ID` variable corresponds to the `project_id` element of a JSON keyfile. Typically, only the following variables are mandatory:
150
+ * `GOOGLE_TRANSLATE_API_PROJECT_ID`
151
+ * `GOOGLE_TRANSLATE_API_PRIVATE_KEY` (make sure that it contains correct line breaks and markers of the private key's begin and end)
152
+ * `GOOGLE_TRANSLATE_API_CLIENT_EMAIL`
153
+
154
+ #### Yandex.Translate API
155
+
156
+ Insert this into your Lit initializer:
157
+ ```
158
+ require 'lit/cloud_translation/providers/yandex'
159
+
160
+ Lit::CloudTranslation.provider = Lit::CloudTranslation::Providers::Yandex
161
+ ```
162
+
163
+ To use Yandex translation, an [API key must be obtained](https://translate.yandex.com/developers/keys). Then, you can pass it to your application via the `YANDEX_TRANSLATE_API_KEY` environment variable.
164
+
165
+ The API key can also be set programmatically in your Lit initializer (again, be sure to use secrets if you choose to do so):
166
+ ```
167
+ Lit::CloudTranslation.configure do |config|
168
+ config.api_key = 'the_api_key'
169
+ end
170
+ ```
171
+
172
+ ### 0.3 -> 1.0 upgrade guide
173
+
174
+ Also applies to upgrading from `0.4.pre.alpha` versions.
175
+
176
+ 1. Specify `gem 'lit', '~> 1.0'` in your Gemfile and run `bundle update lit`.
177
+ 2. Run Lit migrations - `rails db:migrate`.
178
+ * __Caution:__ One of the new migrations adds a unique index in `lit_localizations` on `(localization_key_id, locale_id)`, which may cause constraint violations in some cases. If you encounter such errors during running this migration - in this case you'll need to enter Rails console and remove duplicates manually. The following query might be helpful to determine duplicate locale/localization key ID pairs:
179
+ ```
180
+ Lit::Localization.group(:locale_id, :localization_key_id).having('count(*) > 1').count
181
+ ```
182
+
110
183
  ### 0.2 -> 0.3 upgrade guide
111
184
 
112
185
  1. Specify exact lit version in your Gemfile: `gem 'lit', '~> 0.3.0'`
@@ -197,10 +270,13 @@ Lit.store_request_info = true
197
270
 
198
271
  ### Testing
199
272
 
200
- For local testing [Appraisal](https://github.com/thoughtbot/appraisal) gem comes into play, run tests via: `bundle exec appraisal rails-4.2 rake test`.
201
-
202
- Please remember to edit `test/dummy/config/database.yml` file
273
+ 1. `gem install bundler && bundle install` - ensure Bundler and all required gems are installed
274
+ 2. `bundle exec appraisal install` - install gems from appraisal's gemfiles
275
+ 3. `cp test/dummy/config/database.yml.sample test/dummy/config/database.yml` - move a database.yml in place (remember to fill your DB credentials in it)
276
+ 4. `RAILS_ENV=test appraisal rails-5.2 rake db:setup` - setup lit DB (see test/config/database.yml); do it only once, it does not matter which Rails version you use for `appraisal`
277
+ 5. `bundle exec appraisal rake` - run the tests!
203
278
 
204
279
  ### License
205
280
 
206
281
  Lit is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
282
+
data/Rakefile CHANGED
@@ -1,10 +1,7 @@
1
- #!/usr/bin/env rake
2
- require 'rubygems'
3
- begin
4
- require 'bundler/setup'
5
- rescue LoadError
6
- puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
- end
1
+ require "bundler/gem_tasks"
2
+
3
+ APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
4
+ load 'rails/tasks/engine.rake'
8
5
 
9
6
  begin
10
7
  require 'rdoc/task'
@@ -22,17 +19,14 @@ RDoc::Task.new(:rdoc) do |rdoc|
22
19
  rdoc.rdoc_files.include('lib/**/*.rb')
23
20
  end
24
21
 
25
- APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
26
- load 'rails/tasks/engine.rake'
27
-
28
- Bundler::GemHelper.install_tasks
29
-
30
- require 'rake/testtask'
31
-
22
+ require "rake/testtask"
32
23
  Rake::TestTask.new(:test) do |t|
33
24
  puts "Storage: #{ENV['LIT_STORAGE'] || 'redis'}"
34
- t.libs << 'lib'
35
25
  t.libs << 'test'
26
+ t.libs << 'lib'
36
27
  t.pattern = 'test/**/*_test.rb'
37
28
  t.verbose = false
38
29
  end
30
+
31
+ task :default => :test
32
+
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lit
4
+ class CloudTranslationsController < ::Lit::ApplicationController
5
+ def show
6
+ params.delete(:from) if params[:from] == 'auto'
7
+ @target_localization = Localization.find(params[:localization_id])
8
+ @localization_key = @target_localization.localization_key
9
+ if params[:from]
10
+ @localization = @localization_key.localizations.joins(:locale)
11
+ .find_by!(lit_locales: { locale: params[:from] })
12
+ end
13
+ opts =
14
+ {
15
+ # if :from was auto, translate from the target localization's
16
+ # current text itself
17
+ text: (@localization || @target_localization).value,
18
+ from: params[:from],
19
+ to: @target_localization.locale.locale
20
+ }.compact
21
+ @translated_text = Lit::CloudTranslation.translate(opts)
22
+ rescue Lit::CloudTranslation::TranslationError => e
23
+ @error_message = "Translation failed. #{e.message}"
24
+ end
25
+ end
26
+ end
@@ -48,7 +48,8 @@ module Lit
48
48
  end
49
49
 
50
50
  def find_localization
51
- @localization = @localization_key.localizations.find(params[:id])
51
+ @localization =
52
+ @localization_key.localizations.includes(:locale).find(params[:id])
52
53
  end
53
54
 
54
55
  def after_update_operations
@@ -12,5 +12,9 @@ module Lit
12
12
  def allow_wysiwyg_editor?(key)
13
13
  Lit.all_translations_are_html_safe || key.to_s =~ /(\b|_|\.)html$/
14
14
  end
15
+
16
+ def available_locales_with_default_first
17
+ I18n.available_locales.sort_by { |l| l == I18n.default_locale ? 0 : 1 }
18
+ end
15
19
  end
16
20
  end
@@ -0,0 +1,14 @@
1
+ <% if @translated_text.is_a?(String) %>
2
+ $('.localization_row[data-id="<%= @target_localization.id %>"] textarea').val("<%= j @translated_text %>");
3
+ <% elsif @translated_text.is_a?(Array) %>
4
+ $inputs = $('.localization_row[data-id="<%= @target_localization.id %>"] input[type=text]');
5
+ var translated_elements = JSON.parse("<%= j @translated_text.to_json.html_safe %>");
6
+ for (i in $inputs.toArray()) {
7
+ $input = $($inputs[i]);
8
+ $input.val(translated_elements[i]);
9
+ }
10
+ <% elsif @error_message.present? %>
11
+ alert("<%= j @error_message %>")
12
+ <% else %>
13
+ alert("An unknown error has occurred while translating.")
14
+ <% end %>
@@ -19,5 +19,32 @@
19
19
 
20
20
  <button class="btn btn-primary btn-sm" type="submit"><%= I18n.t('lit.common.update', default: "Update") %></button>
21
21
  <button class="btn btn-default btn-sm cancel" type="reset"><%= I18n.t('lit.common.cancel', default: 'Cancel') %></button>
22
+
23
+ <% if Lit::CloudTranslation.provider %>
24
+ <div class="btn-group pull-right">
25
+ <%= link_to "Translate using #{Lit::CloudTranslation.provider.name.demodulize}", cloud_translation_path(from: I18n.default_locale, localization_id: @localization.id), remote: true, class: 'btn btn-sm btn-default' %>
26
+ <button class="btn btn-sm dropdown-toggle" data-toggle="dropdown">
27
+ <span class="caret"></span>
28
+ <span class="sr-only">Source languages</span>
29
+ </button>
30
+ <ul class="dropdown-menu">
31
+ <li>
32
+ <a>Translate from:</a>
33
+ </li>
34
+ <li role="separator" class="divider"></li>
35
+ <% (available_locales_with_default_first + [:auto]).each_with_index do |source_locale, i| %>
36
+ <li>
37
+ <%= link_to cloud_translation_path(from: source_locale, localization_id: @localization.id), remote: true do %>
38
+ <% if i == 0 %>
39
+ <strong><%= source_locale.to_s %></strong>
40
+ <% else %>
41
+ <%= source_locale.to_s %>
42
+ <% end %>
43
+ <% end %>
44
+ </li>
45
+ <% end %>
46
+ </ul>
47
+ </div>
48
+ <% end %>
22
49
  <% end %>
23
50
 
@@ -50,5 +50,7 @@ Lit::Engine.routes.draw do
50
50
  end
51
51
  end
52
52
 
53
+ resource :cloud_translation, only: :show
54
+
53
55
  root to: 'dashboard#index'
54
56
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lit
4
+ module CloudTranslation
5
+ class TranslationError < StandardError; end
6
+
7
+ module_function
8
+
9
+ # Sets the provider for cloud translations.
10
+ # @param [Class] provider Selected translation provider,
11
+ # descending from Lit::CloudTranslation::Providers::Base
12
+ def provider=(provider)
13
+ @provider = provider
14
+ end
15
+
16
+ # Returns the currently active cloud translation provider,
17
+ # descending from Lit::CloudTranslation::Providers::Base.
18
+ def provider
19
+ @provider
20
+ end
21
+
22
+ # Uses the active translation provider to translate a text or array of
23
+ # texts.
24
+ # @param [String, Array] text The text (or array of texts) to translate
25
+ # @param [Symbol, String] from The language to translate from. If not given,
26
+ # auto-detection will be attempted.
27
+ # @param [Symbol, String] to The language to translate to.
28
+ # @param [Hash] opts Additional, provider-specific optional parameters.
29
+ def translate(text:, from: nil, to:, **opts)
30
+ provider.translate(text: text, from: from, to: to, **opts)
31
+ end
32
+
33
+ # Optional if provider-speciffic environment variables are set correctly.
34
+ # Configures the cloud translation provider with specific settings,
35
+ # overriding those from environment if needed.
36
+ #
37
+ # @example
38
+ # Lit::CloudTranslation.configure do |config|
39
+ # # For Yandex, this overrides the YANDEX_TRANSLATE_API_KEY env
40
+ # config.api_key = 'my_awesome_api_key'
41
+ # end
42
+ def configure(&block)
43
+ unless provider
44
+ raise 'Translation provider not selected yet. Use `Lit::CloudTranslation' \
45
+ '.provider = PROVIDER_KLASS` before calling #configure.'
46
+ end
47
+ provider.tap do |p|
48
+ p.configure(&block)
49
+ end
50
+ end
51
+ end
52
+ end # descending from Lit::CloudTranslation::Providers::Base.
53
+
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lit::CloudTranslation::Providers
4
+ # Abstract base class for cloud translation providers, providing a skeleton
5
+ # for the provider's functionality (mainly the #translate method) as well as
6
+ # a configuration management mechanism.
7
+ class Base
8
+ attr_reader :config
9
+
10
+ def initialize(config)
11
+ default_config.each do |key, value|
12
+ config[key] ||= value
13
+ end
14
+ @config = config
15
+ end
16
+
17
+ # Translates a given text from a given language to a different one.
18
+ # @param [String, Array] text The text (or array of texts) to translate
19
+ # @param [Symbol, String] from The language to translate from. If not given,
20
+ # auto-detection will be attempted.
21
+ # @param [Symbol, String] to The language to translate to.
22
+ # @param [Hash] opts Additional, provider-specific optional parameters.
23
+ def translate(text:, from: nil, to:, **opts) # rubocop:disable Lint/UnusedMethodArgument, Metrics/LineLength
24
+ raise NotImplementedError
25
+ end
26
+
27
+ private
28
+
29
+ # Loads specific information from environment variables or other sources
30
+ # as the default configuartion for the translation provider.
31
+ #
32
+ # This can be overridden using `Lit::CloudTranslation.configure`.
33
+ def default_config
34
+ {}
35
+ end
36
+
37
+ private_class_method :new
38
+
39
+ class << self
40
+ # Using the provider object's singleton instance, translates a given text from
41
+ # a given language to a different one.
42
+ # @param [String, Array] text The text (or array of texts) to translate
43
+ # @param [Symbol, String] from The language to translate from. If not given,
44
+ # auto-detection will be attempted.
45
+ # @param [Symbol, String] to The language to translate to.
46
+ # @param [Hash] opts Additional, provider-specific optional parameters.
47
+ def translate(text:, from: nil, to:, **opts)
48
+ instance.translate(text: text, from: from, to: to, **opts)
49
+ end
50
+
51
+ def configure
52
+ yield config if block_given?
53
+ end
54
+
55
+ private
56
+
57
+ def instance
58
+ @instance ||= new(config)
59
+ end
60
+
61
+ def config
62
+ @config ||= OpenStruct.new
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ begin
5
+ require 'google/cloud/translate'
6
+ rescue LoadError
7
+ raise StandardError, 'You need to add "gem \'google-cloud-translate\'" to your Gemfile to support Google Cloud Translation'
8
+ end
9
+
10
+ module Lit::CloudTranslation::Providers
11
+ # Google Cloud Translation API provider for Lit translation suggestions.
12
+ #
13
+ # Configuration:
14
+ #
15
+ # require 'lit/cloud_translation/providers/google'
16
+ #
17
+ # Lit::CloudTranslation.provider = Lit::CloudTranslation::Providers::Google
18
+ #
19
+ # # Service account configuration can be given via a file pointed to by
20
+ # # ENV['GOOGLE_TRANSLATE_API_KEYFILE'] (see here:
21
+ # # https://cloud.google.com/iam/docs/creating-managing-service-account-keys)
22
+ # #
23
+ # # Instead of providing the keyfile, credentials can be given using
24
+ # # GOOGLE_TRANSLATE_API_<element> environment variables, where e.g.
25
+ # # the GOOGLE_TRANSLATE_API_PROJECT_ID variable corresponds to the
26
+ # # `project_id` element of your credentials. Typically, only the following
27
+ # # variables are mandatory:
28
+ # # * GOOGLE_TRANSLATE_API_PROJECT_ID
29
+ # # * GOOGLE_TRANSLATE_API_PRIVATE_KEY_ID
30
+ # # * GOOGLE_TRANSLATE_API_PRIVATE_KEY (be sure that it contains correct line breaks)
31
+ # # * GOOGLE_TRANSLATE_API_CLIENT_EMAIL
32
+ # # * GOOGLE_TRANSLATE_API_CLIENT_ID
33
+ # #
34
+ # # Alternatively, the contents of that file can be given as a Ruby hash
35
+ # # and passed like the following (be careful to use secrets or something
36
+ # # that prevents exposing private credentials):
37
+ #
38
+ # Lit::CloudTranslation.configure do |config|
39
+ # config.keyfile_hash = {
40
+ # 'type' => 'service_account',
41
+ # 'project_id' => 'foo',
42
+ # 'private_key_id' => 'keyid',
43
+ # ... # see link above for reference
44
+ # }
45
+ # end
46
+ class Google < Base
47
+ def translate(text:, from: nil, to:, **opts)
48
+ @client ||=
49
+ ::Google::Cloud::Translate.new(project_id: config.keyfile_hash['project_id'],
50
+ credentials: config.keyfile_hash)
51
+ result = @client.translate(sanitize_text(text), from: from, to: to, **opts)
52
+ unsanitize_text(
53
+ case result
54
+ when ::Google::Cloud::Translate::Translation then result.text
55
+ when Array then result.map(&:text)
56
+ end
57
+ )
58
+ rescue Signet::AuthorizationError => e
59
+ error_description =
60
+ 'Google credentials error: ' + # rubocop:disable Style/RescueModifier
61
+ JSON.parse(e.response.body)['error_description'] rescue 'Unknown error'
62
+ raise ::Lit::CloudTranslation::TranslationError, error_description,
63
+ cause: e
64
+ rescue ::Google::Cloud::Error => e
65
+ raise ::Lit::CloudTranslation::TranslationError, e.message, cause: e
66
+ end
67
+
68
+ private
69
+
70
+ def default_config
71
+ if ENV['GOOGLE_TRANSLATE_API_KEYFILE'].blank?
72
+ env_keys = ENV.keys.grep(/\AGOOGLE_TRANSLATE_API_/)
73
+ keyfile_keys = env_keys.map(&:downcase).map { |k| k.gsub('google_translate_api_', '') }
74
+ keyfile_key_to_env_key_mapping = keyfile_keys.zip(env_keys).to_h
75
+ return {
76
+ keyfile_hash: keyfile_key_to_env_key_mapping.transform_values do |env_key|
77
+ ENV[env_key]
78
+ end
79
+ }
80
+ end
81
+ { keyfile_hash: JSON.parse(File.read(ENV['GOOGLE_TRANSLATE_API_KEYFILE'])) }
82
+ rescue JSON::ParserError
83
+ raise
84
+ rescue Errno::ENOENT
85
+ { keyfile_hash: nil }
86
+ end
87
+
88
+ def require_config!
89
+ return if config.keyfile_hash.present?
90
+ raise 'GOOGLE_TRANSLATE_API_KEYFILE env or `config.keyfile_hash` not given'
91
+ end
92
+
93
+ def sanitize_text(text_or_array)
94
+ case text_or_array
95
+ when String
96
+ text_or_array.gsub(/%{(.+?)}/, '<code>__LIT__\1__LIT__</code>')
97
+ when Array
98
+ text_or_array.map { |s| sanitize_text(s) }
99
+ else
100
+ raise TypeError
101
+ end
102
+ end
103
+
104
+ def unsanitize_text(text_or_array)
105
+ case text_or_array
106
+ when String
107
+ text_or_array.gsub(/<code>__LIT__(.+?)__LIT__<\/code>/, '%{\1}')
108
+ when Array
109
+ text_or_array.map { |s| unsanitize_text(s) }
110
+ else
111
+ raise TypeError
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require 'net/http'
5
+
6
+ module Lit::CloudTranslation::Providers
7
+ # Yandex Translate API provider for Lit translation suggestions.
8
+ #
9
+ # Configuration:
10
+ #
11
+ # require 'lit/cloud_translation/providers/yandex'
12
+ #
13
+ # Lit::CloudTranslation.provider = Lit::CloudTranslation::Providers::Yandeex
14
+ #
15
+ # # API key can be given via ENV['YANDEX_TRANSLATE_API_KEY'].
16
+ # #
17
+ # # Alternatively, it can be set programmatically after setting provider:
18
+ #
19
+ # Lit::CloudTranslation.configure do |config|
20
+ # config.api_key = 'the_api_key'
21
+ # end
22
+ class Yandex < Base
23
+ def translate(text:, from: nil, to:, **opts) # rubocop:disable Metrics/MethodLength, Metrics/LineLength
24
+ # puts "api key is: #{config.api_key}"
25
+ # puts "translating #{text} from #{from} to #{to}"
26
+ uri = URI('https://translate.yandex.net/api/v1.5/tr.json/translate')
27
+ params = {
28
+ key: config.api_key,
29
+ text: sanitize_text(text),
30
+ lang: [from, to].compact.join('-'),
31
+ format: opts[:format],
32
+ options: opts[:options]
33
+ }.compact
34
+ uri.query = URI.encode_www_form(params)
35
+ res = Net::HTTP.get_response(uri)
36
+
37
+ unsanitize_text(
38
+ case res
39
+ when Net::HTTPOK
40
+ translations = JSON.parse(res.body)['text']
41
+ translations.size == 1 ? translations.first : translations
42
+ else
43
+ raise ::Lit::CloudTranslation::TranslationError,
44
+ (JSON.parse(res.body)['message'] rescue "Unknown error: #{res.body}") # rubocop:disable Style/RescueModifier, Metrics/LineLength
45
+ end
46
+ )
47
+ end
48
+
49
+ private
50
+
51
+ def default_config
52
+ { api_key: ENV['YANDEX_TRANSLATE_API_KEY'] }
53
+ end
54
+
55
+ def require_config!
56
+ return if config.api_key.present?
57
+ raise 'YANDEX_TRANSLATE_API_KEY env or `config.api_key` not given'
58
+ end
59
+
60
+ def sanitize_text(text_or_array)
61
+ case text_or_array
62
+ when String
63
+ text_or_array.gsub(/%{(.+?)}/, '%{_LIT_\1_LIT_}')
64
+ when Array
65
+ text_or_array.map { |s| sanitize_text(s) }
66
+ else
67
+ raise TypeError
68
+ end
69
+ end
70
+
71
+ def unsanitize_text(text_or_array)
72
+ case text_or_array
73
+ when String
74
+ text_or_array.gsub(/%{_LIT_(.+?)_LIT_}/, '%{\1}')
75
+ when Array
76
+ text_or_array.map { |s| unsanitize_text(s) }
77
+ else
78
+ raise TypeError
79
+ end
80
+ end
81
+ end
82
+ end
@@ -33,7 +33,6 @@ module Lit
33
33
  yml = parsed_yaml[locale.to_s]
34
34
  Hash[*Lit::Cache.flatten_hash(yml)].each do |key, default_translation|
35
35
  next if default_translation.nil? && skip_nil
36
- puts key
37
36
  upsert(locale, key, default_translation)
38
37
  end
39
38
  end
@@ -52,7 +51,6 @@ module Lit
52
51
  row_translations.each do |locale, value|
53
52
  next unless locale_keys.blank? || locale_keys.map(&:to_sym).include?(locale.to_sym)
54
53
  next if value.nil? && skip_nil
55
- puts key
56
54
  upsert(locale, key, value)
57
55
  end
58
56
  end
@@ -3,6 +3,7 @@ require 'lit/i18n_backend'
3
3
  require 'lit/cache'
4
4
  require 'lit/export'
5
5
  require 'lit/import'
6
+ require 'lit/cloud_translation'
6
7
 
7
8
  module Lit
8
9
  class Loader
@@ -10,7 +11,7 @@ module Lit
10
11
  attr_accessor :logger
11
12
  def initialize
12
13
  self.logger ||= Logger.new($stdout)
13
- self.logger.info 'initializing Lit'
14
+ self.logger.info 'initializing Lit' unless ::Rails.env.test?
14
15
  self.cache ||= Cache.new
15
16
  I18n.backend = I18nBackend.new(self.cache)
16
17
  end
@@ -1,3 +1,3 @@
1
1
  module Lit
2
- VERSION = '0.4.0-alpha.5'.freeze
2
+ VERSION = '1.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,16 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0.pre.alpha.5
4
+ version: '1.0'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Litwiniuk
8
8
  - Piotr Boniecki
9
9
  - Michał Buszkiewicz
10
+ - Szymon Soppa
10
11
  autorequire:
11
12
  bindir: bin
12
13
  cert_chain: []
13
- date: 2018-12-04 00:00:00.000000000 Z
14
+ date: 2019-01-09 00:00:00.000000000 Z
14
15
  dependencies:
15
16
  - !ruby/object:Gem::Dependency
16
17
  name: rails
@@ -55,47 +56,131 @@ dependencies:
55
56
  - !ruby/object:Gem::Version
56
57
  version: '0'
57
58
  - !ruby/object:Gem::Dependency
58
- name: appraisal
59
+ name: sass-rails
59
60
  requirement: !ruby/object:Gem::Requirement
60
61
  requirements:
61
62
  - - ">="
62
63
  - !ruby/object:Gem::Version
63
64
  version: '0'
64
- type: :development
65
+ type: :runtime
65
66
  prerelease: false
66
67
  version_requirements: !ruby/object:Gem::Requirement
67
68
  requirements:
68
69
  - - ">="
69
70
  - !ruby/object:Gem::Version
70
71
  version: '0'
72
+ - !ruby/object:Gem::Dependency
73
+ name: appraisal
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - "~>"
77
+ - !ruby/object:Gem::Version
78
+ version: 2.2.0
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: 2.2.0
71
86
  - !ruby/object:Gem::Dependency
72
87
  name: devise
73
88
  requirement: !ruby/object:Gem::Requirement
74
89
  requirements:
75
- - - ">="
90
+ - - "~>"
76
91
  - !ruby/object:Gem::Version
77
- version: '0'
92
+ version: 4.5.0
78
93
  type: :development
79
94
  prerelease: false
80
95
  version_requirements: !ruby/object:Gem::Requirement
81
96
  requirements:
82
- - - ">="
97
+ - - "~>"
83
98
  - !ruby/object:Gem::Version
84
- version: '0'
99
+ version: 4.5.0
85
100
  - !ruby/object:Gem::Dependency
86
- name: pg
101
+ name: google-cloud-translate
87
102
  requirement: !ruby/object:Gem::Requirement
88
103
  requirements:
89
- - - ">="
104
+ - - "~>"
90
105
  - !ruby/object:Gem::Version
91
- version: '0'
106
+ version: 1.2.4
92
107
  type: :development
93
108
  prerelease: false
94
109
  version_requirements: !ruby/object:Gem::Requirement
95
110
  requirements:
96
- - - ">="
111
+ - - "~>"
97
112
  - !ruby/object:Gem::Version
98
- version: '0'
113
+ version: 1.2.4
114
+ - !ruby/object:Gem::Dependency
115
+ name: minitest
116
+ requirement: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: 5.11.3
121
+ type: :development
122
+ prerelease: false
123
+ version_requirements: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - "~>"
126
+ - !ruby/object:Gem::Version
127
+ version: 5.11.3
128
+ - !ruby/object:Gem::Dependency
129
+ name: minitest-vcr
130
+ requirement: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - "~>"
133
+ - !ruby/object:Gem::Version
134
+ version: 1.4.0
135
+ type: :development
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - "~>"
140
+ - !ruby/object:Gem::Version
141
+ version: 1.4.0
142
+ - !ruby/object:Gem::Dependency
143
+ name: pry-byebug
144
+ requirement: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - "~>"
147
+ - !ruby/object:Gem::Version
148
+ version: 3.6.0
149
+ type: :development
150
+ prerelease: false
151
+ version_requirements: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - "~>"
154
+ - !ruby/object:Gem::Version
155
+ version: 3.6.0
156
+ - !ruby/object:Gem::Dependency
157
+ name: vcr
158
+ requirement: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - "~>"
161
+ - !ruby/object:Gem::Version
162
+ version: 4.0.0
163
+ type: :development
164
+ prerelease: false
165
+ version_requirements: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: 4.0.0
170
+ - !ruby/object:Gem::Dependency
171
+ name: webmock
172
+ requirement: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - "~>"
175
+ - !ruby/object:Gem::Version
176
+ version: 3.4.2
177
+ type: :development
178
+ prerelease: false
179
+ version_requirements: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - "~>"
182
+ - !ruby/object:Gem::Version
183
+ version: 3.4.2
99
184
  description: "Translate your apps with pleasure (sort of...) and for free.\n It's
100
185
  simple i18n web interface, build on top of twitter\n bootstrap,
101
186
  that one may find helpful in translating app by\n non-technicals. "
@@ -375,6 +460,7 @@ files:
375
460
  - app/controllers/lit/api/v1/localization_keys_controller.rb
376
461
  - app/controllers/lit/api/v1/localizations_controller.rb
377
462
  - app/controllers/lit/application_controller.rb
463
+ - app/controllers/lit/cloud_translations_controller.rb
378
464
  - app/controllers/lit/concerns/request_info_store.rb
379
465
  - app/controllers/lit/concerns/request_keys_store.rb
380
466
  - app/controllers/lit/dashboard_controller.rb
@@ -410,6 +496,7 @@ files:
410
496
  - app/views/layouts/lit/_messages.html.erb
411
497
  - app/views/layouts/lit/_navigation.html.erb
412
498
  - app/views/layouts/lit/application.html.erb
499
+ - app/views/lit/cloud_translations/show.js.erb
413
500
  - app/views/lit/dashboard/index.html.erb
414
501
  - app/views/lit/incomming_localizations/accept.js.erb
415
502
  - app/views/lit/incomming_localizations/destroy.js.erb
@@ -458,6 +545,10 @@ files:
458
545
  - lib/lit/adapters/hash_storage.rb
459
546
  - lib/lit/adapters/redis_storage.rb
460
547
  - lib/lit/cache.rb
548
+ - lib/lit/cloud_translation.rb
549
+ - lib/lit/cloud_translation/providers/base.rb
550
+ - lib/lit/cloud_translation/providers/google.rb
551
+ - lib/lit/cloud_translation/providers/yandex.rb
461
552
  - lib/lit/engine.rb
462
553
  - lib/lit/export.rb
463
554
  - lib/lit/i18n_backend.rb
@@ -482,12 +573,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
482
573
  version: '0'
483
574
  required_rubygems_version: !ruby/object:Gem::Requirement
484
575
  requirements:
485
- - - ">"
576
+ - - ">="
486
577
  - !ruby/object:Gem::Version
487
- version: 1.3.1
578
+ version: '0'
488
579
  requirements: []
489
580
  rubyforge_project:
490
- rubygems_version: 2.6.13
581
+ rubygems_version: 2.7.8
491
582
  signing_key:
492
583
  specification_version: 4
493
584
  summary: Database powered i18n backend with web gui