エラーメッセージの国際化(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
に変えてください。
今回の教訓は「テストを書け。されど過信するな。」ということになりましょうか。