lolita-translation 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.
- data/.document +5 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +20 -0
- data/README.md +180 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/app/views/components/lolita/configuration/tab/translation/_display.html.erb +76 -0
- data/app/views/lolita/has_translations/_tab.html.erb +78 -0
- data/config/locales/en.yml +8 -0
- data/lib/generators/lolita_translation/USAGE +8 -0
- data/lib/generators/lolita_translation/has_translations_generator.rb +8 -0
- data/lib/lolita-translation/configuration/tab/translation.rb +14 -0
- data/lib/lolita-translation/has_translations.rb +295 -0
- data/lib/lolita-translation/rails.rb +5 -0
- data/lib/lolita-translation/string.rb +19 -0
- data/lib/lolita-translation.rb +12 -0
- data/lib/tasks/has_translations_tasks.rake +4 -0
- data/lolita-translation.gemspec +77 -0
- data/lolita-translations.gemspec +60 -0
- data/spec/has_translations_spec.rb +43 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +98 -0
- metadata +133 -0
data/.document
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 ITHouse
|
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,180 @@
|
|
1
|
+
LolitaHasTranslations
|
2
|
+
======================
|
3
|
+
|
4
|
+
This is a fork of http://github.com/dmitry/has_translations with small changes.
|
5
|
+
|
6
|
+
1. The main difference is that the translations table holds only translations, but not the original data from default_locale, so:
|
7
|
+
|
8
|
+
I18n.default_locale = :en
|
9
|
+
I18n.locale = :lv
|
10
|
+
|
11
|
+
a = Article.create :title => "Title in EN"
|
12
|
+
a.title
|
13
|
+
# returns blank, because current locale is LV and there is no translation in it
|
14
|
+
=> ""
|
15
|
+
I18n.locale = :en
|
16
|
+
a.title
|
17
|
+
=> "Title in EN"
|
18
|
+
a.translations.create :title => "Title in LV", :locale => 'lv'
|
19
|
+
I18n.locale = :lv
|
20
|
+
a.title
|
21
|
+
=> "Title in LV"
|
22
|
+
|
23
|
+
2. When a "find" is executed and current language is not the same as default language then :translations are added to :includes
|
24
|
+
to pre fetch all translations.
|
25
|
+
|
26
|
+
3. The "ModelNameTranslation" class is created for you automaticly with all validations for ranslated fields. Of course you can create it manualy for custom vlidations and other.
|
27
|
+
|
28
|
+
4. You dont have to create migration for the translation table, just add a line for every translated model in `db/seed.rb`
|
29
|
+
|
30
|
+
TextPage.sync_translation_table!
|
31
|
+
Blog::Article.sync_translation_table!
|
32
|
+
|
33
|
+
And run `rake db:seed` and it will do it for you. It also updates the table if you add news columns in the `translations :name, :title .....` method.
|
34
|
+
|
35
|
+
HasTranslations v0.3.1
|
36
|
+
======================
|
37
|
+
|
38
|
+
This simple plugin creates translations for your model.
|
39
|
+
Uses delegation pattern: http://en.wikipedia.org/wiki/Delegation_pattern
|
40
|
+
|
41
|
+
Tested with ActiveRecord versions: 2.3.5, 2.3.9, 3.0.0 (to test with Rails 3 run `rake RAILS_VERSION=3.0`)
|
42
|
+
|
43
|
+
Installation
|
44
|
+
============
|
45
|
+
|
46
|
+
gem install has_translations
|
47
|
+
|
48
|
+
or as a plugin
|
49
|
+
|
50
|
+
script/plugin install git://github.com/dmitry/has_translations.git
|
51
|
+
|
52
|
+
Example
|
53
|
+
=======
|
54
|
+
|
55
|
+
For example you have Article model and you want to have title and text to be translated.
|
56
|
+
|
57
|
+
Create model named ArticleTranslation (Rule: [CamelCaseModelName]Translation)
|
58
|
+
|
59
|
+
Migration should have `locale` as a string with two letters and `belongs_to associative id`, like:
|
60
|
+
|
61
|
+
class CreateArticleTranslations < ActiveRecord::Migration
|
62
|
+
def self.up
|
63
|
+
create_table :article_translations do |t|
|
64
|
+
t.integer :article_id, :null => false
|
65
|
+
t.string :locale, :null => false, :limit => 2
|
66
|
+
t.string :title, :null => false
|
67
|
+
t.text :text, :null => false
|
68
|
+
end
|
69
|
+
|
70
|
+
add_index :article_translations, [:article_id, :locale], :unique => true
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.down
|
74
|
+
drop_table :article_translations
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
Add to article model `translations :value1, :value2`:
|
79
|
+
|
80
|
+
class Article < ActiveRecord::Base
|
81
|
+
translations :title, :text
|
82
|
+
end
|
83
|
+
|
84
|
+
And that's it. Now you can add your translations using:
|
85
|
+
|
86
|
+
article = Article.create
|
87
|
+
|
88
|
+
article.translations.create(:locale => 'en', :title => 'title', :text => 'text') # or ArticleTranslation.create(:article => article, :locale => 'en', :title => 'title', :text => 'text')
|
89
|
+
article.translations.create(:locale => 'ru', :title => 'заголовок', :text => 'текст')
|
90
|
+
article.reload # reload cached translations association array
|
91
|
+
I18n.locale = :en
|
92
|
+
article.text # text
|
93
|
+
I18n.locale = :ru
|
94
|
+
article.title # заголовок
|
95
|
+
|
96
|
+
You can use text filtering plugins, like acts_as_sanitiled and validations, and anything else that is available to the ActiveRecord:
|
97
|
+
|
98
|
+
class ArticleTranslation < ActiveRecord::Base
|
99
|
+
acts_as_sanitiled :title, :text
|
100
|
+
|
101
|
+
validates_presence_of :title, :text
|
102
|
+
validates_length_of :title, :maximum => 100
|
103
|
+
end
|
104
|
+
|
105
|
+
Options:
|
106
|
+
|
107
|
+
* :fallback => true [default: false] - fallback 1) default locale; 2) first from translations;
|
108
|
+
* :reader => false [default: true] - add reader to the model object
|
109
|
+
* :writer => true [default: false] - add writer to the model object
|
110
|
+
* :nil => nil [default: ''] - if no model found by default returns empty string, you can set it for example to `nil` (no `lambda` supported)
|
111
|
+
|
112
|
+
It's better to use translations with `accepts_nested_attributes_for`:
|
113
|
+
|
114
|
+
accepts_nested_attributes_for :translations
|
115
|
+
|
116
|
+
To create a form for this you can use `all_translations` method. It's have all
|
117
|
+
the locales that you have added using the `I18n.available_locales=` method.
|
118
|
+
If translation for one of the locale isn't exists, it will build it with :locale.
|
119
|
+
So an example which I used in the production (using `formtastic` gem):
|
120
|
+
|
121
|
+
<% semantic_form_for [:admin, @article] do |f| %>
|
122
|
+
<%= f.error_messages %>
|
123
|
+
|
124
|
+
<% f.inputs :name => "Basic" do %>
|
125
|
+
<% object.all_translations.values.each do |translation| %>
|
126
|
+
<% f.semantic_fields_for :translations, translation do |ft| %>
|
127
|
+
<%= ft.input :title, :label => "Title #{ft.object.locale.to_s.upcase}" %>
|
128
|
+
<%= ft.input :text, :label => "Text #{ft.object.locale.to_s.upcase}" %>
|
129
|
+
<%= ft.input :locale, :as => :hidden %>
|
130
|
+
<% end %>
|
131
|
+
<% end %>
|
132
|
+
<% end %>
|
133
|
+
<% end %>
|
134
|
+
|
135
|
+
Sometimes you have validations in the translation model, and if you want to skip
|
136
|
+
the translations that you don't want to add to the database, you can use
|
137
|
+
`:reject_if` option, which is available for the `accepts_nested_attributes_for`:
|
138
|
+
|
139
|
+
accepts_nested_attributes_for :translations, :reject_if => lambda { |attrs| attrs['title'].blank? && attrs['text'].blank? }
|
140
|
+
|
141
|
+
named_scope `translated(locale)` - with that named_scope you can find only
|
142
|
+
those models that is translated only to specific locale. For example if you will
|
143
|
+
have 2 models, one is translated to english and the second one isn't, then it
|
144
|
+
`Article.translated(:en)` will find only first one.
|
145
|
+
|
146
|
+
PS
|
147
|
+
==
|
148
|
+
|
149
|
+
I suggest you to use latest i18n gem, include it in your rails 2 environment:
|
150
|
+
|
151
|
+
config.gem 'i18n', :version => '0.4.1' # change version to the latest
|
152
|
+
|
153
|
+
TODO
|
154
|
+
====
|
155
|
+
|
156
|
+
* add installation description to readme
|
157
|
+
* model and migration generators
|
158
|
+
* caching
|
159
|
+
* write more examples: fallback feature
|
160
|
+
* write blog post about comparison and benefits of this plugin between another translation model plugins
|
161
|
+
|
162
|
+
|
163
|
+
Alternatives
|
164
|
+
============
|
165
|
+
|
166
|
+
I know three of them:
|
167
|
+
|
168
|
+
* [puret](http://github.com/jo/puret) - special for Rails 3 and almost the same as this project.
|
169
|
+
* [globalite2](http://github.com/joshmh/globalize2) - a lot of magic.
|
170
|
+
* [model_translations](http://github.com/janne/model_translations) - almost the same as this project, but more with more code in lib.
|
171
|
+
* [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).
|
172
|
+
|
173
|
+
|
174
|
+
Used in
|
175
|
+
=======
|
176
|
+
|
177
|
+
[noch.es](http://noch.es/), [eten.es](http://www.eten.es), [sem.ee](http://sem.ee/)
|
178
|
+
|
179
|
+
|
180
|
+
Copyright (c) 2009-2010 [Dmitry Polushkin], released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "lolita-translation"
|
16
|
+
gem.homepage = "http://github.com/ithouse/lolita-translations"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = %Q{Lolita models translation plugin}
|
19
|
+
gem.description = %Q{Translates models in Lolita}
|
20
|
+
gem.email = "support@ithouse.lv"
|
21
|
+
gem.authors = ["ITHouse", "Gatis Tomsons", "Arturs Meisters"]
|
22
|
+
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
23
|
+
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
24
|
+
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
25
|
+
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
26
|
+
end
|
27
|
+
Jeweler::RubygemsDotOrgTasks.new
|
28
|
+
|
29
|
+
require 'rake/testtask'
|
30
|
+
Rake::TestTask.new(:test) do |test|
|
31
|
+
test.libs << 'lib' << 'spec'
|
32
|
+
test.pattern = 'spec/**/*_spec.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
|
36
|
+
require 'rcov/rcovtask'
|
37
|
+
Rcov::RcovTask.new do |test|
|
38
|
+
test.libs << 'spec'
|
39
|
+
test.pattern = 'spec/**/*_spec.rb'
|
40
|
+
test.verbose = true
|
41
|
+
end
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "lolita-translation #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,76 @@
|
|
1
|
+
<% if resource.new_record? %>
|
2
|
+
<p>-</p>
|
3
|
+
<% else %>
|
4
|
+
<form "#">
|
5
|
+
<div class="tab-content">
|
6
|
+
<div class="field" id="field_translation_language">
|
7
|
+
<label for="translation_language"><%=I18n.t("lolita.tabs.translation.fields.language")%>
|
8
|
+
<div class="field-value" id="field_translation_language_value">
|
9
|
+
<%= select_tag :has_translations_locale, options_for_select([""] + I18n.available_locales.clone.delete_if{|l| l == I18n.default_locale}), :onchange => "switch_has_translations_language($(this))" %>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<div id="has_translations_fields" class="field" style="display: none">
|
14
|
+
<% resource.class.columns_has_translations.each do |column| %>
|
15
|
+
<label for="<%=column.name%>_field"><%=resource.class.human_attribute_name(column.name)%></label>
|
16
|
+
<div class="fl field-value" id="<%=column.name%>_field">
|
17
|
+
<% if column.type == :string %>
|
18
|
+
<%= text_field_tag 'has_translations_' + column.name, nil, :class => 'txt' %>
|
19
|
+
<% elsif column.type == :text %>
|
20
|
+
<%= text_area_tag 'has_translations_' + column.name, nil, :class => "textarea",:rows=>20, :cols=>40 %>
|
21
|
+
<% end %>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
</form>
|
27
|
+
<% resource.build_nested_translations %>
|
28
|
+
<%= form_for resource, :url => "/" do |form| %>
|
29
|
+
<%= form.fields_for :translations do |tf| %>
|
30
|
+
<div id="object_translation_fields_<%= tf.object.locale %>">
|
31
|
+
<% resource.class.columns_has_translations.each do |column| %>
|
32
|
+
<%= tf.hidden_field column.name, :class => column.name %>
|
33
|
+
<% end %>
|
34
|
+
<%= tf.hidden_field :id, :class => "id" %>
|
35
|
+
<%= tf.hidden_field tf.object.class.master_id, :value => resource.id %>
|
36
|
+
<%= tf.hidden_field :locale, :class => "locale" %>
|
37
|
+
</div>
|
38
|
+
<% end %>
|
39
|
+
<% end %>
|
40
|
+
<script type="text/javascript">
|
41
|
+
var columns = [<%= resource.class.columns_has_translations.map{|r| "'#{r.name}'"}.join(",") %>];
|
42
|
+
var blur = false;
|
43
|
+
function switch_has_translations_language(select){
|
44
|
+
var locale = select.val();
|
45
|
+
if(locale == ""){
|
46
|
+
$('#has_translations_fields').hide()
|
47
|
+
}else{
|
48
|
+
$.each(columns,function(i,column){
|
49
|
+
var content = $('#object_translation_fields_'+locale+' .'+column).val();
|
50
|
+
$('#has_translations_'+column).val(content);
|
51
|
+
if($('#has_translations_'+column).get(0).tagName == 'TEXTAREA'){
|
52
|
+
tinymce.EditorManager.getInstanceById('has_translations_'+column).setContent(content);
|
53
|
+
}
|
54
|
+
});
|
55
|
+
$('#has_translations_fields').show()
|
56
|
+
}
|
57
|
+
if(!blur){
|
58
|
+
$.each($('#has_translations_fields textarea'), function(i,area){
|
59
|
+
var item = $(area);
|
60
|
+
var tiny_editor = tinymce.EditorManager.getInstanceById(item.attr("id"));
|
61
|
+
tinymce.dom.Event.add(tiny_editor.getWin(), "blur", function(){
|
62
|
+
var textarea = $(tinymce.EditorManager.getInstanceById(item.attr("id")).getElement());
|
63
|
+
tiny_editor.save();
|
64
|
+
textarea.blur();
|
65
|
+
});
|
66
|
+
});
|
67
|
+
blur = true;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
$.each(columns,function(i,column){
|
71
|
+
$('#has_translations_'+column).blur(function(){
|
72
|
+
$('#object_translation_fields_'+$("#has_translations_locale").val()+' .'+$(this).attr("name").split("_")[2]).val($(this).val())
|
73
|
+
})
|
74
|
+
})
|
75
|
+
</script>
|
76
|
+
<% end %>
|
@@ -0,0 +1,78 @@
|
|
1
|
+
<% if @object.new_record? %>
|
2
|
+
<p>Tulkošana iespējama tikai eksistējošiem ierakstiem.</p>
|
3
|
+
<% else %>
|
4
|
+
<div class="managed-create-line">
|
5
|
+
<span class="brick"><%= t "fields.language" %></span>
|
6
|
+
<div class="fl">
|
7
|
+
<%= select_tag :has_translations_locale, options_for_select([""] + I18n.available_locales.clone.delete_if{|l| l == I18n.default_locale}), :onchange => "switch_has_translations_language($(this))" %>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
<div id="has_translations_fields" style="display: none">
|
11
|
+
<% @object.class.columns_has_translations.each do |column| %>
|
12
|
+
<div class="managed-create-line">
|
13
|
+
<span class="brick"><%= @object.class.human_attribute_name(column.name) %></span>
|
14
|
+
<div class="fl">
|
15
|
+
<% if column.type == :string %>
|
16
|
+
<%= text_field_tag 'has_translations_' + column.name, nil, :class => 'txt' %>
|
17
|
+
<% elsif column.type == :text %>
|
18
|
+
<%= text_area_tag 'has_translations_' + column.name, nil, :class => "textarea" %>
|
19
|
+
<script type="text/javascript">
|
20
|
+
tinyMCE.execCommand('mceRemoveControl', false,'has_translations_<%= column.name %>');
|
21
|
+
tinyMCE.execCommand('mceAddControl', false,'has_translations_<%= column.name %>');
|
22
|
+
</script>
|
23
|
+
<br /><br />
|
24
|
+
<% end %>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
29
|
+
<div class="clear"></div>
|
30
|
+
<div></div>
|
31
|
+
<% @object.build_nested_translations %>
|
32
|
+
<% object_form.fields_for :translations do |tf| %>
|
33
|
+
<div id="object_translation_fields_<%= tf.object.locale %>">
|
34
|
+
<% @object.class.columns_has_translations.each do |column| %>
|
35
|
+
<%= tf.hidden_field column.name, :class => column.name %>
|
36
|
+
<% end %>
|
37
|
+
<%= tf.hidden_field :id, :class => "id" %>
|
38
|
+
<%= tf.hidden_field tf.object.class.master_id, :value => @object.id %>
|
39
|
+
<%= tf.hidden_field :locale, :class => "locale" %>
|
40
|
+
</div>
|
41
|
+
<% end %>
|
42
|
+
<script type="text/javascript">
|
43
|
+
var columns = <%= @object.class.columns_has_translations.map(&:name).inspect %>;
|
44
|
+
var blur = false;
|
45
|
+
function switch_has_translations_language(select){
|
46
|
+
var locale = select.val();
|
47
|
+
if(locale == ""){
|
48
|
+
$('#has_translations_fields').hide()
|
49
|
+
}else{
|
50
|
+
$.each(columns,function(i,column){
|
51
|
+
var content = $('#object_translation_fields_'+locale+' .'+column).val();
|
52
|
+
$('#has_translations_'+column).val(content);
|
53
|
+
if($('#has_translations_'+column).get(0).tagName == 'TEXTAREA'){
|
54
|
+
tinymce.EditorManager.getInstanceById('has_translations_'+column).setContent(content);
|
55
|
+
}
|
56
|
+
});
|
57
|
+
$('#has_translations_fields').show()
|
58
|
+
}
|
59
|
+
if(!blur){
|
60
|
+
$.each($('#has_translations_fields textarea'), function(i,area){
|
61
|
+
var item = $(area);
|
62
|
+
var tiny_editor = tinymce.EditorManager.getInstanceById(item.attr("id"));
|
63
|
+
tinymce.dom.Event.add(tiny_editor.getWin(), "blur", function(){
|
64
|
+
var textarea = $(tinymce.EditorManager.getInstanceById(item.attr("id")).getElement());
|
65
|
+
tiny_editor.save();
|
66
|
+
textarea.blur();
|
67
|
+
});
|
68
|
+
});
|
69
|
+
blur = true;
|
70
|
+
}
|
71
|
+
}
|
72
|
+
$.each(columns,function(i,column){
|
73
|
+
$('#has_translations_'+column).blur(function(){
|
74
|
+
$('#object_translation_fields_'+$("#has_translations_locale").val()+' .'+$(this).attr("name").split("_")[2]).val($(this).val())
|
75
|
+
})
|
76
|
+
})
|
77
|
+
</script>
|
78
|
+
<% end %>
|
@@ -0,0 +1,295 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
class ActiveRecord::Base
|
4
|
+
# Provides ability to add the translations for the model using delegate pattern.
|
5
|
+
# Uses has_many association to the ModelNameTranslation.
|
6
|
+
#
|
7
|
+
# For example you have model Article with attributes title and text.
|
8
|
+
# You want that attributes title and text to be translated.
|
9
|
+
# For this reason you need to generate new model ArticleTranslation.
|
10
|
+
# In migration you need to add:
|
11
|
+
#
|
12
|
+
# create_table :article_translations do |t|
|
13
|
+
# t.references :article, :null => false
|
14
|
+
# t.string :locale, :length => 2, :null => false
|
15
|
+
# t.string :name, :null => false
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# add_index :articles, [:article_id, :locale], :unique => true, :name => 'unique_locale_for_article_id'
|
19
|
+
#
|
20
|
+
# And in the Article model:
|
21
|
+
#
|
22
|
+
# translations :title, :text
|
23
|
+
#
|
24
|
+
# This will adds:
|
25
|
+
#
|
26
|
+
# * named_scope (translated) and has_many association to the Article model
|
27
|
+
# * locale presence validation to the ArticleTranslation model.
|
28
|
+
#
|
29
|
+
# Notice: if you want to have validates_presence_of :article, you should use :inverse_of.
|
30
|
+
# Support this by yourself. Better is always to use artile.translations.build() method.
|
31
|
+
#
|
32
|
+
# For more information please read API. Feel free to write me an email to:
|
33
|
+
# dmitry.polushkin@gmail.com.
|
34
|
+
#
|
35
|
+
# ===
|
36
|
+
#
|
37
|
+
# You also can pass attributes and options to the translations class method:
|
38
|
+
#
|
39
|
+
# translations :title, :text, :fallback => true, :writer => true, :nil => nil
|
40
|
+
#
|
41
|
+
# ===
|
42
|
+
#
|
43
|
+
# Configuration options:
|
44
|
+
#
|
45
|
+
# * <tt>:fallback</tt> - if translation for the current locale not found.
|
46
|
+
# By default true.
|
47
|
+
# Uses algorithm of fallback:
|
48
|
+
# 0) current translation (using I18n.locale);
|
49
|
+
# 1) default locale (using I18n.default_locale);
|
50
|
+
# 2) :nil value (see <tt>:nil</tt> configuration option)
|
51
|
+
# * <tt>:reader</tt> - add reader attributes to the model and delegate them
|
52
|
+
# to the translation model columns. Add's fallback if it is set to true.
|
53
|
+
# * <tt>:writer</tt> - add writer attributes to the model and assign them
|
54
|
+
# to the translation model attributes.
|
55
|
+
# * <tt>:nil</tt> - when reader cant find string, it returns by default an
|
56
|
+
# empty string. If you want to change this setting for example to nil,
|
57
|
+
# add :nil => nil
|
58
|
+
#
|
59
|
+
# ===
|
60
|
+
#
|
61
|
+
# When you are using <tt>:writer</tt> option, you can create translations using
|
62
|
+
# update_attributes method. For example:
|
63
|
+
#
|
64
|
+
# Article.create!
|
65
|
+
# Article.update_attributes(:title => 'title', :text => 'text')
|
66
|
+
#
|
67
|
+
# ===
|
68
|
+
#
|
69
|
+
# <tt>translated</tt> named_scope is useful when you want to find only those
|
70
|
+
# records that are translated to a specific locale.
|
71
|
+
# For example if you want to find all Articles that is translated to an english
|
72
|
+
# language, you can write: Article.translated(:en)
|
73
|
+
#
|
74
|
+
# <tt>has_translation?(locale)</tt> method, that returns true if object's model
|
75
|
+
# have a translation for a specified locale
|
76
|
+
#
|
77
|
+
# <tt>translation(locale)</tt> method finds translation with specified locale.
|
78
|
+
#
|
79
|
+
# <tt>all_translations</tt> method that returns all possible translations in
|
80
|
+
# ordered hash (useful when creating forms with nested attributes).
|
81
|
+
def self.translations(*attrs)
|
82
|
+
options = {
|
83
|
+
:fallback => true,
|
84
|
+
:reader => true,
|
85
|
+
:writer => false,
|
86
|
+
:nil => ''
|
87
|
+
}.merge(attrs.extract_options!)
|
88
|
+
options.assert_valid_keys([:fallback, :reader, :writer, :nil])
|
89
|
+
|
90
|
+
class << self
|
91
|
+
# adds :translations to :includes if current locale differs from default
|
92
|
+
#FIXME is this enough with find or need to create chain for find_last, find_first and others?
|
93
|
+
alias_method(:find_without_translations, :find) unless method_defined?(:find_without_translations)
|
94
|
+
def find(*args)
|
95
|
+
if args[0].kind_of?(Hash)
|
96
|
+
args[0][:include] ||= []
|
97
|
+
args[0][:include] << :translations
|
98
|
+
end unless I18n.locale == I18n.default_locale
|
99
|
+
find_without_translations(*args)
|
100
|
+
end
|
101
|
+
# Defines given class recursively
|
102
|
+
# Example:
|
103
|
+
# create_class('Cms::Text::Page', Object, ActiveRecord::Base)
|
104
|
+
# => Cms::Text::Page
|
105
|
+
def create_class(class_name, parent, superclass, &block)
|
106
|
+
first,*other = class_name.split("::")
|
107
|
+
if other.empty?
|
108
|
+
klass = Class.new superclass, &block
|
109
|
+
parent.const_set(first, klass)
|
110
|
+
else
|
111
|
+
klass = Class.new
|
112
|
+
parent = unless parent.const_defined?(first)
|
113
|
+
parent.const_set(first, klass)
|
114
|
+
else
|
115
|
+
first.constantize
|
116
|
+
end
|
117
|
+
create_class(other.join('::'), parent, superclass, &block)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
# defines "ModelNameTranslation" if it's not defined manualy
|
121
|
+
def define_translation_class name, attrs
|
122
|
+
klass = name.constantize rescue nil
|
123
|
+
unless klass
|
124
|
+
klass = create_class(name, Object, ActiveRecord::Base) do
|
125
|
+
# set's real table name
|
126
|
+
set_table_name name.sub('Translation','').constantize.table_name.singularize + "_translations"
|
127
|
+
cattr_accessor :translate_attrs, :master_id
|
128
|
+
# override validate to vaidate only translate fields from master Class
|
129
|
+
def validate
|
130
|
+
item = self.class.name.sub('Translation','').constantize.new(self.attributes.clone.delete_if{|k,_| !self.class.translate_attrs.include?(k.to_sym)})
|
131
|
+
was_table_name = item.class.table_name
|
132
|
+
item.class.set_table_name self.class.table_name
|
133
|
+
item.valid? rescue
|
134
|
+
self.class.translate_attrs.each do |attr|
|
135
|
+
errors_on_attr = item.errors.on(attr)
|
136
|
+
self.errors.add(attr,errors_on_attr) if errors_on_attr
|
137
|
+
end
|
138
|
+
item.class.set_table_name was_table_name
|
139
|
+
end
|
140
|
+
extend TranslationClassMethods
|
141
|
+
end
|
142
|
+
klass.translate_attrs = attrs
|
143
|
+
else
|
144
|
+
unless klass.respond_to?(:translate_attrs)
|
145
|
+
klass.send(:cattr_accessor, :translate_attrs, :master_id)
|
146
|
+
klass.send(:extend,TranslationClassMethods)
|
147
|
+
klass.translate_attrs = attrs
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
klass.extract_master_id(name)
|
152
|
+
klass
|
153
|
+
end
|
154
|
+
# creates translation table and adds missing fields
|
155
|
+
# So at first add the "translations :name, :desc" in your model
|
156
|
+
# then put YourModel.sync_translation_table! in db/seed.rb and run "rake db:seed"
|
157
|
+
# Later adding more fields in translations array, just run agin "rake db:seed"
|
158
|
+
# If you want to remove fields do it manualy, it's safer
|
159
|
+
def sync_translation_table!
|
160
|
+
out = StringIO.new
|
161
|
+
$stdout = out
|
162
|
+
translations_class = reflections[:translations].class_name.constantize
|
163
|
+
translations_table = translations_class.table_name
|
164
|
+
unless ActiveRecord::Migration::table_exists?(translations_table)
|
165
|
+
ActiveRecord::Migration.create_table translations_table do |t|
|
166
|
+
t.integer translations_class.master_id, :null => false
|
167
|
+
t.string :locale, :null => false, :limit => 5
|
168
|
+
columns_has_translations.each do |col|
|
169
|
+
t.send(col.type,col.name)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
ActiveRecord::Migration.add_index translations_table, [translations_class.master_id, :locale], :unique => true
|
173
|
+
translations_class.reset_column_information
|
174
|
+
else
|
175
|
+
changes = false
|
176
|
+
columns_has_translations.each do |col|
|
177
|
+
unless translations_class.columns_hash.has_key?(col.name)
|
178
|
+
ActiveRecord::Migration.add_column(translations_table, col.name, col.type)
|
179
|
+
changes = true
|
180
|
+
end
|
181
|
+
end
|
182
|
+
translations_class.reset_column_information if changes
|
183
|
+
end
|
184
|
+
$stdout = STDOUT
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
translation_class_name = "#{self.name}Translation"
|
189
|
+
translation_class = self.define_translation_class(translation_class_name, attrs)
|
190
|
+
belongs_to = self.name.demodulize.underscore.to_sym
|
191
|
+
|
192
|
+
write_inheritable_attribute :has_translations_options, options
|
193
|
+
class_inheritable_reader :has_translations_options
|
194
|
+
|
195
|
+
write_inheritable_attribute :columns_has_translations, (columns rescue []).collect{|col| col if attrs.include?(col.name.to_sym)}.compact
|
196
|
+
class_inheritable_reader :columns_has_translations
|
197
|
+
|
198
|
+
# forces given locale
|
199
|
+
# I18n.locale = :lv
|
200
|
+
# a = Article.find 18
|
201
|
+
# a.title
|
202
|
+
# => "LV title"
|
203
|
+
# a.in(:en).title
|
204
|
+
# => "EN title"
|
205
|
+
def in locale
|
206
|
+
locale.to_sym == I18n.default_locale ? self : find_translation(locale)
|
207
|
+
end
|
208
|
+
|
209
|
+
def find_or_build_translation(*args)
|
210
|
+
locale = args.first.to_s
|
211
|
+
build = args.second.present?
|
212
|
+
find_translation(locale) || (build ? self.translations.build(:locale => locale) : self.translations.new(:locale => locale))
|
213
|
+
end
|
214
|
+
|
215
|
+
def translation(locale)
|
216
|
+
find_translation(locale.to_s)
|
217
|
+
end
|
218
|
+
|
219
|
+
def all_translations
|
220
|
+
t = I18n.available_locales.map do |locale|
|
221
|
+
[locale, find_or_build_translation(locale)]
|
222
|
+
end
|
223
|
+
ActiveSupport::OrderedHash[t]
|
224
|
+
end
|
225
|
+
|
226
|
+
def has_translation?(locale)
|
227
|
+
return true if locale == I18n.default_locale
|
228
|
+
find_translation(locale).present?
|
229
|
+
end
|
230
|
+
|
231
|
+
# if object is new, then nested slaves ar built for all available locales
|
232
|
+
def build_nested_translations
|
233
|
+
if (I18n.available_locales.size - 1) > self.translations.size
|
234
|
+
I18n.available_locales.clone.delete_if{|l| l == I18n.default_locale}.each do |l|
|
235
|
+
options = {:locale => l.to_s}
|
236
|
+
options[self.class.reflections[:translations].class_name.constantize.master_id] = self.id unless self.new_record?
|
237
|
+
self.translations.build(options) unless self.translations.map(&:locale).include?(l.to_s)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
if options[:reader]
|
243
|
+
attrs.each do |name|
|
244
|
+
send :define_method, name do
|
245
|
+
unless I18n.default_locale == I18n.locale
|
246
|
+
translation = self.translation(I18n.locale)
|
247
|
+
if translation.nil?
|
248
|
+
if has_translations_options[:fallback]
|
249
|
+
(self[name].nil? || self[name].blank?) ? has_translations_options[:nil] : self[name].set_origins(self,name)
|
250
|
+
else
|
251
|
+
has_translations_options[:nil]
|
252
|
+
end
|
253
|
+
else
|
254
|
+
if @return_raw_data
|
255
|
+
(self[name].nil? || self[name].blank?) ? has_translations_options[:nil] : self[name].set_origins(self,name)
|
256
|
+
else
|
257
|
+
value = translation.send(name) and value.set_origins(self,name)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
else
|
261
|
+
(self[name].nil? || self[name].blank?) ? has_translations_options[:nil] : self[name].set_origins(self,name)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
has_many :translations, :class_name => translation_class_name, :foreign_key => translation_class.master_id, :dependent => :destroy
|
268
|
+
accepts_nested_attributes_for :translations, :allow_destroy => true, :reject_if => proc { |attributes| columns_has_translations.collect{|col| attributes[col.name].blank? ? nil : 1}.compact.empty? }
|
269
|
+
translation_class.belongs_to belongs_to
|
270
|
+
translation_class.validates_presence_of :locale
|
271
|
+
translation_class.validates_uniqueness_of :locale, :scope => translation_class.master_id
|
272
|
+
|
273
|
+
# Workaround to support Rails 2
|
274
|
+
scope_method = if ActiveRecord::VERSION::MAJOR < 3 then :named_scope else :scope end
|
275
|
+
|
276
|
+
send scope_method, :translated, lambda { |locale| {:conditions => ["#{translation_class.table_name}.locale = ?", locale.to_s], :joins => :translations} }
|
277
|
+
|
278
|
+
#private is no good
|
279
|
+
|
280
|
+
def find_translation(locale)
|
281
|
+
locale = locale.to_s
|
282
|
+
translations.detect { |t| t.locale == locale }
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
module TranslationClassMethods
|
288
|
+
# sets real master_id it's aware of STI
|
289
|
+
def extract_master_id name
|
290
|
+
master_class = name.sub('Translation','').constantize
|
291
|
+
#FIXME why need to check superclass ?
|
292
|
+
class_name = master_class.name #!master_class.superclass.abstract_class? ? master_class.superclass.name : master_class.name
|
293
|
+
self.master_id = :"#{class_name.demodulize.underscore}_id"
|
294
|
+
end
|
295
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class String
|
2
|
+
attr_accessor :origin_model, :origin_name
|
3
|
+
def set_origins obj, name
|
4
|
+
self.origin_model = obj
|
5
|
+
self.origin_name = name
|
6
|
+
self
|
7
|
+
end
|
8
|
+
# forces given locale
|
9
|
+
# I18n.locale = :lv
|
10
|
+
# a = Article.find 18
|
11
|
+
# a.title
|
12
|
+
# => "LV title"
|
13
|
+
# a.title.in(:en)
|
14
|
+
# => "EN title"
|
15
|
+
def in locale
|
16
|
+
return self unless self.origin_model
|
17
|
+
translation = self.origin_model.in(locale) and translation.send(self.origin_name)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
|
2
|
+
require 'lolita-translation/string.rb'
|
3
|
+
require 'lolita-translation/has_translations.rb'
|
4
|
+
require 'lolita-translation/rails'
|
5
|
+
|
6
|
+
module Lolita
|
7
|
+
module Configuration
|
8
|
+
module Tab
|
9
|
+
autoload :Translation, "lolita-translation/configuration/tab/translation"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{lolita-translation}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["ITHouse", "Gatis Tomsons", "Arturs Meisters"]
|
12
|
+
s.date = %q{2011-08-01}
|
13
|
+
s.description = %q{Translates models in Lolita}
|
14
|
+
s.email = %q{support@ithouse.lv}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"LICENSE.txt",
|
23
|
+
"README.md",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"app/views/components/lolita/configuration/tab/translation/_display.html.erb",
|
27
|
+
"app/views/lolita/has_translations/_tab.html.erb",
|
28
|
+
"config/locales/en.yml",
|
29
|
+
"lib/generators/lolita_translation/USAGE",
|
30
|
+
"lib/generators/lolita_translation/has_translations_generator.rb",
|
31
|
+
"lib/lolita-translation.rb",
|
32
|
+
"lib/lolita-translation/configuration/tab/translation.rb",
|
33
|
+
"lib/lolita-translation/has_translations.rb",
|
34
|
+
"lib/lolita-translation/rails.rb",
|
35
|
+
"lib/lolita-translation/string.rb",
|
36
|
+
"lib/tasks/has_translations_tasks.rake",
|
37
|
+
"lolita-translation.gemspec",
|
38
|
+
"lolita-translations.gemspec",
|
39
|
+
"spec/has_translations_spec.rb",
|
40
|
+
"spec/spec.opts",
|
41
|
+
"spec/spec_helper.rb"
|
42
|
+
]
|
43
|
+
s.homepage = %q{http://github.com/ithouse/lolita-translations}
|
44
|
+
s.licenses = ["MIT"]
|
45
|
+
s.require_paths = ["lib"]
|
46
|
+
s.rubygems_version = %q{1.6.2}
|
47
|
+
s.summary = %q{Lolita models translation plugin}
|
48
|
+
s.test_files = [
|
49
|
+
"spec/has_translations_spec.rb",
|
50
|
+
"spec/spec_helper.rb"
|
51
|
+
]
|
52
|
+
|
53
|
+
if s.respond_to? :specification_version then
|
54
|
+
s.specification_version = 3
|
55
|
+
|
56
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
57
|
+
s.add_runtime_dependency(%q<rails>, [">= 3.0.0"])
|
58
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
59
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
60
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
61
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
62
|
+
else
|
63
|
+
s.add_dependency(%q<rails>, [">= 3.0.0"])
|
64
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
65
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
66
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
67
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
68
|
+
end
|
69
|
+
else
|
70
|
+
s.add_dependency(%q<rails>, [">= 3.0.0"])
|
71
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
72
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
73
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
74
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{lolita-translations}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["ITHouse", "Gatis Tomsons", "Arturs Meisters"]
|
12
|
+
s.date = %q{2011-08-01}
|
13
|
+
s.description = %q{Translates models in Lolita}
|
14
|
+
s.email = %q{support@ithouse.lv}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"LICENSE.txt",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION"
|
25
|
+
]
|
26
|
+
s.homepage = %q{http://github.com/ithouse/lolita-translations}
|
27
|
+
s.licenses = ["MIT"]
|
28
|
+
s.require_paths = ["lib"]
|
29
|
+
s.rubygems_version = %q{1.6.2}
|
30
|
+
s.summary = %q{Lolita models translation plugin}
|
31
|
+
s.test_files = [
|
32
|
+
"spec/has_translations_spec.rb",
|
33
|
+
"spec/spec_helper.rb"
|
34
|
+
]
|
35
|
+
|
36
|
+
if s.respond_to? :specification_version then
|
37
|
+
s.specification_version = 3
|
38
|
+
|
39
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
40
|
+
s.add_runtime_dependency(%q<rails>, [">= 3.0.0"])
|
41
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
42
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
43
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
44
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
45
|
+
else
|
46
|
+
s.add_dependency(%q<rails>, [">= 3.0.0"])
|
47
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
48
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
49
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
50
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
51
|
+
end
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<rails>, [">= 3.0.0"])
|
54
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
55
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
56
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
57
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
3
|
+
|
4
|
+
describe 'HasTranslations' do
|
5
|
+
it "should switch locales" do
|
6
|
+
g = Group.create!(:name => "Sport")
|
7
|
+
c = Category.create!(:name => "Golf", :groups => [g])
|
8
|
+
z = News.create!(:title => "Tiger Woods sucks", :body => Faker::Lorem::paragraphs(10).join, :category_id => c.id)
|
9
|
+
# translate
|
10
|
+
g.translations.create!(:locale => 'lv', :name => "Sports")
|
11
|
+
c.translations.create!(:locale => 'lv', :name => "Golfs")
|
12
|
+
z.translations.create!(:locale => 'lv', :title => "Taigers Vuds nekam neder")
|
13
|
+
|
14
|
+
g.name.should == "Sport"
|
15
|
+
c.name.should == "Golf"
|
16
|
+
z.title.should == "Tiger Woods sucks"
|
17
|
+
|
18
|
+
I18n.locale = :lv
|
19
|
+
|
20
|
+
g.name.should == "Sports"
|
21
|
+
c.name.should == "Golfs"
|
22
|
+
z.title.should == "Taigers Vuds nekam neder"
|
23
|
+
|
24
|
+
z.category.name.should == "Golfs"
|
25
|
+
z.category.groups.first.name.should == "Sports"
|
26
|
+
|
27
|
+
z.destroy
|
28
|
+
c.destroy
|
29
|
+
g.destroy
|
30
|
+
#--------------------------------
|
31
|
+
I18n.locale = I18n.default_locale
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should load requested locale with 'in' method" do
|
35
|
+
g = Group.create!(:name => "Sport")
|
36
|
+
g.translations.create!(:locale => 'lv', :name => "Sports")
|
37
|
+
|
38
|
+
g = Group.find_by_name "Sport"
|
39
|
+
g.name.should == "Sport"
|
40
|
+
g.name.in(:lv).should == "Sports"
|
41
|
+
g.name.should == "Sport"
|
42
|
+
end
|
43
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rubygems'
|
3
|
+
gem 'rails', '~>2.3'
|
4
|
+
require 'i18n'
|
5
|
+
require 'active_record'
|
6
|
+
require 'spec'
|
7
|
+
require 'faker'
|
8
|
+
|
9
|
+
require 'ruby-debug'
|
10
|
+
|
11
|
+
require File.dirname(__FILE__)+'/../init.rb'
|
12
|
+
ActiveRecord::Base.logger = Logger.new(File.open("#{File.dirname(__FILE__)}/database.log", 'w+'))
|
13
|
+
ActiveRecord::Base.establish_connection({ :database => ":memory:", :adapter => 'sqlite3', :timeout => 500 })
|
14
|
+
|
15
|
+
# setup I18n
|
16
|
+
I18n.available_locales = [:en,:lv,:ru,:fr]
|
17
|
+
I18n.default_locale = :en
|
18
|
+
I18n.locale = :en
|
19
|
+
|
20
|
+
# Add models
|
21
|
+
ActiveRecord::Schema.define do
|
22
|
+
create_table :news, :force => true do |t|
|
23
|
+
t.string :title
|
24
|
+
t.string :slug
|
25
|
+
t.text :body
|
26
|
+
t.integer :category_id
|
27
|
+
t.integer :trx_id
|
28
|
+
end
|
29
|
+
create_table :categories, :force => true do |t|
|
30
|
+
t.string :name
|
31
|
+
t.string :desc
|
32
|
+
t.integer :trx_id
|
33
|
+
end
|
34
|
+
create_table :groups, :force => true do |t|
|
35
|
+
t.string :name
|
36
|
+
end
|
37
|
+
create_table :categories_groups, :force => true, :id => false do |t|
|
38
|
+
t.integer :category_id
|
39
|
+
t.integer :group_id
|
40
|
+
end
|
41
|
+
create_table :meta_datas, :force => true do |t|
|
42
|
+
t.string :title
|
43
|
+
t.string :url
|
44
|
+
t.string :keywords
|
45
|
+
t.text :description
|
46
|
+
t.string :metaable_type
|
47
|
+
t.integer :metaable_id
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class News < ActiveRecord::Base
|
52
|
+
belongs_to :category, :dependent => :destroy
|
53
|
+
has_one :meta_data, :as => :metaable, :dependent => :destroy
|
54
|
+
translations :title, :body
|
55
|
+
end
|
56
|
+
|
57
|
+
class Category < ActiveRecord::Base
|
58
|
+
has_many :news
|
59
|
+
has_and_belongs_to_many :groups
|
60
|
+
translations :name
|
61
|
+
end
|
62
|
+
|
63
|
+
class Group < ActiveRecord::Base
|
64
|
+
has_and_belongs_to_many :categories
|
65
|
+
translations :name
|
66
|
+
end
|
67
|
+
|
68
|
+
class MetaData < ActiveRecord::Base
|
69
|
+
belongs_to :metaable, :polymorphic => true
|
70
|
+
translations :title, :url, :keywords, :description
|
71
|
+
end
|
72
|
+
|
73
|
+
# build translation tables
|
74
|
+
|
75
|
+
News.sync_translation_table!
|
76
|
+
Category.sync_translation_table!
|
77
|
+
Group.sync_translation_table!
|
78
|
+
MetaData.sync_translation_table!
|
79
|
+
|
80
|
+
# this is included in Lolita by default
|
81
|
+
class ::Hash
|
82
|
+
# converts all keys to symbols, but RECURSIVE
|
83
|
+
def symbolize_keys!
|
84
|
+
each do |k,v|
|
85
|
+
sym = k.respond_to?(:to_sym) ? k.to_sym : k
|
86
|
+
self[sym] = Hash === v ? v.symbolize_keys! : v
|
87
|
+
delete(k) unless k == sym
|
88
|
+
end
|
89
|
+
self
|
90
|
+
end
|
91
|
+
end
|
92
|
+
Spec::Runner.configure do |config|
|
93
|
+
config.before(:each) do
|
94
|
+
News.delete_all
|
95
|
+
Category.delete_all
|
96
|
+
MetaData.delete_all
|
97
|
+
end
|
98
|
+
end
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lolita-translation
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- ITHouse
|
9
|
+
- Gatis Tomsons
|
10
|
+
- Arturs Meisters
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
date: 2011-08-01 00:00:00.000000000 +03:00
|
15
|
+
default_executable:
|
16
|
+
dependencies:
|
17
|
+
- !ruby/object:Gem::Dependency
|
18
|
+
name: rails
|
19
|
+
requirement: &74136740 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ! '>='
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 3.0.0
|
25
|
+
type: :runtime
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: *74136740
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: shoulda
|
30
|
+
requirement: &74136150 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ! '>='
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: *74136150
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: bundler
|
41
|
+
requirement: &74135560 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.0.0
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: *74135560
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: jeweler
|
52
|
+
requirement: &74118460 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ~>
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 1.5.2
|
58
|
+
type: :development
|
59
|
+
prerelease: false
|
60
|
+
version_requirements: *74118460
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rcov
|
63
|
+
requirement: &74117810 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
type: :development
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: *74117810
|
72
|
+
description: Translates models in Lolita
|
73
|
+
email: support@ithouse.lv
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files:
|
77
|
+
- LICENSE.txt
|
78
|
+
- README.md
|
79
|
+
files:
|
80
|
+
- .document
|
81
|
+
- Gemfile
|
82
|
+
- LICENSE.txt
|
83
|
+
- README.md
|
84
|
+
- Rakefile
|
85
|
+
- VERSION
|
86
|
+
- app/views/components/lolita/configuration/tab/translation/_display.html.erb
|
87
|
+
- app/views/lolita/has_translations/_tab.html.erb
|
88
|
+
- config/locales/en.yml
|
89
|
+
- lib/generators/lolita_translation/USAGE
|
90
|
+
- lib/generators/lolita_translation/has_translations_generator.rb
|
91
|
+
- lib/lolita-translation.rb
|
92
|
+
- lib/lolita-translation/configuration/tab/translation.rb
|
93
|
+
- lib/lolita-translation/has_translations.rb
|
94
|
+
- lib/lolita-translation/rails.rb
|
95
|
+
- lib/lolita-translation/string.rb
|
96
|
+
- lib/tasks/has_translations_tasks.rake
|
97
|
+
- lolita-translation.gemspec
|
98
|
+
- lolita-translations.gemspec
|
99
|
+
- spec/has_translations_spec.rb
|
100
|
+
- spec/spec.opts
|
101
|
+
- spec/spec_helper.rb
|
102
|
+
has_rdoc: true
|
103
|
+
homepage: http://github.com/ithouse/lolita-translations
|
104
|
+
licenses:
|
105
|
+
- MIT
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ! '>='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
segments:
|
117
|
+
- 0
|
118
|
+
hash: 283995089
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
none: false
|
121
|
+
requirements:
|
122
|
+
- - ! '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 1.6.2
|
128
|
+
signing_key:
|
129
|
+
specification_version: 3
|
130
|
+
summary: Lolita models translation plugin
|
131
|
+
test_files:
|
132
|
+
- spec/has_translations_spec.rb
|
133
|
+
- spec/spec_helper.rb
|