air18n 0.1.27 → 0.1.28

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,23 +2,28 @@
2
2
 
3
3
  Rails plugin for amazing internationalization.
4
4
 
5
- ## Installation
5
+ ## Installation and Usage
6
6
 
7
- Add this line to your application's Gemfile:
7
+ For Airbnb users, see wiki page:
8
+ https://github.com/airbnb/airbnb/wiki/How-to-internationalize-your-app
8
9
 
9
- gem 'air18n'
10
+ ## How to add release a new version
10
11
 
11
- And then execute:
12
+ Ask Jason, Spike, or Sri to run:
12
13
 
13
- $ bundle
14
+ gem owner air18n --add name@example.com
14
15
 
15
- Or install it yourself as:
16
+ where "name@example.com" is the email address associated with your
17
+ RubyGems account. If you don't have a RubyGems account, make one, and
18
+ also follow the instructions under "API Access" on the RubyGems "Edit
19
+ Profile" page.
16
20
 
17
- $ gem install air18n
21
+ When developing air18n you can point monorail or another app using it to
22
+ use your local changes like this in the app's Gemfile:
18
23
 
19
- ## Usage
24
+ gem 'air18n', :path => '~/Code/air18n'
20
25
 
21
- TODO: Write usage instructions here
26
+ Then bundle install.
22
27
 
23
28
  ## Contributing
24
29
 
@@ -56,18 +56,20 @@ module Air18n
56
56
  all_locales
57
57
  end
58
58
 
59
- def self.translator_activity_data_master(user_id = 0)
59
+ def self.translator_activity_data_master(user_id = 0, opts = {})
60
60
  d = Date.today.beginning_of_month
61
61
  activities = []
62
62
 
63
63
  while true
64
64
  if d < Date.new(2012, 8, 02)
65
- activity_for_month = translator_activity_data(user_id, :since => d, :to => (d >> 1), :enumerate_keys => false)
65
+ activity_for_month = translator_activity_data(user_id, opts.merge(:since => d, :to => (d >> 1)))
66
+ elsif d < Date.new(2012, 10, 02)
67
+ activity_for_month = translator_activity_data_new(user_id, opts.merge(:since => d, :to => (d >> 1)))
66
68
  else
67
- activity_for_month = translator_activity_data_new(user_id, :since => d, :to => (d >> 1), :enumerate_keys => false)
69
+ activity_for_month = translator_activity_data_v3(user_id, opts.merge(:since => d, :to => (d >> 1)))
68
70
  end
69
71
 
70
- break if activity_for_month.blank? && d < (Date.today - 3.years)
72
+ break if activity_for_month.blank? && !(user_id == 0 && d >= (Date.today - 2.years))
71
73
 
72
74
  activities += activity_for_month
73
75
  d = d << 1
@@ -241,6 +243,198 @@ module Air18n
241
243
  end
242
244
  end
243
245
 
