i18n
i18n copied to clipboard
#localize for numbers
Cross-posting this here from the rails-i18n Google group, which seems to be almost inactive.
Hey guys,
it's been a while since I've been active on this list. ;-)
There's something that's been bothering me for ages and I'm finally ready to do something about it: That something is the localization of numbers.
If you think about it, two of the main simple things that you need to localize in your international app are dates/times and numbers (leaving out more complex things like different address formats and whatnot). i18n has been doing the former pretty much since the beginning (at least as far as I remember) but the latter is still done in Rails itself, using the NumberHelper. I propose to change this and handle number localization in i18n itself.
Here's an API that could work (based on the default de.yml):
# dates and times
I18n.localize(Date.civil(2014, 7, 28)) # => works as before
I18n.localize(Time.local(2014, 7, 28, 20, 30, 0)) # => works as before
# currency formatting
I18n.localize(19.90, as: :currency) # => 19,90 €
I18n.localize(19.90, as: :currency, format: '%u %n') # => € 19,90
# percentage formatting
I18n.localize(33.3333, as: :percentage) # => 33,33%
I18n.localize(33.3333, as: :percentage, format: '%n %') # => 33,33 %
# all other numbers
I18n.localize(33.3333) # => 33,33
I'm differentiating between these 3 types of numbers because these are the ones defined in the CLDR.
IMO this would be a change with relatively low impact to the existing code base:
- The ArgumentError for I18n#localize (https://github.com/svenfuchs/i18n/blob/master/lib/i18n/backend/base.rb#L53) needs to be adapted to accept both numbers and dates/times.
- Date/time formatting should be extracted into a separate method or (better) class (DateTimeLocalizer?).
- Number formatting needs to be added (again: separate method or class).
Existing discussion points would be which Rails options to include directly in i18n:
- precision
- separator and delimiter
- strip_insignificant_zeros
I'd definitely say yes to precision as well as separator/delimiter but I'm not sure about the zeros.
Rails' NumberHelper could then leverage these helpers where it can and keep its other helpers (human formatting as well as phone numbers) as desired. And it needs to make that whatever it passes to i18n is actually a number.
What do you think? Should I fork and make a spike?
C.
I think this is a great idea! :+1:
Just recently I built a website with Middleman. It supports I18n, but it does not include Rails’ helpers, so I had to manually take care of localizing numbers …
:+1:
Hey @clemens, thanks for a super detailed description. I'd be more than happy to have some numeric localisation built-in right into i18n, we would just need to make sure that Rails users won't be affected by any change on their side if we ever change this integration there.
In fact I went straight to the localize method and noticed this comment:
# Localizes certain objects, such as dates and numbers to local formatting.
So I guess that was under the original plans :smile:. I think the API you propose is fair, I'm just unsure about the addition of the :as option. If we stop to think about the current localize API:
I18n.localize Time.now
I18n.localize Time.now, format: :short
I18n.localize Time.now, format: '%H:%M'
It figures out whether format is a Symbol or a String to know what to do. For numbers, based on your suggested API, it'd work slightly differently which concerns me a little. However, using format: :currency would need something else to be used in combination in case an extra string format is needed, and it seems to be a totally valid use case. I don't have any good suggestion right now but I'll think more about it, maybe it's ok like that.
@tigrish do you have any thoughts or concerns about that?
Thanks :heart:
TL;DNR
I think this functionality should be extracted to a separate gem.
TL;DR
I'm sort of in two minds about this, but I'm certainly very grateful to @clemens for bringing the subject up for discussion.
On the one hand I totally agree that number and date formatting should be separate from rails and rails-i18n (the same can be said for pluralization rules).
On the other hand I'm loathed to change the localize API, specifically adding some more reserved/magic options.
Trying not to think about the implementation too much just yet, it seems like there are three components in play at the moment :
- The number helpers from rails here : https://github.com/rails/rails/blob/master/activesupport/lib/active_support/number_helper
- The format definitions from rails-i18n based off of this : https://github.com/svenfuchs/rails-i18n/blob/master/rails/rails4/active_support.yml and included in localization files here : https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale
- I18n's localize method.... but in reality not. The number helpers never delegate to I18n, they just use
I18n.translateto lookup the correct pattern to be used for formatting. I guess this is the very thing we're talking about ;)
Now starting with the easiest piece - the various format definitions - it seems pretty obvious that these should be extracted out of rails-i18n since we want to use them sans rails. We do not want to merge these into i18n though, because there's no way we want to be releasing a new gem version every time a format is added or changed.
So if we have a new project for number formats (I'm going to call it i18n-numbers for the purpose of this thread), it seems to me that the first step would be to extract all of ActiveSupport's number helpers to it "as is" (ie. without cherry picking the "interesting" ones).
This would give us a stepping stone for further refactorings and has a few notable advantages :
- The number helpers in effect are unchanged - hurray, we didn't break Rails :)
- I18n's API is unchanged - hurray, we didn't break I18n :)
- We can change the implementation when I18n gets smarter and can better handle parsing options (like
:asor:format). Rails would not need to be affected apart from a bump in gem versions. - All of these number format definitions are still opt-in as opposed to getting lumped in to every I18n release.
The disadvantage I can see is that :
- You can't call
I18n.localize(333, as: :percentage)- the public interface would have to be different (eg.I18nNumber.to_currency(333)), but given that this API isn't available right now, I'm not sure that it's such a big deal.
Just my $0.02 (and then some!).
@clemens if you fancy setting up a new repo, ping me and I'll add you to the ruby-i18n org.
Thanks for the extensive comment, @tigrish. I just want to add another disadvantage of your proposed approach: We'd have different APIs for numbers and dates/times – which was one of the main reasons why I brought this issue up in the first place.
Most of the other stuff is, I think, a more extensive question about the whole organization of rails, rails-i18n and i18n itself. The question is: What goes where? Because the issues you're outlining for number formats and such are the same with the existing rails-i18n project, aren't they? Someone says that some Russian translation of whatever isn't correct, it gets added to the locale file and a new release of rails-i18n would strictly speaking be needed, even though it doesn't happen (as far as I can see).
Also other stuff: i18n is carrying around a couple of backends like gettext and such – those could be gems as well.
In short: From an architectural standpoint, there's probably quite a lot to do. Should we maybe take this as a trigger to think about the next big milestone for i18n where we take everything that we've learned in the past few years and apply it?
Just found number_with_delimiter has a locale argument.
How about places like emails which are generated server side?
On 10 Feb 2017, at 10:28, Eddie J [email protected] wrote:
I wonder if this should be done via javascript and toLocaleString. Numbers are easy to localize, there are no translations needed.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.
Why number formatting is also nice to have (nl-NL numbers):
I18n.t :hours, scope: [:count], count: 2
# 2 uren #works
I18n.t :hours, scope: [:count], count: 2
# 1 uur #works
But with fractions
I18n.t :hours, scope: [:count], count: 0.5
# 0,5 uur #0.5 uren
I18n.t :hours, scope: [:count], count: 1.5
# 1,5 uur #1.5 uren
I18n.t :hours, scope: [:count], count: "1,5"
# 1,5 uur #1,5 uren
In a Rails 7.0.5 project, doing this in a view
<%= l 123.45 %>
raises
I18n::ArgumentError:
# Object must be a Date, DateTime or Time object. 123.45 given.
I agree that since currently, when you pass anything else than a Date, DateTime or Time object to localize, it errors out, I18n#localize might be great to be extended to support numbers, without affecting current usage of I18n, only extending it.
And since these seems to be standardized in svenfuchs/rails-i18n under locale.number.format, it looks like to be very possible.
Extracting to a different gem may be the path forward, but feels like a bigger job, could we implement this one and have the extraction happen in a different worktrack?
Would be happy to take a stab at it if we agree on that ☺️
What do you think?