準備一個示範的程式

Rails 如要中文化要在每個 Rails 程式裡單獨設定,所以我們來做一個 “花草記錄簿” 當示範程式。
首先,建一個叫 plants_development 的資料庫。 建好了之後到你習慣放程式的檔案夾裡建一個新的 rails 程式:

rails plants
cd plants

把 Ruby 的編碼方式改成 UTF-8

Ruby 的編碼方式一開始只用英語。 我們要改用 UTF-8 才能使用中文。 打開檔案
plants/config/environment.rb,
在最後一行 “# Include your application configuration below” 後面加上這兩行:

$KCODE = 'u'
require 'jcode'

儲存並關閉檔案。

設定跟資料庫溝通時用的編碼

每種不同的程式,不管是 PHP, Rails, 或 JSP 等等,要跟任何的資料庫溝通時都要先決定溝通時用的編碼。 在 Rails 跟 MySQL 裡頭,溝通的編碼是在 database.yml 裡面設的。 打開 config/database.yml,在每個設定區裡加一個 “encoding: utf8″。
像 development 應該看起來像這樣 (你的 username 跟 password 可能會不同):

development:
  adapter: mysql
  database: plants_development
  username: root
  password:
  host: localhost
  encoding: utf8

這個範例是 development 的設定,”test” 跟 “production” 也要加最後一行的 encoding: utf8。
你用的資料庫如果是 PostgreSQL 的話,把 “encoding: utf8″ 改成 “encoding: unicode”。
SQLite 資料庫不用設這個值,但是要安裝 SQLite 時要啟動 utf8。 例如,在 Linux 系統安裝前要給 ./configure 一個變數 “–enable-utf8″:

./configure --enable-utf8

建立 model

建立一個 model 才有可以修改資料的東西:

ruby script/generate model Plant

這時候 Migration 會建一個叫 db/migrate/001_create_plants.rb 的檔案。 把這個檔案打開,在
def self.up 裡放入:

    create_table (:plants, :options => 'DEFAULT CHARSET=UTF8') do |t|
      t.column "name", :string
      t.column "planted_at", :datetime
      t.column "flower", :boolean
    end

“DEFAULT CHARSET=UTF8″ 是要讓資料庫儲存各國不同的語言。 存檔後回到 plants/ 的目錄裡下這個指令來產生新的 “plants” table:

rake migrate

有了這個 table 之後就可以自動產生 CRUD (Create, Read, Update, Delete) 的架構了:

ruby script/generate scaffold Plant Admin

設定 HTML 的 encoding

雖然我們設定了 Ruby 跟資料庫溝通時用的編碼 , 每一網頁的 <head> </head> 裡頭不要忘了加

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

真的要每頁每頁加嗎? 當然不用啦。直接放在 layout 裡頭就會在每頁上面顯示出來了。 打開
app/views/layouts/admin.rhtml
在這個檔案裡的東西會出現在所有 admin controller 管理的網頁上。 在 <head> 跟 </head> 中間貼上剛剛的那行字串:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

測試中文讀寫

到現在設定的部份總算完成,但要測試真的會不會跑。

啟動伺服器:

ruby script/server

到 http://localhost:3000/admin/ 增加一些中文名字的植物吧!

到這個步驟你如果還是看到亂碼的話就要看看瀏覽器上設的 encoding 是否為 utf-8。
現在程式已經可以讀/寫中文了。 如果要的功能只有這樣,就不用繼續讀下去。 可是如果想要按一個連結就把整個網頁切換成中/英文的話,就繼續讀吧。

國際化

Rails 有很多別人已寫好的軟體。 我們要用的工具是寫的比較完整的 plugin 叫 Globalize (www.globalize-rails.org)。 這一套軟體把你翻譯過的字串存在資料庫裡。 如果改變語言時,它會把翻譯過的字串叫出來。 除此之外,各國不同的數字,金錢顯示方法也可以存在資料庫裡。 我們邊做邊解釋吧。

安裝 Globalize Plugin

到程式的資料夾裡 (plants/) 下此指令:

ruby script/plugin install svn://svn.globalize-rails.org/globalize/branches/for-1.1