246
+ def self.translator_activity_data_v3 user_id=0, opts={}
247
+ activity_from_translation_pairs(get_translation_pairs(user_id, opts[:since], opts[:to]), opts)
248
+ end
249
+
250
+ def self.activity_from_translation_pairs(translation_pairs, opts={})
251
+ per_user_locale_month_year =
252
+ Hash.new {|h, year| h[year] =
253
+ Hash.new {|h, month| h[month] =
254
+ Hash.new{|h, user_id| h[user_id] =
255
+ Hash.new{|h, locale| h[locale] =
256
+ Hash.new {|h, day| h[day] = {
257
+ :phrases_translated => 0,
258
+ :phrases_verified => 0,
259
+ :words_translated => 0,
260
+ :words_verified => 0,
261
+ :keys_translated => [],
262
+ :keys_verified => [],
263
+ } } } } } }
264
+
265
+ translation_pairs.each do |pair|
266
+ words_translated, words_verified = self.word_counts_from_translation_pair(pair)
267
+ sums = per_user_locale_month_year[
268
+ pair[:datetime].year][
269
+ pair[:datetime].month][
270
+ pair[:user_id]][
271
+ pair[:locale]][
272
+ pair[:datetime].day]
273
+ sums[:words_translated] += words_translated
274
+ sums[:words_verified] += words_verified
275
+ if words_verified > 0
276
+ sums[:keys_verified] << pair[:phrase_key]
277
+ sums[:phrases_verified] += 1
278
+ else
279
+ sums[:keys_translated] << pair[:phrase_key]
280
+ sums[:phrases_translated] += 1
281
+ end
282
+ end
283
+
284
+ ret = []
285
+
286
+ per_user_locale_month_year.each do |year, months|
287
+ months.each do |month, user_ids|
288
+ user_ids.each do |user_id, locales|
289
+ locales.each do |locale, days|
290
+ if opts[:daily]
291
+ days.each do |day, sums|
292
+ ret << {
293
+ :year => year,
294
+ :month => month,
295
+ :day => day,
296
+ :locale => locale,
297
+ :user_id => user_id,
298
+ :activity => {
299
+ :num_translations => sums[:phrases_translated],
300
+ :num_verifications => sums[:phrases_verified],
301
+ :word_count_translations => sums[:words_translated],
302
+ :word_count_verifications => sums[:words_verified],
303
+ :translated_keys => sums[:keys_translated],
304
+ :verified_keys => sums[:keys_verified],
305
+ }
306
+ }
307
+ LoggingHelper.info("keys translated: #{sums[:keys_translated]}")
308
+ LoggingHelper.info("keys verified: #{sums[:keys_verified]}")
309
+ LoggingHelper.info("ret now: #{ret.inspect}")
310
+ end
311
+ else
312
+ monthly_sums = {
313
+ :phrases_translated => 0,
314
+ :phrases_verified => 0,
315
+ :words_translated => 0,
316
+ :words_verified => 0,
317
+ :keys_translated => [],
318
+ :keys_verified => [],
319
+ }
320
+ days.each do |day, sums|
321
+ sums.keys.each do |key|
322
+ monthly_sums[key] += sums[key]
323
+ end
324
+ end
325
+ ret << {
326
+ :year => year,
327
+ :month => month,
328
+ :locale => locale,
329
+ :user_id => user_id,
330
+ :activity => {
331
+ :num_translations => monthly_sums[:phrases_translated],
332
+ :num_verifications => monthly_sums[:phrases_verified],
333
+ :word_count_translations => monthly_sums[:words_translated],
334
+ :word_count_verifications => monthly_sums[:words_verified],
335
+ :translated_keys => monthly_sums[:keys_translated],
336
+ :verified_keys => monthly_sums[:keys_verified],
337
+ }
338
+ }
339
+ LoggingHelper.info("keys translated: #{monthly_sums[:keys_translated]}")
340
+ LoggingHelper.info("keys verified: #{monthly_sums[:keys_verified]}")
341
+ LoggingHelper.info("ret now: #{ret.inspect}")
342
+ end
343
+ end
344
+ end
345
+ end
346
+ end
347
+
348
+ ret
349
+ end
350
+
351
+ SPACELESS_LANGUAGES = [:ja, :zh, :th]
352
+
353
+ def self.word_counts_from_translation_pair(translation_pair)
354
+ if translation_pair[:previous_translation].blank?
355
+ proportion_translated = 1.0
356
+ proportion_verified = 0.0
357
+ elsif !translation_pair[:was_stale] &&
358
+ translation_pair[:previous_user_id] == translation_pair[:user_id]
359
+ # Don't pay anything for retranslating your own work.
360
+ proportion_translated = 0.0
361
+ proportion_verified = 0.0
362
+ else
363
+ # Pay for translating changed/added parts, and for verifying the rest.
364
+
365
+ if SPACELESS_LANGUAGES.include?(translation_pair[:locale].to_sym)
366
+ distance = levenshtein_distance_fast(
367
+ translation_pair[:previous_translation].split(''),
368
+ translation_pair[:translation].split(''),
369
+ 1, 0, 1)
370
+ compare_to = translation_pair[:previous_translation].size
371
+ else
372
+ distance = levenshtein_distance_fast(
373
+ PhraseTranslation.segment(translation_pair[:previous_translation]),
374
+ PhraseTranslation.segment(translation_pair[:translation]),
375
+ 1, 0, 1)
376
+ compare_to = PhraseTranslation.segment(translation_pair[:previous_translation]).size
377
+ end
378
+
379
+ proportion_translated = [distance.to_f / compare_to.to_f, 1.0].min
380
+ proportion_verified = 1.0 - proportion_translated
381
+ end
382
+
383
+ [(translation_pair[:source_word_count] * proportion_translated).round,
384
+ (translation_pair[:source_word_count] * proportion_verified).round]
385
+ end
386
+
387
+ def self.get_translation_pairs(user_id, from_date, to_date)
388
+ ret = []
389
+ Phrase.select(:id).find_in_batches do |batch|
390
+ pt_scope = PhraseTranslation.where(:phrase_id => batch)
391
+ if from_date
392
+ pt_scope = pt_scope.where("created_at >= ?", from_date)
393
+ end
394
+ if to_date
395
+ pt_scope = pt_scope.where("created_at < ?", to_date)
396
+ end
397
+ if user_id != 0
398
+ pt_scope = pt_scope.where(:user_id => user_id)
399
+ end
400
+ phrase_to_phrase_translations = pt_scope.all.group_by { |pt| [pt.locale, pt.phrase_id] }
401
+ phrase_to_phrase_translations.each do |(locale, phrase_id), phrase_translations|
402
+ phrase_translations.sort_by! { |pt| pt.created_at }
403
+ LoggingHelper.info "Getting previous translation!"
404
+ previous_translation = PhraseTranslation.where("created_at < ?", phrase_translations.first.created_at - 1.second).
405
+ where(:locale => locale).
406
+ where(:phrase_id => phrase_id).
407
+ order("created_at DESC").
408
+ first
409
+ phrase_translations.each do |pt|
410
+ if previous_translation
411
+ previous_translation_text = previous_translation.value
412
+ previous_translation_user_id = previous_translation.user_id
413
+ was_stale = previous_translation.source_hash != pt.source_hash
414
+ else
415
+ previous_translation_text = ''
416
+ previous_translation_user_id = 0
417
+ was_stale = false
418
+ end
419
+ translation_text = pt.value
420
+ ret << { :translation => translation_text,
421
+ :previous_translation => previous_translation_text,
422
+ :was_stale => was_stale,
423
+ :locale => pt.locale,
424
+ :user_id => pt.user_id,
425
+ :previous_user_id => previous_translation_user_id,
426
+ :datetime => pt.created_at,
427
+ :source_word_count => pt.source_word_count,
428
+ :phrase_key => pt.key,
429
+ }
430
+
431
+ previous_translation = pt
432
+ end
433
+ end
434
+ end
435
+ ret
436
+ end
437
+
244
438
  # Helper method for translator_activity_data which counts words and payment
