air18n 0.0.1

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.
Files changed (39) hide show
  1. data/.gitignore +18 -0
  2. data/.rvmrc +48 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +9 -0
  7. data/air18n.gemspec +25 -0
  8. data/lib/air18n.rb +83 -0
  9. data/lib/air18n/backend.rb +194 -0
  10. data/lib/air18n/class_methods.rb +292 -0
  11. data/lib/air18n/less_silly_chain.rb +54 -0
  12. data/lib/air18n/logging_helper.rb +13 -0
  13. data/lib/air18n/mock_priority.rb +25 -0
  14. data/lib/air18n/phrase.rb +92 -0
  15. data/lib/air18n/phrase_screenshot.rb +76 -0
  16. data/lib/air18n/phrase_translation.rb +348 -0
  17. data/lib/air18n/prim_and_proper.rb +94 -0
  18. data/lib/air18n/priority.rb +13 -0
  19. data/lib/air18n/pseudo_locales.rb +53 -0
  20. data/lib/air18n/reflection.rb +10 -0
  21. data/lib/air18n/screenshot.rb +45 -0
  22. data/lib/air18n/testing_support/factories.rb +3 -0
  23. data/lib/air18n/testing_support/factories/phrase.rb +8 -0
  24. data/lib/air18n/testing_support/factories/phrase_screenshot.rb +8 -0
  25. data/lib/air18n/testing_support/factories/phrase_translation.rb +17 -0
  26. data/lib/air18n/version.rb +3 -0
  27. data/lib/air18n/xss_detector.rb +47 -0
  28. data/lib/generators/air18n/migration/migration_generator.rb +39 -0
  29. data/lib/generators/air18n/migration/templates/active_record/migration.rb +63 -0
  30. data/spec/database.yml +3 -0
  31. data/spec/factories.rb +2 -0
  32. data/spec/lib/air18n/air18n_spec.rb +144 -0
  33. data/spec/lib/air18n/backend_spec.rb +173 -0
  34. data/spec/lib/air18n/phrase_translation_spec.rb +80 -0
  35. data/spec/lib/air18n/prim_and_proper_spec.rb +21 -0
  36. data/spec/lib/air18n/pseudo_locales_spec.rb +17 -0
  37. data/spec/lib/air18n/xss_detector_spec.rb +47 -0
  38. data/spec/spec_helper.rb +62 -0
  39. metadata +212 -0
