Rails 的 i18n

紀錄一下自己處理 i18n 的思考過程,以免我哪天失智又双叒叕忘了。

i18n 是轉換語系用的,全名為「Internationalization」,因為 I 到 n 之間有 18 個英文字母,所以被簡稱為 i18n。


預設值

在 config/application.rb 中加入:

config.i18n.default_locale = "en"

 

使用者自行切換

在 application_controller.rb 加入:
before_action :set_locale

def set_locale
  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" %>

根據 Accept-Language 判斷 1

Accept-Language 是什麼呢?

打開 F12,我們可以看到這個東西:

 

紅框處就是 Accept-Language

在 rails 中,我們可以使用 request.accept_language,在 application_controller.rb 中加入:

before_action :set_locale

def set_locale
  I18n.locale = request.accept_language.scan(/^[a-zA-Z-]{5}/).first || I18n.default_locale
end

這樣取得的就會是「zh-TW」。

如果要配合前面加入使用者自選功能的話,則可以寫成下面這樣:

before_action :set_locale

def set_locale
  if params[:locale] && I18n.available_locales.include?( params[:locale].to_sym )
    session[:locale] = params[:locale]
  end
  
  I18n.locale = session[:locale] || request.accept_language.scan(/^[a-zA-Z-]{5}/).first || I18n.default_locale
end

 request.accept_language 也可用 request.env['HTTP_ACCEPT_LANGUAGE'] 代替。

 

根據 Accept-Language 判斷 2

當然,我們也可以使用 gem(iain/HttpAcceptLanguage)。

為什麼不自己寫而要使用 gem 呢?

這是因為 request.accept_language 的實際內容並不一定可以如上面一樣,能夠透過正規表示法找到,而 iain/HttpAcceptLanguage 便是專門偵測使用者語系的。具體使用方法可以直接看 readme,但下面還是紀錄一下我實作的情形。

一樣是修改 application_controller.rb:

before_action :set_locale

def set_locale
  I18n.locale = http_accept_language.compatible_language_from(I18n.available_locales) || I18n.default_locale
end

然後,我們可能會發現這個訊息:

看看 devise 的登入頁面吧:


……(倒吸一口氣)

這跟我要的不太一樣。

排查原因後發現,「http_accept_language.compatible_language_from(I18n.available_locales)」的回傳值是「zh-CN」……嗯,會不會是「zh-TW」不在 18n.available_locales 的範圍裡面?這篇網誌曾提及 rails 在 4.1.0 中「zh-TW」不在 18n.available_locales 裡。

但實際把「I18n.available_locales」的內容印出來:

[:en, :af, :ar, :az, :be, :bg, :bn, :bs, :ca, :cs, :da, :"de-CH", :de, :el, :"en-GB", :"es-CO", :"es-MX", :es, :et, :fa, :fi, :"fr-CA", :fr, :ha, :he, :hi, :hr, :hu, :id, :ig, :is, :it, :ja, :ka, :km, :ko, :"lo-LA", :lt, :lv, :mk, :ms, :nb, :nl, :"nn-NO", :no, :"pap-AW", :"pap-CW", :pl, :"pt-BR", :pt, :ro, :ru, :si, :sk, :sl, :sq, :"sr-RS", :sr, :sv, :th, :tl, :tr, :uk, :ur, :vi, :yo, :"zh-CN", :"zh-HK", :"zh-TW", :"zh-YUE", :cy, :"de-AT", :"de-DE", :"el-CY", :"en-AU", :"en-CA", :"en-CY", :"en-IE", :"en-IN", :"en-NZ", :"en-TT", :"en-US", :"en-ZA", :eo, :"es-419", :"es-AR", :"es-CL", :"es-CR", :"es-EC", :"es-ES", :"es-NI", :"es-PA", :"es-PE", :"es-US", :"es-VE", :eu, :"fr-CH", :"fr-FR", :fy, :gl, :"hi-IN", :"it-CH", :kk, :kn, :lb, :lo, :mg, :ml, :mn, :"mr-IN", :ne, :nn, :oc, :or, :pa, :rm, :st, :"sv-SE", :sw, :ta, :te, :tt, :ug, :uz, :wo]

嗯,好,有在。


好吧,現在我們有三個選擇,一個是修改這個 gem (這個我們未來再寫一篇文來講解 by 老高語氣),一個是將 *.zh-TW.yml 改為 *.zh-CN.yml,另一個則是在 config/application.rb 加入:


config.i18n.enforce_available_locales = false
config.i18n.available_locales = [:"zh-TW", :"en"]

這麼一來,rails 就能讀到 zh-TW 了。


以 IP 找國家

可以借助 alexreisner/geocoder 這個 gem。

但在 VPN 橫流、人們可以出國的情況下,用到的機會可能不高。

 

 

參考資料

Rails 實戰聖經

https://ithelp.ithome.com.tw/articles/10304324?sc=iThelpR 

https://blog.roachking.net/blog/2014/04/12/rails-4-1-0-invalid-locacle/

https://rails.ruby.tw/i18n.html


留言