245
439
  # of a set of translations and verifications, in ((is verification bool => (phrase id =>
246
440
  # source word count map)).
@@ -457,5 +651,32 @@ module Air18n
457
651
  def self.created_at_year_sql
458
652
  Air18n::PhraseTranslation.connection.adapter_name.starts_with?('Mysql') ? 'YEAR(pt.created_at)' : 'STRFTIME("%Y", pt.created_at)'
459
653
  end
654
+
655
+ # Unlike Text::Levenshtein, this supports passing in arrays, which is useful
656
+ # for computing word-level levenshtein distance.
657
+ def self.levenshtein_distance_fast(input_a, input_b, insertion_penalty=1, deletion_penalty=1, substitution_penalty=1)
658
+ return nil if input_a.nil? || input_b.nil?
659
+
660
+ dm = []
661
+ dm[0] = (0..input_a.length).collect { |i| i * deletion_penalty}
662
+ fill = [0] * (input_a.length)
663
+
664
+ for i in 1..input_b.length
665
+ dm[i] = [i * insertion_penalty] + fill
666
+ end
667
+
668
+ for i in 1..input_b.length
669
+ for j in 1..input_a.length
670
+ dm[i][j] = [
671
+ dm[i-1][j-1] + (input_a[j-1] == input_b[i-1] ? 0 : substitution_penalty),
672
+ dm[i][j-1] + deletion_penalty,
673
+ dm[i-1][j] + insertion_penalty
674
+ ].min
675
+ end
676
+ end
677
+
678
+ # ap dm
679
+ dm[input_b.length][input_a.length]
680
+ end
460
681
  end