@@ -0,0 +1,173 @@
1
+ require 'spec_helper'
2
+
3
+ require 'air18n/mock_priority'
4
+
5
+ describe Air18n::Backend do
6
+
7
+ before do
8
+ @scope = []
9
+ end
10
+
11
+ context 'Translation auto-update' do
12
+ before do
13
+ @backend = Air18n::Backend.new
14
+ end
15
+ it 'Should have default override DB value' do
16
+ phrase = FactoryGirl.create(:phrase, :value => 'original_value')
17
+ @backend.lookup(:en, phrase.key, @scope, :default => 'new_value').should == 'new_value'
18
+ phrase.reload.value.should == 'new_value'
19
+ end
20
+ it 'Should have default override empty DB' do
21
+ @backend.lookup(:en, 'key', @scope, :default => 'brand_new_value').should == 'brand_new_value'
22
+ @backend.lookup(:en, 'key', @scope).should == 'brand_new_value'
23
+ end
24
+ it 'Should have low-priority default override empty DB' do
25
+ @backend.lookup(:en, 'key', @scope, :default => 'low_priority_brand_new_value', :default_is_low_priority => true).should == 'low_priority_brand_new_value'
26
+ @backend.lookup(:en, 'key', @scope).should == 'low_priority_brand_new_value'
27
+ end
28
+ it 'Should have low-priority default *not* override nonempty DB value' do
29
+ phrase = FactoryGirl.create(:phrase, :value => 'original_value')
30
+ @backend.init_translations(I18n.default_locale)
31
+ @backend.lookup(I18n.default_locale, phrase.key, @scope, :default => 'new_value', :default_is_low_priority => true).should == 'new_value'
32
+ phrase.reload.value.should == 'original_value'
33
+ end
34
+ end
35
+
36
+ context 'Robustness to various input formats' do
37
+ before do
38
+ @backend = Air18n::Backend.new
39
+ end
40
+ it 'should not barf on arrays' do
41
+ @backend.lookup(I18n.default_locale, ['january', 'february'], @scope).should == nil
42
+ end
43
+ end
44
+
45
+ context 'I18n backend initialization' do
46
+ before :all do
47
+ @phrase1 = FactoryGirl.create(:phrase, :key => 'init, key 1', :value => 'value 1')
48
+ @phrase2 = FactoryGirl.create(:phrase, :key => 'init, key 2', :value => 'value 2')
49
+ FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :value => 'French value 1', :locale => :fr)
50
+ FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :value => 'Spanish value 1', :locale => :es)
51
+ FactoryGirl.create(:phrase_translation, :phrase => @phrase2, :value => 'Spanish value 2', :locale => :es)
52
+ end
53
+
54
+ it 'should lazily load all translations on startup' do
55
+ @backend = Air18n::Backend.new
56
+ @backend.available_locales.should be_empty
57
+ @backend.lookup(:es, @phrase1.key).should == 'Spanish value 1'
58
+ @backend.lookup(:fr, @phrase1.key).should == 'French value 1'
59
+ @backend.lookup(:es, @phrase2.key).should == 'Spanish value 2'
60
+
61
+ # This is undesirable behavior. Before any English lookup has been done,
62
+ # English translations aren't loaded.
63
+ @backend.lookup(:ko, @phrase1.key).should == nil
64
+
65
+ @backend.lookup(:en, @phrase1.key).should == 'value 1'
66
+ @backend.lookup(:ko, @phrase1.key).should == 'value 1'
67
+ @backend.lookup(:en, @phrase2.key).should == 'value 2'
68
+ @backend.available_locales.size.should == 3
69
+ end
70
+
71
+ it 'should load only still-used translations on startup' do
72
+ Air18n::MockPriority.mock_priority({ @phrase1.key => 1 }) do |mock_priority|
73
+ @backend = Air18n::Backend.new
74
+ @backend.lookup(:es, @phrase1.key, [], :default => @phrase1.value).should == 'Spanish value 1'
75
+ @backend.lookup(:es, @phrase2.key, [], :default => @phrase2.value).should == 'value 2'
76
+ end
77
+ end
78
+ end
79
+
80
+ context 'Pseudolocales' do
81
+ it 'should use pseudolocales for xx locale' do
82
+ @backend = Air18n::Backend.new
83
+ @backend.lookup(:xx, 'xx, key 1', @scope, :default => 'hey there').should == Air18n::PseudoLocales::translate(:xx, 'hey there')
84
+ end
85
+ end
86
+
87
+ context 'Language fallbacks' do
88
+ it 'should fall back to default locale' do
89
+ old_default_locale = I18n.default_locale
90
+ I18n.reset(:fr)
91
+ @backend = Air18n::Backend.new
92
+ @backend.lookup(:fr, 'fallbacks, key 1', @scope, :default => 'merci').should == 'merci'
93
+ @backend.lookup(:es, 'fallbacks, key 1', @scope, :default => 'merci').should == 'merci'
94
+ @backend.lookup(:es, 'fallbacks, key 1', @scope).should == 'merci'
95
+ @backend.lookup(:en, 'fallbacks, key 1', @scope).should == 'merci'
96
+
97
+ # Restore the old default locale.
98
+ I18n.reset(old_default_locale)
99
+ end
100
+
101
+ it 'should fallback to less specific locale' do
102
+ @phrase1 = FactoryGirl.create(:phrase, :key => 'less specific fallbacks, key 1', :value => 'value 1')
103
+ FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :value => 'Spanish Spanish value 1', :locale => :es)
104
+ FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :value => 'Latin American Spanish value 1', :locale => :"es-419")
105
+ @phrase2 = FactoryGirl.create(:phrase, :key => 'less specific fallbacks, key 2', :value => 'value 2')
106
+ FactoryGirl.create(:phrase_translation, :phrase => @phrase2, :value => 'Spanish Spanish value 2', :locale => :es)
107
+
108
+ @backend = Air18n::Backend.new
109
+ @backend.lookup(:es, 'less specific fallbacks, key 1', @scope).should == 'Spanish Spanish value 1'
110
+ @backend.lookup(:"es-419", 'less specific fallbacks, key 1', @scope).should == 'Latin American Spanish value 1'
111
+ @backend.lookup(:es, 'less specific fallbacks, key 2', @scope).should == 'Spanish Spanish value 2'
112
+ @backend.lookup(:"es-419", 'less specific fallbacks, key 2', @scope).should == 'Spanish Spanish value 2'
113
+ end
114
+ end
115
+
116
+ context 'Language variants' do
117
+ before :all do
118
+ I18n.reset(:en)
119
+ @backend = Air18n::Backend.new
120
+ end
121
+
122
+ it 'should produce British English' do
123
+ @backend.lookup(:en, 'british english, key 1', @scope, :default => 'canceled neighborhood')
124
+ @backend.lookup(:"en-GB", 'british english, key 1', @scope, :default => 'value 1').should == 'cancelled neighbourhood'
125
+ end
126
+ end
127
+
128
+ context 'Keeping track of which phrases have screenshots' do
129
+ before do
130
+ @backend = Air18n::Backend.new
131
+ end
132
+
133
+ it 'should take one screenshot of a new page' do
134
+ I18n.retrieve_phrases_needing_screenshot('new_controller#new_page').size.should == 0
135
+ @backend.lookup(:en, 'brand new key', @scope, :default => 'sparkle', :routes_context => 'new_controller#new_page')
136
+ I18n.retrieve_phrases_needing_screenshot('new_controller#new_page').should include({ 'brand new key' => 'sparkle' })
137
+ I18n.retrieve_phrases_needing_screenshot('new_controller#new_page').size.should == 0
138
+ @backend.lookup(:en, 'brand new key', @scope, :default => 'sparkle', :routes_context => 'new_controller#new_page')
139
+ I18n.retrieve_phrases_needing_screenshot('new_controller#new_page').size.should == 0
140
+ end
141
+ end
142
+
143
+ context 'XSS attack detection' do
144
+ before do
145
+ @backend = Air18n::Backend.new
146
+ end
147
+
148
+ it 'should return nil upon new tags' do
149
+ @backend.lookup(:en, 'xss, key 1', @scope, :default => 'Click <a href="/here">here</a>')
150
+
151
+ # Falls back to default when there is a missing tag.
152
+ @backend.store_translations(:es, { 'xss, key 1' => 'Too few tags' })
153
+ @backend.lookup(:es, 'xss, key 1', @scope, :default => 'Click <a href="/here">here</a>').should == 'Click <a href="/here">here</a>'
154
+
155
+ # Falls back to default when there is a missing tag.
156
+ @backend.store_translations(:es, { 'xss, key 1' => 'Too <a href="/here"><b>many</b></a> tags' })
157
+ @backend.lookup(:es, 'xss, key 1', @scope, :default => 'Click <a href="/here">here</a>').should == 'Click <a href="/here">here</a>'
158
+
159
+ # Translates nicely when tags match.
160
+ @backend.store_translations(:es, { 'xss, key 1' => 'Just <a href="/here">right</a> tags' })
161
+ @backend.lookup(:es, 'xss, key 1', @scope, :default => 'Click <a href="/here">here</a>').should == 'Just <a href="/here">right</a> tags'
162
+
163
+ # There is no French translation, so the default translation should be used and allowed.
164
+ @backend.lookup(:fr, 'xss, key 1', @scope, :default => 'Click <a href="/here">here</a>').should == 'Click <a href="/here">here</a>'
165
+
166
+ # With :disable_xss_check, anything goes.
167
+ @backend.store_translations(:fr, { 'xss, key 2' => '<p>translated <b>text</b></p>' })
168
+ @backend.lookup(:fr, 'xss, key 2', @scope, :default => '<p>rich text</p>').should == '<p>rich text</p>'
169
+ @backend.lookup(:fr, 'xss, key 2', @scope, :default => '<p>rich text</p>', :disable_xss_check => true).should == '<p>translated <b>text</b></p>'
170
+ end
171
+ end
172
+
173
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Air18n::PhraseTranslation do
4
+
5
+ context 'latest flag' do
6
+ it "PhraseTranslation Should get unset when a new translation is made" do
7
+ original_translation = FactoryGirl.create(:phrase_translation)
8
+ original_translation.should be_latest
9
+ new_translation = FactoryGirl.create(:phrase_translation, :phrase => original_translation.phrase)
10
+ original_translation.reload.should_not be_latest
11
+ new_translation.should be_latest
12
+ end
13
+ end
14
+
15
+ context 'PhraseTranslation stale flag' do
16
+ it "Should get set when a phrase is updated" do
17
+ translation = FactoryGirl.create(:phrase_translation)
18
+ translation.should_not be_stale
19
+ translation.phrase.value = "new value"
20
+ translation.phrase.save!
21
+ translation.reload.should be_stale
22
+ end
23
+ end
24
+
25
+ describe 'compute monthly translator payments' do
26
+ before do
27
+ # TODO(jason) Stub out User.
28
+ # @user1 = FactoryGirl.create(:user)
29
+ # @user2 = FactoryGirl.create(:user)
30
+ @phrase1 = FactoryGirl.create(:phrase, :value => 'two words')
31
+ @phrase2 = FactoryGirl.create(:phrase, :value => 'three long words')
32
+ end
33
+
34
+ it "should pay for only last month's translation" do
35
+ pending "Stub out User class"
36
+ without_timestamping_of Air18n::PhraseTranslation do
37
+ translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :user => @user1, :created_at => 1.month.ago)
38
+ other_phrase_translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase2, :user => @user1, :created_at => 1.month.ago)
39
+ other_user_translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :user => @user2, :created_at => 1.month.ago)
40
+ older_translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :user => @user1, :created_at => 2.months.ago)
41
+
42
+ data = Air18n::PhraseTranslation.translator_payment_data
43
+
44
+ data[0].should include(
45
+ {:locale => "es", :is_paid => true, :currency => "EUR", :usd_amount => 1, :smart_name => @user1.smart_name, :user_id => @user1.id, :month => 1.month.ago.month, :is_admin => false, :year => 1.month.ago.year })
46
+ data[0][:activity].should include(
47
+ { :payment => 5 * Air18n::PhraseTranslation::COST_PER_WORD_TRANSLATION, :num_translations => 2, :num_phrases_prev_translated => 1, :word_count_translations => 5 })
48
+
49
+ data[1].should include(
50
+ { :locale => "es", :is_paid => true, :currency => "EUR", :usd_amount => 0, :smart_name => @user2.smart_name, :user_id => @user2.id, :is_admin => false })
51
+ data[1][:activity].should include(
52
+ { :payment => 2 * Air18n::PhraseTranslation::COST_PER_WORD_TRANSLATION, :num_translations => 1, :num_phrases_prev_translated => 0, :word_count_translations => 2 })
53
+ end
54
+ end
55
+
56
+ it 'should pay less for verification' do
57
+ pending "Stub out User class"
58
+ without_timestamping_of Air18n::PhraseTranslation do
59
+ translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :user => @user1, :created_at => 1.month.ago)
60
+ other_phrase_translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase2, :user => @user1, :created_at => 1.month.ago)
61
+ other_user_translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :user => @user2, :created_at => 1.month.ago)
62
+ phrase_verification = FactoryGirl.create(:phrase_translation, :phrase => @phrase2, :user => @user2, :is_verification => true, :created_at => 1.month.ago)
63
+
64
+
65
+ data = Air18n::PhraseTranslation.translator_payment_data
66
+
67
+ data[0].should include(
68
+ {:locale => "es", :is_paid => true, :currency => "EUR", :usd_amount => 1, :smart_name => @user1.smart_name, :user_id => @user1.id, :month => 1.month.ago.month, :is_admin => false, :year => 1.month.ago.year })
69
+ data[0][:activity].should include(
70
+ { :payment => 5 * Air18n::PhraseTranslation::COST_PER_WORD_TRANSLATION, :num_translations => 2, :word_count_translations => 5 })
71
+
72
+ data[1].should include(
73
+ { :locale => "es", :is_paid => true, :currency => "EUR", :usd_amount => 0, :smart_name => @user2.smart_name, :user_id => @user2.id, :is_admin => false })
74
+ data[1][:activity].should include(
75
+ { :payment => 2 * Air18n::PhraseTranslation::COST_PER_WORD_TRANSLATION + 3 * Air18n::PhraseTranslation::COST_PER_WORD_VERIFICATION, :num_translations => 1, :num_verifications => 1, :word_count_translations => 2, :word_count_verifications => 3 })
76
+ end
77
+ end
78
+ end
79
+
80
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Air18n::PrimAndProper do
4
+ describe "AmericanToBritish" do
5
+ before do
6
+ @airpnp = Air18n::PrimAndProper.new
7
+ end
8
+ it "should handle simple replacement" do
9
+ @airpnp.guess('flavored inquiries', :en, :"en-GB").should == 'flavoured enquiries'
10
+ end
11
+ it "should handle capitals and punctuation" do
12
+ @airpnp.guess('Flavored inquiries.', :en, :"en-GB").should == 'Flavoured enquiries.'
13
+ end
14
+ it "should handle quotes" do
15
+ @airpnp.guess('Click "canceled" for canceling.', :en, :"en-GB").should == 'Click "cancelled" for cancelling.'
16
+ end
17
+ it "should handle vocab variants and article adaption" do
18
+ @airpnp.guess('"An apartment has an elevator with the couch."', :en, :"en-GB").should == '"A flat has a lift with the sofa."'
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Air18n::PseudoLocales do
4
+ context 'xx' do
5
+ it 'should work' do
6
+ Air18n::PseudoLocales.translate(:xx, 'here be %{num_pirates}, pirates.').should == '[xxxx xx %{num_pirates}, xxxxxxx.]'
7
+ end
8
+ end
9
+
10
+ context 'xx-long' do
11
+ it 'should work' do
12
+ Air18n::PseudoLocales.translate(:xxlong, 'here be %{num_pirates}, pirates.').should == 'here be %{num_pirates}, pirates. one two three four five six seven'
13
+ Air18n::PseudoLocales.translate(:xxlong, 'hey there').should == 'hey there one two'
14
+ Air18n::PseudoLocales.translate(:xxlong, 'h').should == 'h one'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ require 'air18n/xss_detector'
4
+
5
+ describe Air18n::XssDetector do
6
+ describe "has_dubious_escape_characters?" do
7
+ it "should detect backslashes" do
8
+ Air18n::XssDetector::has_dubious_escape_characters?("Let's escape a \\' there").should == true
9
+ Air18n::XssDetector::has_dubious_escape_characters?("All's clear").should == false
10
+ end
11
+ end
12
+
13
+ describe "safe?" do
14
+ it "should detect safe texts" do
15
+ Air18n::XssDetector::safe?("safe", "safe").should == { :safe => true }
16
+ Air18n::XssDetector::safe?("<tag>", "<tag>").should == { :safe => true }
17
+ Air18n::XssDetector::safe?("safe", { "safe" => "whatevs" }).should == { :safe => true }
18
+ end
19
+
20
+ it "should detect dubious escape characters" do
21
+ Air18n::XssDetector::safe?("Let's escape a \\' there", "safe").should == { :safe => false, :reason => "Backslashes are not allowed" }
22
+ Air18n::XssDetector::safe?("safe", "Let's escape a \\' there").should == { :safe => false, :reason => "Backslashes are not allowed" }
23
+ end
24
+
25
+ it "should detect tag mismatches" do
26
+ Air18n::XssDetector::safe?("<tag>", "safe").should == { :safe => false, :reason => "HTML tags don't match: #{['<tag>'].inspect} vs #{[].inspect}" }
27
+ Air18n::XssDetector::safe?("safe", "<tag>").should == { :safe => false, :reason => "HTML tags don't match: #{[].inspect} vs #{['<tag>'].inspect}" }
28
+ end
29
+ end
30
+
31
+ describe "extract_tags" do
32
+ def test(text, tags)
33
+ Air18n::XssDetector::extract_tags(text).should == tags
34
+ end
35
+
36
+ it "should work" do
37
+ test("oh boy!<script sup>hello!</script> oh boy!", ["<script sup>", "</script>"])
38
+ test("oh boy!<<script sup>hello!</script> oh boy!", ["<<script sup>hello!</script> oh boy!"])
39
+ test('Success! You\'ve invited %{number} friends. <a href="/referrals">Invite <b>more</b>.</a>', ['<a href="/referrals">', '<b>', '</b>', '</a>'])
40
+ end
41
+ end
42
+
43
+ describe 'Robustness to various input formats' do
44
+ it 'should not barf on arrays' do
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,62 @@
1
+ $LOAD_PATH << "." unless $LOAD_PATH.include?(".")
2
+ require 'logger'
3
+
4
+ begin
5
+ require "rubygems"
6
+ require "bundler"
7
+
8
+ if Gem::Version.new(Bundler::VERSION) <= Gem::Version.new("0.9.5")
9
+ raise RuntimeError, "Your bundler version is too old." +
10
+ "Run `gem install bundler` to upgrade."
11
+ end
12
+
13
+ # Set up load paths for all bundled gems
14
+ Bundler.setup
15
+ rescue Bundler::GemNotFound
16
+ raise RuntimeError, "Bundler couldn't find some gems." +
17
+ "Did you run \`bundle install\`?"
18
+ end
19
+
20
+ require "active_record"
21
+
22
+ # set adapter to use, default is sqlite3
23
+ # to use an alternative adapter run => rake spec DB='postgresql'
24
+ db_name = ENV['DB'] || 'sqlite3'
25
+ database_yml = File.expand_path('../database.yml', __FILE__)
26
+
27
+ if File.exists?(database_yml)
28
+ active_record_configuration = YAML.load_file(database_yml)
29
+
30
+ ActiveRecord::Base.configurations = active_record_configuration
31
+ config = ActiveRecord::Base.configurations[db_name]
32
+
33
+ ActiveRecord::Base.establish_connection(db_name)
34
+ ActiveRecord::Base.connection
35
+
36
+ ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), "debug.log"))
37
+ ActiveRecord::Base.default_timezone = :utc
38
+
39
+ ActiveRecord::Base.silence do
40
+ ActiveRecord::Migration.verbose = false
41
+
42
+ require 'generators/air18n/migration/templates/active_record/migration'
43
+ Air18nMigration.up
44
+ end
45
+ else
46
+ raise "Please create #{database_yml} first to configure your database."
47
+ end
48
+
49
+ Bundler.require
50
+ require File.expand_path('../../lib/air18n', __FILE__)
51
+
52
+ # Why doesn't Bundler.require accomplish the loading of factory_girl?
53
+ require "factory_girl"
54
+ require 'air18n/testing_support/factories'
55
+
56
+ def clean_database!
57
+ Air18n::Reflection::MODELS.each do |model|
58
+ ActiveRecord::Base.connection.execute "DELETE FROM #{model.table_name}"
59
+ end
60
+ end
61
+
62
+ clean_database!
metadata ADDED
@@ -0,0 +1,212 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: air18n
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jason Katz-Brown
9
+ - Nick Grandy
10
+ - Naseem Hakim
11
+ - Horace Ko
12
+ - Matt Baker
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+ date: 2012-05-08 00:00:00.000000000 Z
17
+ dependencies:
18
+ - !ruby/object:Gem::Dependency
19
+ name: i18n
20
+ requirement: !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ! '>='
24
+ - !ruby/object:Gem::Version
25
+ version: 0.5.0
26
+ type: :runtime
27
+ prerelease: false
28
+ version_requirements: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.5.0
34
+ - !ruby/object:Gem::Dependency
35
+ name: activerecord
36
+ requirement: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: '3.0'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ~>
48
+ - !ruby/object:Gem::Version
49
+ version: '3.0'
50
+ - !ruby/object:Gem::Dependency
51
+ name: rspec
52
+ requirement: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ type: :development
59
+ prerelease: false
60
+ version_requirements: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ - !ruby/object:Gem::Dependency
67
+ name: sqlite3
68
+ requirement: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: guard
84
+ requirement: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: guard-rspec
100
+ requirement: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ - !ruby/object:Gem::Dependency
115
+ name: factory_girl
116
+ requirement: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ~>
120
+ - !ruby/object:Gem::Version
121
+ version: '3.0'
122
+ type: :development
123
+ prerelease: false
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ~>
128
+ - !ruby/object:Gem::Version
129
+ version: '3.0'
130
+ description: Rails plugin with dyanmic I18n backend for amazing internationalization.
131
+ email:
132
+ - jason@airbnb.com
133
+ - nick@airbnb.com
134
+ - naseem@airbnb.com
135
+ - horace@airbnb.com
136
+ - matt.baker@airbnb.com
137
+ executables: []
138
+ extensions: []
139
+ extra_rdoc_files: []
140
+ files:
141
+ - .gitignore
142
+ - .rvmrc
143
+ - Gemfile
144
+ - LICENSE
145
+ - README.md
146
+ - Rakefile
147
+ - air18n.gemspec
148
+ - lib/air18n.rb
149
+ - lib/air18n/backend.rb
150
+ - lib/air18n/class_methods.rb
151
+ - lib/air18n/less_silly_chain.rb
152
+ - lib/air18n/logging_helper.rb
153
+ - lib/air18n/mock_priority.rb
154
+ - lib/air18n/phrase.rb
155
+ - lib/air18n/phrase_screenshot.rb
156
+ - lib/air18n/phrase_translation.rb
157
+ - lib/air18n/prim_and_proper.rb
158
+ - lib/air18n/priority.rb
159
+ - lib/air18n/pseudo_locales.rb
160
+ - lib/air18n/reflection.rb
161
+ - lib/air18n/screenshot.rb
162
+ - lib/air18n/testing_support/factories.rb
163
+ - lib/air18n/testing_support/factories/phrase.rb
164
+ - lib/air18n/testing_support/factories/phrase_screenshot.rb
165
+ - lib/air18n/testing_support/factories/phrase_translation.rb
166
+ - lib/air18n/version.rb
167
+ - lib/air18n/xss_detector.rb
168
+ - lib/generators/air18n/migration/migration_generator.rb
169
+ - lib/generators/air18n/migration/templates/active_record/migration.rb
170
+ - spec/database.yml
171
+ - spec/factories.rb
172
+ - spec/lib/air18n/air18n_spec.rb
173
+ - spec/lib/air18n/backend_spec.rb
174
+ - spec/lib/air18n/phrase_translation_spec.rb
175
+ - spec/lib/air18n/prim_and_proper_spec.rb
176
+ - spec/lib/air18n/pseudo_locales_spec.rb
177
+ - spec/lib/air18n/xss_detector_spec.rb
178
+ - spec/spec_helper.rb
179
+ homepage: http://www.github.com/airbnb/air18n
180
+ licenses: []
181
+ post_install_message:
182
+ rdoc_options: []
183
+ require_paths:
184
+ - lib
185
+ required_ruby_version: !ruby/object:Gem::Requirement
186
+ none: false
187
+ requirements:
188
+ - - ! '>='
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ required_rubygems_version: !ruby/object:Gem::Requirement
192
+ none: false
193
+ requirements:
194
+ - - ! '>='
195
+ - !ruby/object:Gem::Version
196
+ version: '0'
197
+ requirements: []
198
+ rubyforge_project:
199
+ rubygems_version: 1.8.22
200
+ signing_key:
201
+ specification_version: 3
202
+ summary: Dynamic I18n backend
203
+ test_files:
204
+ - spec/database.yml
205
+ - spec/factories.rb
206
+ - spec/lib/air18n/air18n_spec.rb
207
+ - spec/lib/air18n/backend_spec.rb
208
+ - spec/lib/air18n/phrase_translation_spec.rb
209
+ - spec/lib/air18n/prim_and_proper_spec.rb
210
+ - spec/lib/air18n/pseudo_locales_spec.rb
211
+ - spec/lib/air18n/xss_detector_spec.rb
212
+ - spec/spec_helper.rb