has_translations 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,147 @@
1
+ HasTranslations v0.3.pre
2
+ ====================
3
+
4
+ This simple plugin creates translations for your model.
5
+ Uses delegation pattern: http://en.wikipedia.org/wiki/Delegation_pattern
6
+
7
+ Installation
8
+ ============
9
+
10
+ gem install has_translations
11
+
12
+ or as a plugin
13
+
14
+ script/plugin install git://github.com/dmitry/has_translations.git
15
+
16
+ Example
17
+ =======
18
+
19
+ For example you have Article model and you want to have title and text to be translated.
20
+
21
+ Create model named ArticleTranslation (Rule: [CamelCaseModelName]Translation)
22
+
23
+ Migration should have `locale` as a string with two letters and `belongs_to associative id`, like:
24
+
25
+ class CreateArticleTranslations < ActiveRecord::Migration
26
+ def self.up
27
+ create_table :article_translations do |t|
28
+ t.integer :article_id, :null => false
29
+ t.string :locale, :null => false, :limit => 2
30
+ t.string :title, :null => false
31
+ t.text :text, :null => false
32
+ end
33
+
34
+ add_index :article_translations, [:article_id, :locale], :unique => true
35
+ end
36
+
37
+ def self.down
38
+ drop_table :article_translations
39
+ end
40
+ end
41
+
42
+ Add to article model `translations :value1, :value2`:
43
+
44
+ class Article < ActiveRecord::Base
45
+ translations :title, :text
46
+ end
47
+
48
+ And that's it. Now you can add your translations using:
49
+
50
+ article = Article.create
51
+
52
+ article.translations.create(:locale => 'en', :title => 'title', :text => 'text') # or ArticleTranslation.create(:article => article, :locale => 'en', :title => 'title', :text => 'text')
53
+ article.translations.create(:locale => 'ru', :title => 'заголовок', :text => 'текст')
54
+ article.reload # reload cached translations association array
55
+ I18n.locale = :en
56
+ article.text # text
57
+ I18n.locale = :ru
58
+ article.title # заголовок
59
+
60
+ You can use text filtering plugins, like acts_as_sanitiled and validations, and anything else that is available to the ActiveRecord:
61
+
62
+ class ArticleTranslation < ActiveRecord::Base
63
+ acts_as_sanitiled :title, :text
64
+
65
+ validates_presence_of :title, :text
66
+ validates_length_of :title, :maximum => 100
67
+ end
68
+
69
+ Options:
70
+
71
+ * :fallback => true [default: false] - fallback 1) default locale; 2) first from translations;
72
+ * :reader => false [default: true] - add reader to the model object
73
+ * :writer => true [default: false] - add writer to the model object
74
+ * :nil => nil [default: ''] - if no model found by default returns empty string, you can set it for example to `nil` (no `lambda` supported)
75
+
76
+ It's better to use translations with `accepts_nested_attributes_for`:
77
+
78
+ accepts_nested_attributes_for :translations
79
+
80
+ To create a form for this you can use `all_translations` method. It's have all
81
+ the locales that you have added using the `I18n.available_locales=` method.
82
+ If translation for one of the locale isn't exists, it will build it with :locale.
83
+ So an example which I used in the production (using `formtastic` gem):
84
+
85
+ <% semantic_form_for [:admin, @article] do |f| %>
86
+ <%= f.error_messages %>
87
+
88
+ <% f.inputs :name => "Basic" do %>
89
+ <% object.all_translations.values.each do |translation| %>
90
+ <% f.semantic_fields_for :translations, translation do |ft| %>
91
+ <%= ft.input :title, :label => "Title #{ft.object.locale.to_s.upcase}" %>
92
+ <%= ft.input :text, :label => "Text #{ft.object.locale.to_s.upcase}" %>
93
+ <%= ft.input :locale, :as => :hidden %>
94
+ <% end %>
95
+ <% end %>
96
+ <% end %>
97
+ <% end %>
98
+
99
+ Sometimes you have validations in the translation model, and if you want to skip
100
+ the translations that you don't want to add to the database, you can use
101
+ `:reject_if` option, which is available for the `accepts_nested_attributes_for`:
102
+
103
+ accepts_nested_attributes_for :translations, :reject_if => lambda { |attrs| attrs['title'].blank? && attrs['text'].blank? }
104
+
105
+ named_scope `translated(locale)` - with that named_scope you can find only
106
+ those models that is translated only to specific locale. For example if you will
107
+ have 2 models, one is translated to english and the second one isn't, then it
108
+ `Article.translated(:en)` will find only first one.
109
+
110
+ PS
111
+ ==
112
+
113
+ I suggest you to use latest i18n gem, include it in your rails 2 environment:
114
+
115
+ config.gem 'i18n', :version => '0.4.1' # change version to the latest
116
+
117
+ TODO
118
+ ====
119
+
120
+ * active record 3 (rails 3) support
121
+ * add installation description to readme
122
+ * model and migration generators
123
+ * caching
124
+ * write more examples: fallback feature
125
+ * write blog post about comparison and benefits of this plugin between another translation model plugins
126
+
127
+
128
+ Alternatives
129
+ ============
130
+
131
+ I know three of them:
132
+
133
+ * [puret](http://github.com/jo/puret) - special for Rails 3 and almost the same as this project.
134
+ * [globalite2](http://github.com/joshmh/globalize2) - a lot of magic.
135
+ * [model_translations](http://github.com/janne/model_translations) - almost the same as this project, but more with more code in lib.
136
+ * [translatable_columns](http://github.com/iain/translatable_columns) - different approach: every column have own postfix "_#{locale}" in the same table (sometimes it could be fine).
137
+
138
+
139
+ Used in
140
+ =======
141
+
142
+ * [noch.es](http://noch.es/)
143
+ * [eten.es](http://www.eten.es))
144
+ * [sem.ee](http://sem.ee/)
145
+
146
+
147
+ Copyright (c) 2009-2010 [Dmitry Polushkin], released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the has_translations plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the has_translations plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'HasTranslations'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+ begin
26
+ require 'jeweler'
27
+ Jeweler::Tasks.new do |gemspec|
28
+ gemspec.name = "has_translations"
29
+ gemspec.summary = "Create translations for your ActiveRecord models."
30
+ gemspec.description = "Create translations for your ActiveRecord models. Uses delegate pattern. Fully tested and used in a several production sites."
31
+ gemspec.email = "dmitry.polushkin@gmail.com"
32
+ gemspec.homepage = "http://github.com/dmitry/has_translations"
33
+ gemspec.authors = ["Dmitry Polushkin"]
34
+ gemspec.version = '0.3.0'
35
+ end
36
+ rescue LoadError
37
+ puts "Jeweler not available. Install it with: gem install jeweler"
38
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ ./script/generate has_translations Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -0,0 +1,8 @@
1
+ class HasTranslationsGenerator < Rails::Generator::NamedBase
2
+ def manifest
3
+ record do |m|
4
+ # m.directory "lib"
5
+ # m.template 'README', "README"
6
+ end
7
+ end
8
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'has_translations'
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,152 @@
1
+ class ActiveRecord::Base
2
+ # Provides ability to add the translations for the model using delegate pattern.
3
+ # Uses has_many association to the ModelNameTranslation.
4
+ #
5
+ # For example you have model Article with attributes title and text.
6
+ # You want that attributes title and text to be translated.
7
+ # For this reason you need to generate new model ArticleTranslation.
8
+ # In migration you need to add:
9
+ #
10
+ # create_table :article_translations do |t|
11
+ # t.references :article, :null => false
12
+ # t.string :locale, :length => 2, :null => false
13
+ # t.string :name, :null => false
14
+ # end
15
+ #
16
+ # add_index :articles, [:article_id, :locale], :unique => true, :name => 'unique_locale_for_article_id'
17
+ #
18
+ # And in the Article model:
19
+ #
20
+ # translations :title, :text
21
+ #
22
+ # This will adds:
23
+ #
24
+ # * named_scope (translated) and has_many association to the Article model
25
+ # * locale presence validation to the ArticleTranslation model.
26
+ #
27
+ # Notice: if you want to have validates_presence_of :article, you should use :inverse_of.
28
+ # Support this by yourself. Better is always to use artile.translations.build() method.
29
+ #
30
+ # For more information please read API. Feel free to write me an email to:
31
+ # dmitry.polushkin@gmail.com.
32
+ #
33
+ # ===
34
+ #
35
+ # You also can pass attributes and options to the translations class method:
36
+ #
37
+ # translations :title, :text, :fallback => true, :writer => true, :nil => nil
38
+ #
39
+ # ===
40
+ #
41
+ # Configuration options:
42
+ #
43
+ # * <tt>:fallback</tt> - if translation for the current locale not found.
44
+ # By default false. Set to true if you want to use reader fallback.
45
+ # Uses algorithm of fallback:
46
+ # 0) current translation (using I18n.locale);
47
+ # 1) default locale (using I18n.default_locale);
48
+ # 2) first from translations association;
49
+ # 3) :nil value (see <tt>:nil</tt> configuration option)
50
+ # * <tt>:reader</tt> - add reader attributes to the model and delegate them
51
+ # to the translation model columns. Add's fallback if it is set to true.
52
+ # * <tt>:writer</tt> - add writer attributes to the model and assign them
53
+ # to the translation model attributes.
54
+ # * <tt>:nil</tt> - when reader cant find string, it returns by default an
55
+ # empty string. If you want to change this setting for example to nil,
56
+ # add :nil => nil
57
+ #
58
+ # ===
59
+ #
60
+ # When you are using <tt>:writer</tt> option, you can create translations using
61
+ # update_attributes method. For example:
62
+ #
63
+ # Article.create!
64
+ # Article.update_attributes(:title => 'title', :text => 'text')
65
+ #
66
+ # ===
67
+ #
68
+ # <tt>translated</tt> named_scope is useful when you want to find only those
69
+ # records that are translated to a specific locale.
70
+ # For example if you want to find all Articles that is translated to an english
71
+ # language, you can write: Article.translated(:en)
72
+ #
73
+ # <tt>has_translation?(locale)</tt> method, that returns true if object's model
74
+ # have a translation for a specified locale
75
+ #
76
+ # <tt>translation(locale)</tt> method finds translation with specified locale.
77
+ #
78
+ # <tt>all_translations</tt> method that returns all possible translations in
79
+ # ordered hash (useful when creating forms with nested attributes).
80
+ def self.translations(*attrs)
81
+ options = {
82
+ :fallback => false,
83
+ :reader => true,
84
+ :writer => false,
85
+ :nil => ''
86
+ }.merge(attrs.extract_options!)
87
+
88
+ options.assert_valid_keys([:fallback, :reader, :writer, :nil])
89
+
90
+ translation_class_name = "#{self.model_name}Translation"
91
+ translation_class = translation_class_name.constantize
92
+ belongs_to = self.model_name.demodulize.singularize.underscore.to_sym
93
+
94
+ write_inheritable_attribute :has_translations_options, options
95
+ class_inheritable_reader :has_translations_options
96
+
97
+ send :define_method, :find_or_build_translation do |*args|
98
+ locale = args.first.to_s
99
+ build = args.second.present?
100
+ find_translation(locale) || (build ? self.translations.build(:locale => locale) : self.translations.new(:locale => locale))
101
+ end
102
+
103
+ def translation(locale, fallback=has_translations_options[:fallback])
104
+ locale = locale.to_s
105
+ find_translation(locale) || (fallback && !translations.blank? ? translations.detect { |t| t.locale == I18n.default_locale.to_s } || translations.first : nil)
106
+ end
107
+
108
+ def all_translations
109
+ t = I18n.available_locales.map do |locale|
110
+ [locale, find_or_build_translation(locale)]
111
+ end
112
+ ActiveSupport::OrderedHash[t]
113
+ end
114
+
115
+ def has_translation?(locale)
116
+ find_translation(locale).present?
117
+ end
118
+
119
+ if options[:reader]
120
+ attrs.each do |name|
121
+ send :define_method, name do
122
+ translation = self.translation(I18n.locale)
123
+ translation.nil? ? has_translations_options[:nil] : translation.send(name)
124
+ end
125
+ end
126
+ end
127
+
128
+ if options[:writer]
129
+ attrs.each do |name|
130
+ send :define_method, "#{name}=" do |value|
131
+ translation = find_or_build_translation(I18n.locale, true)
132
+ translation.send(:"#{name}=", value)
133
+ end
134
+ end
135
+ end
136
+
137
+ has_many :translations, :class_name => translation_class_name, :dependent => :destroy
138
+
139
+ translation_class.belongs_to belongs_to
140
+ translation_class.validates_presence_of :locale
141
+ translation_class.validates_uniqueness_of :locale, :scope => :"#{belongs_to}_id"
142
+
143
+ named_scope :translated, lambda { |locale| {:conditions => ["#{translation_class.table_name}.locale = ?", locale.to_s], :joins => :translations} }
144
+
145
+ private
146
+
147
+ def find_translation(locale)
148
+ locale = locale.to_s
149
+ translations.detect { |t| t.locale == locale }
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :has_translations do
3
+ # # Task goes here
4
+ # end
data/test/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ has_translations_plugin.sqlite3.db
2
+ debug.log
@@ -0,0 +1,113 @@
1
+ require 'test_helper'
2
+
3
+ class HasTranslationsTest < Test::Unit::TestCase
4
+ def setup
5
+ setup_db
6
+
7
+ [Article, ArticleTranslation, Team, TeamTranslation].each do |k|
8
+ k.delete_all
9
+ end
10
+ I18n.available_locales = :ru, :en, :es
11
+ I18n.locale = :ru
12
+ end
13
+
14
+ def teardown
15
+ teardown_db
16
+ end
17
+
18
+ def test_schema_has_loaded_correctly
19
+ [Article, ArticleTranslation, Team, TeamTranslation].each do |k|
20
+ assert_equal [], k.all
21
+ end
22
+ assert_equal :ru, I18n.locale
23
+ end
24
+
25
+ def test_reader_text_for_a_given_locale
26
+ article = Article.create!
27
+ article_translation = ArticleTranslation.create!(:article => article, :locale => 'en', :description => 'desc', :text => 'text')
28
+ assert_not_equal article.text, article_translation.text
29
+ I18n.locale = :en
30
+ assert_equal article.text, article_translation.text
31
+ end
32
+
33
+ def test_writer_text_for_a_given_locale
34
+ article = Article.create!
35
+ article.text = 'text'
36
+ assert_equal 0, article.translations.count
37
+ article.save!
38
+ assert_equal 1, article.translations.length
39
+ assert_equal 1, article.translations.count
40
+ I18n.locale = :en
41
+ assert_equal '', article.text
42
+ article.update_attributes!(:text => 'text')
43
+ assert_equal 2, Article.first.translations.count
44
+ end
45
+
46
+ def test_translations_association_and_translations
47
+ article = Article.create!
48
+ assert_equal [], article.translations
49
+ article_translation = ArticleTranslation.create!(:article => article, :locale => 'ru', :description => 'описание', :text => 'текст')
50
+ assert_equal [], article.translations
51
+ assert_equal [article_translation], article.reload.translations
52
+ assert_equal 'текст', article.text
53
+ I18n.locale = :en
54
+ assert_equal '', article.text
55
+ assert_equal article_translation, article.translation('ru')
56
+ assert_equal article_translation, article.translation(:ru)
57
+ assert article.destroy
58
+ assert_equal [], ArticleTranslation.all
59
+ end
60
+
61
+ def test_translation_validations
62
+ article_translation = ArticleTranslation.create(:description => 'description', :text => 'text')
63
+ assert article_translation.errors[:locale].present?
64
+ # TODO may be add :inverse_of to has_many and add presence validation for the belongs_to
65
+ end
66
+
67
+ def test_fallback_and_nil_options
68
+ article = Article.create!
69
+ assert_equal '', article.text
70
+ team = Team.create!
71
+ assert_equal nil, team.text
72
+ first_translation = TeamTranslation.create!(:team => team, :locale => 'es', :text => 'text')
73
+ assert_equal first_translation.text, team.reload.text
74
+ default_translation = TeamTranslation.create!(:team => team, :locale => 'en', :text => 'text')
75
+ assert_equal default_translation.text, team.reload.text
76
+ real_translation = TeamTranslation.create!(:team => team, :locale => 'ru', :text => 'текст')
77
+ assert_equal real_translation.text, team.reload.text
78
+ end
79
+
80
+ def test_all_translations_sorted_build_or_translation_getted
81
+ team = Team.create!
82
+ team_translation = TeamTranslation.create!(:team => team, :locale => 'en', :text => 'text')
83
+ assert_equal team_translation, team.all_translations[:en]
84
+ assert_equal 'ru', team.all_translations[:ru].locale
85
+ team_translation_new = team.translations.build(:locale => :ru)
86
+ assert_equal team_translation_new.locale.to_s, team.all_translations[:ru].locale
87
+ end
88
+
89
+ def test_all_translations_should_not_have_build_translations
90
+ team = Team.create!
91
+ assert_equal 0, team.translations.length
92
+ team.all_translations
93
+ assert_equal 0, team.translations.length
94
+ end
95
+
96
+ def test_has_translation?
97
+ team = Team.create!
98
+ assert !team.has_translation?(:en)
99
+ team.translations.create!(:locale => 'en', :text => 'text')
100
+ assert team.has_translation?(:en)
101
+ end
102
+
103
+ def test_named_scope_translated
104
+ assert_equal 0, Team.translated(:en).count
105
+ assert_equal 0, Team.translated(:ru).count
106
+ team = Team.create!
107
+ team.translations.create!(:locale => 'en', :text => 'text')
108
+ assert_equal 0, Team.translated(:ru).count
109
+ assert_equal 1, Team.translated(:en).count
110
+ team.translations.create!(:locale => 'ru', :text => 'текст')
111
+ assert_equal 1, Team.translated(:ru).count
112
+ end
113
+ end
data/test/schema.rb ADDED
@@ -0,0 +1,36 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :articles, :force => true do |t|
3
+ t.string :title
4
+ end
5
+
6
+ create_table :article_translations, :force => true do |t|
7
+ t.references :article, :null => false
8
+ t.string :locale, :null => false, :limit => 2
9
+ t.string :description
10
+ t.text :text
11
+ end
12
+
13
+ create_table :teams, :force => true do |t|
14
+ t.string :title
15
+ end
16
+
17
+ create_table :team_translations, :force => true do |t|
18
+ t.references :team, :null => false
19
+ t.string :locale, :null => false, :limit => 2
20
+ t.text :text
21
+ end
22
+ end
23
+
24
+ class ArticleTranslation < ActiveRecord::Base
25
+ end
26
+
27
+ class Article < ActiveRecord::Base
28
+ translations :description, :text, :writer => true
29
+ end
30
+
31
+ class TeamTranslation < ActiveRecord::Base
32
+ end
33
+
34
+ class Team < ActiveRecord::Base
35
+ translations :text, :fallback => true, :nil => nil
36
+ end
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ gem 'activerecord', '~> 2.3'
5
+ gem 'i18n', '~> 0.4'
6
+
7
+ require 'active_record'
8
+ require 'logger'
9
+
10
+ require 'has_translations'
11
+
12
+ #ActiveRecord::Base.logger = Logger.new(STDOUT)
13
+ ActiveRecord::Base.logger = nil
14
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
15
+
16
+ def setup_db
17
+ ActiveRecord::Migration.verbose = false
18
+ load "schema.rb"
19
+ end
20
+
21
+ def teardown_db
22
+ ActiveRecord::Base.connection.tables.each do |table|
23
+ ActiveRecord::Base.connection.drop_table(table)
24
+ end
25
+ end
data/uninstall.rb ADDED
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_translations
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
+ platform: ruby
12
+ authors:
13
+ - Dmitry Polushkin
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-25 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Create translations for your ActiveRecord models. Uses delegate pattern. Fully tested and used in a several production sites.
23
+ email: dmitry.polushkin@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.md
30
+ files:
31
+ - MIT-LICENSE
32
+ - README.md
33
+ - Rakefile
34
+ - generators/has_translations/USAGE
35
+ - generators/has_translations/has_translations_generator.rb
36
+ - init.rb
37
+ - install.rb
38
+ - lib/has_translations.rb
39
+ - tasks/has_translations_tasks.rake
40
+ - test/.gitignore
41
+ - test/has_translations_test.rb
42
+ - test/schema.rb
43
+ - test/test_helper.rb
44
+ - uninstall.rb
45
+ has_rdoc: true
46
+ homepage: http://github.com/dmitry/has_translations
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options:
51
+ - --charset=UTF-8
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.7
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Create translations for your ActiveRecord models.
79
+ test_files:
80
+ - test/has_translations_test.rb
81
+ - test/schema.rb
82
+ - test/test_helper.rb