air18n 0.1.57 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/air18n.rb CHANGED
@@ -6,6 +6,7 @@ require 'air18n/class_methods'
6
6
  require 'air18n/backend'
7
7
  require 'air18n/priority'
8
8
  require 'air18n/default_text_change_observer'
9
+ require 'air18n/metrics'
9
10
 
10
11
  require 'air18n/reflection'
11
12
 
@@ -216,7 +216,8 @@ module Air18n
216
216
 
217
217
  # Use locale fallbacks that always include default locale as the last
218
218
  # fallback.
219
- @fallbacks = I18n::Locale::Fallbacks.new(default_locale)
219
+ @fallbacks ||= I18n::Locale::Fallbacks.new
220
+ @fallbacks.defaults = [default_locale]
220
221
 
221
222
  # Make the single instance of Air18n::Backend.
222
223
  @air18n_backend = Air18n::Backend.new
@@ -0,0 +1,211 @@
1
+ module Air18n
2
+ module Metrics
3
+ TARGET_PRIORITY_PHRASE_TRANSLATION_PERCENT = 98
4
+
5
+ def self.translation_progress(page_ids)
6
+ search_scope = Air18n::Phrase.still_used
7
+ search_scope = search_scope.select(
8
+ '`phrases`.`id`, `phrases`.`key`, ' +
9
+ '`phrase_translations`.`locale` AS latest_translation_locale, ' +
10
+ '`phrase_translations`.`user_id` AS latest_translation_user_id, ' +
11
+ '`phrase_translations`.`created_at` AS latest_translation_created_at, ' +
12
+ '`phrase_translations`.`is_stale` AS latest_translation_is_stale, ' +
13
+ '`phrase_translations`.`is_verification` AS latest_translation_is_verification'
14
+ )
15
+ search_scope = search_scope.pull_in_all_up_to_date_phrase_translations
16
+ search_scope = search_scope.group('`phrases`.`id`, `phrase_translations`.`phrase_id`, `latest_translation_locale`')
17
+
18
+ if page_ids.present?
19
+ search_scope = search_scope.joins(:phrase_screenshots)
20
+ conditions = page_ids.map do |page_id|
21
+ controller_action = Air18n::PhraseScreenshot.deserialize_page_id(page_id)
22
+ "(phrase_screenshots.controller = '#{controller_action.controller}' AND phrase_screenshots.action = '#{controller_action.action}')"
23
+ end
24
+ search_scope = search_scope.where(conditions.join(' OR '))
25
+ end
26
+
27
+ progress = {}
28
+
29
+ phrases_and_translations = search_scope.all
30
+
31
+ # This block is useful for debugging.
32
+ if false
33
+ LoggingHelper.info "Phrases and translations:"
34
+ phrases_and_translations.each do |thing|
35
+ LoggingHelper.info "key #{thing.key} locale #{thing.latest_translation_locale} user #{thing.latest_translation_user_id} at #{thing.latest_translation_created_at} stale? #{thing.latest_translation_is_stale} verified? #{thing.latest_translation_is_verification}"
36
+ end
37
+ end
38
+
39
+ priority_keys = phrases_and_translations.map { |phrase_and_translation| phrase_and_translation.key }.uniq
40
+ number_of_priority_phrases = priority_keys.size
41
+ number_of_priority_words = priority_keys.map { |k| word_counts[k] }.sum
42
+ locale_to_translations = phrases_and_translations.group_by { |phrase_and_translation| phrase_and_translation.latest_translation_locale }
43
+ locale_to_translations.each do |locale, phrases_and_translations_for_locale|
44
+ if locale.present?
45
+ locale = locale.to_sym
46
+ progress[locale] ||= {}
47
+ phrases_with_translations_for_locale = phrases_and_translations_for_locale.find_all { |phrase_and_translation| phrase_and_translation.latest_translation_user_id }
48
+ phrases_with_verifications_for_locale = phrases_with_translations_for_locale.find_all { |phrase_and_translation| true_value?(phrase_and_translation.latest_translation_is_verification) }
49
+
50
+ words_with_translations_for_locale = phrases_with_translations_for_locale.map { |p| word_counts[p.key] }.sum
51
+ words_with_verifications_for_locale = phrases_with_verifications_for_locale.map { |p| word_counts[p.key] }.sum
52
+ progress[locale][:phrases] = number_of_priority_phrases
53
+ progress[locale][:words] = number_of_priority_words
54
+
55
+ progress[locale][:phrases_translated] = phrases_with_translations_for_locale.size
56
+ progress[locale][:words_translated] = words_with_translations_for_locale
57
+
58
+ progress[locale][:phrases_verified] = phrases_with_verifications_for_locale.size
59
+ progress[locale][:words_verified] = words_with_verifications_for_locale
60
+
61
+ phrases_translated_in_last_day = phrases_with_translations_for_locale.find_all { |phrase_and_translation| since?(phrase_and_translation.latest_translation_created_at, 1.day.ago) }
62
+ phrases_translated_in_last_week = phrases_with_translations_for_locale.find_all { |phrase_and_translation| since?(phrase_and_translation.latest_translation_created_at, 1.week.ago) }
63
+ progress[locale][:phrases_translated_in_last_day] = phrases_translated_in_last_day.size
64
+ progress[locale][:words_translated_in_last_day] = phrases_translated_in_last_day.map { |p| word_counts[p.key] }.sum
65
+ progress[locale][:phrases_translated_in_last_week] = phrases_translated_in_last_week.size
66
+ progress[locale][:words_translated_in_last_week] = phrases_translated_in_last_week.map { |p| word_counts[p.key] }.sum
67
+
68
+ progress[locale][:phrases_translated_percent] = (100 * progress[locale][:phrases_translated].to_f / progress[locale][:phrases]).round
69
+ progress[locale][:words_translated_percent] = (100 * progress[locale][:words_translated].to_f / progress[locale][:words]).round
70
+ progress[locale][:phrases_verified_percent] = (100 * progress[locale][:phrases_verified].to_f / progress[locale][:phrases]).round
71
+ progress[locale][:words_verified_percent] = (100 * progress[locale][:words_verified].to_f / progress[locale][:words]).round
72
+ if progress[locale][:phrases_translated_percent] >= TARGET_PRIORITY_PHRASE_TRANSLATION_PERCENT
73
+ progress[locale][:phrases_progress] = :good
74
+ elsif progress[locale][:phrases_translated_in_last_day] < 50
75
+ progress[locale][:phrases_progress] = :bad
76
+ else
77
+ progress[locale][:phrases_progress] = :okay
78
+ end
79
+
80
+ progress[locale][:phrases_remaining_to_translate] = progress[locale][:phrases] - progress[locale][:phrases_translated]
81
+ progress[locale][:words_remaining_to_translate] = progress[locale][:words] - progress[locale][:words_translated]
82
+
83
+ progress[locale][:phrases_remaining_to_verify] = progress[locale][:phrases] - progress[locale][:phrases_verified]
84
+ progress[locale][:words_remaining_to_verify] = progress[locale][:words] - progress[locale][:words_verified]
85
+
86
+ progress[locale][:translators] = phrases_with_translations_for_locale.inject(Set.new) do |carry, value|
87
+ if since?(value.latest_translation_created_at, 28.days.ago)
88
+ carry.add(value.latest_translation_user_id)
89
+ end
90
+ carry
91
+ end
92
+ end
93
+ end
94
+
95
+ progress
96
+ end
97
+
98
+ def self.translator_progress(locale, user_id)
99
+ scope = PhraseTranslation.where(:locale => locale).where(:user_id => user_id)
100
+ scope = scope.where('created_at >= ?', 28.days.ago)
101
+ scope = scope.group(:phrase_id, :source_hash)
102
+ scope.select("`id`, `key`, `source_word_count`")
103
+
104
+ words_translated_last_month = 0
105
+ words_translated_last_week = 0
106
+ words_verified_last_month = 0
107
+ words_verified_last_week = 0
108
+ phrases_translated_last_month = 0
109
+ number_of_later_modified_phrases_last_month = 0
110
+ phrases_translated = {}
111
+ scope.find_each do |pt|
112
+ if pt.created_at >= 1.week.ago
113
+ if pt.is_verification?
114
+ words_verified_last_week += pt.source_word_count
115
+ else
116
+ words_translated_last_week += pt.source_word_count
117
+ end
118
+ end
119
+
120
+ if pt.is_verification?
121
+ words_verified_last_month += pt.source_word_count
122
+ else
123
+ words_translated_last_month += pt.source_word_count
124
+ end
125
+
126
+ if !pt.is_verification?
127
+ later_modifications = PhraseTranslation.
128
+ where(:phrase_id => pt.phrase_id, :source_hash => pt.source_hash).
129
+ where('created_at > ?', pt.created_at).
130
+ where('user_id != ?', pt.user_id).
131
+ where('payment_details NOT LIKE ?', '{"v3":{"t":0,%').
132
+ count
133
+ if later_modifications > 0
134
+ number_of_later_modified_phrases_last_month += 1
135
+ end
136
+ phrases_translated_last_month += 1
137
+ end
138
+ end
139
+
140
+ daily_average = (words_translated_last_month + words_verified_last_month) / 28
141
+ later_modified_percent = if phrases_translated_last_month == 0
142
+ 0
143
+ else
144
+ (100.0 * number_of_later_modified_phrases_last_month.to_f / phrases_translated_last_month).round
145
+ end
146
+ classification = words_translated_last_month > words_verified_last_month ? :translator : :verifier
147
+
148
+ {
149
+ :user_id => user_id,
150
+ :classification => classification,
151
+ :phrases_translated_last_month => phrases_translated_last_month,
152
+ :words_translated_last_month => words_translated_last_month,
153
+ :words_translated_last_week => words_translated_last_week,
154
+ :words_verified_last_month => words_verified_last_month,
155
+ :words_verified_last_week => words_verified_last_week,
156
+ :words_verified_last_week => words_verified_last_week,
157
+ :daily_average => daily_average,
158
+ :number_of_later_modified_phrases_last_month => number_of_later_modified_phrases_last_month,
159
+ :later_modified_percent => later_modified_percent
160
+ }
161
+ end
162
+
163
+ # Computes translation progress for all pages, then translator progress for
164
+ # all translators in each locale.
165
+ def self.metrics_suite
166
+ p = translation_progress(nil)
167
+ p.each do |locale, info|
168
+ info[:translator_progress] = {}
169
+ info[:translators].each do |user_id|
170
+ info[:translator_progress][user_id] = translator_progress(locale, user_id)
171
+ end
172
+ end
173
+ end
174
+
175
+ # Returns scope for searching for most recent PhraseTranslations of a
176
+ # translator.
177
+ def self.recent_work(locale, user_id)
178
+ scope = Air18n::PhraseTranslation
179
+ scope = scope.order('`phrase_translations`.`created_at` DESC')
180
+ scope.includes(:phrase_revision)
181
+ if user_id.present?
182
+ scope = scope.where(:user_id => user_id)
183
+ end
184
+ if locale.present?
185
+ scope = scope.where(:locale => locale)
186
+ end
187
+ scope
188
+ end
189
+
190
+ private
191
+
192
+ def self.word_counts
193
+ # Set up a map of phrase key -> word count.
194
+ @word_counts ||=
195
+ {}.tap do |ret|
196
+ Air18n::Phrase.still_used.find_each do |p|
197
+ ret[p.key] = PhraseTranslation.segment(p.value).size
198
+ end
199
+ end
200
+ end
201
+
202
+ def self.since?(created_at, time_ago)
203
+ time_obj = created_at.is_a?(String) ? Time.parse(created_at) : created_at
204
+ time_obj >= time_ago
205
+ end
206
+
207
+ def self.true_value?(tf_value)
208
+ tf_value == 1 || tf_value == "t" || tf_value == "1"
209
+ end
210
+ end
211
+ end
@@ -12,6 +12,7 @@ module Air18n
12
12
  :"en-CA" => AmericanToCanadian.new,
