Skip to content

Commit

Permalink
Continue working on doc structure. Guides no longer appear - they wil…
Browse files Browse the repository at this point in the history
…l go to the dependent packages. Readme now includes all documentation.
  • Loading branch information
kipcole9 committed Nov 18, 2017
1 parent 264d942 commit 3194521
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 123 deletions.
160 changes: 97 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Elixir Cldr
# Getting Started with Cldr
![Build Status](http://sweatbox.noexpectations.com.au:8080/buildStatus/icon?job=cldr)
![Deps Status](https://beta.hexfaktor.org/badge/all/github/kipcole9/cldr.svg)
[![Hex pm](http://img.shields.io/hexpm/v/ex_cldr.svg?style=flat)](https://hex.pm/packages/ex_cldr)
Expand All @@ -8,6 +8,26 @@

`Cldr` is an Elixir library for the [Unicode Consortium's](http://unicode.org) [Common Locale Data Repository (CLDR)](http://cldr.unicode.org). The intentions of CLDR, and this library, is to simplify the locale specific formatting of numbers, lists, currencies, calendars, units of measure and dates/times. As of November 2nd 2017 and Version 0.10.0, `Cldr` is based upon [CLDR version 32.0.0](http://cldr.unicode.org/index/downloads/cldr-32).

The functions you are mostly likely to user are in the modules `Cldr` and `Cldr.Locale`. In particular:

* `Cldr.default_locale/0`
* `Cldr.set_current_locale/1`
* `Cldr.get_current_locale/0`
* `Cldr.known_locale_names/0`
* `Cldr.Locale.new/1`

To access the raw Cldr data for a locale the `Cldr.Config` module is available. Note that the functions in `Cldr.Config` are typically used by library authors. The most useful function is:

* `Cldr.Config.get_locale/1` which returns a map of all the CLDR data known to `Cldr`. Since this data is read from a file, parsed and then formatted it is a function that should be used with care due to the material performance implications. `Cldr` uses this function during compilation to build functions that return the relevant data with high performance and these functions are to be preferred over the use of `Cldr.Config.get_locale/1`.

## Use this package when you have a requirement to...

* Support multiple languages and locales in your application and need to support formatting numbers, dates, times, date-times, SI units and lists in a locale-specific manner

* Access the data maintained in the CLDR repository in a functional manner

* Parse an [Accept-Language](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4) http header or a [language tag](https://tools.ietf.org/html/bcp47)

**It is highly likely that you will also want to install one or more of the dependent packages that provide localization and formatting for a particular data domain. See [Additional Cldr Packages](#additional_cldr_packages) below**.

## Elixir Version Requirements
Expand All @@ -20,7 +40,7 @@ Add `ex_cldr` as a dependency to your `mix` project:

defp deps do
[
{:ex_cldr, "~> 0.13"}
{:ex_cldr, "~> 1.0"}
]
end

Expand All @@ -37,7 +57,7 @@ Although `Cldr` is purely a library application, it should be added to your appl

## Additional Cldr Packages

`ex_cldr` includes only basic functions to maintain the CLDR data repository in an accessible manner. Additional functionality is available by adding additional packages:
`ex_cldr` includes only basic functions to maintain the CLDR data repository in an accessible manner and to manage locale definitions. Additional functionality is available by adding additional packages:

* Number formatting: [ex_cldr_numbers](https://hex.pm/packages/ex_cldr_numbers)
* List formatting: [ex_cldr_lists](https://hex.pm/packages/ex_cldr_lists)
Expand All @@ -46,22 +66,54 @@ Although `Cldr` is purely a library application, it should be added to your appl

Each of these packages includes `ex_cldr` as a dependency so configuring any of these additional packages will automatically install `ex_cldr`.

## Quick Configuration
## Configuration

`Cldr` attempts to maximise runtime performance at the expense of additional compile time. Where possible `Cldr` will create functions to encapsulate data at compile time. To perform these optimizations for all 523 locales known to Cldr wouldn't be an effective use of your time or your computer's. Therefore `Cldr` requires that you configure the locales you want to use. You can do this in your `mix.exs` by specifying the locales you want to configure or by telling `Cldr` about a `Gettext` module you may already have configured - in which case `Cldr` will configure whatever locales you have configured in `Gettext` as well.

Here's an example configuration that uses all of the available configuration keys:

config :cldr,
default_locale: "en",
locales: ["fr", "en", "bs", "si", "ak", "th"],
gettext: MyApp.Gettext,
data_dir: "./priv/cldr",
precompile_number_formats: ["¤¤#,##0.##"]
precompile_transliterations: [{:latn, :arab}, {:thai, :latn}]

### Configuration Keys

The configuration keys available for `Cldr` are:

* `default_locale` specifies the default locale to be used if none has been set by `Cldr.put_locale/2` and none has been set in a configured `Gettext` module. The default locale in case no other locale has been set is `"en"`. Default locale calculated by:

* If set by the `:default_locale` key, then this is the priority
* If no `:default_locale` key, then a configured `Gettext` default locale is chosen
* If no `:default_locale` key is specified and no `Gettext` module is configured, or is configured but has no default set, then the default locale will be `en-001`

Without any specific configuration Cldr will support the "en" locale only. To support additional locales update your `config.exs` file (or the relevant environment version).
* `locales`: Defines what locales will be configured in `Cldr`. Only these locales will be available and an exception `Cldr.UnknownLocaleError` will be raised if there is an attempt to use an unknown locale. This is the same behaviour as `Gettext`. Locales are configured as a list of binaries (strings). For convenince it is possible to use wildcard matching of locales which is particulalry helpful when there are many regional variances of a single language locale. For example, there are over 100 regional variants of the "en" locale in CLDR. A wildcard locale is detected by the presence of `.`, `[`, `*` and `+` in the locale string. This locale is then matched using the pattern as a `regex` to match against all available locales. The example below will configure all locales that start with `en-` and the locale `fr`.

config :ex_cldr,
default_locale: "en-001",
locales: ["fr-*", "pt-BR", "en", "pl", "ru", "th", "he"],
gettext: MyApp.Gettext

Configures a default locale of "en-001" (which is itself the `Cldr` default). Additional locales are configured with the `:locales` key. In this example, all locales starting with "fr-" will be configured along with Brazilian Portuguese, English, Polish, Russian, Thai and Hebrew.
config :cldr,
default_locale: "en",
locales: ["en-*", "fr"]

There is one additional setting which is `:all` which will configure all 523 locales. **This is highly discouraged** since it will take many minutes to compile your project and will consume more memory than you really want. This setting is there to aid in running the test suite. Really, don't use this setting.

* `gettext`: configures `Cldr` to use a `Gettext` module as an additional source of locales you want to configure. Since `Gettext` uses the Posix locale name format (locales with an '\_' in them) and `Cldr` uses the Unicode format (a '-' as the subtag separator), `Cldr` will transliterate locale names from `Gettext` into the `Cldr` canonical form.

* `data_dir`: indicates where downloaded locale files will be stored. The default is `:code.priv_dir(:ex_cldr)`. It is highly recommended you do not change this setting.

* `precompile_number_formats`: provides a means to have user-defined format strings precompiled at application compile time. This has a performance benefit since precompiled formats execute approximately twice as fast as formats that are not precompiled.

* `precompile_transliterations`: defines those transliterations between the digits of two different number systems that will be precompiled. The is a list of 2-tuples where each tuple is of the form `{from_number_system, to_number_system}` where each number system is expressed as an atom. The available number systems is returned by `Cldr.Number.System.systems_with_digits/0`. The default is the empty list `[]`.

### Recompiling after a configuration change

Note that Elixir can't determine dependencies based upon configuration so when you make changes to your `Cldr` configuration a forced recompilation is required in order for the changes to take affect. To recompile:

iex> mix deps.compile ex_cldr --force

# If you have dependent packages installed
iex> mix deps.compile ex_cldr_numbers --force
iex> mix deps.compile ex_cldr_lists --force
iex> mix deps.compile ex_cldr_units --force
Expand All @@ -76,11 +128,11 @@ or from [hex](https://hex.pm/packages/ex_cldr).

* If installed from github then all 523 locales are installed when the repo is cloned into your application deps.

* If installed from hex then only the locales "en" and "root" are installed. When you configure additional locales these will be downloaded during application compilation. Please note above the requirement for a force recompilation in this situation.
* If installed from hex then only the locales "en", "en-001" and "root" are installed. When you configure additional locales these will be downloaded during application compilation. Please note above the requirement for a force recompilation in this situation.

## Localizing and Formatting Numbers

The `Cldr.Number` module provides number formatting. The public API for number formatting is `Cldr.Number.to_string/2`. Some examples:
The `Cldr.Number` module implemented in the [ex_cldr_numbers](https://hex.pm/packages/ex_cldr_numbers) package provides number formatting. The public API for number formatting is `Cldr.Number.to_string/2`. Some examples:

iex> Cldr.Number.to_string 12345
"12,345"
Expand All @@ -107,7 +159,7 @@ See `h Cldr.Number` and `h Cldr.Number.to_string` in `iex` for further informati

## Localizing Lists

The `Cldr.List` module provides list formatting. The public API for list formating is `Cldr.List.to_string/2`. Some examples:
The `Cldr.List` module provides list formatting and is implemented in the [ex_cldr_lists](https://hex.pm/packages/ex_cldr_lists) package. The public API for list formating is `Cldr.List.to_string/2`. Some examples:

iex> Cldr.List.to_string(["a", "b", "c"], locale: "en")
"a, b, and c"
Expand All @@ -122,51 +174,47 @@ Seer `h Cldr.List` and `h Cldr.List.to_string` in `iex` for further information.

## Localizing Units

The `Cldr.Unit` module provides unit localization. The public API for unit localization is `Cldr.Unit.to_string/3`. Some examples:
The `Cldr.Unit` module provides unit localization and is implemented in the [ex_cldr_units](https://hex.pm/packages/ex_cldr_units) package. The public API for unit localization is `Cldr.Unit.to_string/3`. Some examples:

iex> Cldr.Unit.to_string 123, :volume_gallon
iex> Cldr.Unit.to_string 123, :gallon
"123 gallons"

iex> Cldr.Unit.to_string 1234, :volume_gallon, format: :long
iex> Cldr.Unit.to_string 1234, :gallon, format: :long
"1 thousand gallons"

iex> Cldr.Unit.to_string 1234, :volume_gallon, format: :short
iex> Cldr.Unit.to_string 1234, :gallon, format: :short
"1K gallons"

iex> Cldr.Unit.to_string 1234, :frequency_megahertz
iex> Cldr.Unit.to_string 1234, :megahertz
"1,234 megahertz"

iex> Cldr.Unit.available_units
[:volume_gallon, :pressure_pound_per_square_inch, :digital_terabyte,
:digital_bit, :digital_gigabit, :digital_kilobit, :volume_pint,
:speed_kilometer_per_hour, :concentr_part_per_million, :energy_calorie,
:volume_milliliter, :length_fathom, :length_foot, :volume_cubic_yard,
:mass_microgram, :length_nautical_mile, :volume_deciliter,
:consumption_mile_per_gallon, :volume_bushel, :volume_cubic_centimeter,
:length_light_year, :volume_gallon_imperial, :speed_meter_per_second,
:power_kilowatt, :power_watt, :length_millimeter, :digital_gigabyte,
:duration_nanosecond, :length_centimeter, :volume_cup_metric,
:length_kilometer, :angle_degree, :acceleration_g_force, :electric_ampere,
:volume_quart, :duration_century, :angle_revolution, :volume_hectoliter,
:area_square_meter, :digital_megabyte, :light_lux, :duration_year,
:energy_kilocalorie, :frequency_megahertz, :power_horsepower,
:volume_cubic_meter, :area_hectare, :frequency_hertz, :length_furlong,
:length_astronomical_unit, ...]
[:acre, :acre_foot, :ampere, :arc_minute, :arc_second, :astronomical_unit, :bit,
:bushel, :byte, :calorie, :carat, :celsius, :centiliter, :centimeter, :century,
:cubic_centimeter, :cubic_foot, :cubic_inch, :cubic_kilometer, :cubic_meter,
:cubic_mile, :cubic_yard, :cup, :cup_metric, :day, :deciliter, :decimeter,
:degree, :fahrenheit, :fathom, :fluid_ounce, :foodcalorie, :foot, :furlong,
:g_force, :gallon, :gallon_imperial, :generic, :gigabit, :gigabyte, :gigahertz,
:gigawatt, :gram, :hectare, :hectoliter, :hectopascal, :hertz, :horsepower,
:hour, :inch, ...]

See `h Cldr.Unit` and `h Cldr.Unit.to_string` in `iex` for further information.

## Localizing Dates, Times and DateTimes

As of version 0.2.0, formatting of relative dates and date times is supported in the `Cldr.DateTime.Relative` module. The public API is `Cldr.DateTime.Relative.to_string/2`. Some examples:
Formatting of relative dates and date times is supported in the `Cldr.DateTime.Relative` module implemented in the [ex_cldr_dates_times](https://hex.pm/packages/ex_cldr_dates_times) package. The public API is `Cldr.DateTime.to_string/2` and `Cldr.DateTime.Relative.to_string/2`. Some examples:

iex> Cldr.DateTime.Relative.to_string(-1)
"1 second ago"
iex> Cldr.Date.to_string Date.utc_today()
{:ok, "Aug 18, 2017"}

iex> Cldr.DateTime.Relative.to_string(1)
"in 1 second"
iex> Cldr.Time.to_string Time.utc_now
{:ok, "11:38:55 AM"}

iex> Cldr.DateTime.Relative.to_string(1, unit: :day)
"tomorrow"
iex> Cldr.DateTime.to_string DateTime.utc_now
{:ok, "Aug 18, 2017, 11:39:08 AM"}

iex> Cldr.DateTime.Relative.to_string 1, unit: :day, format: :narrow
{:ok, "tomorrow"}

iex> Cldr.DateTime.Relative.to_string(1, unit: :day, locale: "fr")
"demain"
Expand All @@ -180,39 +228,25 @@ As of version 0.2.0, formatting of relative dates and date times is supported in
iex> Cldr.DateTime.Relative.to_string(1234, unit: :year, locale: "fr")
"dans 1 234 ans"

iex> Cldr.DateTime.Relative.to_string(31)
"in 31 seconds"

iex> Cldr.DateTime.Relative.to_string(~D[2017-04-29], relative_to: ~D[2017-04-26])
"in 3 days"

iex> Cldr.DateTime.Relative.to_string(310, format: :short, locale: "fr")
"dans 5 min"

iex> Cldr.DateTime.Relative.to_string(310, format: :narrow, locale: "fr")
"+5 min"
## Gettext Backend Pluralization Support

iex> Cldr.DateTime.Relative.to_string(~D[2017-04-29], unit: :ziggeraut)
{:error,
"Unknown time unit :ziggeraut. Valid time units are [:day, :hour, :minute, :month, :second, :week, :year, :mon, :tue, :wed, :thu, :fri, :sat, :sun, :quarter]"}

## Gettext Integration

There is an experimental plurals module for Gettext called `Cldr.Gettext.Plural`. **Its not yet fully tested**. It is configured in `Gettext` by:
There is an experimental plurals module for Gettext called `Cldr.Gettext.Plural`. It is configured in `Gettext` by:

defmodule MyApp.Gettext do
use Gettext, plural_forms: Cldr.Gettext.Plural
end

`Cldr.Gettext.Plural` will fall back to `Gettext` pluralisation if the locale is not known to `Cldr`. This module is only compiled if `Gettext` is configured as a dependency in your project.

Note that `Cldr.Gettext.Plural` does not guarantee to return the same `plural index` as `Gettext`'s own pluralization engine which can introduce some compatibility issues if you plan to mix plural engines.

## Plugs

`Cldr` provides two plugs to aid integration into an HTTP workflow. These two plugs are:

* `Cldr.Plug.AcceptLanguage` which will parse an `accept-language` header and resolve the best matched and configured `Cldr` locale. The result is stored in `conn.private[:cldr_locale]` which is also returned by `Cldr.Plug.AcceptLanguage.get_cldr_locale/1`.

* `Cldr.Plug.SetLocale` which will look for a locale in the several places and then call `Cldr.set_current_locale/1` and `Gettext.put_locale/2` if configured so to do. Finally, The result is stored in `conn.private[:cldr_locale]` which is then available through. `Cldr.Plug.SetLocale.get_cldr_locale/1`. The plug will look for a locale in the following locations depending on the plug configuration:
* `Cldr.Plug.SetLocale` which will look for a locale in the several places and then call `Cldr.set_current_locale/1` and `Gettext.put_locale/2` if configured so to do. Finally, The result is stored in `conn.private[:cldr_locale]` which is then available through `Cldr.Plug.SetLocale.get_cldr_locale/1`. The plug will look for a locale in the following locations depending on the plug configuration:

* `url_params`
* `query_params`
Expand All @@ -225,14 +259,14 @@ There is an experimental plurals module for Gettext called `Cldr.Gettext.Plural`
Note that `Cldr` defines locale string according to the Unicode standard:

* Language codes are two lowercase letters (ie "en", not "EN")
* Potentially one or more modifiers separated by "-" (dash), not a "\_". (underscore). If you configure a `Gettext` module then `Cldr` will transliterate `Gettext`'s "\_" into "-" for compatibility.
* Typically the modifier is a territory code. This is commonly a two-letter uppercase combination. For example "pt-BR" is the locale referring to Brazilian Portugese.
* Potentially one or more modifiers separated by `-` (dash), not a `_`. (underscore). If you configure a `Gettext` module then `Cldr` will transliterate `Gettext`'s `_` into `-` for compatibility.
* Typically the modifier is a territory code. This is commonly a two-letter uppercase combination. For example `pt-BR` is the locale referring to Brazilian Portugese.
* In `Cldr` a locale name is always a `binary` and never an `atom`. Internally a locale is parsed and stored as a `Cldr.LanguageTag` struct.
* The locales known to `Cldr` can be retrieved by `Cldr.known_locales/0` to get the locales known to this configuration of `Cldr` and `Cldr.all_locales/0` to get the locales available in the CLDR data repository.
* The locales known to `Cldr` can be retrieved by `Cldr.known_locale_names/0` to get the locales known to this configuration of `Cldr` and `Cldr.all_locale_names/0` to get the locales available in the CLDR data repository.

## Testing

Tests cover the full 516 locales defined in CLDR. Since `Cldr` attempts to maximize the work done at compile time in order to minimize runtime execution, the compilation phase for tests is several minutes.
Tests cover the full 523 locales defined in CLDR. Since `Cldr` attempts to maximize the work done at compile time in order to minimize runtime execution, the compilation phase for tests is several minutes.

Tests are run on Elixir 1.5.x. `Cldr` will not run on Elixir version before 1.5.

Expand Down
Loading

0 comments on commit 3194521

Please sign in to comment.