Chúng ta sẽ dùng I18n để hiển thị website trên nhiều ngôn ngữ khác nhau.
Đa ngôn ngữ là một bài toán khó, do đó chúng ta sẽ không thực hiện bằng cách dịch bình thường, mà thay vào đó là các chuỗi text cố định sẽ được hiển thị khác nhau với từng ngôn ngữ khác nhau.
Mặc định thì các ứng dụng Rails đều sử dụng I18n rồi, và ngôn ngữ duy nhất được sử dụng là tiếng Anh, các file ngôn ngữ sẽ được đặt trong thư mục config/locale với đuôi .yml, mặc định thư mục này chứa 1 file là en.yml:
# Files in the config/locales directory are used for internationalization
# and are automatically loaded by Rails. If you want to use locales other
# than English, add the necessary files in this directory.
#
# To use the locales, use `I18n.t`:
#
# I18n.t 'hello'
#
# In views, this is aliased to just `t`:
#
# <%= t('hello') %>
#
# To use a different locale, set it with `I18n.locale`:
#
# I18n.locale = :es
#
# This would use the information in config/locales/es.yml.
#
# To learn more, please read the Rails Internationalization guide
# available at http://guides.rubyonrails.org/i18n.html.
en:
hello: "Hello world"
Chúng ta sẽ làm việc với các file này sau.
Bây giờ chúng ta sẽ thêm chức năng hỗ trợ URL đa ngôn ngữ.
Chúng ta sẽ đưa tham số ngôn ngữ vào sau đuôi URL, ví dụ http://localhost:3000/en/products.
Đầu tiên chúng ta sửa lại file routes.rb trong thư mục config như sau:
Rails.application.routes.draw do
get 'admin/index'
get 'sessions/new'
get 'sessions/create'
get 'sessions/destroy'
get 'store/index'
get 'admin' => 'admin#index'
controller :sessions do
get 'login' => :new
post 'login' => :create
delete 'logout' => :destroy
end
scope '(:locale)' do
resources :users
resources :orders
resources :line_items
resources :carts
resources :products do
get :who_bought, :on => :member
end
root :to => 'store#index', :as => 'store'
end
end
Chúng ta gom các đoạn routing cho model – tức là các phương thức resources vào trong phương thức scope.
Phương thức scope ':locale' sẽ nối chuỗi trong biến :locale vào trước URL, tức là /products thì sẽ là en/products, ở đây chúng ta bọc :locale trong cặp dấu ngoặc tròn (), tức là có thể dùng hoặc không dùng cũng được.
Tiếp theo chúng ta cần khai báo biến :locale đó, chúng ta sửa lại lớp ApplicationController như sau:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
before_filter :authorize
before_filter :set_i18n_locale
protect_from_forgery with: :exception
private
def current_cart
Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
cart = Cart.create
session[:cart_id] = cart.id
cart
end
helper_method :current_cart
protected
def authorize
@user = User.find_by_id(session[:user_id])
if @user == nil
redirect_to '/login', :notice => 'You must login first'
end
end
def set_i18n_locale
if params[:locale]
if I18n.available_locales.include?(params[:locale].to_sym)
I18n.locale = params[:locale]
else
flash.now[:notice] = params[:locale] + ' is not supported'
end
end
end
def default_url_options
{ :locale => I18n.locale }
end
end
Phương thức set_i18n_locale sẽ được dùng trong phương thức before_filter, ở đây phương thức này sẽ kiểm tra xem trong URL gửi lên có tham số nào là :locale hay không, nếu có thì kiểm tra xem tham số đó có trong danh sách ngôn ngữ của I18n không, nếu có thì gán giá trị của tham số đó vào thuộc tính I18n.locale, không thì hiển thị lỗi.
Phương thức default_url_options là phương thức có sẵn của lớp ActionController::Base, phương thức này làm nhiệm vụ thiết lập các tham số trong lệnh HTTP được gửi lên. Chúng ta override phương thức này, ở đây chúng ta khai báo biến :locale có giá trị là giá trị của thuộc tính I18n.locale, tham số này sẽ được dùng trong phương thức helper là url_for.
Chúng ta sẽ tiếp tục làm việc với các phương thức này sau. Bây giờ nếu chúng ta trỏ đến URL http://localhost:3000/en/products thì sẽ không có gì khác biệt, còn nếu truyền vào một giá trị khác như http://localhost:3000/vi/products thì sẽ có dòng thông báo như ‘vi is not supported’.