I am currently working on a project to internationalize one of our ruby-on-rails web applications so that it can be used in other countries (France will be the first one in this case).
A particular issue I haven't worked out yet is with the displaying of numeric fields. When displaying numbers for display purposes only, I do the following:
<%= number_to_percentage(tax.rate, :precision => 2)%>
In English, this shows 17.50, but in French it shows 17,50 (with a comma in place of the decimal point) which is as expected. The problem comes in the Edit form, when I show a text field
<%= f.text_field :rate, :size => 15 %>
When this renders a text box on the screen, the text box always shows 17.50 with a full stop rather than a comma for French. I am not sure that is correct.
When I tried doing the following:
<%= f.text_field :rate, :size => 15, :value => number_with_precision(f.object.rate, :precision => 2) %>
This did indeed show 17,50 in the text box for French, but when I click on the Update button to save the form, the Ruby validation kicks in and tells me that 17,50 is not a number (or rather it says "n'est pas un nombre"). I have to enter 17.50 to get it to save.
To be honest, I am not entirely sure on the correct thing to do here. Should all countries enter numbers with full stops in text boxes, or is there a way to get Ruby-on-Rails to display commas, and validate them appropriately?
This is the kind of things I hate to do over and over again (I'm serving french users, they're easily confused with dots as the decimal separator).
I exclusively use the delocalize gem now, which does the format translation automatically for you. You just have to install the gem and leave your forms as-is, everything should be taken care of for you.
The basic conversion is quite simple, you have to convert back and forth between the following formats:
The backend one, which is usually English, used by your persistent storage (SQL database, NoSQL store, YAML, flat text file, whatever struck your fancy, ...).
The frontend one, which is whatever format your user prefers. I'm going to use French here to stick to the question*.
* also because I'm quite partial towards it ;-)
This means that you have two points where you need to do a conversion:
Outbound: when outputting your HTML, you will need to convert from English to French.
Inbound: When processing the result of the form POST, you will need to convert back from French to English.
Let's say I have the following model, with the rate
field as a decimal number with a precision of 2 (eg. 19.60
):
class Tax < ActiveRecord::Base
# the attr_accessor isn't really necessary here, I just want to show that there's a database field
attr_accessor :rate
end
The outbound conversion step (English => French) can be done by overriding 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
The inbound conversion step (French => English) will be handled in the model. We will override the rate
attribute writer to replace every French separator with the English one:
class Tax < ActiveRecord::Base
def rate=(rate)
write_attribute(:rate, rate.gsub(I18n.t('number.format.separator'), '.')
end
end
This look nice because there's only one attribute in the example and one type of data to parse, but imagine having to do this for every number, date or time field in your model. That's not my idea of fun.
This also is a naïve* way of doing the conversions, it does not handle:
100,000.84
)* did I already tell you I like French?
Delocalize is working on the same principle I outlined above, but does the job much more comprehensively:
Date
, Time
, DateTime
and numbers.15 janvier 2012
.One caveat though: it doesn't handle client-side validations. If you're using them, you will have to figure out how to use i18n in your favourite JavaScript framework.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With