Ruby-on-rails 如何让 Rails 3 本地化我的日期格式?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3879680/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 23:20:30  来源:igfitidea点击:

How can I make Rails 3 localize my date formats?

ruby-on-railslocalizationinternationalizationruby-on-rails-3

提问by Matthew Savage

I am working on a Rails 3 project where there is place for date input within a form. The text field with the date uses a date picker so there is no concern about the date being entered in a wrong format, however the date is being displayed in the :db format (e.g. 2010-01-21).

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

(Note: this is specifically in form fields - e.g. <%= f.text_field :publish_date %>, which should automatically use :default format, and shouldn't need to be provided with a value)

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

I have tried adding in a customized locale which has the following date configuration:

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

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"

And then setting my locale to this (config.i18n.default_locale = "en-AU") however this doesn't seem to take and its becoming quite frustrating.

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

The app will eventually support a number of locales, so setting up an initializer to override the date formats at application startup isn't really suitable, and I know that this shouldwork - I'm guessing I've missed something here.

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

The locale file is: config/locales/en-AU.ymland in my application.rb I am including:

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

in my application.rb file.

在我的 application.rb 文件中。

回答by Damien MATHIEU

When displaying a date, you can use I18n.l

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

So you would do :

所以你会这样做:

I18n.l @entry.created_at

And if you want to change it's format :

如果你想改变它的格式:

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

The internationalization rails guideis documenting that.

国际化的轨道导向是记录这一点。

回答by Anson

@damien-mathieu has a solid answer for displaying localized dates with I18n.localize, and his comment raises an important caveat: this breaks form text inputs. Since then, rails gives us a nice solution.

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

As of Rails 5, you can use the Rails attributes APIto customize how user input is transformed into a model or database value. Actually, it was available in Rails 4.2, just not fully documented.

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

Through Sean Griffin'sconsiderable efforts, all models' types are now defined as ActiveRecord::Typeobjects. This defines a single source of truth for how an attribute is handled. The type defines how the attribute is serialized (from a ruby type to a database type), deserialized (from a database type to a ruby type) and cast (from user input to a ruby type). This is a big deal, because messing with this used to be a minefield of special cases developers should avoid.

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

First, skim the attributedocs to understand how to override an attribute's type. You probably need to read the docs to understand this answer.

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

How Rails Transforms Attributes

Rails 如何转换属性

Here's a quick tour of the Rails Attributes API. You can skip this section, but then you won't know how this stuff works. What fun is that?

这是 Rails Attributes API 的快速浏览。你可以跳过这一节,但是你不会知道这些东西是如何工作的。那有什么乐趣?

Understanding how Rails handles user input for your attribute will let us override only one method instead of making a more complete custom type. It will also help you write better code, since rails' code is pretty good.

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

Since you didn't mention a model, I'll assume you have a Postwith a :publish_dateattribute(some would prefer the name :published_on, but I digress).

由于您没有提到模型,我假设您有一个Postwith:publish_date属性(有些人更喜欢 name :published_on,但我离题了)。

What is your type?

你的类型是什么?

Find out what type :publish_dateis. We don't care that it is an instance of Date, we need to know what type_for_attributereturns:

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

This method is the only valid source of information for anything related to the types of a model's attributes.

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

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

Now we know the :publish_dateattribute is a :datetype. This is defined by ActiveRecord::Type::Date, which extends ActiveModel::Type::Date, which extends ActiveModel::Type::Value. I've linked to rails 5.1.3, but you'll want to read the source for your version.

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

How is user input transformed by ActiveRecord::Type::Date?

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

So, when you set :publish_date, the value is passed to cast, which calls cast_value. Since form input is a String, it will try a fast_string_to_datethen fallback_string_to_datewhich uses Date._parse.

所以,当你设置:publish_date的值传递给,这就要求cast_value。由于表单输入是一个字符串,它将尝试使用Date._parsefast_string_to_date然后fallback_string_to_date

If you're getting lost, don't worry. You don't need to understand rails' code to customize an attribute.

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

Defining a Custom Type

定义自定义类型

Now that we understand how Rails uses the attributes API, we can easily make our own. Just create a custom type to override cast_valueto expect localized date strings:

现在我们了解了 Rails 如何使用属性 API,我们可以轻松地制作自己的 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

See how I just copied rails' code and made a small tweak. Easy. You might want to improve on this with a call to superand move the :shortformat to an option or constant.

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

Register your type so it can be referenced by a symbol:

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

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

Override the :publish_datetype with your custom type:

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

class Post < ApplicationRecord
  attribute :publish_date, :localized_date
end

Now you can use localized values in your form inputs:

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

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

回答by mkon

What I found to be the best solution is this:

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

  • I localize date formats in my locale file like you do
  • In my forms I localize the date by setting the value directly
  • 我像你一样在我的语言环境文件中本地化日期格式
  • 在我的表单中,我通过直接设置值来本地化日期

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

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

It is not perfect sadly, but at least this way I can use my form for both new and existing records. Also the app will stay compatible with multiple locales compared to changing the default format with initializers. If you fully want to comply with DRY you could always write a custom helper.

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

回答by aguazales

You could override your getters for the :publish_dateattribute.

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

i.e. in your model:

即在您的模型中:

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

and in your view you could do either

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

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

or

或者

@income.date

This would cause strftime to use the passed format string unless it was nil, in which case it would fall back to your default string.

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

Note: I used the splat operator to add support for getting the date without an argument. There may be a cleaner way to do that.

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