整個指令不要分兩行打入喔。 這會安裝 Globalize 在程式裡。 打開 /vendor/plugins 檔案夾,裡面會看到一個 trunk 的檔案夾。 這裡面放的就是 Globalize 的軟體了。 在 plants/ 的目錄裡再下此指令來準備 Globalize:

ruby script/generate globalize

這個指令會透過 Migration 產生一個檔,叫 db/migrate/002_globalize_migration.rb。 打開來看它是在做啥麼的吧! 裡面喵了一下,它會產生 3 個 table (globalize_countries
globalize_translations, 和 globalize_languages),然後它會做一些看不太懂的東西,然後加了一大堆有關世界各國的資料。 仔細的看看,它有加一些 index,原來加 index 的語法是這樣寫的。 以後自己寫 Migration 檔案時可以拿這個來做參考。 不知道為什麼,這個檔案在新增 table 的時候,並不會自己把 table 的編碼用成 UTF-8。 我們只好自己去修改。 在每一個 create_table 的指令裡多放 :options => ‘DEFAULT CHARSET=UTF8′ 如下:

create_table (:globalize_countries, :force => true, :options => 'DEFAULT CHARSET=UTF8') do |t|

檔案裡有三個 create_table, 所以要改三個地方。 改好後就可以執行這個檔案了:

rake migrate

跑了 35.2505 秒之後,去看資料庫,果真多了三個 table.

設定基本語言

在運用這些 table 之前,我們還得設定基本語言。 打開 config/environment.rb,在最後面加這幾行:

include Globalize
Locale.set_base_language 'zh-TW'
Locale.set 'zh-TW'

第一行就是說此程式需要包含 Globalize plugin 的程式碼。 第二行把基本語言設成 zh-TW, zh-TW 就是中文-台灣。 你的基本語言如要是英語的話,把 zh-TW 改成 en-US (英文-美國)。 第三行把地點設成台灣。

加入翻譯字串

回到 http://localhost:3000/admin/ 的網頁上,你應該看到以前加入的花名。 在每個花名後有三個連結: ‘Show’ ‘Edit’ 跟 ‘Destroy’。 現在的目的就是把這三個連結翻譯成中文。 翻譯的第一步驟是輸入翻譯字串。
要加入翻譯字串最快的方法就是用 script/console 。 在 plants/ 目錄裡下此指令:

ruby script/console

這會把程式裡有用到的 class 載入記憶體裡,讓你方便測試。打一個指令:

Locale.language

它應該會跟你說 “Chinese”。 這是因為我們剛剛把基本語言設成 zh-TW。如果你的不是 Chinese 的話,用此指令讓它變成 Chinese:

Locale.set 'zh-TW'

加入下面幾個翻譯字串。 加的時候你的中文碼在螢幕上可能會變成數字字串: \207\346\226\231 這不用擔心,存入資料庫時它會轉成 UTF-8 碼。 下這些指令來輸入翻譯字串:

Locale.set_translation('Show', '詳細資料')
Locale.set_translation('Edit', '修改')
Locale.set_translation('Destroy', '刪除')

每輸入一行它會回覆類似:
[nil, "詳細資料"]
注意大小寫是有差的喔。 Show 跟 show 是兩個不一樣的字串。

設定那些字串可被翻譯

翻譯字串輸入了,可是這並不代表每次程式遇到 “Show” 時會自動換入 “詳細資料”。 必須要在想翻譯的那串字後加一個 .t 才可以。 打開
app/views/admin/list.rhtml 。
這個檔案是 scaffold 自動產生的。 使用者到 http://localhost:3000/admin/ 時其實叫出來的就是 list.rhtml。 檔案裡面,在 ‘Show’ 的後面加個 .t,如下:

    <td><%= link_to 'Show'.t, :action => 'show', :id => plant %></td>

‘Edit’ 跟 ‘Destroy’ 後面也要加。 現在瀏覽 http://localhost:3000/admin/ 的話, Show, Edit, 跟 Destroy 應該就是用中文顯示了。

切換中英文

那要怎麼切換中英文呢? 以下三個步驟大致說明一下,再來仔細看看:

1) 在我們的網頁上,我們要放兩個連結。 一個可以把英文改成中文,另一個可以把中文改成英文。 中文的連結裡有個叫 locale 的變數,變數的值是 ‘zh-TW’。 使用者按了連結後,這個變數會透過 params 傳到下一頁去。
2) 在下一頁還沒出現時,把變數裡的值複製到 session 裡一個也是叫做 locale 的變數。
3) 下一頁要出現時,先去檢查 session 裡頭的 locale 變數。 查到了 ‘zh-TW’ 就知道要用中文了。

