エラーメッセージの国際化(3)
2009/01/17
前回に引き続き、サンプルアプリケーション asagao のエラーメッセージを国際化していきましょう。
member.rb に次のような記述があります。
# 値の検証
def validate
if member_number and !Member.positive_integer?(member_number)
errors.add(:member_number, 'は1以上の整数で記入してください。')
end
if !email.blank? and !email.well_formed_as_email_address?
errors.add(:email, 'の書式が正しくありません。')
end
if birthday and !birthday.is_valid?
errors.add(:birthday, 'が存在しない日付です。')
end
if member_image and member_image.data.size > 65535
errors.add(:uploaded_image,
'のサイズが大き過ぎます(最大64KB)。')
end
end
Errors#add を使ってエラーを登録する場合に、どのようにエラーメッセージを指定するのがスマートでしょうか。
これが、今回のテーマです。
Errors#add の API は Rails 2.2 で少し変更になりました。
Rails 2.1.2 のソースコードが、これです。
def add(attribute, msg = @@default_error_messages[:invalid])
@errors[attribute.to_s] = [] if @errors[attribute.to_s].nil?
@errors[attribute.to_s] << msg
end
第 2 引数にはエラーメッセージを文字列で指定しますが、省略すると @@default_error_messages[:invalid] の値(上書きしていなければ 'is invalid')になりました。
Rails 2.2.2 ではこうなりました。
def add(attribute, message = nil, options = {})
message ||= :invalid
message = generate_message(attribute, message, options) if message.is_a?(Symbol)
@errors[attribute.to_s] ||= []
@errors[attribute.to_s] << message
end
第 2 引数にシンボルを指定すると、翻訳ファイルによって変換されます。
まず、先ほどの member.rb を次のように修正します。
# 値の検証
def validate
if member_number and !Member.positive_integer?(member_number)
errors.add(:member_number, :positive_integer)
end
if !email.blank? and !email.well_formed_as_email_address?
errors.add(:email, :invalid_format)
end
if birthday and !birthday.is_valid?
errors.add(:birthday, :invalid_date)
end
if member_image and member_image.data.size > 65535
errors.add(:uploaded_image, :too_large)
end
end
次に、config/locales/activerecord_en.yml を次のように修正します。
en:
activerecord:
attributes:
member:
member_number: Member number
player: Player
family_name: Family name
given_name: Given name
furigana: Furigana
email: E-mail address
phone: Phone number
birthday: Birthday
sex: Sex
remarks: Remarks
login_name: Login name
password: Password
administrator: Administrator
uploaded_image: Uploaded Image
blog_entry:
heading: Heading
body1: Body 1
body2: Body 2
note: Note
blog_date: Date
group:
name: Name
remarks: Remarks
article:
place: Place
heading: Heading
body: Body
note: Note
released_at: Released at
expired_at: Expired at
errors:
messages:
positive_integer: "must be a positive integer"
invalid_format: "has an invalid format"
invalid_date: "is an invalid date"
models:
member:
attributes:
uploaded_image:
too_large: "is too large (maximum is 64KB)"
続いて、config/locales/activerecord_ja.yml を次のように修正します。
ja:
activerecord:
attributes:
member:
member_number: 背番号
player: 選手登録
family_name: 名前(姓)
given_name: 名前(名)
furigana: ふりがな
email: メールアドレス
phone: 電話番号
birthday: 生年月日
sex: 性別
remarks: 備考
login_name: ログイン名
password: パスワード
administrator: サイト管理者
uploaded_image: 画像'
blog_entry:
heading: 見出し
body1: 本文
body2: 続き
note: 注
blog_date: 日付
group:
name: 名称
remarks: 備考
article:
place: 掲載場所
heading: 見出し
body: 本文
note: 注
released_at: 掲載開始日時
expired_at: 掲載終了日時
errors:
messages:
inclusion: "は一覧にありません。"
exclusion: "は予約されています。"
invalid: "が不正な値です。"
confirmation: "が一致しません。"
accepted: "を承諾してください。"
empty: "が記入されていません。"
blank: "が記入されていません。"
too_long: "は{{count}}文字以内で記入してください。"
too_short: "は{{count}}文字以上で記入してください。"
wrong_length: "は{{count}}文字で記入してください。"
taken: "はすでに使用されています。"
not_a_number: "は数値で入力してください。"
greater_than: "は{{count}}より大きい値を指定してください。"
greater_than_or_equal_to: "は{{count}}以上の値を指定してください。"
equal_to: "は{{count}}を指定してください。"
less_than: "は{{count}}より小さい値を指定してください。"
less_than_or_equal_to: "は{{count}}以下の値を指定してください。"
odd: "は奇数を指定してください。"
even: "は偶数を指定してください。"
positive_integer: "は1以上の整数で記入してください。"
invalid_format: "の書式が正しくありません。"
invalid_date: "が存在しない日付です。"
models:
member:
taken: が他の方と重なっています。
attributes:
password:
confirmation: が確認用パスワードと一致しません。
uploaded_image:
too_large: "のサイズが大き過ぎます(最大64KB)。"
Errors#add だけでなく、validates_format_of などのメソッドでも :message オプションにシンボルが指定できます。
member.rb に次のような記述があります。
validates_format_of :phone,
:with => /^\d+(-\d+)*$/,
:message => 'の書式が不正です。',
:if => Proc.new {|member| !member.phone.blank? }
Rails 2.2 では次のように書けます。
validates_format_of :phone,
:with => /^\d+(-\d+)*$/,
:message => :invalid_format,
:if => Proc.new {|member| !member.phone.blank? }
rake test でテストが通ることを確認し、ブラウザで目視によるテストを行います。
すると、ここでバグが発見されました。
ログイン中のユーザーが自分自身のアカウントの情報を不正な値に変更しようとすると、NoMethodError が発生してしまうのです。
機能テストがカバーしていない部分が存在したため、スクリプトによるテストでは発見できなかったバグです。
このような場合は、すぐに直すのではなく、バグが再現できるように機能テストを修正してから、アプリケーション本体を修正するのが定石です。
test/functional/accounts_controller_test.rb を開いてください。
update アクションをテストしているコードを注意深く見ていくと、何と test_update2 が 2 つ存在しています!
つまり 1 つ目の test_update2 は上書きされてしまって実行されていなかったのです!
誠にお恥ずかしい限りです。
2 つ目の test_update2 を test_update3 に修正して機能テストを実行すると、正しくテストが失敗します。
その上で、app/views/accounts/_errors.rhtml の 13 行目を次のように修正します。
<span class="attributeWithErrors"><%= h(Member.human_attribute_name(attrib.to_s)) %></span> <%= h(msg) %>
attrib を attrib.to_s に変えてください。
今回の教訓は「テストを書け。されど過信するな。」ということになりましょうか。
