Локализация текстового поля, содержащего номер в Ruby on Rails

В настоящее время я работаю над проектом по интернационализации одного из наших веб-приложений на Ruby-on-rails, чтобы его можно было использовать в других странах (Франция в этом случае будет первой).

Частная проблема, которую я еще не разработал, - это отображение числовых полей. При отображении номеров только для отображения я делаю следующее:

<%= number_to_percentage(tax.rate, :precision => 2)%>

На английском языке это показывает 17.50, но на французском языке он показывает 17,50 (с запятой вместо десятичной точки), которая как и ожидалось. Проблема возникает в форме "Редактировать", когда я показываю текстовое поле

<%= f.text_field :rate, :size => 15 %>

Когда это отображает текстовое поле на экране, текстовое поле всегда показывает 17.50 с полной остановкой, а не запятой для французского языка. Я не уверен, что это правильно.

Когда я попытался сделать следующее:

<%= f.text_field :rate, :size => 15, :value => number_with_precision(f.object.rate, :precision => 2) %>

Это действительно показало 17,50 в текстовом поле для французского языка, но когда я нажимаю кнопку "Обновить", чтобы сохранить форму, проверка на Ruby запускается и сообщает мне, что 17, 50 не является числом (точнее, он говорит "n'est pas un nombre" ). Мне нужно ввести 17.50, чтобы сохранить его.

Если честно, я не совсем уверен в правильности этого. Должны ли все страны вводить цифры с полными остановками в текстовых полях или есть способ заставить Ruby-on-Rails отображать запятые и соответствующим образом проверять их?

Ответы

Ответ 1

TL; DR

Это то, что мне не нравится делать снова и снова (я служу французским пользователям, их легко путать с точками как разделитель с десятичной запятой).

Я использую delocalize gem, который автоматически преобразует формат для вас. Вам просто нужно установить драгоценный камень и оставить свои формы как есть, все должно заботиться о вас.

Мне нравится читать, дайте мне длинное объяснение

Базовое преобразование довольно простое, вам нужно конвертировать туда и обратно между следующими форматами:

  • Бэкэнд, который обычно используется на английском языке, используется вашим постоянным хранилищем (база данных SQL, хранилище NoSQL, YAML, текстовый файл, все, что поразило ваше воображение,...).

  • Внешний интерфейс, который является тем форматом, который предпочитает ваш пользователь. Я собираюсь использовать французский здесь, чтобы придерживаться вопроса *.

* также потому, что я отношусь к нему частично, -)

Это означает, что у вас есть две точки, где вам нужно сделать преобразование:

  • Исходящие: при выводе вашего HTML вам нужно будет преобразовать с английского языка на французский.

  • Входящие. При обработке результата формы POST вам нужно будет перевести обратно с французского на английский.

Ручной способ

Скажем, у меня есть следующая модель с полем rate как десятичное число с точностью 2 (например, 19.60):

class Tax < ActiveRecord::Base
  # the attr_accessor isn't really necessary here, I just want to show that there a database field
  attr_accessor :rate
end

Шаг преобразования исходящий (английский = > французский) можно сделать, переопределив text_field_tag:

ActionView::Helpers::FormTagHelper.class_eval do
  include ActionView::Helpers::NumberHelper

  alias original_text_field_tag text_field_tag
  def text_field_tag(name, value = nil, options = {})
    value = options.delete(:value) if options.key?(:value)
    if value.is_a?(Numeric)
      value = number_with_delimiter(value) # this method uses the current locale to format our value
    end
    original_text_field_tag(name, value, options)
  end
end

В модели будет обрабатываться шаг входящего преобразования (французский = > английский). Мы заменим атрибут атрибута rate, чтобы заменить каждый французский разделитель на английский:

class Tax < ActiveRecord::Base
  def rate=(rate)
    write_attribute(:rate, rate.gsub(I18n.t('number.format.separator'), '.')
  end
end

Это выглядит хорошо, потому что в этом примере используется только один атрибут и один тип данных для анализа, но представьте, что вам нужно сделать это для каждого числа, даты или времени в вашей модели. Это не моя идея веселья.

Это также наивный способ выполнения конверсий * который не обрабатывается:

  • Даты
  • Времена
  • Разделители (например, 100,000.84)

* Я уже говорил, что мне нравится французский?

Введите делокализацию

Delocalize работает над тем же принципом, который я изложил выше, но делает работу гораздо более всесторонне:

  • Он обрабатывает Date, Time, DateTime и числа.
  • Вам не нужно ничего делать, просто установите драгоценный камень. Он проверяет ваши столбцы ActiveRecord, чтобы определить, является ли он типом, который нуждается в преобразовании, и делает это автоматически.
  • Конверсии чисел довольно просты, но даты действительно интересны. При анализе результата формы он будет пытаться использовать формат даты, определенный в вашем файле локали, в порядке убывания и должен понимать дату, отформатированную следующим образом: 15 janvier 2012.
  • Каждое исходящее преобразование будет выполнено автоматически.
  • Он протестирован.
  • Он активен.

Одно предупреждение: оно не обрабатывает проверки на стороне клиента. Если вы используете их, вам нужно будет выяснить, как использовать i18n в своей любимой структуре JavaScript.

Ответ 2

Это метод gsub:

В вашей модели:

before_validation :prepare_number


def prepare_number
  self.rate.gsub(/,/, '.') if self.rate.match /,\S/
end