[ruby-on-rails] 如何讓Rails 3本地化我的日期格式?



Question

我正在開發一個Rails 3項目,在該項目中,表單中有日期輸入的位置。 帶有日期的文本字段使用日期選擇器,因此不會擔心以錯誤的格式輸入日期,但是日期以:db格式顯示(例如2010-01-21)。

(注意:這特別是在表單字段中 - 例如<%= f.text_field :publish_date %> ,它應該自動使用:默認格式,並且不需要提供值)

我嘗試添加具有以下日期配置的自定義區域設置:

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文件中。




@ 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的代碼非常好。

既然你沒有提到模型, 我會假設你有一個帶有: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 。 由於表單輸入是一個String,它將嘗試使用Date._parsefast_string_to_date然後fallback_string_to_date

如果你迷路了,別擔心。 您無需了解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 %>





Related