air18n 0.1.30 → 0.1.31

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- d = Date.today.beginning_of_month
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 activity_for_month.blank? && !(user_id == 0 && d >= (Date.today - 2.years))
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
- proportion_translated = [distance.to_f / compare_to.to_f, 1.0].min
380
- proportion_verified = 1.0 - proportion_translated
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
@@ -1,3 +1,3 @@
1
1
  module Air18n
2
- VERSION = "0.1.30"
2
+ VERSION = "0.1.31"
3
3
  end
@@ -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.30
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-14 00:00:00.000000000 Z
16
+ date: 2012-11-15 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: i18n