461
682
  end
@@ -1,3 +1,3 @@
1
1
  module Air18n
2
- VERSION = "0.1.27"
2
+ VERSION = "0.1.28"
3
3
  end
@@ -46,7 +46,7 @@ describe Air18n::PhraseTranslation do
46
46
 
47
47
  it 'Should check variables' do
48
48
  phrase = FactoryGirl.create(:phrase, :key => 'phone_number_not_revealed_time_zone', :value => "Your number won't be revealed. They can only call from 9am to 9pm %{time_zone}.")
49
- translation = FactoryGirl.create(:phrase_translation, :phrase => phrase, :key => 'phone_number_not_revealed_time_zone', :value => '您的電話號碼將不會被透露。他們只能在上午9時至晚上9時%{time_zone}這段時間給您致電')
49
+ translation = FactoryGirl.create(:phrase_translation, :locale => :zh, :phrase => phrase, :key => 'phone_number_not_revealed_time_zone', :value => '您的電話號碼將不會被透露。他們只能在上午9時至晚上9時%{time_zone}這段時間給您致電')
50
50
  validated = translation.save
51
51
  validated.should == true
52
52
  translation.value = '您的電話號碼將不會被透露。他們只能在上午9時至晚上9時%{time_zon}這段時間給您致電'
@@ -133,7 +133,9 @@ describe Air18n::PhraseTranslation do
133
133
  end
134
134
 
135
135
  context 'word counts' do
136
- it 'should keep track of translated and verified word count in new regime' do
136
+ it 'should keep track of translated and verified word count in old regime' do
137
+ Air18n::PhraseTranslation.destroy_all
138
+
137
139
  @user1 = 1
138
140
  @user2 = 2
139
141
  @phrase1 = FactoryGirl.create(:phrase, :key => 'old regime key 1', :value => 'two words')
@@ -177,6 +179,8 @@ describe Air18n::PhraseTranslation do
177
179
 
178
180
 
179
181
  it 'should keep track of translated and verified word count in new regime' do
182
+ Air18n::PhraseTranslation.destroy_all
183
+
180
184
  @user1 = 3
181
185
  @user2 = 4
182
186
  @phrase1 = FactoryGirl.create(:phrase, :key => 'new regime payments key 1', :value => 'two words')
@@ -240,6 +244,307 @@ describe Air18n::PhraseTranslation do
240
244
  :verified_keys=>[["new regime payments key 1", "38a72c02b5febe4975fdcf19d32e412d"]]}
241
245
  )
242
246
  end
243
- end
244
247
 
