tr8n 3.1.7 → 3.1.8
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.
- data/Gemfile.lock +1 -1
- data/app/controllers/tr8n/api/v1/application_controller.rb +129 -0
- data/app/controllers/tr8n/api/v1/base_controller.rb +1 -1
- data/app/controllers/tr8n/api/v1/language_controller.rb +1 -1
- data/app/controllers/tr8n/api/v1/translation_controller.rb +1 -1
- data/app/models/tr8n/language_rule.rb +1 -1
- data/app/models/tr8n/sync_log.rb +67 -43
- data/app/models/tr8n/translation.rb +28 -21
- data/app/models/tr8n/translation_key.rb +37 -28
- data/app/models/tr8n/translator.rb +40 -2
- data/config/routes.rb +1 -1
- data/db/migrate/20111003194443_create_tr8n_sync_tables.rb +2 -0
- data/lib/generators/tr8n/templates/config/tr8n/config.yml +8 -7
- data/lib/generators/tr8n/templates/db/create_tr8n_tables.rb +1 -1
- data/lib/tr8n/config.rb +10 -5
- data/lib/tr8n/version.rb +1 -1
- data/local/tr8n_server/config/tr8n/config.yml +15 -6
- data/local/tr8n_server/db/migrate/20110930041150_create_tr8n_tables.rb +1 -1
- metadata +37 -36
data/Gemfile.lock
CHANGED
@@ -0,0 +1,129 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2010-2012 Michael Berkovich, tr8n.net
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
###########################################################################
|
25
|
+
## API for getting translations from the main server
|
26
|
+
###########################################################################
|
27
|
+
|
28
|
+
class Tr8n::Api::V1::ApplicationController < ActionController::Base
|
29
|
+
# for ssl access to the translator - using ssl_requirement plugin
|
30
|
+
ssl_allowed :sync if respond_to?(:ssl_allowed)
|
31
|
+
|
32
|
+
def sync
|
33
|
+
ensure_push_enabled
|
34
|
+
|
35
|
+
sync_log = Tr8n::SyncLog.create(:started_at => Time.now,
|
36
|
+
:keys_received => 0, :translations_received => 0,
|
37
|
+
:keys_sent => 0, :translations_sent => 0)
|
38
|
+
|
39
|
+
method = params[:method] || "push"
|
40
|
+
|
41
|
+
payload = []
|
42
|
+
|
43
|
+
if method == "push"
|
44
|
+
payload = push_translations(sync_log)
|
45
|
+
|
46
|
+
elsif method == "pull"
|
47
|
+
payload = pull_translations(sync_log)
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
sync_log.finished_at = Time.now
|
52
|
+
sync_log.save
|
53
|
+
|
54
|
+
sanitize_api_response(:translation_keys => payload)
|
55
|
+
rescue Tr8n::Exception => ex
|
56
|
+
sanitize_api_response("error" => ex.message)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def ensure_push_enabled
|
62
|
+
raise Tr8n::Exception.new("Push is disabled") unless Tr8n::Config.synchronization_push_enabled?
|
63
|
+
raise Tr8n::Exception.new("Unauthorized server push attempt") unless Tr8n::Config.synchronization_push_servers.include?(request.env['REMOTE_HOST'])
|
64
|
+
end
|
65
|
+
|
66
|
+
def translator
|
67
|
+
@translator ||= Tr8n::Config.system_translator
|
68
|
+
end
|
69
|
+
|
70
|
+
def languages
|
71
|
+
@languages ||= Tr8n::Language.enabled_languages
|
72
|
+
end
|
73
|
+
|
74
|
+
def batch_size
|
75
|
+
@batch_size ||= Tr8n::Config.synchronization_batch_size
|
76
|
+
end
|
77
|
+
|
78
|
+
def push_translations(sync_log, opts = {})
|
79
|
+
payload = []
|
80
|
+
|
81
|
+
# already parsed by Rails
|
82
|
+
# keys = JSON.parse(params[:translation_keys])
|
83
|
+
keys = params[:translation_keys]
|
84
|
+
return payload unless keys
|
85
|
+
|
86
|
+
sync_log.keys_received += keys.size
|
87
|
+
|
88
|
+
keys.each do |tkey_hash|
|
89
|
+
# pp tkey_hash
|
90
|
+
tkey, remaining_translations = Tr8n::TranslationKey.create_from_sync_hash(tkey_hash, translator)
|
91
|
+
next unless tkey
|
92
|
+
|
93
|
+
sync_log.translations_received += tkey_hash[:translations].size if tkey_hash[:translations]
|
94
|
+
sync_log.translations_sent += remaining_translations.size
|
95
|
+
|
96
|
+
tkey.mark_as_synced!
|
97
|
+
|
98
|
+
payload << tkey.to_sync_hash(:translations => remaining_translations, :languages => languages)
|
99
|
+
end
|
100
|
+
|
101
|
+
payload
|
102
|
+
end
|
103
|
+
|
104
|
+
def pull_translations(sync_log, opts = {})
|
105
|
+
payload = []
|
106
|
+
|
107
|
+
# find all keys that have changed since the last sync
|
108
|
+
changed_keys = Tr8n::TranslationKey.where("synced_at is null or updated_at > synced_at").limit(batch_size)
|
109
|
+
sync_log.keys_sent += changed_keys.size
|
110
|
+
|
111
|
+
changed_keys.each do |tkey|
|
112
|
+
tkey_hash = tkey.to_sync_hash(:languages => languages)
|
113
|
+
payload << tkey_hash
|
114
|
+
sync_log.translations_sent += tkey_hash["translations"].size if tkey_hash["translations"]
|
115
|
+
tkey.mark_as_synced!
|
116
|
+
end
|
117
|
+
|
118
|
+
payload
|
119
|
+
end
|
120
|
+
|
121
|
+
def sanitize_api_response(response)
|
122
|
+
if Tr8n::Config.api[:response_encoding] == "xml"
|
123
|
+
render(:text => response.to_xml)
|
124
|
+
else
|
125
|
+
render(:json => response.to_json)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
@@ -123,7 +123,7 @@ class Tr8n::LanguageRule < ActiveRecord::Base
|
|
123
123
|
###############################################################
|
124
124
|
## Synchronization Methods
|
125
125
|
###############################################################
|
126
|
-
def to_sync_hash(token)
|
126
|
+
def to_sync_hash(token, opts = {})
|
127
127
|
{
|
128
128
|
"token" => token,
|
129
129
|
"type" => self.class.keyword,
|
data/app/models/tr8n/sync_log.rb
CHANGED
@@ -22,27 +22,39 @@
|
|
22
22
|
#++
|
23
23
|
|
24
24
|
class Tr8n::SyncLog < ActiveRecord::Base
|
25
|
-
|
25
|
+
set_table_name :tr8n_sync_logs
|
26
|
+
|
26
27
|
def self.sync(opts = {})
|
27
|
-
sync_log = Tr8n::SyncLog.create(:started_at => Time.now
|
28
|
+
sync_log = Tr8n::SyncLog.create(:started_at => Time.now,
|
29
|
+
:keys_received => 0, :translations_received => 0,
|
30
|
+
:keys_sent => 0, :translations_sent => 0)
|
31
|
+
sync_log.sync(opts)
|
32
|
+
sync_log
|
33
|
+
end
|
34
|
+
|
35
|
+
def sync(opts = {})
|
36
|
+
access_token
|
28
37
|
|
29
|
-
|
30
|
-
payload = []
|
38
|
+
translation_keys = []
|
31
39
|
batch_count = 0
|
32
|
-
total_key_count = 0
|
33
40
|
|
34
41
|
log("Begin synchronization process...")
|
35
|
-
log("
|
42
|
+
log("Pushing keys...")
|
36
43
|
|
37
44
|
conditions = "synced_at is null or updated_at > synced_at"
|
38
45
|
conditions = nil if opts[:force]
|
39
46
|
|
40
47
|
# STDOUT.sync = true
|
41
48
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
49
|
+
languages = Tr8n::Language.enabled_languages
|
50
|
+
total_key_count = Tr8n::TranslationKey.count(:conditions => conditions)
|
51
|
+
log("#{total_key_count} translation keys will be synchronized with the remote server in chunks of #{batch_size} keys...")
|
52
|
+
|
53
|
+
Tr8n::TranslationKey.find_each(:conditions => conditions, :batch_size => batch_size) do |key|
|
54
|
+
self.keys_sent += 1
|
55
|
+
tkey_hash = key.to_sync_hash(:languages => languages)
|
56
|
+
self.translations_sent += tkey_hash["translations"].size if tkey_hash["translations"]
|
57
|
+
translation_keys << tkey_hash
|
46
58
|
|
47
59
|
key.mark_as_synced!
|
48
60
|
|
@@ -50,49 +62,43 @@ class Tr8n::SyncLog < ActiveRecord::Base
|
|
50
62
|
# payload << sync_hash
|
51
63
|
# end
|
52
64
|
|
53
|
-
if
|
54
|
-
# pp "Sending #{batch_count+1} batch of #{Tr8n::Config.synchronization_batch_size} keys..."
|
65
|
+
if translation_keys.size >= Tr8n::Config.synchronization_batch_size
|
55
66
|
batch_count += 1
|
56
|
-
|
57
|
-
|
67
|
+
push_translations(translation_keys, opts)
|
68
|
+
translation_keys = []
|
69
|
+
log("Sent #{self.keys_sent} keys and #{self.translations_sent} translations.")
|
58
70
|
end
|
59
71
|
end
|
60
72
|
|
61
|
-
if
|
62
|
-
# pp "Sending final batch..."
|
73
|
+
if translation_keys.size > 0
|
63
74
|
batch_count += 1
|
64
|
-
|
75
|
+
push_translations(translation_keys, opts)
|
76
|
+
log("Sent #{self.keys_sent} keys with #{self.translations_sent} translations.")
|
65
77
|
end
|
66
|
-
|
67
|
-
sync_log.keys_sent = total_key_count
|
68
78
|
|
69
|
-
log("Sent #{total_key_count} keys in #{batch_count}
|
79
|
+
log("Done. Sent #{total_key_count} keys with #{self.translations_sent} translations in #{batch_count} calls.")
|
70
80
|
|
71
81
|
batch_count = 0
|
72
|
-
total_key_count = 0
|
73
82
|
|
74
83
|
unless opts[:force]
|
75
|
-
log("
|
84
|
+
log("Pulling translations...")
|
76
85
|
|
77
|
-
key_count =
|
86
|
+
key_count = pull_translations(opts)
|
78
87
|
while key_count > 0
|
79
88
|
batch_count += 1
|
80
|
-
|
81
|
-
key_count = download(opts)
|
89
|
+
key_count = pull_translations(opts)
|
82
90
|
end
|
83
|
-
log("Downloaded #{
|
91
|
+
log("Done. Downloaded #{self.translations_received} keys with #{self.translations_received} translations.")
|
84
92
|
end
|
85
93
|
|
86
|
-
|
87
|
-
|
88
|
-
sync_log.save
|
89
|
-
|
94
|
+
self.finished_at = Time.now
|
95
|
+
save
|
90
96
|
rescue Exception => ex
|
91
|
-
|
92
|
-
pp ex.backtrace
|
97
|
+
log_error(ex)
|
98
|
+
# pp ex.backtrace
|
93
99
|
end
|
94
100
|
|
95
|
-
def
|
101
|
+
def access_token
|
96
102
|
@access_token ||= begin
|
97
103
|
uri = URI.parse("#{Tr8n::Config.synchronization_server}/platform/oauth/request_token?client_id=#{Tr8n::Config.synchronization_key}&client_secret=#{Tr8n::Config.synchronization_secret}&grant_type=client_credentials")
|
98
104
|
response = Net::HTTP.get_response(uri)
|
@@ -102,9 +108,17 @@ class Tr8n::SyncLog < ActiveRecord::Base
|
|
102
108
|
end
|
103
109
|
end
|
104
110
|
|
105
|
-
def
|
111
|
+
def batch_size
|
112
|
+
@batch_size ||= Tr8n::Config.synchronization_batch_size
|
113
|
+
end
|
114
|
+
|
115
|
+
def translator
|
116
|
+
@translator ||= Tr8n::Config.system_translator
|
117
|
+
end
|
118
|
+
|
119
|
+
def push_translations(payload, opts = {})
|
106
120
|
uri = URI.parse("#{Tr8n::Config.synchronization_server}/api/application/sync")
|
107
|
-
params = {:method => "
|
121
|
+
params = {:method => "push", :batch_size => batch_size, :translation_keys => payload}
|
108
122
|
|
109
123
|
req = Net::HTTP::Post.new(uri.path)
|
110
124
|
req["Content-Type"] = "application/json"
|
@@ -124,17 +138,21 @@ class Tr8n::SyncLog < ActiveRecord::Base
|
|
124
138
|
raise Tr8n::Exception.new("Synchronization failed") unless response.is_a?(Net::HTTPOK)
|
125
139
|
|
126
140
|
data = HashWithIndifferentAccess.new(JSON.parse(response.body))
|
141
|
+
return unless data[:translation_keys]
|
142
|
+
|
127
143
|
# pp data
|
144
|
+
self.keys_received += data[:translation_keys].size
|
128
145
|
|
129
146
|
data[:translation_keys].each do |tkey_hash|
|
130
147
|
# pp tkey_hash
|
131
|
-
|
148
|
+
self.translations_received += tkey_hash["translations"].size if tkey_hash["translations"]
|
149
|
+
Tr8n::TranslationKey.create_from_sync_hash(tkey_hash, translator)
|
132
150
|
end
|
133
151
|
end
|
134
152
|
|
135
|
-
def
|
153
|
+
def pull_translations(opts = {})
|
136
154
|
uri = URI.parse("#{Tr8n::Config.synchronization_server}/api/application/sync")
|
137
|
-
params = {:method=>"
|
155
|
+
params = {:method=>"pull", :batch_size => batch_size}
|
138
156
|
params[:force] = true if opts[:force]
|
139
157
|
|
140
158
|
req = Net::HTTP::Post.new(uri.path)
|
@@ -153,19 +171,25 @@ class Tr8n::SyncLog < ActiveRecord::Base
|
|
153
171
|
raise Tr8n::Exception.new("Synchronization failed") unless response.is_a?(Net::HTTPOK)
|
154
172
|
|
155
173
|
data = HashWithIndifferentAccess.new(JSON.parse(response.body))
|
156
|
-
|
174
|
+
return 0 unless data[:translation_keys]
|
175
|
+
|
176
|
+
self.keys_received += data[:translation_keys].size
|
157
177
|
|
158
178
|
data[:translation_keys].each do |tkey_hash|
|
159
|
-
|
160
|
-
tkey, translations = Tr8n::TranslationKey.create_from_sync_hash(tkey_hash,
|
179
|
+
self.translations_received += tkey_hash["translations"].size if tkey_hash["translations"]
|
180
|
+
tkey, translations = Tr8n::TranslationKey.create_from_sync_hash(tkey_hash, translator)
|
161
181
|
tkey.mark_as_synced!
|
162
182
|
end
|
163
183
|
|
164
184
|
data[:translation_keys].size
|
165
185
|
end
|
166
186
|
|
167
|
-
def
|
187
|
+
def log(msg)
|
168
188
|
pp "#{Time.now}: #{msg}"
|
169
189
|
end
|
190
|
+
|
191
|
+
def log_error(msg)
|
192
|
+
pp "Error: #{msg}"
|
193
|
+
end
|
170
194
|
|
171
|
-
end
|
195
|
+
end
|
@@ -223,41 +223,48 @@ class Tr8n::Translation < ActiveRecord::Base
|
|
223
223
|
## Synchronization Methods
|
224
224
|
###############################################################
|
225
225
|
# generates the hash without rule ids, but with full definitions
|
226
|
-
def
|
227
|
-
|
226
|
+
def mark_as_synced!
|
227
|
+
update_attributes(:synced_at => Time.now + 2.seconds)
|
228
|
+
end
|
229
|
+
|
230
|
+
def rules_sync_hash(opts = {})
|
231
|
+
@rules_sync_hash ||= (rules || []).collect{|rule| rule[:rule].to_sync_hash(rule[:token], opts)}
|
228
232
|
end
|
229
233
|
|
230
234
|
# serilaize translation to API hash to be used for synchronization
|
231
|
-
def to_sync_hash(
|
232
|
-
|
233
|
-
|
235
|
+
def to_sync_hash(opts = {})
|
236
|
+
return {"locale" => language.locale, "label" => label, "rules" => rules_sync_hash(opts)} if opts[:comparible]
|
237
|
+
|
238
|
+
hash = {"locale" => language.locale, "label" => label, "rank" => rank, "rules" => rules_sync_hash(opts)}
|
239
|
+
if translator
|
240
|
+
if opts[:include_translator] # tr8n.net => local = include full translator info
|
241
|
+
hash["translator"] = translator.to_sync_hash(opts)
|
242
|
+
elsif translator.remote_id # local => tr8n.net = include only the remote id of the translator if the translator is linked
|
243
|
+
hash["translator_id"] = translator.remote_id
|
244
|
+
end
|
245
|
+
end
|
234
246
|
hash
|
235
247
|
end
|
236
248
|
|
237
249
|
# create translation from API hash for a specific key
|
238
|
-
def self.create_from_sync_hash(tkey, translator,
|
239
|
-
# don't add empty translations
|
240
|
-
return if hash["label"].blank?
|
250
|
+
def self.create_from_sync_hash(tkey, translator, thash, opts = {})
|
251
|
+
return if thash["label"].blank? # don't add empty translations
|
241
252
|
|
242
|
-
lang = Tr8n::Language.for(
|
243
|
-
# don't add translations for an unsupported language
|
244
|
-
return unless lang
|
253
|
+
lang = Tr8n::Language.for(thash["locale"])
|
254
|
+
return unless lang # don't add translations for an unsupported language
|
245
255
|
|
246
256
|
# generate rules for the translation
|
247
|
-
rules = []
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
rule = Tr8n::LanguageRule.create_from_sync_hash(lang, translator, rule_hash, opts)
|
252
|
-
pp rule
|
253
|
-
|
257
|
+
rules = []
|
258
|
+
if thash["rules"] and thash["rules"].any?
|
259
|
+
thash["rules"].each do |rhash|
|
260
|
+
rule = Tr8n::LanguageRule.create_from_sync_hash(lang, translator, rhash, opts)
|
254
261
|
return unless rule # if the rule has not been created, we should not even add the translation
|
255
|
-
rules << {:token =>
|
262
|
+
rules << {:token => rhash["token"], :rule_id => rule.id}
|
256
263
|
end
|
257
264
|
end
|
258
|
-
|
259
265
|
rules = nil if rules.empty?
|
260
|
-
|
266
|
+
|
267
|
+
tkey.add_translation(thash["label"], rules, lang, translator)
|
261
268
|
end
|
262
269
|
|
263
270
|
###############################################################
|
@@ -219,10 +219,7 @@ class Tr8n::TranslationKey < ActiveRecord::Base
|
|
219
219
|
|
220
220
|
def add_translation(label, rules = nil, language = Tr8n::Config.current_language, translator = Tr8n::Config.current_translator)
|
221
221
|
raise Tr8n::Exception.new("The sentence contains dirty words") unless language.clean_sentence?(label)
|
222
|
-
pp rules
|
223
222
|
translation = Tr8n::Translation.create(:translation_key => self, :language => language, :translator => translator, :label => label, :rules => rules)
|
224
|
-
pp translation
|
225
|
-
|
226
223
|
translation.vote!(translator, 1)
|
227
224
|
translation
|
228
225
|
end
|
@@ -230,7 +227,9 @@ class Tr8n::TranslationKey < ActiveRecord::Base
|
|
230
227
|
# returns all translations for the key, language and minimal rank
|
231
228
|
def translations_for(language = nil, rank = nil)
|
232
229
|
translations = Tr8n::Translation.where("translation_key_id = ?", self.id)
|
233
|
-
|
230
|
+
if language
|
231
|
+
translations = translations.where("language_id in (?)", [language].flatten.collect{|lang| lang.id})
|
232
|
+
end
|
234
233
|
translations = translations.where("rank >= ?", rank) if rank
|
235
234
|
translations.order("rank desc").all
|
236
235
|
end
|
@@ -531,49 +530,59 @@ class Tr8n::TranslationKey < ActiveRecord::Base
|
|
531
530
|
## Synchronization Methods
|
532
531
|
###############################################################
|
533
532
|
def mark_as_synced!
|
534
|
-
update_attributes(:synced_at => Time.now +
|
533
|
+
update_attributes(:synced_at => Time.now + 2.seconds)
|
535
534
|
end
|
536
535
|
|
537
|
-
def to_sync_hash(
|
536
|
+
def to_sync_hash(opts = {})
|
538
537
|
{
|
539
538
|
"key" => self.key,
|
540
539
|
"label" => self.label,
|
541
540
|
"description" => self.description,
|
542
541
|
"locale" => (locale || Tr8n::Config.default_locale),
|
543
|
-
"translations" =>
|
542
|
+
"translations" => opts[:translations] || translations_for(opts[:languages], opts[:threshold] || Tr8n::Config.translation_threshold).collect{|t| t.to_sync_hash(opts)}
|
544
543
|
}
|
545
544
|
end
|
546
545
|
|
547
|
-
def transations_sync_hashes
|
548
|
-
@transations_sync_hashes ||=
|
546
|
+
def transations_sync_hashes(opts = {})
|
547
|
+
@transations_sync_hashes ||= begin
|
548
|
+
translations.collect{|t| t.to_sync_hash(:comparible => true)}
|
549
|
+
end
|
549
550
|
end
|
550
551
|
|
552
|
+
def self.can_create_from_sync_hash?(tkey_hash, translator, opts = {})
|
553
|
+
return false if tkey_hash["key"].blank? or tkey_hash["label"].blank? or tkey_hash["locale"].blank?
|
554
|
+
true
|
555
|
+
end
|
556
|
+
|
551
557
|
# create translation key from API hash
|
552
|
-
def self.create_from_sync_hash(tkey_hash,
|
553
|
-
return
|
554
|
-
|
558
|
+
def self.create_from_sync_hash(tkey_hash, default_translator, opts = {})
|
559
|
+
return unless can_create_from_sync_hash?(tkey_hash, default_translator, opts)
|
560
|
+
|
561
|
+
# find or create translation key
|
555
562
|
tkey = Tr8n::TranslationKey.find_or_create(tkey_hash["label"], tkey_hash["description"])
|
556
563
|
|
557
|
-
#
|
564
|
+
# we will keep the translations that need to be sent back
|
565
|
+
remaining_translations = tkey.transations_sync_hashes(opts).dup
|
558
566
|
|
559
|
-
# opts[:force_create] = Tr8n::Config.synchronization_create_rules? if opts[:force_create].nil?
|
560
|
-
|
561
|
-
remaining_translations = tkey.transations_sync_hashes.dup
|
562
|
-
# pp :before, remaining_sync_hashes
|
563
|
-
|
564
567
|
added_trans = []
|
565
|
-
(tkey_hash["translations"] || []).each do |
|
568
|
+
(tkey_hash["translations"] || []).each do |thash|
|
569
|
+
remaining_translations.delete(thash)
|
570
|
+
|
566
571
|
# if the translation came from a linked translator, use the translator
|
567
|
-
|
568
|
-
if
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
+
translator = default_translator
|
573
|
+
if thash["translator_id"] # incoming translations from the remote server
|
574
|
+
translator = Tr8n::Translator.find_by_id(thash["translator_id"]) || default_translator
|
575
|
+
elsif thash["translator"]
|
576
|
+
translator = Tr8n::Translator.create_from_sync_hash(thash["translator"], opts) || default_translator
|
572
577
|
end
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
578
|
+
|
579
|
+
# don't insert duplicate translations
|
580
|
+
comparible_hash = thash.slice("locale", "label", "rules")
|
581
|
+
next if tkey.transations_sync_hashes.include?(comparible_hash)
|
582
|
+
|
583
|
+
translation = Tr8n::Translation.create_from_sync_hash(tkey, translator, thash, opts)
|
584
|
+
|
585
|
+
translation.mark_as_synced! if translation
|
577
586
|
end
|
578
587
|
|
579
588
|
# need to send back translations that have not been added, but exist in the system
|
@@ -190,6 +190,12 @@ class Tr8n::Translator < ActiveRecord::Base
|
|
190
190
|
false
|
191
191
|
end
|
192
192
|
|
193
|
+
# for translators registered on the exchange server
|
194
|
+
def remote?
|
195
|
+
return false if user
|
196
|
+
not remote_id.nil?
|
197
|
+
end
|
198
|
+
|
193
199
|
def system?
|
194
200
|
level == Tr8n::Config.system_level
|
195
201
|
end
|
@@ -204,13 +210,14 @@ class Tr8n::Translator < ActiveRecord::Base
|
|
204
210
|
|
205
211
|
def name
|
206
212
|
return "Tr8n Network" if system?
|
213
|
+
return super if remote?
|
207
214
|
|
208
215
|
unless Tr8n::Config.site_user_info_enabled?
|
209
216
|
translator_name = super
|
210
217
|
return translator_name unless translator_name.blank?
|
211
218
|
return "No Name"
|
212
219
|
end
|
213
|
-
|
220
|
+
|
214
221
|
return "Deleted User" unless user
|
215
222
|
user_name = Tr8n::Config.user_name(user)
|
216
223
|
return "No Name" if user_name.blank?
|
@@ -220,6 +227,7 @@ class Tr8n::Translator < ActiveRecord::Base
|
|
220
227
|
|
221
228
|
def gender
|
222
229
|
return "unknown" if system?
|
230
|
+
return super if remote?
|
223
231
|
|
224
232
|
unless Tr8n::Config.site_user_info_enabled?
|
225
233
|
translator_gender = super
|
@@ -230,9 +238,10 @@ class Tr8n::Translator < ActiveRecord::Base
|
|
230
238
|
Tr8n::Config.user_gender(user)
|
231
239
|
end
|
232
240
|
|
241
|
+
# TODO: change db to mugshot_url
|
233
242
|
def mugshot
|
234
243
|
return Tr8n::Config.system_image if system?
|
235
|
-
|
244
|
+
return super if remote?
|
236
245
|
return super unless Tr8n::Config.site_user_info_enabled?
|
237
246
|
return Tr8n::Config.silhouette_image unless user
|
238
247
|
img_url = Tr8n::Config.user_mugshot(user)
|
@@ -240,7 +249,9 @@ class Tr8n::Translator < ActiveRecord::Base
|
|
240
249
|
img_url
|
241
250
|
end
|
242
251
|
|
252
|
+
# TODO: change db to link_url
|
243
253
|
def link
|
254
|
+
# return super if remote?
|
244
255
|
return super unless Tr8n::Config.site_user_info_enabled?
|
245
256
|
return Tr8n::Config.default_url unless user
|
246
257
|
Tr8n::Config.user_link(user)
|
@@ -263,12 +274,14 @@ class Tr8n::Translator < ActiveRecord::Base
|
|
263
274
|
|
264
275
|
def level
|
265
276
|
return Tr8n::Config.admin_level if admin?
|
277
|
+
return super if remote?
|
266
278
|
return 0 if super.nil?
|
267
279
|
super
|
268
280
|
end
|
269
281
|
|
270
282
|
def title
|
271
283
|
return 'admin' if admin?
|
284
|
+
return super if remote?
|
272
285
|
Tr8n::Config.translator_levels[level.to_s] || 'unknown'
|
273
286
|
end
|
274
287
|
|
@@ -303,4 +316,29 @@ class Tr8n::Translator < ActiveRecord::Base
|
|
303
316
|
def to_s
|
304
317
|
name
|
305
318
|
end
|
319
|
+
|
320
|
+
###############################################################
|
321
|
+
## Synchronization Methods
|
322
|
+
###############################################################
|
323
|
+
def to_sync_hash(opts = {})
|
324
|
+
{
|
325
|
+
"id" => opts[:remote] ? self.remote_id : self.id,
|
326
|
+
"name" => self.name,
|
327
|
+
"gender" => self.gender,
|
328
|
+
"mugshot" => self.mugshot,
|
329
|
+
"link" => self.link
|
330
|
+
}
|
331
|
+
end
|
332
|
+
|
333
|
+
def self.create_from_sync_hash(thash, opts = {})
|
334
|
+
Tr8n::Translator.find_by_remote_id(thash[:id]) || Tr8n::Translator.create(
|
335
|
+
:user_id => 0,
|
336
|
+
:remote_id => thash[:id],
|
337
|
+
:name => thash[:name],
|
338
|
+
:gender => thash[:gender],
|
339
|
+
:mugshot => thash[:mugshot],
|
340
|
+
:link => thash[:link]
|
341
|
+
)
|
342
|
+
end
|
343
|
+
|
306
344
|
end
|
data/config/routes.rb
CHANGED
@@ -32,7 +32,7 @@ Tr8n::Engine.routes.draw do
|
|
32
32
|
match "admin/#{ctrl}(/:action)", :controller => "admin/#{ctrl}"
|
33
33
|
end
|
34
34
|
|
35
|
-
[:language, :translation, :translator].each do |ctrl|
|
35
|
+
[:application, :language, :translation, :translator].each do |ctrl|
|
36
36
|
match "api/v1/#{ctrl}(/:action)", :controller => "api/v1/#{ctrl}"
|
37
37
|
end
|
38
38
|
|
@@ -34,7 +34,9 @@ class CreateTr8nSyncTables < ActiveRecord::Migration
|
|
34
34
|
end
|
35
35
|
|
36
36
|
add_column :tr8n_translation_keys, :synced_at, :timestamp
|
37
|
+
add_index :tr8n_translation_keys, :synced_at
|
37
38
|
add_column :tr8n_translations, :synced_at, :timestamp
|
39
|
+
add_index :tr8n_translations, :synced_at
|
38
40
|
end
|
39
41
|
|
40
42
|
def down
|
@@ -264,13 +264,14 @@ defaults:
|
|
264
264
|
# You can run this command as often as your account on tr8n.net permits you.
|
265
265
|
#############################################################################
|
266
266
|
synchronization:
|
267
|
-
server: "http://tr8n.net" #
|
268
|
-
key: "YOUR APP KEY" # replace
|
269
|
-
secret: "YOUR APP SECRET" # replace
|
270
|
-
batch_size: 50
|
271
|
-
|
272
|
-
|
273
|
-
|
267
|
+
server: "http://tr8n.net" # visit this url to register your application
|
268
|
+
key: "YOUR APP KEY" # replace with your application key
|
269
|
+
secret: "YOUR APP SECRET" # replace with your application secret
|
270
|
+
batch_size: 50 # how many keys to send to the server at a time - if your site supports many language, decrease the batch size
|
271
|
+
all_languages: false # use only enabled languages, or all languages
|
272
|
+
enable_push: false # allows translations to be pushed from the main server using the sync API
|
273
|
+
push_servers: ["tr8n.net"] # only allows those servers to push translations
|
274
|
+
|
274
275
|
#############################################################################
|
275
276
|
# Environment Settings
|
276
277
|
# You can overload any feature defined in the defaults for any environment
|
@@ -104,7 +104,7 @@ class CreateTr8nTables < ActiveRecord::Migration
|
|
104
104
|
add_index :tr8n_language_metrics, [:created_at], :name => :tr8n_lm_c
|
105
105
|
|
106
106
|
create_table :tr8n_translators do |t|
|
107
|
-
t.integer :user_id
|
107
|
+
t.integer :user_id
|
108
108
|
t.boolean :inline_mode, :default => false
|
109
109
|
t.boolean :blocked, :default => false
|
110
110
|
t.boolean :reported, :default => false
|
data/lib/tr8n/config.rb
CHANGED
@@ -100,7 +100,7 @@ module Tr8n
|
|
100
100
|
Tr8n::Translator, Tr8n::TranslatorLog, Tr8n::TranslatorMetric,
|
101
101
|
Tr8n::TranslatorFollowing, Tr8n::TranslatorReport,
|
102
102
|
Tr8n::LanguageForumTopic, Tr8n::LanguageForumMessage, Tr8n::LanguageForumAbuseReport,
|
103
|
-
Tr8n::Glossary, Tr8n::IpLocation
|
103
|
+
Tr8n::Glossary, Tr8n::IpLocation, Tr8n::SyncLog
|
104
104
|
]
|
105
105
|
end
|
106
106
|
|
@@ -763,12 +763,17 @@ module Tr8n
|
|
763
763
|
synchronization[:secret]
|
764
764
|
end
|
765
765
|
|
766
|
-
def self.synchronization_create_rules?
|
767
|
-
synchronization[:create_rules]
|
768
|
-
end
|
769
|
-
|
770
766
|
def self.synchronization_all_languages?
|
771
767
|
synchronization[:all_languages]
|
772
768
|
end
|
769
|
+
|
770
|
+
def self.synchronization_push_enabled?
|
771
|
+
synchronization[:enable_push]
|
772
|
+
end
|
773
|
+
|
774
|
+
def self.synchronization_push_servers
|
775
|
+
synchronization[:push_servers]
|
776
|
+
end
|
777
|
+
|
773
778
|
end
|
774
779
|
end
|
data/lib/tr8n/version.rb
CHANGED
@@ -258,18 +258,27 @@ defaults:
|
|
258
258
|
# You can run this command as often as your account on tr8n.net permits you.
|
259
259
|
#############################################################################
|
260
260
|
synchronization:
|
261
|
-
server: "http://tr8n.net" #
|
262
|
-
key: "
|
263
|
-
secret: "
|
264
|
-
batch_size: 50
|
265
|
-
|
266
|
-
|
261
|
+
server: "http://tr8n.net" # visit this url to register your application
|
262
|
+
key: "YOUR APP KEY" # replace with your application key
|
263
|
+
secret: "YOUR APP SECRET" # replace with your application secret
|
264
|
+
batch_size: 50 # how many keys to send to the server at a time - if your site supports many language, decrease the batch size
|
265
|
+
all_languages: false # use only enabled languages, or all languages
|
266
|
+
enable_push: false # allows translations to be pushed from the main server using the sync API
|
267
|
+
push_servers: ["tr8n.net"] # only allows those servers to push translations
|
267
268
|
|
268
269
|
#############################################################################
|
269
270
|
# Environment Settings
|
270
271
|
# You can overload any feature defined in the defaults for any environment
|
271
272
|
#############################################################################
|
272
273
|
development:
|
274
|
+
# synchronization:
|
275
|
+
# server: "http://localhost:3000" # alternative, regional locations will be available in the future
|
276
|
+
# key: "14ngS3YadRF54PArKR8mre3MJfPzJA0jxW2j2RJu" # replace this with your key
|
277
|
+
# secret: "ycFdAAQOEmxtpb8gE9M4gqQrgfU4SR1LetkXnLDZ" # replace this with your secret
|
278
|
+
# batch_size: 50 # how many kesy to send to the server at a time
|
279
|
+
# all_languages: false # use only enabled languages, or all languages
|
280
|
+
# enable_push: true # allows translations to be pushed from the main server using the sync API
|
281
|
+
# push_servers: ["localhost", "tr8n.net"] # only allows those servers to push translations
|
273
282
|
|
274
283
|
test:
|
275
284
|
|
@@ -104,7 +104,7 @@ class CreateTr8nTables < ActiveRecord::Migration
|
|
104
104
|
add_index :tr8n_language_metrics, [:created_at], :name => :tr8n_lm_c
|
105
105
|
|
106
106
|
create_table :tr8n_translators do |t|
|
107
|
-
t.integer :user_id
|
107
|
+
t.integer :user_id
|
108
108
|
t.boolean :inline_mode, :default => false
|
109
109
|
t.boolean :blocked, :default => false
|
110
110
|
t.boolean :reported, :default => false
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tr8n
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1.
|
4
|
+
version: 3.1.8
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-01-
|
12
|
+
date: 2012-01-23 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
16
|
-
requirement: &
|
16
|
+
requirement: &2161128960 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.1.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2161128960
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: will_filter
|
27
|
-
requirement: &
|
27
|
+
requirement: &2161122860 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 3.1.2
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2161122860
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: kaminari
|
38
|
-
requirement: &
|
38
|
+
requirement: &2161121960 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *2161121960
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: sass
|
49
|
-
requirement: &
|
49
|
+
requirement: &2161120940 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *2161120940
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: coffee-script
|
60
|
-
requirement: &
|
60
|
+
requirement: &2161120060 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :runtime
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *2161120060
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: pry
|
71
|
-
requirement: &
|
71
|
+
requirement: &2161119400 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *2161119400
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: bundler
|
82
|
-
requirement: &
|
82
|
+
requirement: &2161118620 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: 1.0.0
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *2161118620
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: sqlite3
|
93
|
-
requirement: &
|
93
|
+
requirement: &2161117900 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ! '>='
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: '0'
|
99
99
|
type: :development
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *2161117900
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: rspec
|
104
|
-
requirement: &
|
104
|
+
requirement: &2161117200 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ! '>='
|
@@ -109,10 +109,10 @@ dependencies:
|
|
109
109
|
version: '0'
|
110
110
|
type: :development
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *2161117200
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
114
|
name: rspec-rails
|
115
|
-
requirement: &
|
115
|
+
requirement: &2161116420 !ruby/object:Gem::Requirement
|
116
116
|
none: false
|
117
117
|
requirements:
|
118
118
|
- - ! '>='
|
@@ -120,10 +120,10 @@ dependencies:
|
|
120
120
|
version: '0'
|
121
121
|
type: :development
|
122
122
|
prerelease: false
|
123
|
-
version_requirements: *
|
123
|
+
version_requirements: *2161116420
|
124
124
|
- !ruby/object:Gem::Dependency
|
125
125
|
name: factory_girl
|
126
|
-
requirement: &
|
126
|
+
requirement: &2161115780 !ruby/object:Gem::Requirement
|
127
127
|
none: false
|
128
128
|
requirements:
|
129
129
|
- - ! '>='
|
@@ -131,10 +131,10 @@ dependencies:
|
|
131
131
|
version: '0'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
|
-
version_requirements: *
|
134
|
+
version_requirements: *2161115780
|
135
135
|
- !ruby/object:Gem::Dependency
|
136
136
|
name: rr
|
137
|
-
requirement: &
|
137
|
+
requirement: &2161115020 !ruby/object:Gem::Requirement
|
138
138
|
none: false
|
139
139
|
requirements:
|
140
140
|
- - ! '>='
|
@@ -142,10 +142,10 @@ dependencies:
|
|
142
142
|
version: '0'
|
143
143
|
type: :development
|
144
144
|
prerelease: false
|
145
|
-
version_requirements: *
|
145
|
+
version_requirements: *2161115020
|
146
146
|
- !ruby/object:Gem::Dependency
|
147
147
|
name: steak
|
148
|
-
requirement: &
|
148
|
+
requirement: &2161114300 !ruby/object:Gem::Requirement
|
149
149
|
none: false
|
150
150
|
requirements:
|
151
151
|
- - ! '>='
|
@@ -153,10 +153,10 @@ dependencies:
|
|
153
153
|
version: '0'
|
154
154
|
type: :development
|
155
155
|
prerelease: false
|
156
|
-
version_requirements: *
|
156
|
+
version_requirements: *2161114300
|
157
157
|
- !ruby/object:Gem::Dependency
|
158
158
|
name: capybara
|
159
|
-
requirement: &
|
159
|
+
requirement: &2161113340 !ruby/object:Gem::Requirement
|
160
160
|
none: false
|
161
161
|
requirements:
|
162
162
|
- - ! '>='
|
@@ -164,10 +164,10 @@ dependencies:
|
|
164
164
|
version: '0'
|
165
165
|
type: :development
|
166
166
|
prerelease: false
|
167
|
-
version_requirements: *
|
167
|
+
version_requirements: *2161113340
|
168
168
|
- !ruby/object:Gem::Dependency
|
169
169
|
name: database_cleaner
|
170
|
-
requirement: &
|
170
|
+
requirement: &2161112380 !ruby/object:Gem::Requirement
|
171
171
|
none: false
|
172
172
|
requirements:
|
173
173
|
- - ! '>='
|
@@ -175,10 +175,10 @@ dependencies:
|
|
175
175
|
version: '0'
|
176
176
|
type: :development
|
177
177
|
prerelease: false
|
178
|
-
version_requirements: *
|
178
|
+
version_requirements: *2161112380
|
179
179
|
- !ruby/object:Gem::Dependency
|
180
180
|
name: memcache-client
|
181
|
-
requirement: &
|
181
|
+
requirement: &2161111320 !ruby/object:Gem::Requirement
|
182
182
|
none: false
|
183
183
|
requirements:
|
184
184
|
- - ! '>='
|
@@ -186,7 +186,7 @@ dependencies:
|
|
186
186
|
version: 1.8.5
|
187
187
|
type: :development
|
188
188
|
prerelease: false
|
189
|
-
version_requirements: *
|
189
|
+
version_requirements: *2161111320
|
190
190
|
description: Crowd-sourced translation and localization engine for Rails.
|
191
191
|
email:
|
192
192
|
- michael@tr8n.net
|
@@ -588,6 +588,7 @@ files:
|
|
588
588
|
- app/controllers/tr8n/admin/translation_controller.rb
|
589
589
|
- app/controllers/tr8n/admin/translation_key_controller.rb
|
590
590
|
- app/controllers/tr8n/admin/translator_controller.rb
|
591
|
+
- app/controllers/tr8n/api/v1/application_controller.rb
|
591
592
|
- app/controllers/tr8n/api/v1/base_controller.rb
|
592
593
|
- app/controllers/tr8n/api/v1/language_controller.rb
|
593
594
|
- app/controllers/tr8n/api/v1/translation_controller.rb
|
@@ -1007,7 +1008,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
1007
1008
|
version: '0'
|
1008
1009
|
segments:
|
1009
1010
|
- 0
|
1010
|
-
hash: -
|
1011
|
+
hash: -1543792512908225439
|
1011
1012
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
1012
1013
|
none: false
|
1013
1014
|
requirements:
|
@@ -1016,7 +1017,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1016
1017
|
version: '0'
|
1017
1018
|
segments:
|
1018
1019
|
- 0
|
1019
|
-
hash: -
|
1020
|
+
hash: -1543792512908225439
|
1020
1021
|
requirements: []
|
1021
1022
|
rubyforge_project: tr8n
|
1022
1023
|
rubygems_version: 1.8.11
|