elixir-cldr / cldr_units

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

[Question] List units by category? #19

Closed coladarci closed 3 years ago

coladarci commented 3 years ago

Hey Kip! We're excited to dive into another one of your libraries - is it not possible to simply list all units by category? i.e if I wanted to list all value volume units, I'm not seeing a way to do this. Am I missing something?

Thanks!

kipcole9 commented 3 years ago

@coladarci Glad you're taking it for a spin. Its maybe my favourite because of the very flexible approach the CLDR team has taken.

Can definitely do as you ask, I'll need to add it in. The is a caveat to be understood.

Units can be composed. For example:

iex> {:ok, unit} = Cldr.Unit.new 3, "square_hectare_per_light_year"
{:ok, #Cldr.Unit<"square_hectare_per_light_year", 3>}

iex> Cldr.Unit.base_unit unit
{:ok, "square_square_meter_per_meter"}

Since this is valid but derived unit, I can never return it as a value for a given category.

The compromise I make is that categories, and the units in a category, are those units which are translatable without composition (ie they have entries in CLDR for each locale for localisation). I think this fits most use cases.

Therefore yes, I can return all the units for a category. Where "all" means those units that can be translated. Not those that are derived or composed.

Make sense? Will work for you?

If you let me know the use case then I can probably give a clearer answer.

coladarci commented 3 years ago

Yes! We are making very simple wrappers around these, identical to how you did Money. The two we are making are Volume and Weight; just want to be able to get a list of possible values for UI purposes. No rush, no big deal, pumped with the library 🙌🏻🚀🚀💎💎

kipcole9 commented 3 years ago

As of this commit the basic functions to support retrieving units by category exist.

In addition, I added the long-overdue function Cldr.Unit.measurement_system_from_locale/1 that returns the measurement system (metric, UK, US) applicable to the specified locale.

Next steps this weekend are:

  1. Add a function to filter a list of unit types by measurement system. That way you can choose to show any unit, or only units in one or more measurement systems. This makes it easy to localise the unit list. Why? If you have a user in Europe and you list all of the volume units you would list the metric ones. In the US, the US ones and so on.

  2. Add a function to localise a list of units (which are atoms) into their localised display format that you can put in a UI. This requires an update to ex_cldr version 2.19.0 since I had not previously been packaging the unit display names.

dhedlund commented 3 years ago

@kipcole9 There's a typo in one of the function definitions that you might want to address before releasing this. The readme also has the same typo. https://github.com/elixir-cldr/cldr_units/commit/313eeccfb3ecb84457a34b1d6a41032b8d57e09b#diff-662130a1c7f523fbc1a14b20281561e71fe5c822fa72e341da485851e97657a9R1275

kipcole9 commented 3 years ago

As of this commit, the basic functionality you're after is in place. The function that does the localisation is Cldr.Unit.display_name/1.

Examples

# Get the units for a category and enumerate their display names
# in the current locale and default style
iex> {:ok, units} = Cldr.Unit.known_units_for_category(:volume)
{:ok,
 [:jigger, :cubic_yard, :pinch, :drop, :cup_metric, :acre_foot,
  :cubic_centimeter, :liter, :pint, :quart_imperial, :tablespoon, :deciliter,
  :cubic_foot, :megaliter, :milliliter, :cup, :centiliter,
  :dessert_spoon_imperial, :fluid_ounce_imperial, :teaspoon, :hectoliter,
  :dessert_spoon, :cubic_meter, :barrel, :cubic_kilometer, :fluid_ounce, :dram,
  :cubic_mile, :bushel, :cubic_inch, :pint_metric, :quart, :gallon_imperial,
  :gallon]}
iex> Enum.map(units, &Cldr.Unit.display_name/1)
["jigger", "cubic yards", "pinch", "drop", "metric cups", "acre-feet",
 "cubic centimeters", "liters", "pints", "Imp. quart", "tablespoons",
 "deciliters", "cubic feet", "megaliters", "milliliters", "cups", "centiliters",
 "Imp. dessert spoon", "Imp. fluid ounces", "teaspoons", "hectoliters",
 "dessert spoon", "cubic meters", "barrels", "cubic kilometers", "fluid ounces",
 "dram", "cubic miles", "bushels", "cubic inches", "metric pints", "quarts",
 "Imp. gallons", "gallons"]

# Get the units for a category and enumerate their display names
# in a specific locale and style
iex> Enum.map(units, &Cldr.Unit.display_name(&1, locale: "de", style: :short))
["Jigger", "yd³", "Prise", "Trpf.", "Ta", "Acre-Feet", "cm³", "Liter", "pt",
 "qt Imp", "EL", "dl", "ft³", "Ml", "ml", "Cups", "cl", "Imp. DL",
 "Imp. fl oz", "TL", "hl", "DL", "m³", "bbl", "km³", "fl oz", "Flüssigdram",
 "mi³", "Bushel", "in³", "mpt", "qt", "Imp. gal", "gal"]

# Filter only metric units first
# Note that most metric units support SI prefixes (mega, giga, milli, ... there are 15 prefixes)
# Is it useful to expand those prefixes for UI purposes?
iex> metric_units = Enum.filter(units, &Cldr.Unit.measurement_system?(&1, :metric))             
[:cup_metric, :liter, :cubic_meter, :pint_metric]

# Filter US system units then enumerate display names
iex> us_units = Enum.filter(units, &Cldr.Unit.measurement_system?(&1, [:ussystem]))             
[:jigger, :cubic_yard, :pinch, :drop, :pint, :tablespoon, :cubic_foot, :cup,
 :teaspoon, :dessert_spoon, :barrel, :fluid_ounce, :dram, :cubic_mile, :bushel,
 :cubic_inch, :quart, :gallon]
iex> Enum.map(us_units, &Cldr.Unit.display_name(&1, locale: "de", style: :short))               
["Jigger", "yd³", "Prise", "Trpf.", "pt", "EL", "ft³", "Cups", "TL", "DL",
 "bbl", "fl oz", "Flüssigdram", "mi³", "Bushel", "in³", "qt", "gal"]

Open Items

kipcole9 commented 3 years ago

As of this commit, measurement systems are resolved for all units returned by Cldr.Unit.known_units/0 as well as binary composable units.

Feedback is welcome, otherwise I think the requirements of your original request are now met by:

I will now work on an update to ex_cldr_html to add a select helper.

I will finish up the work to support #18 too however if you need this functionality now I can provide an interim release.

kipcole9 commented 3 years ago

As of this commit, cldr_html adds the Cldr.HTML.Unit.select/3 function to aid in Phoenix form creation.

For example:

iex> Cldr.HTML.Unit.select(:my_form, :my_field, units: [:foot, :inch], selected: :foot, locale: "th")
....

Still some additional tests and some dialyzer noise to quieten but otherwise in good shape.

kipcole9 commented 3 years ago

I have published ex_cldr_units version 3.4.0-rc.0 and would welcome any feedback or suggestions. The changelog entry is:

Bug Fixes

Enhancements