多國語系及時區
最后更新于:2022-04-01 02:31:27
> It Works on My Machine! - 數以萬計的程式設計師
## 安裝Rails中文翻譯詞彙檔
Rails預設的語系是英文。要換成繁體中文,可以安裝rails-i18n這個gem有社群幫忙翻譯的繁體中文:
* 在`Gemfile`加上`gem "rails-i18n"`,然後執行bundle
* 修改 config/application.rb 的預設語系
~~~
config.i18n.default_locale = "zh-TW"
~~~
這樣就會使用[http://github.com/svenfuchs/rails-i18n](https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/zh-TW.yml)的繁體中文翻譯。
## 自訂翻譯檔案
要讓你的網站可以支援多國語系,必須定義出翻譯的詞彙對應檔案。這些詞彙檔放在config/locales下,使用YAML格式,例如新增一個config/locales/zh-TW.yml的檔案,內容如下:
~~~
"zh-TW":
hello_world: 哈囉
admin:
event: 活動管理
~~~
> 注意 YAML 格式的縮排必須使用兩個空隔,Tab是不允許的。直接複製貼上可能會有問題,請小心檢查縮排。
這樣就可以用`I18n.t`這個方法來做翻譯詞彙的替換。如果在View中可以直接使用`t`這個Helper方法。翻譯關鍵字可以用字串或 Symbol,也可以加上 Scope,例如:
~~~
t("admin.event")
t(:event, :scope => :admin )
I18n.t(:hello_world) # 如果不在View中,則需要加上 I18n 類別
~~~
如果要在詞彙內嵌變數的話,可以使用`%{variable_name}`的語法,修改config/locales/zh-TW.yml:
~~~
"zh-TW"
hello: "親愛的%{name}你好!"
~~~
這樣在template中改成傳入參數即可:
~~~
t(:hello, :name => @user.name) # 親愛的XXX你好
~~~
> 就算你的網站不需要支援多國語系,這個功能對於團隊協作開發網站仍然非常有幫助,因為寫程式的時候不一定會先確定文案規格,用i18n來處理的話,最後只需要讓PM統一修改翻譯詞彙檔即可。
## 搭配Model使用
在套用上述的翻譯詞彙檔之後,你可能會注意到Model驗證錯誤訊息會變成如Name 不能是空白字元,如果需要近一步中文化欄位名稱,你可以新增config/locales/events.yml內容如下:
~~~
zh-TW:
activerecord:
attributes:
event:
name: "活動名稱"
description: "描述"
~~~
其實,翻譯檔檔名叫events.yml、zh-TW.yml、en.yml什麼都無所謂,重要的是YAML結構中第一層要對應locale的名稱,也就是`zh-TW`,Rails會載入config/locales下所有的YAML詞彙檔案。
## 如何讓使用者可以切換多語系
在 application_controller.rb 中加入:
~~~
before_action :set_locale
def set_locale
# 可以將 ["en", "zh-TW"] 設定為 VALID_LANG 放到 config/environment.rb 中
if params[:locale] && I18n.available_locales.include?( params[:locale].to_sym )
session[:locale] = params[:locale]
end
I18n.locale = session[:locale] || I18n.default_locale
end
~~~
在 View 中可以這樣做:
~~~
<%= link_to "中文版", :controller => controller_name, :action => action_name, :locale => "zh-TW" %>
<%= link_to "English", :controller => controller_name, :action => action_name, :locale => "en" %>
~~~
## 語系樣板
除了上述一個單字一個單字的翻譯詞彙替換之外,如果樣板內大多是屬於較為靜態的內容,Rails也提供了不同語系可以有不同樣板,你只要將樣板命名加上語系附檔名即可,例如:
~~~
app/views/pages/faq.zh-TW.html.erb
app/views/pages/faq.en.html.erb
~~~
如此在英文版的時候就會使用faq.en.html.erb這個樣板,中文版時使用faq.zh-TW.html.erb這個樣板。
## 時區 TimeZone
首先,資料庫裡面的時間一定都是儲存 UTC 時間。而 Rails 提供的機制是讓你從資料庫拿資料時,自動幫你轉換時區。例如,要設定台北 +8 時區:
首先設定 config/application.rb 中預設時區為 config.time_zone = “Taipei”,如此 ActiveRecord 便會幫你自動轉換時區,也就是拿出來時 +8,存回去時 -8
### 如何根據使用者切換時區?
首先,你必須找個地方儲存不同使用者的時區,例如 User model 有一個欄位叫做 time_zone:string。然後在編輯設定的地方,可以讓使用者自己選擇時區:
~~~
<%= time_zone_select :user, :time_zone %>
~~~
接著在 application_controller.rb 中加入:
~~~
before_action :set_timezone
def set_timezone
if logged_in? && current_user.time_zone
Time.zone = current_user.time_zone
end
end
~~~
### 時區處理方法
Ruby原生的Time類別對於時區的處理一律是參考唯一的系統環境變數`ENV['TZ']`,這在使用者多時區的應用程式中就顯的見拙。因此在Rails中的時間類別使用的是ActiveSupport::TimeWithZone,我們已經知道可以使用`Time.zone`可以改變時區,其他的用法例如:
~~~
Time.zone = "Taipei"
Time.zone.local(2011, 8, 3, 9, 0) # 建立一個Taipei當地時間
=> Wed, 03 Aug 2011 09:00:00 CST +08:00
t = Time.zone.now # 目前時間
=> Wed, 03 Aug 2011 22:17:54 CST +08:00
t.in_time_zone("Tokyo") # 將這個時間換時區
=> Wed, 03 Aug 2011 23:18:34 JST +09:00
Time.utc(2005,2,1,15,15,10).in_time_zone # 將UTC時間換Taipei當地時間
=> Tue, 01 Feb 2005 23:15:10 CST +08:00
~~~
### 時間的顯示
除了使用Ruby內建的[`Datetime#strftime`](http://www.ruby-doc.org/core-2.1.5/Time.html#method-i-strftime)格式化時間之外,Rails也可以直接呼叫`to_s`轉換輸出格式:
~~~
datetime.to_s(:db) # => "2007-12-04 00:00:00"
datetime.to_s(:number) # => "20071204000000"
datetime.to_s(:short) # => "04 Dec 00:00"
datetime.to_s(:long) # => "December 04, 2007 00:00"
datetime.to_s(:long_ordinal) # => "December 4th, 2007 00:00"
datetime.to_s(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
datetime.to_s(:iso8601) # => "2007-12-04T00:00:00+00:00"
~~~
也可以自行註冊專案常用的格式在config/initializers/time_formats.rb裡:
~~~
Time::DATE_FORMATS[:month_and_year] = '%B %Y'
Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
~~~
或是透過I18n的機制,在翻譯詞彙檔中編輯格式,然後使用:
~~~
I18n.l( Time.now )
I18n.l( Time.now, :format => :short )
~~~
## 更多線上資源
* [Rails Internationalization (I18n) API](http://guides.rubyonrails.org/i18n.html)