elixir-cldr / cldr_units

Unit formatting (volume, area, length, ...) functions for the Common Locale Data Repository (CLDR)
Other
16 stars 13 forks source link

Are there any plans to support ranges in units? #33

Closed ribanez7 closed 1 year ago

ribanez7 commented 1 year ago

Currently it is possible to pass float, integer, Decimal or Ratio, but would it be a possibility to pass a range? Or maybe this should be responsibility of Cldr.Number.to_range_string ?

If we include it in Cldr.Number.to_range_string, maybe it can be passed as an option, like the currency one. Why not allow an option for unit, if there is an option for currency?

And in case that should be responsibility of Cldr.Unit, I could try to do a POC.

kipcole9 commented 1 year ago

@ribanez7, thanks for the suggestion. I'm not sure I completely understand what you're suggesting. I think what you are saying is something like:

iex> Cldr.Unit.new(:kilogram, 100..1000)

Is that the idea? I'm interested but I can see some challenges.

I'm curious about the use case you have in mind. Can you share an example?

ribanez7 commented 1 year ago

Sure. I am using a custom unit (person), to show company sizes. The japanese case gives us some juice since it also changes the way a range is formatted (using tilde instead of dash).

So given this implementation:

  unit_localization(:person, "ja", :long,
    nominative: %{
      one: "{0} 人",
      other: "{0} 人"
    },
    display_name: "人"
  )

Let's say I want something like: 1-10 people, or even with a current unit, hour => 1-2 hr.

I would like something like:

iex> MyApp.Cldr.Unit.to_string!(MyApp.Cldr.Unit.new!(:hour, 1..2), [unit: :hour, style: :long, locale: :en])     
"1-2 hours"

But that implies calling MyApp.Cldr.Number.to_range_string(1..2, locale: :en, format: :long) and appending the Unit.to_string! result to it, calculated maybe with the last number in the range.

The japanese case I was talking about would result in:

MyApp.Cldr.Unit.to_string!(MyApp.Cldr.Unit.new!(:person 1..2), [style: :long, locale: :ja])     
"1〜2人"
ribanez7 commented 1 year ago

I can't come up with examples of it using math operations.

kipcole9 commented 1 year ago

Thanks for the example, I understand better now. I think there is a way to do a unit version analogous to Date.Range called Unit.Range with the caveat that both units in the range would need to be the same unit type.

I am working on a series of updates for CLDR 43 data that will launch in about 3 weeks. Is that manageable timing for you?

kipcole9 commented 1 year ago

I can also implement Enumerable for a Unit.Range too.

ribanez7 commented 1 year ago

Yep, that will work perfectly. For the meantime I have a helper function to do it "the kind-of-hardcoded-way", by calculating both things separately (the number.to_range_string and the unit.to_string) and placing them together just with a concat. To extend the use of it in more languages I'll wait for the implementation 👍🏼 , now I'm using my function just for controlled situations.

kipcole9 commented 1 year ago

I've closed this issue for now. Still on track for shipping in a couple of weeks. When everything across all the cldr projects is merged it'll be easier for you to test for your own use cases. For now, here's an example:

iex> {:ok, range} = Cldr.Unit.Range.new(Cldr.Unit.new!(:gram, 1), Cldr.Unit.new!(:gram, 5))
iex> Cldr.Unit.to_string(range, locale: :ja)
{:ok, "1~5 グラム"}
ribanez7 commented 1 year ago

Many thanks @kipcole9 , that's awesome.

kipcole9 commented 1 year ago

You're welcome. As a bonus, Cldr.Unit.Range.t implements the Enumeration protocol:

iex> range = Cldr.Unit.Range.new!(Cldr.Unit.new!(:gram, 1), Cldr.Unit.new!(:gram, 5))
iex> Enum.to_list(range)
[Cldr.Unit.new!(:gram, 1), Cldr.Unit.new!(:gram, 2), Cldr.Unit.new!(:gram, 3),
 Cldr.Unit.new!(:gram, 4), Cldr.Unit.new!(:gram, 5)]
ribanez7 commented 1 year ago

Yess, I've seen that going through the code, brilliant 👍🏼