# Intl
An Elixir interface to internationalization functions modelled on the [JavaScript Intl API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl).
If you are familiar with the JS Intl API, you already know how to use this library. The module names, function purposes, and option names mirror their JS counterparts — adapted to idiomatic Elixir conventions (snake_case, keyword options, `{:ok, result}` / `{:error, reason}` tuples).
## Relationship to Localize
`Intl` is a thin shim over the [Localize](https://hexdocs.pm/localize) library which provides the full CLDR-based locale data and formatting engine. Each `Intl` module translates JS Intl option names and conventions into the corresponding `Localize` calls:
* `Intl.NumberFormat` delegates to `Localize.Number` and `Localize.Unit`.
* `Intl.DateTimeFormat` delegates to `Localize.DateTime`, `Localize.Date`, `Localize.Time`, and `Localize.Interval`.
* `Intl.ListFormat` delegates to `Localize.List`.
* `Intl.DisplayNames` delegates to `Localize.Territory`, `Localize.Language`, `Localize.Currency`, and `Localize.Script`.
* `Intl.RelativeTimeFormat` delegates to `Localize.DateTime.Relative`.
* `Intl.PluralRules` delegates to `Localize.Number.PluralRule`.
* `Intl.Collator` delegates to `Localize.Collation`.
* `Intl.DurationFormat` delegates to `Localize.Duration`.
* `Intl.Segmenter` uses `String.graphemes/1` for grapheme segmentation and the optional `unicode_string` library for word and sentence segmentation.
If you need lower-level control or access to features beyond the Intl API surface (such as unit conversion, message formatting, or calendar metadata), use `Localize` directly.
## Compatibility and Differences
The full compatibility matrix is in [the Compatibility guide](https://github.com/elixir-cldr/intl/blob/v0.1.0/guides/compatibility.md). Key points:
* **Functional, not object-oriented.** JS creates formatter instances with `new Intl.NumberFormat(locale, options)`. Elixir passes options directly: `Intl.NumberFormat.format(number, options)`.
* **snake_case options.** JS `minimumFractionDigits` becomes `:minimum_fraction_digits`.
* **Tagged return tuples.** All functions return `{:ok, result}` or `{:error, reason}`. Bang variants (`format!/2`) raise on error.
* **Collator returns atoms.** `Intl.Collator.compare/3` returns `:lt`, `:eq`, or `:gt` instead of -1, 0, or 1.
* **`formatToParts` is not supported** in any module. The underlying Localize library does not expose structured format parts.
* **`resolvedOptions` and `supportedLocalesOf` are not wrapped.** Use `Localize.available_locale_id?/1` to check locale support directly.
* **Segmenter output is simplified.** Returns a flat list of strings rather than the JS iterable of rich segment objects.
## Examples
### Number Formatting
```elixir
iex> Intl.NumberFormat.format(1_234_567.89, locale: :en)
{:ok, "1,234,567.89"}
iex> Intl.NumberFormat.format(1_234_567.89, locale: :de)
{:ok, "1.234.567,89"}
iex> Intl.NumberFormat.format(0.56, locale: :en, style: :percent)
{:ok, "56%"}
iex> Intl.NumberFormat.format(1234.5, locale: :en, style: :currency, currency: :USD)
{:ok, "$1,234.50"}
```
### Date and Time Formatting
```elixir
iex> Intl.DateTimeFormat.format(~D[2025-03-15], locale: :en, date_style: :full)
{:ok, "Saturday, March 15, 2025"}
iex> Intl.DateTimeFormat.format(~D[2025-03-15], locale: :en, date_style: :short)
{:ok, "3/15/25"}
```
### List Formatting
```elixir
iex> Intl.ListFormat.format(["Monday", "Tuesday", "Wednesday"], locale: :en)
{:ok, "Monday, Tuesday, and Wednesday"}
iex> Intl.ListFormat.format(["tea", "coffee", "juice"], locale: :en, type: :disjunction)
{:ok, "tea, coffee, or juice"}
iex> Intl.ListFormat.format(["lundi", "mardi", "mercredi"], locale: :fr)
{:ok, "lundi, mardi et mercredi"}
```
### Display Names
```elixir
iex> Intl.DisplayNames.of("US", type: :region, locale: :en)
{:ok, "United States"}
iex> Intl.DisplayNames.of("DE", type: :region, locale: :fr)
{:ok, "Allemagne"}
iex> Intl.DisplayNames.of("fr", type: :language, locale: :en)
{:ok, "French"}
iex> Intl.DisplayNames.of(:Latn, type: :script, locale: :en)
{:ok, "Latin"}
iex> Intl.DisplayNames.of("JPY", type: :currency, locale: :en)
{:ok, "Japanese Yen"}
iex> Intl.DisplayNames.of(:gregorian, type: :calendar, locale: :en)
{:ok, "Gregorian Calendar"}
```
### Relative Time
```elixir
iex> Intl.RelativeTimeFormat.format(-1, :day, locale: :en)
{:ok, "yesterday"}
iex> Intl.RelativeTimeFormat.format(3, :hour, locale: :en)
{:ok, "in 3 hours"}
iex> Intl.RelativeTimeFormat.format(-3, :day, locale: :fr)
{:ok, "il y a 3 jours"}
```
### Plural Rules
```elixir
iex> Intl.PluralRules.select(1, locale: "en")
{:ok, :one}
iex> Intl.PluralRules.select(5, locale: "en")
{:ok, :other}
iex> Intl.PluralRules.select(2, locale: "ar")
{:ok, :two}
```
### Collation
```elixir
iex> Intl.Collator.compare("ä", "z", locale: :de, sensitivity: :base)
:lt
iex> Intl.Collator.sort(["banana", "apple", "cherry"], locale: :en)
["apple", "banana", "cherry"]
```
### Duration Formatting
```elixir
iex> Intl.DurationFormat.format(%{hours: 2, minutes: 30}, locale: :en)
{:ok, "2 hours and 30 minutes"}
```
### Text Segmentation
```elixir
iex> Intl.Segmenter.segment("héllo", granularity: :grapheme)
{:ok, ["h", "é", "l", "l", "o"]}
iex> Intl.Segmenter.segment("Hello world", granularity: :word, trim: true)
{:ok, ["Hello", "world"]}
iex> Intl.Segmenter.segment("Hello. How are you?", granularity: :sentence)
{:ok, ["Hello. ", "How are you?"]}
```
### Locale Utilities
```elixir
iex> Intl.get_canonical_locales(["en-us", "fr-fr"])
{:ok, ["en-US", "fr-FR"]}
iex> {:ok, calendars} = Intl.supported_values_of(:calendar)
iex> :gregorian in calendars
true
```
## Installation
Add `intl` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:intl, "~> 0.1.0"}
]
end
```
Word and sentence segmentation require the optional `unicode_string` dependency:
```elixir
{:unicode_string, "~> 1.8"}
```
## Locale Data
`Intl` delegates to `Localize` for locale data. The `:en` locale is always available. To use other locales, either pre-download their data at build time:
```bash
mix localize.download_locales de fr ja
```
Or enable runtime downloading in `config/runtime.exs`:
```elixir
config :localize, :allow_runtime_locale_download, true
```
Locale data is loaded lazily into `:persistent_term` on first access. See the [Localize documentation](https://hexdocs.pm/localize) for full configuration details.