英文要換成中文也是一樣的原理。 現在一步一步來看:

步驟一: View 裡的連結

我們的連結可以放在 app/views/layouts/admin.rhtml 裡,因為放這裡面的東西會顯示在所有網頁上。

<%= link_to "中文", :action => "change_locale", :locale => "zh-TW"%>

這一段碼會產生一個連結。 連結上會寫 “中文”,按了之後 controller 會把控制權轉給一個叫 “change_locale” 的 method。 同時,’zh-TW’ 也放入了 :locale 的變數裡。 :locale 這個名字是我們自己取的,我們也可以隨便放個名字,像 :booboo => “zh-TW” 也可以。 不管是什麼名字,我們不用事先宣告,直接把值給任何的名字,然後用同樣的名字去 params 裡面找,就能把裡面的值叫出來了。
我們多加一點功能吧。 如果 session[:locale] 裡面已經有了 ‘zh-TW’,就表示我們現在已經在顯示中文了,所以 “中文” 這個連結就不用顯示。 把第一行跟第三行加入:

<% unless session[:locale] == ‘zh-TW’ %>
  <%= link_to "中文", :action => "change_locale", :locale => "zh-TW"%>
<% end %>

這段讀起來就是: 顯示 “中文” 這連結,除非 session[:locale] 裡已有 ‘zh-TW’。 英文的程式碼也要:

<% unless session[:locale] == ‘en-US’ %>
  <%= link_to "English", :action => "change_locale", :locale => "en-US"%>
<% end %>

步驟二: 傳 param 的值進去 session 裡

“中文” 跟 “English” 這兩個連結按了之後的 action 是 change_locale。 現在就來寫 change_locale 的內容吧。 這個 method 應放在 app/controllers/application.rb 裡頭:

  def change_locale
    session[:locale] = params[:locale] unless params[:locale].blank?
    redirect_to :back
  end

這個 method 的作法就是 “除非 params[:locale] 是空白,把 params[:locale] 裡的變數放入 session[:locale] 裡”。 以我們這個程式來說,裡面放的值不是 ‘zh-TW’ 就是 ‘en-US’。

步驟三: 設定 Locale

每次一有人要瀏覽網頁時,我們就要設 locale。 因為設 locale 的 method 是整個程式都可用到的,所以最適當的地方是放在 app/controllers/application.rb 裡。 我們把這個 method 叫 set_locale。

剛剛看過改 Locale 的方法是用 Locale.set。 要 set 成什麼呢?就是 set 成我們存在 session[:locale] 的變數了。 所以我們改變 locale 的方法如下:

  def set_locale
    Locale.set session[:locale] unless session[:locale].blank?
    true
  end

這個 method 的作法就是 “除非 session[:locale] 是空白,把 Locale 設為 session[:locale]“。
這個 method 要在每次程式要跑之前就要叫,所以要放在 before_filter:

class ApplicationController &lt; ActionController::Base
before_filter :set_locale
...

在 set_locale 方法的第二行有一個 “true”,這是為什麼呢? 因為 before_filter 所叫的方法一定要回傳 true, 整個程式才會繼續執行,所以我們就回傳一個 true。

現在來瀏覽 http://localhost:3000/admin/ 吧。 一開始 “中文” 跟 “English” 兩個都會顯現,因為 session 裡面沒有 zh-TW 或 en-US 的參數。 “詳細資料”, “修改”, 跟 “刪除” 也會顯示,因為我們在 “設定基本語言” 裡有把預設值設為 ‘zh-TW’。 按一下 “English” 的連結吧。 “Show”, “Edit”, 跟 “Destroy” 果真出現了! 其他的如果要翻譯的話就照這些方法去翻譯。

中文版

英文版

如果你的程式支持個人客製化(使用者可以 login),你可以在使用者的 table 裡加一個 “locale” 的欄位。 在使用者登入時,把那欄位裡的值叫出來,放入 session[:locale] 裡頭。 使用者在改變語言時要把新的 locale 值寫回資料庫裡,這樣你的程式就會記住每個使用者的愛好了。