air18n 0.1.30 → 0.1.31
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/air18n/phrase_translation.rb +111 -4
- data/lib/air18n/version.rb +1 -1
- data/spec/lib/air18n/phrase_translation_spec.rb +99 -2
- metadata +2 -2
@@ -56,11 +56,97 @@ module Air18n
|
|
56
56
|
all_locales
|
57
57
|
end
|
58
58
|
|
59
|
+
def self.create_translation(phrase_id, phrase_key, target_locale, value, user_id, do_xss_check, allow_verification)
|
60
|
+
pt = PhraseTranslation.new
|
61
|
+
pt.user_id = user_id
|
62
|
+
pt.phrase_id = phrase_id
|
63
|
+
pt.key = phrase_key
|
64
|
+
pt.locale = target_locale
|
65
|
+
|
66
|
+
latest = pt.phrase.latest_translation(pt.locale)
|
67
|
+
|
68
|
+
pt.value = normalize_translation_value(value)
|
69
|
+
normalized_latest_value = latest.present? && normalize_translation_value(latest.value)
|
70
|
+
|
71
|
+
value_same_as_latest = pt.value == normalized_latest_value
|
72
|
+
|
73
|
+
if latest && value_same_as_latest && latest.is_stale && user_id == latest.user_id
|
74
|
+
latest.is_stale = false
|
75
|
+
if latest.save
|
76
|
+
response_obj = {
|
77
|
+
:status => 'success',
|
78
|
+
:message => 'Translation marked as up-to-date.',
|
79
|
+
:phrase_id => latest.phrase_id,
|
80
|
+
:key => latest.key,
|
81
|
+
:locale => latest.locale,
|
82
|
+
:value => latest.value,
|
83
|
+
:is_verification => latest.is_verification
|
84
|
+
}
|
85
|
+
else
|
86
|
+
response_obj = { :status => 'error', :message => latest.errors.values.join('; ') }
|
87
|
+
end
|
88
|
+
else
|
89
|
+
do_save = false
|
90
|
+
message = ""
|
91
|
+
|
92
|
+
safeness = XssDetector.safe?(pt.phrase.value, pt.value, I18n.default_locale, pt.locale)
|
93
|
+
|
94
|
+
if pt.value.empty?
|
95
|
+
message = "Translation empty; nothing saved."
|
96
|
+
elsif do_xss_check && !safeness[:safe]
|
97
|
+
message = safeness[:reason]
|
98
|
+
elsif (latest && value_same_as_latest) && (latest.user_id != 0)
|
99
|
+
if !allow_verification
|
100
|
+
allowed, error = false, "Verification of non-stale phrases is currently disabled in '#{pt.locale}'"
|
101
|
+
else
|
102
|
+
allowed, error = pt.verification_allowed?(latest)
|
103
|
+
end
|
104
|
+
if allowed
|
105
|
+
pt.is_verification = true
|
106
|
+
do_save = true
|
107
|
+
message = "Translation verified."
|
108
|
+
else
|
109
|
+
message = error
|
110
|
+
end
|
111
|
+
else
|
112
|
+
do_save = true
|
113
|
+
message = "Translation saved."
|
114
|
+
end
|
115
|
+
|
116
|
+
if do_save
|
117
|
+
if pt.save
|
118
|
+
response_obj = {
|
119
|
+
:status => 'success',
|
120
|
+
:message => message,
|
121
|
+
:phrase_id => pt.phrase_id,
|
122
|
+
:key => pt.key,
|
123
|
+
:locale => pt.locale,
|
124
|
+
:value => pt.value,
|
125
|
+
:is_verification => pt.is_verification
|
126
|
+
}
|
127
|
+
else
|
128
|
+
response_obj = {:status => 'error', :message => pt.errors.values.join('; ')}
|
129
|
+
end
|
130
|
+
else
|
131
|
+
response_obj = {:status => 'error', :message => message}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
response_obj
|
136
|
+
end
|
137
|
+
|
138
|
+
# Computes monthly activity for months between opts[:since] and opts[:to].
|
139
|
+
# If opts[:to] is not set, starts from the current month.
|
140
|
+
# If opts[:since] is not set, goes back until a month in which there is no activity.
|
141
|
+
# If user_id is 0, computes activity for all users.
|
59
142
|
def self.translator_activity_data_master(user_id = 0, opts = {})
|
60
|
-
|
143
|
+
to_date = opts[:to] ? opts[:to] : Date.today
|
144
|
+
d = to_date.beginning_of_month
|
61
145
|
activities = []
|
62
146
|
|
63
147
|
while true
|
148
|
+
break if opts[:since] && d < opts[:since]
|
149
|
+
|
64
150
|
if d < Date.new(2012, 8, 02)
|
65
151
|
activity_for_month = translator_activity_data(user_id, opts.merge(:since => d, :to => (d >> 1)))
|
66
152
|
elsif d < Date.new(2012, 10, 02)
|
@@ -69,7 +155,7 @@ module Air18n
|
|
69
155
|
activity_for_month = translator_activity_data_v3(user_id, opts.merge(:since => d, :to => (d >> 1)))
|
70
156
|
end
|
71
157
|
|
72
|
-
break if
|
158
|
+
break if !opts[:since] && activity_for_month.blank?
|
73
159
|
|
74
160
|
activities += activity_for_month
|
75
161
|
d = d << 1
|
@@ -376,8 +462,16 @@ module Air18n
|
|
376
462
|
compare_to = translation_pair[:previous_translation].scan(/[[:alnum:]]+/).size
|
377
463
|
end
|
378
464
|
|
379
|
-
|
380
|
-
|
465
|
+
if compare_to == 0
|
466
|
+
proportion_translated = 0.0
|
467
|
+
proportion_verified = 0.0
|
468
|
+
|
469
|
+
LoggingHelper.error("Word counts for weird pair: #{translation_pair.inspect}")
|
470
|
+
LoggingHelper.error("distance: #{distance.inspect}, compare to: #{compare_to.inspect}")
|
471
|
+
else
|
472
|
+
proportion_translated = [distance.to_f / compare_to.to_f, 1.0].min
|
473
|
+
proportion_verified = 1.0 - proportion_translated
|
474
|
+
end
|
381
475
|
end
|
382
476
|
|
383
477
|
[(translation_pair[:source_word_count] * proportion_translated).round,
|
@@ -637,6 +731,7 @@ module Air18n
|
|
637
731
|
end
|
638
732
|
|
639
733
|
private
|
734
|
+
|
640
735
|
def quote_vars(vars)
|
641
736
|
vars.map { |var| "%{#{var}}" }.join(", ")
|
642
737
|
end
|
@@ -679,5 +774,17 @@ module Air18n
|
|
679
774
|
# ap dm
|
680
775
|
dm[input_b.length][input_a.length]
|
681
776
|
end
|
777
|
+
|
778
|
+
# Called on every translation before it is stored in the database.
|
779
|
+
# Strips whitespace from ends and makes an attempt to remove spurious HTML
|
780
|
+
# changes like reordering of HTML attributes within tags.
|
781
|
+
def self.normalize_translation_value(value)
|
782
|
+
# Translators like to add extra whitespace, and extra whitespace sometimes
|
783
|
+
# messes up things!
|
784
|
+
# We also remove alt="" because ckeditor likes to add that for no reason,
|
785
|
+
# and it adds it in different order arbitrarily which makes translations
|
786
|
+
# change even when they don't really change.
|
787
|
+
value.strip.gsub(' alt=""', '')
|
788
|
+
end
|
682
789
|
end
|
683
790
|
end
|
data/lib/air18n/version.rb
CHANGED
@@ -132,6 +132,88 @@ describe Air18n::PhraseTranslation do
|
|
132
132
|
end
|
133
133
|
end
|
134
134
|
|
135
|
+
context 'create_translation' do
|
136
|
+
it 'should create translations properly' do
|
137
|
+
phrase = FactoryGirl.create(:phrase, :key => 'create translation', :value => 'why hello there')
|
138
|
+
user_id_first = 9
|
139
|
+
user_id_second = 10
|
140
|
+
do_xss_check = true
|
141
|
+
allow_verification = true
|
142
|
+
|
143
|
+
##### 1. First translation of new phrase.
|
144
|
+
|
145
|
+
response = Air18n::PhraseTranslation.create_translation(
|
146
|
+
phrase.id, phrase.key, :es, 'whyo helloo thereo newword0', user_id_first, do_xss_check, allow_verification)
|
147
|
+
response.should include(
|
148
|
+
:status => 'success', :message => 'Translation saved.')
|
149
|
+
|
150
|
+
translation = phrase.latest_translation(:es)
|
151
|
+
translation.value.should == 'whyo helloo thereo newword0'
|
152
|
+
translation.key.should == 'create translation'
|
153
|
+
translation.source_word_count.should == 3
|
154
|
+
translation.source_hash.should == "2ac1a5cdad0d3ca0b0c318000c2f7378"
|
155
|
+
|
156
|
+
##### 2. Tweak to existing translation to add some words.
|
157
|
+
|
158
|
+
response = Air18n::PhraseTranslation.create_translation(
|
159
|
+
phrase.id, phrase.key, :es, 'whyo helloo newword1 thereo newword2', user_id_first, do_xss_check, allow_verification)
|
160
|
+
|
161
|
+
translation = phrase.latest_translation(:es)
|
162
|
+
translation.value.should == 'whyo helloo newword1 thereo newword2'
|
163
|
+
translation.source_word_count.should == 3
|
164
|
+
translation.source_hash.should == "2ac1a5cdad0d3ca0b0c318000c2f7378"
|
165
|
+
|
166
|
+
##### 3. Tweak to existing translation to delete a word.
|
167
|
+
|
168
|
+
response = Air18n::PhraseTranslation.create_translation(
|
169
|
+
phrase.id, phrase.key, :es, 'whyo helloo newword1 newword2', user_id_first, do_xss_check, allow_verification)
|
170
|
+
|
171
|
+
translation = phrase.latest_translation(:es)
|
172
|
+
translation.value.should == 'whyo helloo newword1 newword2'
|
173
|
+
translation.source_word_count.should == 3
|
174
|
+
translation.source_hash.should == "2ac1a5cdad0d3ca0b0c318000c2f7378"
|
175
|
+
|
176
|
+
##### 4. Submission of unchanged translation.
|
177
|
+
|
178
|
+
response = Air18n::PhraseTranslation.create_translation(
|
179
|
+
phrase.id, phrase.key, :es, 'whyo helloo newword1 newword2', user_id_first, do_xss_check, allow_verification)
|
180
|
+
response.should include(
|
181
|
+
:status => 'error', :message => 'You last translated this phrase, so somebody else must verify it.')
|
182
|
+
|
183
|
+
unchanged_translation = phrase.latest_translation(:es)
|
184
|
+
unchanged_translation.id.should == translation.id
|
185
|
+
|
186
|
+
##### 5. Submission of unchanged translation for stale phrase.
|
187
|
+
|
188
|
+
phrase.value = 'why hello there, good day'
|
189
|
+
phrase.save!
|
190
|
+
|
191
|
+
response = Air18n::PhraseTranslation.create_translation(
|
192
|
+
phrase.id, phrase.key, :es, 'whyo helloo newword1 newword2', user_id_first, do_xss_check, allow_verification)
|
193
|
+
response.should include(
|
194
|
+
:status => 'success', :message => 'Translation marked as up-to-date.')
|
195
|
+
|
196
|
+
translation = phrase.latest_translation(:es)
|
197
|
+
translation.value.should == 'whyo helloo newword1 newword2'
|
198
|
+
translation.source_word_count.should == 3
|
199
|
+
translation.is_verification.should == false # Not actually a verification.
|
200
|
+
translation.source_hash.should == "2ac1a5cdad0d3ca0b0c318000c2f7378"
|
201
|
+
|
202
|
+
##### 6. Verification by a different translator.
|
203
|
+
|
204
|
+
response = Air18n::PhraseTranslation.create_translation(
|
205
|
+
phrase.id, phrase.key, :es, 'whyo helloo newword1 newword2', user_id_second, do_xss_check, allow_verification)
|
206
|
+
response.should include(
|
207
|
+
:status => 'success', :message => 'Translation verified.')
|
208
|
+
|
209
|
+
translation = phrase.latest_translation(:es)
|
210
|
+
translation.value.should == 'whyo helloo newword1 newword2'
|
211
|
+
translation.source_word_count.should == 5
|
212
|
+
translation.is_verification.should == true
|
213
|
+
translation.source_hash.should == "6f20684ed3e1fa2c9ccd30249d5c9f7a"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
135
217
|
context 'word counts' do
|
136
218
|
it 'should keep track of translated and verified word count in old regime' do
|
137
219
|
Air18n::PhraseTranslation.destroy_all
|
@@ -148,7 +230,7 @@ describe Air18n::PhraseTranslation do
|
|
148
230
|
phrase_verification = FactoryGirl.create(:phrase_translation, :phrase => @phrase2, :user_id => @user2, :is_verification => true, :created_at => Date.new(2012, 05, 04))
|
149
231
|
end
|
150
232
|
|
151
|
-
data = Air18n::PhraseTranslation.translator_activity_data_master
|
233
|
+
data = Air18n::PhraseTranslation.translator_activity_data_master(0, :since => Date.new(2012, 4, 1))
|
152
234
|
|
153
235
|
data.should include(
|
154
236
|
{:year=>2012,
|
@@ -198,7 +280,7 @@ describe Air18n::PhraseTranslation do
|
|
198
280
|
verification = FactoryGirl.create(:phrase_translation, :value => 'oogie boogie boo', :phrase => @phrase1, :user_id => @user1, :source_hash => 'new default text', :source_word_count => 13, :is_verification => true, :created_at => Date.new(2012, 10, 5))
|
199
281
|
end
|
200
282
|
|
201
|
-
data = Air18n::PhraseTranslation.translator_activity_data_master
|
283
|
+
data = Air18n::PhraseTranslation.translator_activity_data_master(0, :since => Date.new(2012, 9, 1))
|
202
284
|
|
203
285
|
# Example Scenario:
|
204
286
|
# Translator A translates phrase Y when it is 8 English words
|
@@ -530,6 +612,21 @@ describe Air18n::PhraseTranslation do
|
|
530
612
|
words_translated, words_verified = Air18n::PhraseTranslation.word_counts_from_translation_pair(pair)
|
531
613
|
words_translated.should == 0
|
532
614
|
words_verified.should == 1
|
615
|
+
|
616
|
+
pair = {
|
617
|
+
:translation=>".",
|
618
|
+
:previous_translation=>".",
|
619
|
+
:was_stale=>true,
|
620
|
+
:locale=>"pl",
|
621
|
+
:user_id=>2055948,
|
622
|
+
:previous_user_id=>1701571,
|
623
|
+
:datetime=>Date.new(2012, 11, 13),
|
624
|
+
:source_word_count=>0,
|
625
|
+
:phrase_key=>"helpers.label.collection.name"
|
626
|
+
}
|
627
|
+
words_translated, words_verified = Air18n::PhraseTranslation.word_counts_from_translation_pair(pair)
|
628
|
+
words_translated.should == 0
|
629
|
+
words_verified.should == 0
|
533
630
|
end
|
534
631
|
|
535
632
|
it 'should compute correct monthly activities' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: air18n
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.31
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2012-11-
|
16
|
+
date: 2012-11-15 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: i18n
|