Support for localized numbers

Hi guys,

I was wondering if there's any interest in including support for
localized numbers directly in Rails core.

Example:
  @product.price = '19,95' # => 19.95

There's a really simply way to get this done as can be seen at
http://github.com/clemens/delocalize/blob/f6b3dcf3203612c41baeb0f3c0a2de5f99bbe4c3/lib/delocalize/rails_ext/active_record.rb
(line 41). In addition it would probably make to include something
similar to the FormHelper hack I used in this plugin so text fields
localize the number (see
http://github.com/clemens/delocalize/blob/f6b3dcf3203612c41baeb0f3c0a2de5f99bbe4c3/lib/delocalize/rails_ext/action_view.rb).

I'm definitely sure the Date/Time stuff in this plugin isn't ready for
core but the Number stuff IMO is pretty simple and unobtrusive.

Any opinions on that? I can submit a patch with tests at any time.
- Clemens

If I'm remembering correctly, you should be able to override this in
your localization yaml files for your project.
The keys of interest for what you're wanting to do are here:
http://github.com/rails/rails/blob/6d4a4fabbbb04c20cee51c4e374045cc75e2ec16/actionpack/lib/action_view/locale/en.yml

All you *should* have to do is create/update a translation yml file in
your project, make sure it's in your I18n load path, then use one of
the ActionView number helpers and the output should be localized the
way you want. Most all of the ActionView helpers lookup translation
keys for these kinds of things. So if your application has those keys
values overridden, those will be used instead.

Not sure that helps, but worth a try.
-Brian

Hi guys,

I was wondering if there’s any interest in including support for

localized numbers directly in Rails core.

Example:

@product.price = ‘19,95’ # => 19.95

Is that a good idea? That would mean that if in my application I write, using a literal string,
@product.price = ‘19,95’
the result will be dependent on the locale setting? Surely not a good idea?

Colin

There is a rails-i18n mailing list you maybe better posting on there,
I'm not sure how many on that list also subscribe to this one. I've
not got time to look at moment, but I think I've already seen this
feature somewhere. A quick Google returned this:

# translations in Ruby
{ :'en-US' => {
  :date => {
    :formats => {
      :default => "%Y-%m-%d"
      # ...
    }
  }
}}

@Brian and Matt:

I think you misunderstand what I was trying to say. I'm not an i18n
newcomer and I should know most of the i18n stuff in NumberHelper
since it was written by me! :wink:

What I was trying to say is that Rails i18n handles the number output
but not the input: A user from a country where the comma is used as
the decimal separator wants to say the product's price is 19,95 (not
19.95) and therefore use this in a text field (and also see it in this
format again when they come back to edit it). What I sure can do is
something like the following:

  # in the model
  def price=(price)
    price = price.gsub(/[^0-9#{I18n.t(:'number.format.separator')}]/,
'').gsub(I18n.t(:'number.format.separator'), '.') if price.is_a?
(String)
    write_attribute(:price, price)
  end

  # in the view
  <%= f.text_field :price, :value => number_to_currency
(@product.price, :unit => '') %>

This is in fact pretty much what I do in my delocalize plugin that I
linked above. However, it's definitely not a nice thing to do if you
have an application that relies heavily on number input. And this is
what my post was about.

@Colin:

Your concern seems valid at first but it probably isn't. The change
I'd propose relies on database column types, so if price is a numeric
column it can't ever take a literal string (or, rather, it
automatically converts it to a number - so @product.price = '19,95'
would become 19.0 and @product.price = 'a string' would become 0.0).

Or am I misunderstanding the point you're trying to make?

In any case: Thanks for the feedback so far - it helped me see that I
needed to present my ideas more clearly.

- Clemens

Just thinking: There are actually two more options (maybe more, but I
can think of two right now) if accepting localized input for all
numbers seems too drastic.

a) Have a config hook that is disabled by default, e.g.
config.active_record.delocalize_input = false. Only when this is set
to true, numbers (and maybe in the future dates as well) are
delocalized. As for the ActionView part of the change (if we'd want to
change that at all - personally, I'm not sure), the same thing could
be done: config.action_view.localize_form_field_values = false (anyone
got a better name? ;-).

b) Explicitly whitelist fields to be delocalized with a DSL-ish
statement like Product.localized_attribute :price. This could probably
be even more explicit if additional options are passed (e.g. :as
=> :number, :type => :currency).

I personally like option a), mainly because I don't really see the use
case for b) - why should anyone want to only localize/delocalize some
numbers instead of all?

I'm pretty certain the ActiveRecord change makes sense, even without
the corresponding ActionView change.

Any opinions on that? I can submit a patch with tests at any time.

I'd be hesitant to add just number support when date functionality is
also pretty important. Would it take much more effort to do it for
numbers and dates / times? Shipping half a feature can be more
frustrating than not shipping any localised input at all.

Your plan for having an AR option to do this conversion sounds like a
good way to wire it up.

I'd love to have Date/Time parsing as well but I'm not sure the
solution I have right now is really fit for production. As far as I
see it, the only way to rely on output formats, turn them into regular
expressions and then parse them accordingly (I just pushed an update
to my plugin where I switched to DateTime#strptime for parsing - works
like a charm).

There are different approaches to parsing (Unicode suggests using
"lenient parsing") but in the end you always kind of depend on the
user: AFAIK, 02/03/04 as an English date could mean 2nd February 04,
3rd April 02 and 4th March 03, right? The only dates that are fairly
unambiguous are dates including month names and 4-digit years. So how
do I circumvent this issue when trying to parse? Question is: What
would/should happen if the user passes a date/time that can't be
parsed? Raise errors? Just set it to nil?

- Clemens

Wondering whether my validates_timeliness plugin may give some ideas
on the date/time parsing front. The Formats class handles I18n
parsing http://github.com/adzap/validates_timeliness/blob/572d80d227f4e727350c3adc82e174cdf55a0de2/lib/validates_timeliness/formats.rb

It also includes a suboptimal solution for the date ambiguities you
mentioned. Suboptimal because its either one or the other
interpretation for the whole app, which is not ideal. I plan to
refactor the class to allow format filtering on the fly and also date
disambiguation based on the locale.

Adam

Hi Adam,

from the name I would have never guessed that your plugin does things
similar to mine! :wink: It looks pretty awesome, though.

Maybe we should join forces on this issue? If you want to do this you
can get me in IRC - I'm usually hanging out in the Rails-Core channel
and my nick is clemensk. I'd love to discuss this with you and hear
your opinions.

- Clemens

Hi Clemens

Proper date/time validation needs a good parser and Ruby #parse
methods don't really cut it I'm afraid hence my solution. But we can
discuss that more later. Would be great to to get an accurate,
customizable date/time parser in Rails.

I'm not on IRC much but happy to jump on to discuss. You are 9 hours
behind me (Austria still? from github) so I will look for you at after
9am your time if you don't mind chatting during the day.

Cheers,
Adam