Ruby on Rails:實作簡潔的多國語系對應表單 — 善用simple_form 及 enum_help

icelandcheng
10 min readApr 6, 2019

--

表單基本作法

在建置網站的時候,我們時常需要在網頁上製作表單(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 Account
zn-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 %>

參考資料:

ActiveRecord::Enum

simple_form

Using Rails Active Record enum types with Simple_Form

enum_help

--

--

icelandcheng
icelandcheng

Written by icelandcheng

Programming Skill learner and Sharer | Ruby on Rails | Golang | Vue.js | Web Map API

No responses yet