tr8n 3.1.7 → 3.1.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|