web-dev-qa-db-ja.com

どうすればRails 3日付形式をローカライズできますか?

私は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")しかし、これには時間がかかりそうになく、かなりイライラします。

アプリは最終的に多くのロケールをサポートするため、アプリケーションの起動時にイニシャライザーを設定して日付形式をオーバーライドすることは実際には適切ではなく、これはshould work-I'm guessing I 'ここで何かを逃しました。

ロケールファイルは次のとおりです。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ファイルに。

41
Matthew Savage

日付を表示するときは、I18n.l

だからあなたがするだろう:

I18n.l @entry.created_at

そして、フォーマットを変更したい場合:

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

internationalization Rails guide はそれを文書化しています。

85
Damien MATHIEU
7
taelor

@ damien-mathieuには、ローカライズされた日付をI18n.localize、および彼のコメントは重要な警告を提起します:これはフォーム入力テキストを壊します。それ以来、Railsは私たちに素敵なソリューションを提供します。

Rails 5の時点で、 Rails属性API を使用して、ユーザー入力をモデルまたはデータベース値に変換する方法をカスタマイズできます。実際には、それは Rails 4.2 で利用可能で、完全に文書化されていません。

Sean Griffin's かなりの努力により、すべてのモデルのタイプはActiveRecord::Typeオブジェクト。これにより、属性の処理方法に関する単一の真実のソースが定義されます。タイプは、属性のシリアル化(Rubyタイプからデータベースタイプへ)、デシリアライズ(データベースタイプからRubyタイプ)およびキャスト)を定義します。 (ユーザー入力からRubyタイプ)へ。これを混乱させることは、開発者が避けるべき特別な場合の地雷原であったためです。

まず、 attribute ドキュメントをざっと読んで、属性のタイプをオーバーライドする方法を理解します。この答えを理解するには、おそらくドキュメントを読む必要があります。

How Rails Transforms Attributes

Rails Attributes API。このセクションはスキップできますが、この機能がどのように機能するかわかりません。何が面白いのでしょうか?

Rails属性のユーザー入力を処理する方法を理解すると、より完全なカスタム型を作成するのではなく、1つのメソッドのみをオーバーライドできます。Railsのコードは非常に優れているため、 。

モデルについては言及しなかったため、Post:publish_date attribute(一部の名前は: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_dateDate._parse を使用します。

迷子になっても心配しないでください。属性をカスタマイズするためにRailsのコードを理解する必要はありません。

カスタムタイプの定義

これで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

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

私が最善の解決策であることがわかったのはこれです:

  • あなたのようにロケールファイルの日付形式をローカライズします
  • 私のフォームでは、値を直接設定して日付をローカライズします

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

残念ながら完全ではありませんが、少なくともこの方法で、新規および既存のレコードの両方にフォームを使用できます。また、アプリは、初期化子を使用してデフォルトの形式を変更する場合と比較して、複数のロケールとの互換性を維持します。 DRYに完全に準拠したい場合は、常にカスタムヘルパーを記述できます。

4
mkon

:publish_date属性のゲッターをオーバーライドできます。

すなわち、あなたのモデルで:

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

あなたの意見では、あなたはどちらかをすることができます

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

または

@income.date

これにより、strftimeは、nilでない限り、渡されたフォーマット文字列を使用します。nilでない場合、デフォルト文字列にフォールバックします。

注:スプラット演算子を使用して、引数なしで日付を取得するためのサポートを追加しました。よりクリーンな方法があるかもしれません。

1
aguazales