我如何使Rails 3本地化我的date格式?

我正在一个Rails 3项目中,有一个表单中的dateinput的地方。 带有date的文本字段使用dateselect器,因此不用担心input的date格式错误,但date显示为:db格式(例如2010-01-21)。

(注意:这是特别在表单字段 – 例如<%= f.text_field :publish_date %> ,它应该自动使用:默认格式,而不应该提供一个值)

我已经尝试添加具有以下dateconfiguration的自定义语言环境:

 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" ),但是这似乎并没有采取,它变得相当沮丧。

该应用程序最终将支持一些语言环境,所以在应用程序启动时设置一个初始化程序以覆盖date格式并不合适,而且我知道这应该起作用 – 我猜我已经错过了一些东西。

语言环境文件是: 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文件中。

显示date时,可以使用I18n.l

所以你会这样做:

 I18n.l @entry.created_at 

如果你想改变它的格式:

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

国际化轨道指南正在logging。

我发现最好的解决办法是:

  • 我像你一样在本地化date格式本地化文件
  • 在我的表单中,我通过直接设置值来本地化date

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

这不是完美的悲伤,但至less这样我可以使用我的表格为新的和现有的logging。 此外,与使用初始值设定项更改默认格式相比,该应用程序将保持兼容多个区域设置。 如果你完全想遵守DRY,你总是可以写一个自定义的帮手。

@ damien-mathieu对使用I18n.localize显示本地化date有一个坚实的回答,他的评论提出了一个重要的警告:这打破了文本input。 从那以后,Rails给了我们一个很好的解决scheme

Rails 5开始 ,您可以使用Rails属性API自定义用户input如何转换为模型或数据库值。 实际上,它在Rails 4.2中可用 ,但没有完整的文档。

通过Sean Griffin的相当大的努力,所有模型的types现在被定义为ActiveRecord::Type对象。 这定义了如何处理属性的单一来源。 这个types定义了属性如何序列化(从rubytypes到数据库types),反序列化(从数据库types到rubytypes)和cast(从用户input到rubytypes)。 这是一个大问题,因为搞乱这个曾经是开发者应该避免的特殊情况的雷区。

首先,浏览attribute文档以了解如何覆盖属性的types。 你可能需要阅读文档来理解这个答案。

Rails如何转换属性

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

了解Rails如何处理属性的用户input将让我们覆盖只有一个方法,而不是一个更完整的自定义types。 它也将帮助你编写更好的代码,因为rails的代码非常好。

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

你的types是什么?

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

此方法是与模型属性types相关的唯一有效信息来源。

 $ 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属性是:datetypes。 这是由ActiveRecord :: Type :: Date定义的,它扩展了ActiveModel :: Type :: Date ,它扩展了ActiveModel :: Type :: Value 。 我已经链接到5.1.3的轨道,但你会想要阅读你的版本的来源。

用户input如何通过ActiveRecord :: Type :: Date转换?

所以,当你设置:publish_date ,这个值被传递给cast ,它调用cast_value 。 由于表单input是一个string,它会尝试一个fast_string_to_date然后fallback_string_to_date使用Date._parse 。

如果你迷路了,别担心。 你不需要理解rails的代码来定制属性。

定义一个自定义types

现在我们已经了解了Rails如何使用属性API,我们可以轻松地创build自己的属性。 只需创build一个自定义types来覆盖cast_value以期望本地化的datestring:

 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格式移至选项或常量。

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

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

用您的自定义types覆盖:publish_datetypes:

 class Post < ApplicationRecord attribute :publish_date, :localized_date end 

现在,您可以在表单input中使用本地化值:

 # 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 %> 

你可以覆盖你的getters :publish_date属性。

即在你的模型中:

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

在你看来,你也可以做

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

要么

 @income.date 

这将导致strftime使用传递的格式string,除非它是零,在这种情况下,它会回落到您的默认string。

注意:我使用splat运算符来添加支持获取没有参数的date。 可能有更干净的方法来做到这一点。