13
13
  :"en-GB" => AmericanToBritish.new,
14
14
  :"en-NZ" => AmericanToKiwi.new,
15
+ :"en-SG" => AmericanToSinglish.new,
15
16
  },
16
17
  :de => { :"de-CH" => GermanToSwissGerman.new }
17
18
  }
@@ -127,6 +128,13 @@ module Air18n
127
128
  end
128
129
  end
129
130
 
131
+ # Singlish is just normal Colonial English for now.
132
+ class AmericanToSinglish < AmericanToColonial
133
+ def initialize
134
+ super
135
+ end
136
+ end
137
+
130
138
  # Only changes
131
139
  # "[flav]or" to "[flav]our"
132
140
  # "[travel]ed" to "[travel]led"
@@ -1,3 +1,3 @@
1
1
  module Air18n
2
- VERSION = "0.1.57"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -158,12 +158,12 @@ describe Air18n::Backend do
158
158
  context 'Language fallbacks' do
159
159
  it 'should fall back to default locale' do
160
160
  old_default_locale = I18n.default_locale
161
- I18n.reset(:fr)
161
+ I18n.reset(:zq)
162
162
  @backend = Air18n::Backend.new
163
- @backend.lookup(:fr, 'fallbacks, key 1', @scope, :default => 'merci').should == 'merci'
164
- @backend.lookup(:es, 'fallbacks, key 1', @scope, :default => 'merci').should == 'merci'
165
- @backend.lookup(:es, 'fallbacks, key 1', @scope).should == 'merci'
166
- @backend.lookup(:en, 'fallbacks, key 1', @scope).should == 'merci'
163
+ @backend.lookup(:zq, 'fallbacks, key 1', @scope, :default => 'merci').should == 'merci'
164
+ @backend.lookup(:zv, 'fallbacks, key 1', @scope, :default => 'merci').should == 'merci'
165
+ @backend.lookup(:zv, 'fallbacks, key 1', @scope).should == 'merci'
166
+ @backend.lookup(:zy, 'fallbacks, key 1', @scope).should == 'merci'
167
167
 