248
+ context 'v3 regime' do
249
+ before :all do
250
+ Air18n::PhraseTranslation.destroy_all
251
+
252
+ @user1 = 5
253
+ @user2 = 6
254
+
255
+ @phrase1 = FactoryGirl.create(:phrase, :key => 'v3 regime payments key 1', :value => "one two three four five six seven eight")
256
+ @phrase2 = FactoryGirl.create(:phrase, :key => 'v3 regime payments key 2', :value => "one two three four five six")
257
+
258
+ without_timestamping_of Air18n::PhraseTranslation do
259
+ ##### Phrase 1
260
+
261
+ # User 1 translates Phrase 1 for the first time [edit of untranslated phrase]
262
+ translation = FactoryGirl.create(:phrase_translation, :locale => :de, :value => 'urgle burgle', :phrase => @phrase1, :user_id => @user1, :created_at => Date.new(2012, 11, 1))
263
+
264
+ # User 1 tweaks his own translation of Phrase 1. [edit of own unverified translation]
265
+ translation_tweak = FactoryGirl.create(:phrase_translation, :locale => :de, :value => 'urgle burgle boo', :phrase => @phrase1, :user_id => @user1, :created_at => Date.new(2012, 11, 2))
266
+
267
+ # User 2 verifies translation of Phrase 1. [pure verification]
268
+ verification_by_other_user = FactoryGirl.create(:phrase_translation, :locale => :de, :value => 'urgle burgle boo', :phrase => @phrase1, :user_id => @user2, :is_verification => true, :created_at => Date.new(2012, 11, 3))
269
+
270
+ # Phrase 1 becomes stale.
271
+ @phrase1.value = "one two three four five six seven eight nine ten eleven twelve thirteen"
272
+ @phrase1.save!
273
+
274
+ # User 1 reedits translation of Phrase 1. [edit of other's stale translation]
275
+ retranslation = FactoryGirl.create(:phrase_translation, :locale => :de, :value => 'oogie boogie boo', :phrase => @phrase1, :user_id => @user1, :created_at => Date.new(2012, 11, 4))
276
+
277
+ # User 2 pure-verifies of Phrase 1.
278
+ other_user_translation = FactoryGirl.create(:phrase_translation, :locale => :de, :value => 'oogie boogie boo', :is_verification => true, :phrase => @phrase1, :user_id => @user2, :created_at => Date.new(2012, 11, 5))
279
+
280
+ # User 1 edits verified translation of Phrase 1. [edit of other's verified translation]
281
+ verification = FactoryGirl.create(:phrase_translation, :locale => :de, :value => 'oogie booooogie boo hat ogl', :phrase => @phrase1, :user_id => @user1, :is_verification => true, :created_at => Date.new(2012, 11, 6))
282
+
283
+ ##### Phrase 2
284
+
285
+ # User 1 translates Phrase 2 for the first time, but earlier in the year.
286
+ translation = FactoryGirl.create(:phrase_translation, :locale => :de, :value => 'The only verdict is vengence', :phrase => @phrase2, :user_id => @user1, :created_at => Date.new(2012, 9, 1))
287
+
288
+ # User 1 tweaks their own translation later.
289
+ translation = FactoryGirl.create(:phrase_translation, :locale => :de, :value => 'The only verdict is VENGENCE', :phrase => @phrase2, :user_id => @user1, :created_at => Date.new(2012, 11, 1))
290
+
291
+ # User 2 tweaks translation of Phrase 2.
292
+ translation = FactoryGirl.create(:phrase_translation, :locale => :de, :value => 'a vendetta, held as a votive', :is_verification => true, :phrase => @phrase2, :user_id => @user2, :created_at => Date.new(2012, 11, 2))
293
+
294
+ # User 2 tweaks their own translation of Phrase 2. [edit of own verified translation]
295
+ translation = FactoryGirl.create(:phrase_translation, :locale => :de, :value => 'a vendetta, held as a votive, not in vain', :is_verification => true, :phrase => @phrase2, :user_id => @user2, :created_at => Date.new(2012, 11, 3))
296
+
297
+ # Phrase 2 becomes stale.
298
+ @phrase2.value = "one two three four five six seven eight nine"
299
+ @phrase2.save!
300
+
301
+ # User 2 verifies existing translation of Phrase 2. [pure verification of own stale translation]
302
+ translation = FactoryGirl.create(:phrase_translation, :locale => :de, :value => 'a vendetta, held as a votive, not in vain', :is_verification => true, :phrase => @phrase2, :user_id => @user2, :created_at => Date.new(2012, 11, 4))
303
+
304
+ # Phrase 2 becomes stale again.
305
+ @phrase2.value = "one two three"
306
+ @phrase2.save!
307
+
308
+ # User 2 tweaks translation of Phrase 2. [pure verification of own stale translation]
309
+ translation = FactoryGirl.create(:phrase_translation, :locale => :de, :value => 'a vendetta, held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous', :is_verification => true, :phrase => @phrase2, :user_id => @user2, :created_at => Date.new(2012, 11, 5))
310
+ end
311
+
312
+ @golden_pairs =
313
+ [{:translation=>"urgle burgle",
314
+ :previous_translation=>"",
315
+ :was_stale=>false,
316
+ :locale=>"de",
317
+ :user_id=>5,
318
+ :previous_user_id=>0,
319
+ :datetime=>Date.new(2012, 11, 01),
320
+ :source_word_count=>8,
321
+ :phrase_key=>'v3 regime payments key 1'},
322
+ {:translation=>"urgle burgle boo",
323
+ :previous_translation=>"urgle burgle",
324
+ :was_stale=>false,
325
+ :locale=>"de",
326
+ :user_id=>5,
327
+ :previous_user_id=>5,
328
+ :datetime=>Date.new(2012, 11, 02),
329
+ :source_word_count=>8,
330
+ :phrase_key=>'v3 regime payments key 1'},
331
+ {:translation=>"urgle burgle boo",
332
+ :previous_translation=>"urgle burgle boo",
333
+ :was_stale=>false,
334
+ :locale=>"de",
335
+ :user_id=>6,
336
+ :previous_user_id=>5,
337
+ :datetime=>Date.new(2012, 11, 03),
338
+ :source_word_count=>8,
339
+ :phrase_key=>'v3 regime payments key 1'},
340
+ {:translation=>"oogie boogie boo",
341
+ :previous_translation=>"urgle burgle boo",
342
+ :was_stale=>true,
343
+ :locale=>"de",
344
+ :user_id=>5,
345
+ :previous_user_id=>6,
346
+ :datetime=>Date.new(2012, 11, 04),
347
+ :source_word_count=>13,
348
+ :phrase_key=>'v3 regime payments key 1'},
349
+ {:translation=>"oogie boogie boo",
350
+ :previous_translation=>"oogie boogie boo",
351
+ :was_stale=>false,
352
+ :locale=>"de",
353
+ :user_id=>6,
354
+ :previous_user_id=>5,
355
+ :datetime=>Date.new(2012, 11, 05),
356
+ :source_word_count=>13,
357
+ :phrase_key=>'v3 regime payments key 1'},
358
+ {:translation=>"oogie booooogie boo hat ogl",
359
+ :previous_translation=>"oogie boogie boo",
360
+ :was_stale=>false,
361
+ :locale=>"de",
362
+ :user_id=>5,
363
+ :previous_user_id=>6,
364
+ :datetime=>Date.new(2012, 11, 06),
365
+ :source_word_count=>13,
366
+ :phrase_key=>'v3 regime payments key 1'},
367
+ {:translation=>"The only verdict is VENGENCE",
368
+ :previous_translation=>"The only verdict is vengence",
369
+ :was_stale=>false,
370
+ :locale=>"de",
371
+ :user_id=>5,
372
+ :previous_user_id=>5,
373
+ :datetime=>Date.new(2012, 11, 01),
374
+ :source_word_count=>6,
375
+ :phrase_key=>'v3 regime payments key 2'},
376
+ {:translation=>"a vendetta, held as a votive",
377
+ :previous_translation=>"The only verdict is VENGENCE",
378
+ :was_stale=>false,
379
+ :locale=>"de",
380
+ :user_id=>6,
381
+ :previous_user_id=>5,
382
+ :datetime=>Date.new(2012, 11, 02),
383
+ :source_word_count=>6,
384
+ :phrase_key=>'v3 regime payments key 2'},
385
+ {:translation=>"a vendetta, held as a votive, not in vain",
386
+ :previous_translation=>"a vendetta, held as a votive",
387
+ :was_stale=>false,
388
+ :locale=>"de",
389
+ :user_id=>6,
390
+ :previous_user_id=>6,
391
+ :datetime=>Date.new(2012, 11, 03),
392
+ :source_word_count=>6,
393
+ :phrase_key=>'v3 regime payments key 2'},
394
+ {:translation=>"a vendetta, held as a votive, not in vain",
395
+ :previous_translation=>"a vendetta, held as a votive, not in vain",
396
+ :was_stale=>true,
397
+ :locale=>"de",
398
+ :user_id=>6,
399
+ :previous_user_id=>6,
400
+ :datetime=>Date.new(2012, 11, 04),
401
+ :source_word_count=>9,
402
+ :phrase_key=>'v3 regime payments key 2'},
403
+ {:translation=>
404
+ "a vendetta, held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous",
405
+ :previous_translation=>"a vendetta, held as a votive, not in vain",
406
+ :was_stale=>true,
407
+ :locale=>"de",
408
+ :user_id=>6,
409
+ :previous_user_id=>6,
410
+ :datetime=>Date.new(2012, 11, 05),
411
+ :source_word_count=>3,
412
+ :phrase_key=>'v3 regime payments key 2'}]
413
+
414
+ @golden_word_counts =
415
+ [[8, 0], # "" -> "urgle burgle", user 5
416
+ [0, 0], # same translator making an edit, user 5
417
+ [0, 8], # different translator verifying, user 6
418
+ [9, 4], # edit of stale phrase; 2/3 words changed, 2/3*13=8.66; rounds to 9, user 5
419
+ [0, 13], # verification, user 6
420
+ [13, 0], # 3 new words; 3/3 words changed, 3/3*13=13, user 5
421
+ [0, 0], # Tweak of a translation that was done in past by same person, user 5
422
+ [6, 0], # Rewrite of translation by other person, user 6
423
+ [0, 0], # Tweak by same person, user 6
424
+ [0, 9], # Pure verification of stale, user 6
425
+ [3, 0]] # Edit of stale, user 6
426
+
427
+ @golden_monthly_activity =
428
+ [{:year=>2012,
429
+ :month=>11,
430
+ :locale=>"de",
431
+ :user_id=>5,
432
+ :activity=>
433
+ {:num_translations=>4,
434
+ :num_verifications=>1,
435
+ :word_count_translations=>30,
436
+ :word_count_verifications=>4,
437
+ :translated_keys=>["v3 regime payments key 1",
438
+ "v3 regime payments key 2",
439
+ "v3 regime payments key 1",
440
+ "v3 regime payments key 1"],
441
+ :verified_keys=>["v3 regime payments key 1"]}},
442
+ {:year=>2012,
443
+ :month=>11,
444
+ :locale=>"de",
445
+ :user_id=>6,
446
+ :activity=>
447
+ {:num_translations=>3,
448
+ :num_verifications=>3,
449
+ :word_count_translations=>9,
450
+ :word_count_verifications=>30,
451
+ :translated_keys=>["v3 regime payments key 2",
452
+ "v3 regime payments key 2",
453
+ "v3 regime payments key 2"],
454
+ :verified_keys=>["v3 regime payments key 1",
455
+ "v3 regime payments key 1",
456
+ "v3 regime payments key 2"]}}]
457
+ end
458
+
459
+ it 'should be able to enumerate pairs of translations' do
460
+ pairs = Air18n::PhraseTranslation.get_translation_pairs(0, Date.new(2012, 11, 1), Date.new(2012, 12, 1))
461
+ pairs = pairs.select { |pair| pair[:locale] == 'de' }
462
+
463
+ pairs.should == @golden_pairs
464
+ end
465
+
466
+ it 'should be able to count words proportional to amount of translation edited' do
467
+ word_counts = @golden_pairs.map { |pair| Air18n::PhraseTranslation.word_counts_from_translation_pair(pair) }
468
+ word_counts.should == @golden_word_counts
469
+ end
470
+
471
+ it 'should be able to count words proportional to amount of translation edited for spaceless languages' do
472
+ pair = {
473
+ :translation=>"泊まりたいところ、きっと見つかる。",
474
+ :previous_translation=>"いいところに泊まろう。",
475
+ :was_stale=>false,
476
+ :locale=>"ja",
477
+ :user_id=>5,
478
+ :previous_user_id=>6,
479
+ :datetime=>Date.new(2012, 11, 04),
480
+ :source_word_count=>5,
481
+ :phrase_key=>'find_a_place_to_stay'
482
+ }
483
+ words_translated, words_verified = Air18n::PhraseTranslation.word_counts_from_translation_pair(pair)
484
+ words_translated.should == 5
485
+ words_verified.should == 0
486
+
487
+ pair = {
488
+ :translation=>"泊まりたいところ、きっと見つかる。",
489
+ :previous_translation=>"泊まりたいところがきっと見つかる。",
490
+ :was_stale=>false,
491
+ :locale=>"ja",
492
+ :user_id=>5,
493
+ :previous_user_id=>6,
494
+ :datetime=>Date.new(2012, 11, 04),
495
+ :source_word_count=>5,
496
+ :phrase_key=>'find_a_place_to_stay'
497
+ }
498
+ words_translated, words_verified = Air18n::PhraseTranslation.word_counts_from_translation_pair(pair)
499
+ words_translated.should == 0 # 1 / 17 * 5 = 0.29, rounds to 0
500
+ words_verified.should == 5
501
+
502
+ pair = {
503
+ :translation=>"泊まりたい所、見つかるよ。",
504
+ :previous_translation=>"泊まりたいところがきっと見つかる。こういうテキストも有り!",
505
+ :was_stale=>false,
506
+ :locale=>"ja",
507
+ :user_id=>5,
508
+ :previous_user_id=>6,
509
+ :datetime=>Date.new(2012, 11, 04),
510
+ :source_word_count=>5,
511
+ :phrase_key=>'find_a_place_to_stay'
512
+ }
513
+ words_translated, words_verified = Air18n::PhraseTranslation.word_counts_from_translation_pair(pair)
514
+ words_translated.should == 1 # 3 / 17 * 5 = 0.88, rounds to 1
515
+ words_verified.should == 4
516
+ end
517
+
518
+ it 'should compute correct monthly activities' do
519
+ data = Air18n::PhraseTranslation.activity_from_translation_pairs(@golden_pairs, :daily => false)
520
+
521
+ data.should == @golden_monthly_activity
522
+ end
523
+
524
+ it 'should compute correct daily activities' do
525
+ data = Air18n::PhraseTranslation.activity_from_translation_pairs(@golden_pairs, :daily => true)
526
+
527
+ data.first.should ==
528
+ {:year=>2012,
529
+ :month=>11,
530
+ :day=>1,
531
+ :locale=>"de",
532
+ :user_id=>5,
533
+ :activity=>
534
+ {:num_translations=>2,
535
+ :num_verifications=>0,
536
+ :word_count_translations=>8,
537
+ :word_count_verifications=>0,
538
+ :translated_keys=>["v3 regime payments key 1", "v3 regime payments key 2"],
539
+ :verified_keys=>[]}}
540
+ end
541
+
542
+ it 'should keep track of translated and verified word count in v3 regime' do
543
+ data = Air18n::PhraseTranslation.translator_activity_data_master
544
+ @golden_monthly_activity.each do |a|
545
+ data.should include(a)
546
+ end
547
+ end
548
+ end
549
+ end
245
550
  end
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.27
4
+ version: 0.1.28
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-08 00:00:00.000000000 Z
16
+ date: 2012-11-10 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: i18n