During the development of a Rails app, you frequently need to use internationalization. There are some gems for this. In this case, we will use Globalize3. You can see the Github repository for other informations.
Globalize3 allows you to translate content dynamically. For a basic usage, you do not need anything in your views. This gem displays the content in the current locale (I18n.locale). In the database, translations tables contain one line by locale for each object (in example, for a product, one product_translations table line for ‘fr’ and one for ‘en’). You can also use different locale in the same page and, in this case, Globalize3 allows you to use blocks to translate some parts of your page.
Globalize3 requires I18n and ActiveRecord 3.0 or more. There is a version of Globalize for Rails 2 named Globalize2.
In order to install this gem in your app, you just need to add this line to your Gemfile :
gem 'globalize3', :git => 'git://github.com/svenfuchs/globalize3.git'
Next, you have to run bundle install.
To use Globalize3, you must specify the translated fields in your model, for example, if you need translation for a product :
class Product < ActiveRecord::Base
translates :name, :description
end
Then, you can generate the migration to create the translation table :
class CreateProductTransalations < ActiveRecord::Migration
def up
Product.create_translation_table! :name => :string, :description => :text
end
def down
Product.drop_translation_table!
end
end
Be careful, with Rails 3.1, do not use the change method. Indeed, Rails fail to do the reverse migration so we have to create up and down methods.
In the views, if there is content from different locales on the same page, you have to use the blocks below :
<% # specify the locale
# you can have several blocks like this in the same page with different locale %>
<% Globalize.with_locale(:en) do %>
<% render :partial => "my_translated_partial" %>
<% end %>
There is a method to create translation table but not to add translated field to an existing translations table. You must add columns like columns in a generic table :
class AddFieldToProductTranslations < ActiveRecord::Migration
def up
add_column :product_translations, :some_field, :string
end
def down
remove_column :product_translations, :some_field
end
end
But in some cases, this can cause some problems because fields are present in the model (in translates method) but not in the database and migrations failed… For example, if you add some fields in this method in your controller :
class Product < ActiveRecord::Base
translates :name, :description, :benefits, :warranty_informations
end
And this fields are added to the database in several migrations this can cause issues. In order to avoid this issues, you can do this :
# in your model
class Product < ActiveRecord::Base
TRANSLATED_FIELD = [
:name, :description, :benefits, :warranty_informations
].freeze
translates *TRANSLATED_FIELD
end
# in your migrations
class AddTranslationsForProducts < ActiveRecord::Migration
include Globalize::ActiveRecord::Migration
class Product < ActiveRecord::Base
@translated_fields = {
:name => :string,
:description => :text
}
def self.translated_fields
@translated_fields
end
translates *@translated_fields.keys
end
def up
# create translation table
Product.reset_column_information
Product.create_translation_table!(Product.translated_fields, :migrate_data => true)
end
def self.down
# drop translations table
Product.drop_translation_table!
end
end
And next, in the other migrations (to add fields) yo can do like that :
class AddFieldsToProductTranslation < ActiveRecord::Migration
include Globalize::ActiveRecord::Migration
class Product < ActiveRecord::Base
@translated_fields = { :benefits => :string, :warranty_informations => :string }
def self.translated_fields
@translated_fields
end
translates *@translated_fields.keys
end
def self.up
# Adding the columns
# add_column :product_translations
Product.reset_column_information
Product.translated_fields.each_pair { |field, type| add_column :product_translations, field, type }
end
def self.down
# Do stuff to put the data back using the migrator from Globalize
Product.reset_column_information
# And remove unused columns
Product.translated_fields.keys.each { |field| remove_column :product_translations, field }
end
end
In some cases you may also need to add integer fields in your translations tables but Globalize3 only accepts string and text fields. In this case, you can not add this field with the create_translation_table! method, you need to use the example above.
If on your model you have translated fields like name or description, be careful, you can not write this :
Product.where(:name => 'something')
If you do, there is an issue because Rails does not find the name column into products table.
To use the where clause you must do this :
Product.with_translations('en').where('product_translations.name' => 'something')
If there is only one condition, I advise you to use the find_by method, for example with the name column :
Product.find_by_name('something')
In this case, there is no problem, Rails join translations table and find the column in the right table.
Ransack is a gem that allows you to create search forms easily. Indeed, with this gem you can search objects in a model with different conditions. With a multi-languages application, you need to do the search in the correct language.
In your controller, you need to add the with_translations method :
class ProductsController < ApplicationController
def index
@search = Product.with_translations(I18n.locale).search(params[:q])
@products = @search.result(distinct: true)
end
end
In this case, your search is made with the correct language.
In the search form, you can made the search on translated fields :
<%= search_form_for @search do |f| %>
<%# name and description are translated fields %>
<%= f.text_field :translations_name_or_translations_description_cont %>
<%= f.submit %>
We must specify translations in the form because this fields are in the translations table. The search is made with the locale defined in the controller.
Globalize3 is a good gem for internationalization, it is a good complement of I18n. If you have to translate fields in different languages this is very helpful.
The Synbioz Team.
Free to be together.