【问题标题】:How can I make Rails 3 localize my date formats?如何让 Rails 3 本地化我的日期格式?
【发布时间】:2011-04-22 05:39:36
【问题描述】:

我正在开发一个 Rails 3 项目,在该项目中可以在表单中输入日期。带有日期的文本字段使用日期选择器,因此无需担心以错误格式输入日期,但日期以 :db 格式显示(例如 2010-01-21)。

(注意:这是专门在表单字段中 - 例如<%= f.text_field :publish_date %>,它应该自动使用 :default 格式,并且不需要提供值)

我已尝试添加具有以下日期配置的自定义语言环境:

date:
    formats:
      # Use the strftime parameters for formats.
      # When no format has been given, it uses default.
      # You can provide other formats here if you like!
      default: "%d/%m/%Y"
      short: "%b %d"
      long: "%B %d, %Y"

然后将我的语言环境设置为此 (config.i18n.default_locale = "en-AU"),但这似乎并不需要,而且它变得非常令人沮丧。

该应用程序最终将支持多种语言环境,因此在应用程序启动时设置一个初始化程序来覆盖日期格式并不是很合适,而且我知道这应该工作 - 我是我猜我在这里漏掉了一些东西。

语言环境文件是:config/locales/en-AU.yml,在我的 application.rb 中我包括:

config.i18n.load_path += Dir[Rails.root.join("config", "locales", "*.yml").to_s]
config.i18n.default_locale = "en-AU"

在我的 application.rb 文件中。

【问题讨论】:

    标签: ruby-on-rails localization internationalization ruby-on-rails-3


    【解决方案1】:

    @damien-mathieu 对于使用I18n.localize 显示本地化日期有一个可靠的答案,他的评论提出了一个重要的警告:这会破坏文本输入。从那时起,rails 为我们提供了一个很好的解决方案。

    Rails 5 开始,您可以使用 Rails attributes API 自定义用户输入如何转换为模型或数据库值。实际上,它是available in Rails 4.2,只是没有完整记录。

    通过Sean Griffin's 相当大的努力,现在所有模型的类型都定义为ActiveRecord::Type 对象。这定义了如何处理属性的单一事实来源。类型定义属性如何序列化(从 ruby​​ 类型到数据库类型)、反序列化(从数据库类型到 ruby​​ 类型)和强制转换(从用户输入到 ruby​​ 类型)。这是一件大事,因为过去曾是开发人员应避免的特殊情况的雷区。

    首先,浏览attribute 文档以了解如何覆盖属性的类型。您可能需要阅读文档才能理解此答案。

    Rails 如何转换属性

    这里是 Rails 属性 API 的快速浏览。你可以跳过这一部分,但你不会知道这些东西是如何工作的。有什么好玩的?

    了解 Rails 如何处理您的属性的用户输入将使我们只覆盖一种方法,而不是制作更完整的自定义类型。它还可以帮助您编写更好的代码,因为 rails 的代码非常好。

    由于您没有提及模型,我假设您有一个带有 :publish_date 属性的 Post(有些人更喜欢名称 :published_on,但我离题了)。

    你是什么类型的?

    找出:publish_date 是什么类型。我们不关心它是Date 的实例,我们需要知道type_for_attribute 返回什么:

    对于与模型属性类型相关的任何信息,此方法是唯一有效的信息来源。

    $ rails c
    > post = Post.where.not(publish_date: nil).first
    > post.publish_date.class
    => Date
    > Post.type_for_attribute('publish_date').type
    => :date
    

    现在我们知道:publish_date 属性是:date 类型。这是由ActiveRecord::Type::Date 定义的,它扩展了ActiveModel::Type::Date,它扩展了ActiveModel::Type::Value。我已链接到 rails 5.1.3,但您需要阅读您的版本的源代码。

    ActiveRecord::Type::Date 如何转换用户输入?

    因此,当您设置:publish_date 时,该值将传递给cast,它调用cast_value。由于表单输入是一个字符串,它会尝试一个fast_string_to_date 然后fallback_string_to_date 使用Date._parse

    如果您迷路了,请不要担心。您无需了解 rails 的代码即可自定义属性。

    定义自定义类型

    现在我们了解了 Rails 如何使用属性 API,我们可以轻松地制作自己的。只需创建一个自定义类型来覆盖 cast_value 以期望本地化日期字符串:

    class LocalizedDate < ActiveRecord::Type::Date
    
      private
    
        # Convert localized date string to Date object. This takes I18n formatted date strings
        # from user input and casts them back to Date objects.
        def cast_value(value)
          if value.is_a?(::String)
            return if value.empty?
            format = I18n.translate("date.formats.short")
            Date.strptime(value, format) rescue nil
          elsif value.respond_to?(:to_date)
            value.to_date
          else
            value
          end
        end
    end
    

    看看我是如何复制 rails 的代码并做一些小调整的。简单。您可能想通过调用super 来改进这一点,并将:short 格式移动到一个选项或常量。

    注册您的类型,以便它可以被符号引用:

    # config/initializers/types.rb
    ActiveRecord::Type.register(:localized_date, LocalizedDate)
    

    用您的自定义类型覆盖 :publish_date 类型:

    class Post < ApplicationRecord
      attribute :publish_date, :localized_date
    end
    

    现在您可以在表单输入中使用本地化值:

    # app/views/posts/_form.html.erb
    <%= form_for(@post) do |f| %>
      <%= f.label :publish_date %>
      <%= f.text_field :publish_date, value: (I18n.localize(value, format: :short) if value.present?) %>
    <% end %>
    

    【讨论】:

      【解决方案2】:

      您可以覆盖 :publish_date 属性的 getter。

      即在您的模型中:

      def date( *format)
          self[:publish_date].strftime(format.first || default)
      end
      

      在你看来,你可以做任何一个

      @income.date("%d/%m/%Y")
      

      @income.date
      

      这将导致 strftime 使用传递的格式字符串,除非它是 nil,在这种情况下它将回退到您的默认字符串。

      注意:我使用了 splat 运算符来添加对获取不带参数的日期的支持。可能有更清洁的方法来做到这一点。

      【讨论】:

        【解决方案3】:

        我发现最好的解决方案是:

        • 我和你一样在我的语言环境文件中本地化日期格式
        • 在我的表单中,我通过直接设置值来本地化日期

        &lt;%= f.text_field :publish_date, :value =&gt; (@model.publish_date.nil? ? nil : l(@model.publish_date)) %&gt;

        遗憾的是,它并不完美,但至少这样我可以将我的表单用于新记录和现有记录。此外,与使用初始化程序更改默认格式相比,该应用程序将与多个语言环境兼容。如果您完全想遵守 DRY,您可以随时编写自定义帮助程序。

        【讨论】:

          【解决方案4】:

          【讨论】:

            【解决方案5】:

            显示日期时,可以使用I18n.l

            所以你会这样做:

            I18n.l @entry.created_at
            

            如果你想改变它的格式:

            I18n.l @entry.created_at, :format => :short
            

            internationalization rails guide 正在记录这一点。

            【讨论】:

            • 谢谢,但我实际上希望它显示在表单值中 - 例如如果我使用: = field_for :publish_from 它应该以正确的日期格式显示。
            • 你不能那样做。任何日期字段都应该可以被 TimeTime 解析。所以 rails 强制格式。
            • @everyone 请给这个评论更多的代表↑ :)
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-04-10
            • 1970-01-01
            • 1970-01-01
            • 2012-07-10
            相关资源
            最近更新 更多