Closed DamienFriot closed 3 years ago
First of all, thanks for finding the library useful - its one of my favourites. And the way that the CLDR team have built the data it lets you work with all sorts of crazy compound units like:
iex> MyApp.Cldr.Unit.new "light year kilogram per cubic millimole", 3
{:ok, #Cldr.Unit<"kilogram_light_year_per_cubic_millimole", 3>}
Developing the code for this kind of generalised unit was a lot of fun and really tricky. But ... at the moment its not extensible. The good news is that the underlying data model isn't too complicated. If you can live with defining new units statically (ie configured in the Cldr backend and used at compile time) then I think I can get what. you want completed over the weekend. It's been on the to-do list for a while.
If you let me know what new base units
you need (like person
in your example) and what new compound units
(like person kilometre
) you are working with I can use them as test data.
BTW, if you are curious about the underlying raw data, you can execute Cldr.Config.units/0
.
Thanks a lot for your answer! Defining units at compile time would be perfect. I am not in a hurry so I can wait or a few weeks before any update without any problems.
Two main base units -> compound units are of interest for a Transportation category:
In addition for an "Unspecified" category, e.g. infrastructure, the base unit "unit" is of interest. The compound units will be unit per year and unit per day.
Not forgotten, have been working on a bunch of different experiments and I have a good path forward now. Expecting to complete the next version over this coming weekend.
I expect to have release 3.4.0 out this weekend with custom unit support and some additional changes related to #19
Good progress today. Customer units can be defined, even supporting SI prefixes and square/cube - not that I suspect that is important to you.
iex> Cldr.Unit.new :person_kilometer, 1
{:ok, #Cldr.Unit<"person_kilometer", 1>}
iex> Cldr.Unit.new :vehicle_kilometer, 1
{:ok, #Cldr.Unit<"vehicle_kilometer", 1>}
# With an SI prefix
iex> Cldr.Unit.new :milliperson_kilometer, 1
{:ok, #Cldr.Unit<"milliperson_kilometer", 1>}
# Also cube or square
iex> Cldr.Unit.new :square_person_kilometer, 1
{:ok, #Cldr.Unit<"square_person_kilometer", 1>}
Since units are defined at compile time, so custom units are defined. Here is an example from the repo's dev.exs
configuration:
config :ex_cldr_units, :additional_units,
vehicle: [base_unit: :unit, factor: 1, offset: 0, sort_before: :all],
person: [base_unit: :unit, factor: 1, offset: 0, sort_before: :all]
base_unit
is the base unit of the unit. It can be anything and is used for conversions and compatibility testingfactor
is the factor used to convert to the base unitoffset
is added when converting to the base unit (and subtracted in the other direction)sort_before
determines ordering of compound units. :all
means before all other units, :none
means after all other base units and some_unit
means this base unit is inserted before the specified unit.At the moment the mechanism to define a custom unit appears to be OK. However localisation (Cldr.Unit.to_string/2
and friends) will raise an exception since there is no way to define localisations yet. Thats the next step.
As of this commit, custom units can now be localised. I need to write some tests and docs before release of this and the work in #19. Comments, suggestions are very welcome.
The units themselves are configured as per the previous comment, in config.exs
. Localisations are contained in a backend module. For example:
defmodule MyApp.Cldr do
use Cldr.Unit.Additional
use Cldr,
locales: ["en", "fr", "de", "bs", "af", "af-NA", "se-SE"],
default_locale: "en",
providers: [Cldr.Number, Cldr.Unit, Cldr.List]
unit_localization(:person, "en", :long,
one: "{0} person",
other: "{0} people",
display_name: "people"
)
unit_localization(:person, "en", :short,
one: "{0} per",
other: "{0} pers",
display_name: "people"
)
unit_localization(:person, "en", :narrow,
one: "{0} p",
other: "{0} p",
display_name: "p"
)
end
Note the use Cldr.Unit.Additional
and the use of the macro unit_localization/4
.
iex> Cldr.Unit.to_string Cldr.Unit.new!(:person, 1)
{:ok, "1 person"}
iex> Cldr.Unit.to_string Cldr.Unit.new!(:person, 2), style: :short
{:ok, "2 pers"}
iex> Cldr.Unit.to_string Cldr.Unit.new!(:person, 2), style: :narrow
{:ok, "2 p"}
iex> Cldr.Unit.to_string Cldr.Unit.new!(:person_kilometer, 2), style: :long
{:ok, "2 person-kilometers"}
iex> Cldr.Unit.to_string Cldr.Unit.new!(:person_kilometer, 2), style: :short
{:ok, "2 per⋅km"}
iex> Cldr.Unit.to_string Cldr.Unit.new!(:person_kilometer, 2), style: :narrow
{:ok, "2 p⋅km"}
I have published ex_cldr_units version 3.4.0-rc.0 and would welcome any feedback or suggestions. The changelog entry is:
Fix readme example for MyApp.Cldr.Unit.convert/2
. Thanks to @DamienFF. Closes #16.
Add missing <backend>.convert!/2
Supports the definition of custom units in config.exs
. Units can be defined and operated on however they cannot yet be localised (that functionality will be in place before release). See the examples in dev.exs
.
Add Cldr.Unit.display_name/2
Add Cldr.Unit.known_units_by_category/0
Add Cldr.Unit.known_units_for_category/1
Add Cldr.Unit.measurement_system_units/0
Add Cldr.Unit.measurement_system_from_locale/{2, 3}
Add Cldr.Unit.measurement_system_for_territory/1
Add Cldr.Unit.measurement_systems_for_unit/1
Improve Cldr.Unit.IncompatibleUnit
exception error message
Deprecate Cldr.Unit.measurement_systems/0
in favour of Cldr.Unit.measurement_systems_by_territory/0
Requires ex_cldr
version ~> 2.19
which includes the localised display name of units
Hi,
We have a few specific categories and units in my domain (environmental footprints), e.g. transportation in person.km (person x km) or infrastructure in unit (for example for a building unit).
What would be the preferred way to extend the library with additional categories and units?
Thank you in advance for your answer.