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 +1 -0
- data/lib/air18n/class_methods.rb +2 -1
- data/lib/air18n/metrics.rb +211 -0
- data/lib/air18n/prim_and_proper.rb +8 -0
- data/lib/air18n/version.rb +1 -1
- data/spec/lib/air18n/backend_spec.rb +5 -5
- data/spec/lib/air18n/metrics_spec.rb +276 -0
- data/spec/lib/air18n/prim_and_proper_spec.rb +13 -0
- metadata +6 -3
data/lib/air18n.rb
CHANGED
data/lib/air18n/class_methods.rb
CHANGED
@@ -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
|
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"
|
data/lib/air18n/version.rb
CHANGED
@@ -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(:
|
161
|
+
I18n.reset(:zq)
|
162
162
|
@backend = Air18n::Backend.new
|
163
|
-
@backend.lookup(:
|
164
|
-
@backend.lookup(:
|
165
|
-
@backend.lookup(:
|
166
|
-
@backend.lookup(:
|
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.
|
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-
|
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.
|
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
|