Как я могу заставить Rails 3 локализовать мои форматы дат? - PullRequest
41 голосов
/ 07 октября 2010

Я работаю над проектом 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.

Ответы [ 5 ]

85 голосов
/ 07 октября 2010

При отображении даты вы можете использовать I18n.l

Так вы бы сделали:

I18n.l @entry.created_at

А если вы хотите изменить его формат:

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

Руководство по интернационализации документирует это.

7 голосов
/ 17 августа 2017

@ damien-mathieu имеет четкий ответ для отображения локализованных дат с помощью I18n.localize, и его комментарий вызывает важное предостережение: это нарушает ввод текста. С тех пор рельсы дают нам хорошее решение.

Начиная с Rails 5 , вы можете использовать API-атрибуты Rails для настройки преобразования пользовательского ввода в значение модели или базы данных. На самом деле, это было доступно в Rails 4.2 , но не полностью документировано.

Благодаря значительным усилиям Шона Гриффина все типы моделей теперь определены как ActiveRecord::Type объекты. Это определяет единственный источник правды для того, как обрабатывается атрибут. Тип определяет способ сериализации (от типа ruby ​​до типа базы данных), десериализации (от типа базы данных до типа ruby) и приведения (от ввода пользователя к типу ruby). Это большое дело, потому что возиться с этим раньше было минным полем, которого разработчики должны избегать.

Сначала просмотрите документы attribute, чтобы понять, как переопределить тип атрибута. Вам, вероятно, нужно прочитать документы, чтобы понять этот ответ.

Как Rails преобразует атрибуты

Вот краткий обзор API атрибутов Rails. Вы можете пропустить этот раздел, но тогда вы не будете знать, как это работает. Что это весело?

Понимание того, как 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

Посмотрите, как я только что скопировал код рельсов и сделал небольшую настройку. Легко. Возможно, вы захотите улучшить это с помощью вызова 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 голосов
/ 06 декабря 2010

посмотрите драгоценный камень delocalize, он может вам помочь.

https://github.com/clemens/delocalize

http://www.railway.at/articles/2009/05/03/new-plugin-delocalize/

4 голосов
/ 21 января 2013

Лучшим решением для меня оказалось следующее:

  • Я локализую форматы даты в моем файле локали, как вы
  • В моих формах я локализую дату, установивпрямое значение

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

К сожалению, это не идеально, но, по крайней мере, таким образом я могу использовать свою форму как для новых, так и для существующих записей.Также приложение останется совместимым с несколькими локалями по сравнению с изменением формата по умолчанию с инициализаторами.Если вы полностью хотите соблюдать DRY, вы всегда можете написать специальный помощник.

1 голос
/ 27 февраля 2014

Вы можете переопределить свои методы получения для атрибута :publish_date.

т.е. в вашей модели:

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

и, по вашему мнению, вы можете сделать либо

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

или

@income.date

Это приведет к тому, что strftime будет использовать переданную строку формата, если она не равна nil, в этом случае она будет возвращаться к вашей строке по умолчанию.

Примечание. Я использовал оператор splat для добавления поддержки получения даты без аргумента. Там может быть более чистый способ сделать это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...