Rails gem approach, MySQL approach and a Multilingual Rails structure
Most of the world doesn’t speak English. That’s where internationalization and localization come in. Rails has a great i18n API. Reference: http://guides.rubyonrails.org/i18n.html It provides an easy-to-use and extensible framework for translating your application to a single custom language other than English or for providing multi-language support in our application. I18n API: The most important methods of the I18n API are: translate # Lookup text translations localize # Localize Date and Time objects to local formats These have the aliases #t and #l so you can use them like this:I18n.t 'app.title'
I18n.l Time.now
Rails-i18n gem:
Installation:
Add to the Gemfile:
gem 'rails-i18n', github: 'svenfuchs/rails-i18n', branch: 'master' # For 5.x
Configuration:
By default rails-i18n loads all locale files, pluralization and transliteration rules available in the gem. This behaviour can be changed, if we specify in config/environments/* the locales which have to be loaded via I18n.available_locales option:
config.i18n.available_locales = ['es-CO', :de]
or
config.i18n.available_locales = :nl
We can use another gem also:
Globalize gem:
Rails I18n de-facto standard library for ActiveRecord model/data translation. Globalize builds on the I18n API in Ruby on Rails to add model translations to ActiveRecord models.
Installation:
When using bundler put this in our Gemfile:
gem 'globalize', '~> 5.0.0'
To use globalize with Rails 5 add this in our Gemfile
gem 'activemodel-serializers-xml'
Documentation: https://github.com/globalize/globalize
DB Design for Multilingual App (English and Arabic):
Approach for supporting 2 or 3 languages:
1. Column Approach:
Create column approach model with language columns;
`title_en` varchar(255) NOT NULL,
`title_ar` varchar(255) NOT NULL,
Now, the way you would query it is also simple enough. We may do it by automatically selecting the right columns according to the chosen language.
Advantages:
- Simplicity – easy to implement
- Easy querying – no JOINs required
- No duplicates – doesn’t have duplicate content
- Hard to maintain – works in easy way for 2-3 languages, but it becomes a really hard when you have a lot of columns or a lot of languages
- Hard to add a new language
CREATE TABLE PRODUCT (id int, PRICE NUMBER(18, 2))
CREATE TABLE PRODUCT_tr (id int, product_id INT FK, languagecode varchar, name text, description text)
This way if we have multiple translatable column it would only require a single join to get it and it may be easier to import items together with their related translations.
- Doesn’t require altering the database schema for new languages (and thus limiting code changes)
- Doesn’t require a lot of space for unimplemented languages or translations of a particular item Provides the most flexibility
- We don’t end up with sparse tables
- We don’t have to worry about null keys and checking that we’re displaying an existing translation instead of some null entry.
Myapp::Application.routes.draw do
scope "(:locale)", :locale => /en|ar/ do
root :to => 'page#index'
get "page/index"
end
end
Set a before_filter in the app/controllers/application_controller.rb.
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :set_locale
private
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
end
To test it, Go to the URL;
http://0.0.0.0:3000/ar
Or
http://0.0.0.0:3000/ar/page/index
Setting a default language:
app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :set_locale
private
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
Rails.application.routes.default_url_options[:locale]= I18n.locale
end
end
As a result, We do not need to do anything else. All links generated via the scaffold generator are automatically changed accordingly.
Setting I18n.locale via Accept Language HTTP Header of Browser:
app/controllers/application_controller.rbclass
ApplicationController < ActionController::Base
protect_from_forgery
before_filter :set_locale
private
def extract_locale_from_accept_language_header
request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
end
def set_locale
I18n.locale = extract_locale_from_accept_language_header || I18n.default_locale
end
end
Here we can change clean our routes file:
config/routes.rb:
Myapp::Application.routes.draw do
get "page/index"
root :to => 'page#index'
end
Storing I18n.locale in Session
app/controllers/set_mylanguage_controller.rb:
class SetMylanguageController < ApplicationController
#English
def en
I18n.locale = :en
set_session_and_redirect
end
#Arabic
def ar
I18n.locale = :ar
set_session_and_redirect
end
private
def set_session_and_redirect
session[:locale] = I18n.locale
redirect_to :back
rescue ActionController::RedirectBackError
redirect_to :root
end
end
Change in application controller:
app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :set_locale
private
def set_locale
I18n.locale = session[:locale] || I18n.default_locale
session[:locale] = I18n.locale
end
end
For setting Arabic:
http://0.0.0.0:3000/set_mylanguage/ar
For setting English:
http://0.0.0.0:3000/set_mylanguage/en
Text Blocks in YAML Format:
Create the below directories first:
$ mkdir -p config/locales/models/item
$ mkdir -p config/locales/views/item
Insert the following lines into the file config/application.rb:
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', 'models', '*', '*.yml').to_s]
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', 'views', '*', '*.yml').to_s]
config.i18n.default_locale = :en
We will have to create a file here; config/locales/models/item
config/locales/models/item/ar.yml for Arabic
and make sure that en.yml is present for English.
English YAML:
As most things are already present in the system for English
Insert the below into en.yml file.
# ruby encoding: utf-8
en:
views:
show: Show
edit: Edit
destroy: Delete
are_you_sure: Are you sure?
back: Back
item:
index:
title: List of all items
new:
title: New Item
flash_messages:
item_was_successfully_created: ‘Item was successfully created.'
Arabic YAML:
We will have to insert values/texts in Arabic into ar.yml like in en.yml
Even we can copy a ready-made default translation by Sven Fuchs from his github repository https://github.com/svenfuchs/rails-i18n:
$ cd config/locales
$ curl -O https://raw.github.com/svenfuchs/rails-i18n/master/rails/locale/ar.yml
Translating in View:
We can use human_attribute_name() for the attributes and the links need to be translated with I18n.t. We can use number_to_currency() to show the amount/price in formatted form:
Examples:
<% = t ‘views.item.index.listing_items’ %>
<% = link_to I18n.t('views.show'),@item %>
<%= Item.human_attribute_name(:name) %><%=Item.human_attribute_name(:description) %>
Translating Flash Messages in the Controller
if @item.save
format.html { redirect_to @item, notice:
I18n.t('views.item.flash_messages.item_was_successfully_created') }
format.json { render json: @item, status: :created, location: @item }
else
format.html { render action: "new" }
format.json { render json: @item.errors, status: :unprocessable_entity }
end
For other ready-made language translations:
https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale
LUBAIB CEEJEY
Sr. Ruby on Rails Developer
Subscribe For Latest Updates
Related Posts