Skip to content

Commit

Permalink
Adding support for disabling translations by setting the locale of the
Browse files Browse the repository at this point in the history
parent object.
  • Loading branch information
samlown committed Sep 24, 2009
1 parent 06f9a8f commit f5dca5d
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 19 deletions.
42 changes: 37 additions & 5 deletions README.rdoc
Expand Up @@ -23,13 +23,16 @@ added to an existing application.

== Updates

=== 24th September 2009

- Added support for setting the locale variable on the parent, which disables translations. See below.

=== 23rd September 2009

- Testing finally added (hope to add more tests soon)
- Validations now only performed by the parent model
- Changed namespace to TranslateColumns (as opposed to Translate::Columns)
- Parent model's +locale+ variable changed to +translation_locale+ so that it can be
added as a column/attribute if needed.
- Parent model's +locale+ variable changed to +translation_locale+ so that it can be added as a column/attribute if needed.

=== 20th May 2009

Expand Down Expand Up @@ -176,9 +179,8 @@ won't even notice the plugin is there.

Here's a really basic example of what we can do on the console.

Loading development environment.
>> I18n.locale = 'en' # First try default language
=> "en"
>> I18n.locale = I18n.default_locale # First try default language
=> :en
>> doc = Document.find(:first)
-- output hidden --
>> doc.title
Expand All @@ -205,6 +207,36 @@ Here's a really basic example of what we can do on the console.
As can be seen, just by setting the locale we are able to edit the data
without having to worry about the details.

The current version also has support for disabling translations by giving the
parent object a +locale+ field and setting it to something. This is actually a
very powerful feature as it allows new objects to be created under a specific locale
and filtered as such. A typical example would be a blog where most of the posts
you'd like to be translated into several languages, but occaisionly some posts will only
be relevant for a specific region:

>> I18n.locale = I18n.default_locale
>> post = Post.new(:title => "Example") # WIN
>>
>> I18n.locale = 'es'
>> post = Post.new(:title => "Ejemplo") # FAIL
TranslateColumns::MissingParent: Cannot create translations without a stored parent
>>
>> post = Post.new(:locale => 'es', :title => "Ejemplo") # WIN
>>
>> # Provide posts, with either a translation of for the current locale
>> posts = Post.paginate(:conditions => ['posts.locale IS NULL OR posts.locale = ?', I18n.locale])

A useful +named_scope+ could be as follows:

class Post < ActiveRecord::Base
# ... translate columns stuff ...
named_scope :for_current_locale, :conditions => ['posts.locale IS NULL OR posts.locale = ?', I18n.locale]
end

@posts = Post.for_current_locale.paginate

Of course, if you don't want this funcionality simply do not add a locale attribute or method to
the parent model.

== How it works

Expand Down
46 changes: 33 additions & 13 deletions lib/translate_columns.rb
Expand Up @@ -51,6 +51,7 @@ def translate_columns( *options )
# Rails magic to override the normal save process
alias_method_chain :save, :translation
alias_method_chain :save!, :translation
alias_method_chain :attributes=, :locale

# Generate a module containing methods that override access
# to the ActiveRecord methods.
Expand Down Expand Up @@ -133,7 +134,7 @@ def translation_locale
# Setting the locale will always enable translation.
# If set to nil the global locale is used.
def translation_locale=(locale)
@disable_translation = false
enable_translation
# TODO some checks for available translations would be nice.
# I18n.available_locales only available as standard with rails 2.3
@translation_locale = locale.to_s.empty? ? nil : locale.to_s
Expand All @@ -143,16 +144,29 @@ def translation_locale=(locale)
def disable_translation
@disable_translation = true
end
def enable_translation
@disable_translation = false
end

# Important check to see if the parent has a locale method.
# If so, translations should be disabled if it is set to something!
def has_locale_value?
respond_to?(:locale) && !self.locale.to_s.empty?
end

# determine if the conditions are set for a translation to be used
def translation_enabled?
(!@disable_translation && translation_locale) and !has_locale_value?
end

# If the current object has a locale set, return
# a translation object from the translations set
# Provide a translation object based on the parent and the translation_locale
# current value.
def translation
if !@disable_translation and translation_locale
if translation_enabled?
if !@translation || (@translation.locale != translation_locale)
# try to find entity in translations array
raise MissingParent, "Cannot create translations without a stored parent" if new_record?
@translation = translations.find_by_locale(translation_locale)
@translation = self.translations.build(:locale => translation_locale) unless @translation
# try to find translation or build a new one
@translation = translations.find_by_locale(translation_locale) || translations.build(:locale => translation_locale)
end
@translation
else
Expand Down Expand Up @@ -195,13 +209,19 @@ def save_with_translation!
raise
end


protected
# Override the default mass assignment method so that the locale variable is always
# given preference.
def attributes_with_locale=(new_attributes, guard_protected_attributes = true)
return if new_attributes.nil?
attributes = new_attributes.dup
attributes.stringify_keys!

attributes = remove_attributes_protected_from_mass_assignment(attributes) if guard_protected_attributes
send(:locale=, attributes["locale"]) if attributes.has_key?("locale") and respond_to?(:locale=)

send(:attributes_without_locale=, attributes, guard_protected_attributes)
end

def enable_translation
@disable_translation = false
end

end

end
1 change: 1 addition & 0 deletions test/fixtures/documents.yml
Expand Up @@ -7,6 +7,7 @@ document1:
document2:
id: 2
title: Test Document Number 2
locale: en
body: This is a second test document with some random content for the body.
published_at: "2009-09-23 21:53:46"

1 change: 1 addition & 0 deletions test/fixtures/schema.rb
@@ -1,5 +1,6 @@
ActiveRecord::Schema.define do
create_table "documents", :force => true do |t|
t.column "locale", :string, :length => 8
t.column "title", :string
t.column "body", :text
t.column "published_at", :datetime
Expand Down
26 changes: 25 additions & 1 deletion test/translate_columns_test.rb
@@ -1,5 +1,6 @@
require 'test/unit'
require 'test/lib/activerecord_test_helper'
require 'test/unit'
require 'mocha'
require 'init'

class TranslateColumnsTest < Test::Unit::TestCase
Expand Down Expand Up @@ -121,5 +122,28 @@ def test_failed_validations_on_translation
assert doc.errors.on(:title)
end

def test_locale_attribute_detection
doc = Document.find(:first)
assert !doc.has_locale_value?
doc.locale = "en"
assert doc.has_locale_value?
end

def test_locale_attribute_detection_without_attribute
doc = Document.find(:first)
doc.locale = "en"
doc.stubs(:respond_to?).with(:locale).returns(false)
assert !doc.has_locale_value?
end

def test_create_new_document_with_specific_locale
I18n.locale = 'es'
doc = nil
assert_nothing_thrown do
doc = Document.new(:locale => 'es', :title => "A new document", :body => "Test Body")
end
assert doc.locale, 'es'
assert doc.save
end

end

0 comments on commit f5dca5d

Please sign in to comment.