lolita-translation 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", ">=3.0.0"
4
+ group :development do
5
+ gem "shoulda", ">= 0"
6
+ gem "bundler", "~> 1.0.0"
7
+ gem "jeweler", "~> 1.5.2"
8
+ gem "rcov", ">= 0"
9
+ end
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,8 @@
1
+ en:
2
+ lolita:
3
+ tabs:
4
+ titles:
5
+ translation: Translation
6
+ translation:
7
+ fields:
8
+ language: Language
@@ -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
@@ -0,0 +1,14 @@
1
+ module Lolita
2
+ module Configuration
3
+ module Tab
4
+ class Translation < Lolita::Configuration::Tab::Base
5
+
6
+ def initialize(dbi,*args,&block)
7
+ @type=:translation
8
+ super
9
+ end
10
+
11
+ end
12
+ end
13
+ end
14
+ 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,5 @@
1
+ module LolitaHasTranslation
2
+ class Engine < Rails::Engine
3
+
4
+ end
5
+ 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,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :has_translations do
3
+ # # Task goes here
4
+ # 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
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --reverse
3
+ --backtrace
@@ -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