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