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 +20 -0
- data/README.md +147 -0
- data/Rakefile +38 -0
- data/generators/has_translations/USAGE +8 -0
- data/generators/has_translations/has_translations_generator.rb +8 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/has_translations.rb +152 -0
- data/tasks/has_translations_tasks.rake +4 -0
- data/test/.gitignore +2 -0
- data/test/has_translations_test.rb +113 -0
- data/test/schema.rb +36 -0
- data/test/test_helper.rb +25 -0
- data/uninstall.rb +1 -0
- metadata +82 -0
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
|
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
|
data/test/.gitignore
ADDED
@@ -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
|
data/test/test_helper.rb
ADDED
@@ -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
|