icon 《基础 Ruby on Rails》的示例程序asagao与Rails2.2相适应

错误信息的国际化(3)

接着上一章继续进行示例应用程序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 在 Rails2.2 中有少许变化。

这是Rails2.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

第二参数中用字符串指定了错误信息,但是省略的话便成了@@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: 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#addvalidates_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

总之,第一个的 test_update2 被覆盖,尚未运行。

我确实感到丢人。

将第二个 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

这次的教训应该是“写测试的时候不要过分自信”吧。

(2009/01/17)