Permalink
Browse files

Updating to use Rails 2.2 I18n code and simplifying some of the old code

  • Loading branch information...
1 parent a355de9 commit d8d03d1d66328bd9adc1cfff9b4caabcb2e5f880 @samlown committed May 20, 2009
Showing with 73 additions and 144 deletions.
  1. +1 −0 .gitignore
  2. +48 −59 README.rdoc
  3. +24 −85 lib/translate_columns.rb
View
@@ -0,0 +1 @@
+*.swp
View
@@ -1,6 +1,6 @@
= Translate Columns Plugin
-Copyright (c) 2007 Samuel Lown <me (AT) samlown.com>
+Copyright (c) 2007-2009 Samuel Lown <me (AT) samlown.com>
This Plugin is released under the MIT license, as Rails itself. Please see the
attached LICENSE file for further details.
@@ -21,6 +21,17 @@ columns will not require any major refactoring of your code (unless you're
really unlucky), and can be simply added. Indeed, the plugin was written to be
added to an existing application.
+== Updates
+
+=== 20th May 2009
+
+- Finally got round to moving to github
+- Added support for the rails 2.2 I18n stuff (WIN!)
+
+*WARNING* If you're using and older version of Rails (< 2.2) or upgrading,
+translate columns has been updated to use the new I18n code *ONLY* and will not
+work with old code! See Upgrade section for a few hints.
+
== Architecture
@@ -54,8 +65,8 @@ The data contained by these entities may be similar to the following:
| Column | Type |
--------------------------
| id | integer |
- | locale_id | integer |
| document_id | integer |
+ | locale | string |
| title | string |
| sub_title | string |
| body | text |
@@ -68,14 +79,18 @@ In Rails, thsee models would be defined as follows:
class DocumentTranslation < ActiveRecord::Base
belongs_to :document
- belongs_to :locale
end
Each DocumentTranslation belongs to a Document and defines the locale of the
translation and only those fields that require a translation. If you really
-wanted to, a composite key could be used on the document_id and the locale_id,
+wanted to, a composite key could be used on the document_id and the locale,
as these should always uniquely identify the translation.
+In previous versions of translate_columns a Locale model and associations was
+used to determine the language of a translation, this is no longer required
+with the new Rails 2.2 I18n code and simple string for the locale code
+of your choice can be used instead.
+
IMPORTANT: Default locale. In order for this setup to work, there must be a
single, pre-defined locale for the default data, this is the data contained
in the 'Document' entity and will be used whenever we're operating in default
@@ -88,38 +103,22 @@ correct class. Aside from saving on typing, this is an essential requirement
of the translate_columns plugin. (At least until I get chance to add an option
to allow for different names.)
-The Locale class is left up to the developer to decide how to define and use
-specifically, but suffice to say that it must exist as an ActiveRecord so that
-associations will work correctly. Additionally, it must always be called
-'Locale' for the translation_column plugin to find it. More details
-are provided below, but in my implementations, I generally create my own Locale
-class and add wrapper functions to control the Globalize plugin's Locale class.
-
-
== Installation
Assuming you've read the above and understand the basic requirements, the
plugin can now be installed and setup.
-The latest version should always be available from:
-
-https://ityzen.com/svn/translate_columns/trunk/vendor/plugins/translate_columns
-
-Should you want to work on developing or testing the plugin, I'd suggest
-checking out the complete mini sample project:
+The latest details and updates are available on the github repository:
-https://ityzen.com/svn/translate_columns/trunk
+http://github.com/samlown/translate_columns
To install plugin, use the standard rails plugin install method:
- ruby script/plugin install SVN
-
-(Replace SVN with the long URL above.)
+ ./script/plugin install git://github.com/samlown/translate_columns.git
There are no more installation steps, and the plugin does not install any extra
files or customise the setup. To uninstall, simply remove the directory.
-
== Setup
Now for the hard part :-) Re-using the example above for documents, to use the
@@ -133,58 +132,50 @@ plugin modify the model so that it looks like the following:
I'm working on getting it so that you don't need to specify the columns
manually, but it is not yet ready.
-As mentioned earlier, the plugin requires a Locale class and should look
-something like the following:
+In earlier versions you'd need to mess around with a Locale class but thanks to
+the Rails I18n extension, this is no longer necessary.
- class Locale < ActiveRecord::Base
- @@global_locale = nil
+== Upgrading
- def self.global
- @@global_locale
- end
+If your using an older version of Translate Columns, then you'll need to perform an
+upgrade and migration to use the fabulous new I18n Rails code. Fortunately, this is
+very easy to do.
+
+To upgrade, remove and previous entries to your Locale class in you translation models
+and generate a migration to convert the local_id column into a string. Something like
+the following will surfice.
- def self.global=( locale )
- if locale.is_a? Locale
- @@global_locale = locale
- elsif locale.is_a? String
- locale = Locale.find(:first, :conditions => ['short = ? OR code = ?', locale, locale])
- return false if (! locale)
- @@global_locale = locale
- else
- # empty
- @@global_locale = nil
- end
+ class UpgradeTranslationModels < ActiveRecord::Migration
+ def self.up
+ alter_column :product_translations, :locale_id, :string, :length => 10
+ rename_column :product_translations, :locale_id, :locale
end
-
- def master?
- self.master == true
+
+ def self.down
+ rename_column :product_translations, :locale, :locale_id
+ alter_column :product_translations, :locale_id, :integer
end
end
-In summary, your Locale class must provide one class and one instance methods;
-access to the current global locale accessed through Locale.global and a check
-to see if the current locale instance is the master/default locale. The
-Locale.global= method is provided as an example of how you could set it.
-
-With the hard part done, you can start playing.
-
+After ensuring you're using the I18n.locale calls throughout your application, it
+should work fine.
== Usage
The idea here is that you forget about the fact your models can be translated
-and just use the app as normal. Indeed, if you don't set a global locale, you
+and just use the app as normal. Indeed, if you don't set a locale, you
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.
- >> Locale.global = 'en' # First try default language
+ >> I18n.locale = 'en' # First try default language
=> "en"
>> doc = Document.find(:first)
-- output hidden --
>> doc.title
=> "Sample Document" # title in english
- >> Locale.global = 'es' # set to other language
+ >> I18n.locale = 'es' # set to other language
=> "es"
>> doc = Document.find(:first) # Reload to avoid caching problems!
-- output hidden --
@@ -196,14 +187,14 @@ Here's a really basic example of what we can do on the console.
=> "Nuevo Título Español"
>> doc.save # set the title and save
=> true
- >> Locale.global = 'en'
+ >> I18n.locale = 'en'
=> "en" # return to english
>> doc = Document.find(:first)
-- output hidden --
>> doc.title
=> "Sample Document"
-As can be seen, just by setting the Locale we are able to edit the data
+As can be seen, just by setting the locale we are able to edit the data
without having to worry about the details.
@@ -212,9 +203,7 @@ without having to worry about the details.
The plugin overrides the default attribute accessor functions and automatically
uses the 'translations' association to find the request fields. It also
provides a new method that extends the original method name to access
-the original values. The process used is called meta-programming and is one
-of the powerful features of Ruby that allows Rails to do its magic.
-
+the original values.
== Todos / Bugs
View
@@ -10,8 +10,6 @@ def self.included(mod)
mod.extend(ClassMethods)
end
- DEFAULT_LOCALE_CLASS_NAME = 'Locale'
-
# methods used in the class definition
module ClassMethods
@@ -20,17 +18,12 @@ module ClassMethods
#
# Possible options, after the columns, include:
#
- # * :locale_class - String of the name of the locale class used to determine
- # the current language in use. This overrides the
- # Translate::Columns::DEFAULT_LOCALE_CLASS_NAME constant.
- # * :foreign_key - Name of the field in the parents translation table
- # of the locale. This defaults to the current locale class's name with
- # +_id+ on the end, e.g. 'locale_id' for 'Locale'
+ # * :locale_field - Name of the field in the parents translation table
+ # of the locale. This defaults to 'locale'.
#
def translate_columns( *options )
- locale_class = Translate::Columns::DEFAULT_LOCALE_CLASS_NAME
- locale_foreign_key = nil
+ locale_field = 'locale'
columns = [ ]
if ! options.is_a? Array
@@ -42,49 +35,16 @@ def translate_columns( *options )
columns << opt
elsif opt.is_a? Hash
# Override the locale class if set.
- locale_class = opt[:locale_class] if opt[:locale_class]
- locale_foreign_key = opt[:foreign_key]
+ locale_field = opt[:locale_field]
end
end
-
- # Perform some checks on the Locale class to ensure its for real
- locale_class = locale_class.constantize
- begin
- locale_class.global
- rescue
- raise "The locale class '#{locale_class}' does not provide a class method named 'global'."
- end
- if ! locale_class.method_defined? :id
- raise "No 'id' method provided by Locale."
- elsif ! locale_class.method_defined? 'master?'
- raise "Locale class does not provide 'master?' method."
- end
-
- # Create a semi-hidden method used to get hold of the locale class
- define_method '_locale_class' do
- locale_class
- end
-
- define_method '_locale_foreign_key' do
- if locale_foreign_key.blank?
- locale_class.to_s.underscore + '_id'
- else
- locale_foreign_key
- end
- end
-
- # The name of the association used by the translation table
- define_method '_locale_association' do
- locale_class.to_s.underscore
- end
define_method 'columns_to_translate' do
columns.collect{ |c| c.to_s }
end
# set the instance Methods first
include Translate::Columns::InstanceMethods
-
# Generate a module containing methods that override access
# to the ActiveRecord methods.
@@ -93,6 +53,8 @@ def translate_columns( *options )
mod = Module.new do | m |
columns.each do | column |
+
+ next if ['id', locale_field].include?(column.to_s)
# This is strange, so allow me to explain:
# We define access to the original method and its super,
@@ -113,7 +75,7 @@ def translate_columns( *options )
super()
end
- alias_method("#{column}_default", column)
+ alias_method("#{column}_before_translation", column)
# overwrite accessor to read
define_method("#{column}") do
@@ -153,35 +115,21 @@ def translate_columns( *options )
# Methods that are specific to the current class
# and only called when translate_columns is used
module InstanceMethods
-
- # The locale variable is used by the translation function
- # to determine if a translation should be used.
- # If no locale has been set for this object, the Locale
- # class is checked if one has been set globally.
- # If a global is found, the object locale is set apropriatly.
- # Additionally, if the locale has the default value set to
- # true, it will not be used!
+
+ # Provide the locale which is currently in use with the object
+ # or nil if we're using the default translation
def locale
- if @locale.nil? and ! _locale_class.global.nil?
- @locale = _locale_class.global
- end
- if @locale and ! @locale.master?
- return @locale
- end
- return nil
+ I18n.locale.to_s == I18n.default_locale.to_s ? nil : (@locale ||= I18n.locale.to_s)
end
# Setting the locale will always enable translation.
# If set to nil the global locale is used.
- def locale=(val)
+ def locale=(locale)
@disable_translation = false
- return if (! val)
- if (val.is_a?(Integer))
- @locale = _locale_class.find_by_id(val)
- raise "Invalid id provided to search for locale object." if @locale.nil?
- else
- @locale = val
- end
+ return unless locale.to_s.empty?
+ # TODO some checks for available translations would be nice.
+ # I18n.available_locales only available as standard with rails 2.3
+ @locale = locale.to_s
end
# Do not allow translations!
@@ -192,25 +140,16 @@ def disable_translation
# If the current object has a locale set, return
# a translation object from the translations set
def translation
- if ((! @disable_translation) and locale)
- if (! (@translation and (@translation.send(_locale_foreign_key) == locale.id)))
+ if !@disable_translation and locale
+ if !@translation || (@translation.locale != locale)
# try to find entity in translations array
- @translation = nil
- self.translations.each do | t |
- if (t.send(_locale_foreign_key) == locale.id)
- @translation = t
- break;
- end
- end
- # @translation = self.translations.find(:first, :conditions=>['locale_id = ?', locale.id])
- if (! @translation)
- @translation = self.translations.build()
- @translation.send("#{_locale_association}=", locale)
- end
+ @translation = translations.find_by_locale(locale)
+ @translation = self.translations.build(:locale => locale) unless @translation
end
- return @translation
+ @translation
+ else
+ nil
end
- return nil
end
# As this is included in a mixin, a "super" call from inside the
@@ -253,4 +192,4 @@ def enable_translation
end
end
-end
+end

0 comments on commit d8d03d1

Please sign in to comment.