如何让Rails 3本地化我的日期格式?

41
我正在开发一个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文件中。

5个回答

86
当显示日期时,可以使用I18n.l。因此,您需要执行以下操作:
I18n.l @entry.created_at

如果你想改变它的格式:

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

国际化 Rails 指南正在记录此内容。


1
谢谢,但实际上我想让它显示在表单值中 - 例如,如果我使用:= field_for :publish_from,则应该以正确的日期格式显示。 - Matthew Savage
9
你不能这样做。任何日期字段都应该可以被 TimeTime 解析。所以 Rails 强制要求格式。 - Damien MATHIEU
@所有人请给这条评论更多的赞↑ :) - Mauricio Pasquier Juan

9

@damien-mathieu提供了一个稳定的方法来使用I18n.localize显示本地化日期,他的评论提到了一个重要的注意点:这会破坏表单文本输入。自那时以来,Rails为我们提供了一个好的解决方案。

Rails 5开始,您可以使用Rails属性API来自定义用户输入如何转换为模型或数据库值。实际上,在Rails 4.2中就已经可用,只是没有完全记录。

通过Sean Griffin的大力支持,所有模型类型现在都被定义为ActiveRecord::Type对象。这定义了一个处理属性的真正来源。该类型定义了属性如何序列化(从ruby类型到数据库类型),反序列化(从数据库类型到ruby类型)和强制类型转换(从用户输入到ruby类型)。这非常重要,因为之前搞乱了开发人员应该避免的特殊情况。

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

Rails如何转换属性

这里是Rails属性API的快速介绍。您可以跳过此部分,但那样您就不会知道这些东西是如何工作的。有什么乐趣呢?

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

由于您没有提到模型,我假设您有一个名为Post的模型,其中有一个:publish_date属性(有些人可能更喜欢名称: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 %>


4
我认为最好的解决方案是:
  • 我像你一样在我的locale文件中本地化日期格式
  • 在我的表单中,我通过直接设置值来本地化日期

<%= f.text_field :publish_date, :value => (@model.publish_date.nil? ? nil : l(@model.publish_date)) %>

不完美的地方在于,这种方式虽然可以同时用于新记录和现有记录的表单,但至少可以保持与多个语言环境的兼容性,而不是使用初始化程序更改默认格式。如果您想完全遵循DRY原则,您可以编写自定义助手函数。

1
你可以覆盖 :publish_date 属性的 getter 方法。
例如,在你的模型中:
def date( *format)
    self[:publish_date].strftime(format.first || default)
end

在您看来,您可以选择任一种方式

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

或者

@income.date

这将导致strftime使用传递的格式字符串,除非它为nil,在这种情况下,它将回退到您的默认字符串。
注意:我使用星号运算符添加了获取日期而无需参数的支持。可能有更简洁的方法来实现这一点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接