Ruby on Rails:實作簡潔的多國語系對應表單 — 善用simple_form 及 enum_help
表單基本作法
在建置網站的時候,我們時常需要在網頁上製作表單(form)頁面來新增、更新或是用來查詢在資料庫中的資料,例如用來新增或更新使用者資訊,這時候就會需要用到一個表格來填寫使用者的一些基本資訊,例如帳號、密碼或是帳號類型等等,大概會長得像下面這樣
如果我們單純的使用Ruby on Rails提供內建的 form_for寫法,程式可能會像下面這樣
<%= form_for @user do |f| %> <div class="form-group col-sm-6">
<%= f.label :account, “Account” %>
<%= f.text_field :account %>
</div><div class="form-group col-sm-6">
<%= f.label :name, “Name” %>
<%= f.text_area :name %>
</div><div class="form-group col-sm-6">
<%= f.label :id, “ID” %>
<%= f.text_area :name %>
</div><div class="form-group col-sm-6">
<%= f.label :user_type_id, “User Type” %>
<%= f.collection_select :user_type_id, @user_types %>
</div><div class="form-group col-sm-6">
<%= f.label :password, “password” %>
<%= f.text_area :password %>
</div><div class="form-group">
<%= f.submit class: “btn btn-primary” %>
</div>
但像上面的寫法,如果網站想支援多國語系,可能就無法達到想要的效果,所以可以接著使用以下做法。
設定多國語系對應
利用rails-i18n這個gem來設定,先建立好要用到的多國語系檔(例如zh-TW.yml, en.yml),然後在裡面定義表單要填的資訊的每個label對應文字,例如account,設定好英文為 ‘Account’ 以及繁體中文為 ‘帳號‘ 的對應,像下面這樣
en:
form:
account: Account
user_type: User Type
id: ID
name: Name
password: Password
user_type:
normal: Normal Account
test: Test Accountzn-TW:
form:
account: 帳 號
user_type: 帳號類別
id: 身份證號
name: 姓 名
password: 密 碼
user_type:
normal: 一般帳號
test: 測試帳號
就可以在f.label中用使用i18n的顯示方式,像下面這樣
<%= f.label :account, t('form.account') %>
所以在原本的form_for程式加上i18n,就會像下面這樣
<%= form_for @user do |f| %><div class="form-group col-sm-6">
<%= f.label :account, t('form.account') %>
<%= f.text_field :account %>
</div><div class="form-group col-sm-6">
<%= f.label :name, t('form.name') %>
<%= f.text_area :name %>
</div><div class="form-group col-sm-6">
<%= f.label :id, t('form.id') %>
<%= f.text_area :name %>
</div><div class="form-group col-sm-6">
<%= f.label :user_type_id, t('form.user_type') %>
<%= f.collection_select :user_type_id, @user_types.map { |type| [t(user_type.#{type.name}), type.id] } %>
</div><div class="form-group col-sm-6">
<%= f.label :password, t('form.password') %>
<%= f.text_area :password %>
</div><div class="form-group">
<%= f.submit class: “btn btn-primary” %>
</div>
這樣網站切換不同語系的時候,account也會跟著顯示不同語系對應的文字,畫面看起來就會像這樣
上面解決了不同語系切換顯示label的問題,但我們可能還會想讓程式更精簡一點,那可以接著使用下面要介紹的simple_form的方法。
運用simple_form精簡程式碼
使用simple_form這個gem,simple_form內建了將表單label自行對應多國語系設定檔的功能,所以不用另外撰寫label設定在form程式,裡面只要在多國語系檔(例如zh-TW.yml, en.yml)做好labels設定,像下面這樣
en:
simple_form:
labels:
account: Account
name: Name
原本form_for裡面用來設定label顯示的程式,像下面這個就可以省略
<%= f.label :account, t('form.account') %>
程式就可以改寫成像下面這樣,完全省略了label的程式碼了
<%= simple_form_for @user do |f| %><div class="form-group col-sm-6">
<%= f.input :account %>
</div><div class="form-group col-sm-6">
<%= f.input :name %>
</div><div class="form-group col-sm-6">
<%= f.input :id %>
</div><div class="form-group col-sm-6">
<%= f.input :user_type_id, collection: @user_types.map { |type| [t('user_type.#{type.name}'), type.id] %>
</div><div class="form-group col-sm-6">
<%= f.input :password %>
</div><div class="form-group">
<%= f.button :submit %>
</div><% end %>
但在上面simple_form的例子,可以看到輸入的使用者的帳號類別資訊的下拉選單,為了要作出多國語系的對應,程式碼變得有點長,像下面這樣
<%= f.input :user_type_id, collection: @user_types.map { |type| [t(user_type.#{type.name}), type.id] %>
因為這邊的做法,是在使用者的資料表(User model)加一個欄位叫user_type_id,再創一個紀錄使用者類別的資料表(UserType model),讓user跟user_type關聯,然後在controller撈出所有使用者類別存到變數傳到前端的表單裡面,作為下拉選單的選項
@user_types = UserType.all
為了讓下拉選單可以印出所有帳號類別的多國語系對應,我們還要將所有類別一一印出然後做多國語系對應,像下面這樣
<%= f.input :user_type_id, collection: @user_types.map { |type| [t(user_type.#{type.name}), type.id] %>
collection這邊收到的格式,大概會像下面這樣array
@user_types.map { |type| [t('user_type.#{type.name}'), type.id]
=> [ ['Normal', 0], ['Test', 1] ]
整體效果像下面這樣,從頁面上看起來沒有不好
但其實光使用者的資料表是怎麼設的,整個就很繁瑣,為了下拉選單呈現所寫出來的程式碼也是落落長,想要讓它簡化,其實可以不用像上面的做法那麼麻煩,還另外製作一張資料表紀錄有哪些帳號類型然後跟使用者帳號關聯,然後下拉選單選項的多國語系對應還要自己寫,可以採用下面的做法。
善用enum_help讓下拉選單程式碼簡化
使用enum_help這個gem,只要在使用者資料表User model新增一個欄位user_type,然後在model中定義user_type會有哪一些類別,像下面這樣
enum user_type: {
normal: 1,
test: 2
}
在多國語系檔(例如zh-TW.yml, en.yml)做好enum設定
en:
enums:
user:
user_type:
normal: Normal Account
test: Test Account
然後就可以直接在simple_form使用了,程式碼變成下面這樣,相較於一開始,變得簡單乾淨許多了,而且同時又保有多國語系的對應以及下拉選單的功能呢!
<%= simple_form_for @user do |f| %><div class="form-group col-sm-6">
<%= f.input :account %>
</div><div class="form-group col-sm-6">
<%= f.input :name %>
</div><div class="form-group col-sm-6">
<%= f.input :id %>
</div><div class="form-group col-sm-6">
<%= f.input :user_type, collection: User.user_type%>
</div><div class="form-group col-sm-6">
<%= f.input :password %>
</div><div class="form-group">
<%= f.button :submit %>
</div><% end %>