air18n 0.1.57 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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