《基础 Ruby on Rails》的示例程序asagao与Rails2.2相适应
基于Cookie的区域的切换
上一章中,迈出了将示例应用 asagao 国际化(i18n)的第一步。
这一章,将增加区域切换的功能。
首先,在 app/controllers/application.rb 定义新的常量 AVAILABLE_LOCALES 。
class ApplicationController < ActionController::Base AVAILABLE_LOCALES = %w(en ja) # Pick a unique cookie name to distinguish our session data from others' session :session_key => '_asagao_session_id' (省略)
值是由 'en' 和 'ja' 两个字符串组成的数组。%w(en ja) 这样的写法是特殊的表示法,用空白字符分割括号内的字符串,成为数组。
接下来,生成适用于单例资源 locale 的控制台 locales 。
> ruby script/generate controller locales show
用这个控制台进行用户区域切换。
编辑 config/routes.rb ,注册 locale 资源。
(省略) # 我的帐户控制台 map.resource :account # 我的帐户控制台 map.resource :locale # 基本的 URL 模式 map.connect ':controller/:action/:id.:format' map.connect ':controller/:action/:id' end
接下来,用Rails2.0中的新写法指定到首页的路由。
map.connect '', :controller => 'main'
将此做如下改写:
map.root :controller => 'main'
这样一来,就可以使用方法 root_path 了。
用测试驱动进行实际操作吧。
要点如下:
- 在cookie变量
my_locale将用户现在的区域存为字符串。 - 用
GET /locale表示现在区域和其他区域的列表。 - 各个区域都为 HTML 链接,用户点击其中一个链接,向
/localePUTlocale参数。 PUT /locale改写cookie变量my_locale。PUT /locale中,通过referer参数指向区域选择页面前一页的 URL,处理完成后重定向至该URL。
打开 test/functionals/locales_controller_test.rb 。
require 'test_helper'
class LocalesControllerTest < ActionController::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
end
end
这是 Rails 2.2 式的新的测试方法的写法。这个和下面的写法一样。
require 'test_helper'
class LocalesControllerTest < ActionController::TestCase
# Replace this with your real tests.
def test_the_truth
assert true
end
end
好像变得容易读一点了。
接下来,在测试中进行翻译。
require 'test_helper'
class LocalesControllerTest < ActionController::TestCase
# 表示现在自己的区域。
test "should show my locale" do
get :show
assert_response :success
assert_equal 'ja', assigns(:my_locale)
end
# 变更现在自己的区域。。
# 没有指定referer 参数的情况下,重定向至首页。
test "should change my locale" do
put :update, { :locale => 'en' }
assert_equal ['en'], cookies['my_locale']
assert_redirected_to root_path
end
# 变更现在自己的区域。。
# 没有指定 referer 参数的情况下,重定向至该 URL。
test "should change my locale with referer" do
url = 'http://example.com/main/news'
put :update, { :locale => 'en', :referer => url }
assert_equal ['en'], cookies['my_locale']
assert_redirected_to url
end
end
要点是功能测试里存在的 cookie 的表现方式。cookies['my_locale'] 的话 OK, cookies[:my_locale] 的话 NG。
另外,cookies['my_locale'] 返回数组。所以比较对象不是 'en' ,而是 ['en'] 。
执行测试,确认失败。
Loaded suite test/functional/locales_controller_test
Started
EF
Finished in 0.069264 seconds.
1) Error:
test_should_change_my_locale(LocalesControllerTest):
ActionController::UnknownAction: No action responded to update. Actions:
exception, exception=, rescue_action_without_handler translation missing:
ja, support, array, sentence_connector show
(省略)
2) Failure:
test_should_show_my_locale(LocalesControllerTest)
[test/functional/locales_controller_test.rb:8:in `test_should_show_my_locale'
/usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in `__send__'
/usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in `run']:
<"ja"> expected but was
<nil>.
接下来,安装 locales 控制台。
class LocalesController < ApplicationController
def show
if AVAILABLE_LOCALES.include?(cookies[:my_locale])
@my_locale = cookies[:my_locale]
else
@my_locale = I18n.default_locale.to_s
end
end
def update
if AVAILABLE_LOCALES.include?(params[:locale])
cookies[:my_locale] = params[:locale]
end
if params[:referer]
redirect_to params[:referer]
else
redirect_to root_path
end
end
end
要点是数组(Array)的实例方法 include? 的使用方法。
if cookies[:my_locale] == 'en' || cookies[:my_locale] == 'ja'
代替这个书写如下。因为今后区域的个数可能会增加,所以应该这样写。
if AVAILABLE_LOCALES.include?(cookies[:my_locale])
确认测试成功。
> ruby -Itest test/functional/locales_controller_test.rb Loaded suite test/functional/locales_controller_test Started ... Finished in 0.086196 seconds. 3 tests, 6 assertions, 0 failures, 0 errors
接下来,创建 HTML 模板。打开 app/views/locales/show.html.erb 。
从Rails2.0开始 HTML 模板的扩展名变成 .html.erb 。
.erb 的部分,显示了模板引擎的名称,并暗示可以利用 ERB 以外的模板引擎。
做如下修改。
<h1><%= t('title.switch_language') %></h1>
<p><%= t('locales.select_language') %></p>
<ul>
<% ApplicationController::AVAILABLE_LOCALES.each do |locale| -%>
<li>
<%=
link_to_unless(
locale == I18n.locale.to_s,
t('locales.language_name.' + locale),
{ :locale => locale, :referer => request.env['HTTP_REFERER'] },
{ :method => :put }) do
"<strong>#{t('locales.language_name.' + locale)}</strong>"
end
%>
</li>
<% end -%>
</ul>
将app/views/shared/_menu_bar.rhtml 做如下修改。
<% menu_items = [
{ :link => { :controller => '/main', :action => 'index' },
:name => t('title.top') },
{ :link => { :controller => '/main', :action => 'activities' },
:name => t('title.our_activities') },
{ :link => { :controller => '/main', :action => 'news'},
:name => t('title.news') },
{ :link => { :controller => '/blog_entries', :action => 'index' },
:name => t('title.blog') },
{ :link => locale_path, :name => t('title.switch_language') }
]
if @current_user
menu_items << { :link => { :controller => '/members',
:action => 'index',
:sort => nil, :group_id => nil },
:name => t('title.members') }
end
if @current_user and @current_user.administrator?
menu_items << { :link => { :controller => '/admin/main',
:action => 'index' },
:name => t('title.administration') }
end -%>
<div id='menu_bar'>
<% menu_items.each_with_index do |item, index| -%>
<% if index > 0 %> | <% end -%>
<%= menu_link_to item -%>
<% end -%>
</div>
为了能随着 cookies[:my_locale] 的值进行语言切换,修改 app/controllers/application.rb 。
class ApplicationController < ActionController::Base
AVAILABLE_LOCALES = %w(en ja)
# Pick a unique cookie name to distinguish our session data from others'
session :session_key => '_asagao_session_id'
before_filter :select_locale
before_filter :resume_session
private
# 区域选择
def select_locale
if AVAILABLE_LOCALES.include?(cookies[:my_locale])
I18n.locale = cookies[:my_locale]
end
end
(省略)
最后,生成翻译文件。
config/locales/titles_en.yml
en:
title:
news: News
top: TOP
our_activities: Our Activities
news: News
blog: Blog
switch_language: Switch Language
members: Members
administration: Administration
config/locales/titles_ja.yml
ja:
title:
top: TOP
our_activities: 我们的活动
news: 新闻
blog: 博客
switch_language: 语言切换
members: 会员名册
administration: 管理页
config/locales/locales_en.yml
en:
locales:
select_language: Please select your preferred language below.
language_name:
en: English
ja: Japanese
config/locales/locales_en.yml
ja:
locales:
select_language: 请选择在此站中使用的语言。
language_name:
en: 英语
ja: 中文
为了翻译开始时更容易整理,我们决定给每个控制台建翻译文件。
这样就可以简单地在中文和英语间进行区域转换了。

今天就到这儿吧。
(2009/01/04)
- 前言
- 功能测试开头部分的修改等 (2008/12/01)
- rake rails:update (2008/12/15)
- Cookie Store (2008/12/16)
- 分页 (2008/12/17)
- blog_entries 控制台的修改 (2008/12/20)
- 单例资源 (2008/12/21)
- 一口气通过所有功能测试 (2008/12/21)
- 综合测试和rak (2008/12/22)
- 国际化(i18n)的第一步 (2008/12/26)
- 基于Cookie的区域的切换 (2009/01/04)
- ActiveRecord 模型的字段名的国际化 (2009/01/08)
- 错误信息的国际化(1) (2009/01/09)
- 错误信息的国际化(2) (2009/01/10)
- 错误信息的国际化(3) (2009/01/17)
- 错误信息的国际化(4) (2009/01/24)