168
168
  # Restore the old default locale.
169
169
  I18n.reset(old_default_locale)
@@ -0,0 +1,276 @@
1
+ require 'spec_helper'
2
+
3
+ describe Air18n::Metrics do
4
+ before :all do
5
+ @translator1_fr_id = 7
6
+ @translator2_fr_id = 9
7
+
8
+ # Translator 3 for French hasn't translated for 2 months. Shouldn't be
9
+ # included in recent translators list.
10
+ @translator3_fr_id = 11
11
+
12
+ # Translator 4 fixes translator 1 and 2's work.
13
+ @translator4_fr_id = 17
14
+
15
+ @translator1_it_id = 13
16
+ @translator2_it_id = 19
17
+
18
+ @translator1_ko_id = 15
19
+
20
+ @phrase1 = FactoryGirl.create(:phrase, :value => 'one two three four five six seven eight nine')
21
+ @phrase2 = FactoryGirl.create(:phrase)
22
+ @phrase3 = FactoryGirl.create(:phrase)
23
+ @phrase4 = FactoryGirl.create(:phrase)
24
+ @phrase5 = FactoryGirl.create(:phrase)
25
+ @phrase6 = FactoryGirl.create(:phrase)
26
+ @phrase7 = FactoryGirl.create(:phrase)
27
+
28
+ # info#press is a manually-prioritized page.
29
+ @phrase3_screenshot1 = FactoryGirl.create(
30
+ :phrase_screenshot,
31
+ :controller => 'info',
32
+ :action => 'press',
33
+ :phrase => @phrase3)
34
+
35
+ # info#unimportant is unimportant.
36
+ @phrase2_screenshot1 = FactoryGirl.create(
37
+ :phrase_screenshot,
38
+ :controller => 'info',
39
+ :action => 'unimportant',
40
+ :phrase => @phrase2)
41
+
42
+ # info#obsolete_page is a obsolete page with only obsolete phrases.
43
+ @phrase4_screenshot1 = FactoryGirl.create(
44
+ :phrase_screenshot,
45
+ :controller => 'info',
46
+ :action => 'obsolete_page',
47
+ :phrase => @phrase4)
48
+
49
+ # home#index is high-pageviews high priority.
50
+ @phrase1_screenshot1 = FactoryGirl.create(
51
+ :phrase_screenshot,
52
+ :controller => 'home',
53
+ :action => 'index',
54
+ :phrase => @phrase1)
55
+ @phrase3_screenshot2 = FactoryGirl.create(
56
+ :phrase_screenshot,
57
+ :controller => 'home',
58
+ :action => 'index',
59
+ :phrase => @phrase3)
60
+ @phrase5_screenshot1 = FactoryGirl.create(
61
+ :phrase_screenshot,
62
+ :controller => 'home',
63
+ :action => 'index',
64
+ :phrase => @phrase5)
65
+ @phrase6_screenshot1 = FactoryGirl.create(
66
+ :phrase_screenshot,
67
+ :controller => 'home',
68
+ :action => 'index',
69
+ :phrase => @phrase6)
70
+ @phrase7_screenshot1 = FactoryGirl.create(
71
+ :phrase_screenshot,
72
+ :controller => 'home',
73
+ :action => 'index',
74
+ :phrase => @phrase7)
75
+
76
+ without_timestamping_of Air18n::PhraseTranslation do
77
+ @translation0_of_phrase1_fr_today = FactoryGirl.create(
78
+ :phrase_translation,
79
+ :locale => 'fr',
80
+ :phrase => @phrase1,
81
+ :user_id => @translator1_fr_id,
82
+ :created_at => 3.hours.ago)
83
+ @translation1_of_phrase1_fr_today = FactoryGirl.create(
84
+ :phrase_translation,
85
+ :locale => 'fr',
86
+ :phrase => @phrase1,
87
+ :user_id => @translator1_fr_id,
88
+ :created_at => 2.hours.ago)
89
+ @verification1_of_phrase1_fr_today = FactoryGirl.create(
90
+ :phrase_translation,
91
+ :locale => 'fr',
92
+ :phrase => @phrase1,
93
+ :user_id => @translator2_fr_id,
94
+ :is_verification => true,
95
+ :created_at => 1.hour.ago)
96
+ @translation2_of_phrase1_fr_today = FactoryGirl.create(
97
+ :phrase_translation,
98
+ :locale => 'fr',
99
+ :phrase => @phrase1,
100
+ :user_id => @translator4_fr_id,
101
+ :value => "something brand new!",
102
+ :payment_details => '{"v3":{"t":9,"v":0}}',
103
+ :is_verification => true,
104
+ :created_at => 1.hours.ago)
105
+ @translation1_of_phrase2_fr_today = FactoryGirl.create(
106
+ :phrase_translation,
107
+ :locale => 'fr',
108
+ :phrase => @phrase2,
109
+ :user_id => @translator1_fr_id,
110
+ :created_at => 59.minutes.ago)
111
+ @translation1_of_phrase6_fr_way_old = FactoryGirl.create(
112
+ :phrase_translation,
113
+ :locale => 'fr',
114
+ :phrase => @phrase6,
115
+ :user_id => @translator3_fr_id,
116
+ :created_at => 2.months.ago)
117
+ @translation2_of_phrase3_fr_old = FactoryGirl.create(
118
+ :phrase_translation,
119
+ :locale => 'fr',
120
+ :phrase => @phrase3,
121
+ :user_id => @translator1_fr_id,
122
+ :created_at => 3.days.ago)
123
+ @translation2_of_phrase4_fr_old = FactoryGirl.create(
124
+ :phrase_translation,
125
+ :locale => 'fr',
126
+ :phrase => @phrase4,
127
+ :user_id => @translator1_fr_id,
128
+ :created_at => 3.days.ago)
129
+
130
+ # With Italian, we test the odd case where multiple translations have
131
+ # the "is_latest" flag set.
132
+ #
133
+ # (I'm not sure why this ever happens, but in prod database, it does.)
134
+ @translation1_of_phrase1_it_today = FactoryGirl.create(
135
+ :phrase_translation,
136
+ :locale => 'it',
137
+ :phrase => @phrase1,
138
+ :user_id => @translator1_it_id,
139
+ :created_at => 2.hours.ago)
140
+ @translation2_of_phrase1_it_today = FactoryGirl.create(
141
+ :phrase_translation,
142
+ :locale => 'it',
143
+ :phrase => @phrase1,
144
+ :user_id => @translator2_it_id,
145
+ :created_at => 1.hour.ago)
146
+ @translation1_of_phrase1_it_today.update_column(:is_latest, true)
147
+ @translation2_of_phrase1_it_today.update_column(:is_latest, true)
148
+
149
+ @translation1_of_phrase3_ko_old = FactoryGirl.create(
150
+ :phrase_translation,
151
+ :locale => 'ko',
152
+ :phrase => @phrase3,
153
+ :user_id => @translator1_ko_id,
154
+ :created_at => 3.days.ago)
155
+
156
+ @translation1_of_phrase5_fr_today = FactoryGirl.create(
157
+ :phrase_translation,
158
+ :locale => 'fr',
159
+ :phrase => @phrase5,
160
+ :is_stale => true,
161
+ :user_id => @translator1_fr_id,
162
+ :created_at => 58.minutes.ago)
163
+
164
+ # Phrase 7 is not translated at all.
165
+ end
166
+ end
167
+
168
+ before :each do
169
+ @still_used_phrases = [@phrase1.id, @phrase2.id, @phrase3.id, @phrase5.id, @phrase6.id, @phrase7.id]
170
+ I18n.stub(:still_used_phrase_ids => @still_used_phrases)
171
+ end
172
+
173
+ context "#translation_progress" do
174
+ it "should measure priority phrase progress" do
175
+ ppp = Air18n::Metrics.translation_progress(['home#index', 'home#other_action', 'info#press'])
176
+ ppp.size.should == 3
177
+
178
+ ppp[:fr].should include({
179
+ :phrases => 5,
180
+ :words => 21,
181
+ :phrases_translated => 3,
182
+ :words_translated => 15,
183
+ :phrases_verified => 1,
184
+ :words_verified => 9,
185
+ :phrases_translated_in_last_day => 1,
186
+ :words_translated_in_last_day => 9,
187
+ :phrases_translated_in_last_week => 2,
188
+ :words_translated_in_last_week => 12,
189
+ :phrases_translated_percent => 60,
190
+ :words_translated_percent => 71,
191
+ :phrases_verified_percent => 20,
192
+ :words_verified_percent => 43,
193
+ :phrases_progress => :bad,
194
+ :phrases_remaining_to_translate => 2,
195
+ :words_remaining_to_translate => 6,
196
+ :phrases_remaining_to_verify => 4,
197
+ :words_remaining_to_verify => 12,
198
+
199
+ # translator2_fr doesn't appear because he hasn't last touched any phrase.
200
+ :translators => Set.new([@translator1_fr_id, @translator4_fr_id]) })
201
+
202
+ ppp[:it].should include({
203
+ :phrases => 5,
204
+ :words => 21,
205
+ :phrases_translated => 1,
206
+ :words_translated => 9,
207
+ :phrases_translated_in_last_day => 1,
208
+ :phrases_translated_in_last_week => 1,
209
+ :phrases_translated_percent => 20,
210
+ :words_translated_percent => 43,
211
+ :phrases_progress => :bad,
212
+ :phrases_remaining_to_translate => 4,
213
+ :words_remaining_to_translate => 12 })
214
+ ppp[:ko].should include({
215
+ :phrases => 5,
216
+ :words => 21,
217
+ :phrases_translated => 1,
218
+ :words_translated => 3,
219
+ :phrases_translated_in_last_day => 0,
220
+ :phrases_translated_in_last_week => 1,
221
+ :phrases_translated_percent => 20,
222
+ :words_translated_percent => 14,
223
+ :phrases_progress => :bad,
224
+ :phrases_remaining_to_translate => 4,
225
+ :words_remaining_to_translate => 18,
226
+ :translators => Set.new([@translator1_ko_id]) })
227
+ end
228
+ end
229
+
230
+ context "#translator_progress" do
231
+ it "should measure priority phrase progress" do
232
+ p1 = Air18n::Metrics.translator_progress(:fr, @translator1_fr_id)
233
+ p1.should include({
234
+ :user_id => @translator1_fr_id,
235
+ :classification => :translator,
236
+ :words_translated_last_month => 21,
237
+ :words_translated_last_week => 21,
238
+ :words_verified_last_month => 0,
239
+ :words_verified_last_week => 0,
240
+ :daily_average => 0,
241
+ :phrases_translated_last_month => 5,
242
+ :number_of_later_modified_phrases_last_month => 1,
243
+ :later_modified_percent => 20,
244
+ })
245
+
246
+ p2 = Air18n::Metrics.translator_progress(:fr, @translator2_fr_id)
247
+ p2.should include({
248
+ :user_id => @translator2_fr_id,
249
+ :classification => :verifier,
250
+ :words_translated_last_month => 0,
251
+ :words_translated_last_week => 0,
252
+ :words_verified_last_month => 9,
253
+ :words_verified_last_week => 9,
254
+ :daily_average => 0,
255
+ :phrases_translated_last_month => 0,
256
+ :number_of_later_modified_phrases_last_month => 0,
257
+ :later_modified_percent => 0,
258
+ })
259
+ end
260
+ end
261
+
262
+ context "#metrics_suite" do
263
+ it "should work" do
264
+ s = Air18n::Metrics.metrics_suite
265
+ s[:fr][:words_translated].should == 18
266
+ s[:fr][:translator_progress].size.should == 2
267
+ end
268
+ end
269
+
270
+ context "#recent_work" do
271
+ it "should scope somebody's recent work" do
272
+ work = Air18n::Metrics.recent_work(:fr, @translator1_fr_id).limit(2).all
273
+ work.should == [@translation1_of_phrase5_fr_today, @translation1_of_phrase2_fr_today]
274
+ end
275
+ end
276
+ end
@@ -58,6 +58,19 @@ describe Air18n::PrimAndProper do
58
58
  end
59
59
  end
60
60
 
61
+ describe Air18n::AmericanToSinglish do
62
+ it "have a complete travel-related vocab list with Kia ora" do
63
+ @airpnp.guess(
64
+ "traveled realize flavor center learned practices airplane inquire specialties " +
65
+ "inquiry artifact program livable among spelled judgment defense fulfill spilled " +
66
+ "vacation apartment elevator couch coupon cell Hi bathroom neighborhood's",
67
+ :en, :"en-SG").should ==
68
+ "travelled realise flavour centre learnt practises aeroplane enquire specialities " +
69
+ "enquiry artefact programme liveable amongst spelt judgement defence fulfil spilt " +
70
+ "holiday apartment lift sofa voucher mobile Hi bathroom neighbourhood's"
71
+ end
72
+ end
73
+
61
74
  describe Air18n::AmericanToCanadian do
62
75
  it "have a complete travel-related vocab list without ize/ise variants" do
63
76
  @airpnp.guess(
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.57
4
+ version: 0.3.0
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: 2013-01-29 00:00:00.000000000 Z
16
+ date: 2013-02-21 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: i18n
@@ -168,6 +168,7 @@ files:
168
168
  - lib/air18n/default_text_change_observer.rb
169
169
  - lib/air18n/less_silly_chain.rb
170
170
  - lib/air18n/logging_helper.rb
171
+ - lib/air18n/metrics.rb
171
172
  - lib/air18n/mock_priority.rb
172
173
  - lib/air18n/phrase.rb
173
174
  - lib/air18n/phrase_revision.rb
@@ -192,6 +193,7 @@ files:
192
193
  - spec/lib/air18n/air18n_spec.rb
193
194
  - spec/lib/air18n/backend_spec.rb
194
195
  - spec/lib/air18n/chunk_cache_spec.rb
196
+ - spec/lib/air18n/metrics_spec.rb
195
197
  - spec/lib/air18n/phrase_spec.rb
196
198
  - spec/lib/air18n/phrase_translation_spec.rb
197
199
  - spec/lib/air18n/prim_and_proper_spec.rb
@@ -219,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
219
221
  version: '0'
220
222
  requirements: []
221
223
  rubyforge_project:
222
- rubygems_version: 1.8.22
224
+ rubygems_version: 1.8.24
223
225
  signing_key:
224
226
  specification_version: 3
225
227
  summary: Dynamic I18n backend
@@ -229,6 +231,7 @@ test_files:
229
231
  - spec/lib/air18n/air18n_spec.rb
230
232
  - spec/lib/air18n/backend_spec.rb
231
233
  - spec/lib/air18n/chunk_cache_spec.rb
234
+ - spec/lib/air18n/metrics_spec.rb
232
235
  - spec/lib/air18n/phrase_spec.rb
233
236
  - spec/lib/air18n/phrase_translation_spec.rb
234
237
  - spec/lib/air18n/prim_and_proper_spec.rb