# `Localize.Chars`
[🔗](https://github.com/elixir-localize/localize/blob/v0.6.0/lib/localize/chars.ex#L1)

Protocol for locale-aware string formatting.

`Localize.Chars` provides a single dispatch point for formatting
any supported value as a localized string. It mirrors the
`String.Chars` protocol from Elixir core, but every implementation
is locale-aware and returns the standard Localize result tuple
`{:ok, formatted}` / `{:error, exception}`.

## Examples

    iex> Localize.Chars.to_string(1234.5, locale: :de)
    {:ok, "1.234,5"}

    iex> Localize.Chars.to_string(1234.5, locale: :en)
    {:ok, "1,234.5"}

    iex> Localize.Chars.to_string(~D[2025-07-10], locale: :en)
    {:ok, "Jul 10, 2025"}

    iex> {:ok, unit} = Localize.Unit.new(42, "kilometer")
    iex> Localize.Chars.to_string(unit, format: :short, locale: :en)
    {:ok, "42 km"}

## Built-in implementations

| Type | Delegates to |
|---|---|
| `Integer` | `Localize.Number.to_string/2` |
| `Float` | `Localize.Number.to_string/2` |
| `Decimal` | `Localize.Number.to_string/2` |
| `Date` | `Localize.Date.to_string/2` |
| `Time` | `Localize.Time.to_string/2` |
| `DateTime` | `Localize.DateTime.to_string/2` |
| `NaiveDateTime` | `Localize.DateTime.to_string/2` |
| `Range` | `Localize.Number.to_range_string/2` |
| `BitString` | identity (returns the string unchanged) |
| `List` | `Localize.List.to_string/2` |
| `Localize.Unit` | `Localize.Unit.to_string/2` |
| `Localize.Duration` | `Localize.Duration.to_string/2` |
| `Localize.LanguageTag` | `Localize.Locale.LocaleDisplay.display_name/2` |
| `Localize.Currency` | `Localize.Currency.display_name/2` |

Types without a Localize-specific implementation fall through to
`Kernel.to_string/1`, which dispatches via `String.Chars`. This
mirrors the relationship between `Localize.Chars` and
`String.Chars`: where Localize knows how to format the type
in a locale-aware way it does so; otherwise it returns
`{:ok, Kernel.to_string(value)}`. Types with **no** `String.Chars`
implementation either (tuples, maps without an explicit impl,
PIDs, references, anonymous functions) raise the same
`Protocol.UndefinedError` they would from `Kernel.to_string/1`
— `Localize.Chars` does not invent a representation for them.

## Caveats

* The `List` implementation formats the list as a locale-aware
  conjunction (`"a, b, and c"`) by delegating to
  `Localize.List.to_string/2`, which itself recursively formats
  each element via `Localize.to_string/2` so dates, numbers,
  units, etc. inside a list pick up the outer locale and other
  options (e.g. `currency: :USD`). **Charlists** are special-cased:
  a printable list of integer codepoints (`~c"hello"`) is
  converted via `Kernel.to_string/1` rather than being joined
  digit-by-digit, mirroring how `String.Chars`'s `List` impl
  handles them.

* The `Localize.LanguageTag` implementation produces the
  **localized display name** ("English (United States)"), not
  the canonical BCP-47 string. The canonical form is still
  available via `Kernel.to_string/1`, which uses the internal
  `String.Chars` protocol.

## Adding implementations for your own types

Implement the protocol for any struct you want to support
through `Localize.to_string/1` and `Localize.to_string/2`:

    defimpl Localize.Chars, for: MyApp.Money do
      def to_string(money), do: Localize.Chars.to_string(money, [])

      def to_string(%MyApp.Money{amount: amount, currency: currency}, options) do
        options = Keyword.put_new(options, :currency, currency)
        Localize.Number.to_string(amount, options)
      end
    end

After this, `Localize.to_string(%MyApp.Money{...}, locale: :de)`
works exactly like the built-in implementations.

# `t`

```elixir
@type t() :: term()
```

All the types that implement this protocol.

# `to_string`

```elixir
@spec to_string(t()) :: {:ok, String.t()} | {:error, Exception.t()}
```

Formats `value` as a localized string with default options.

Equivalent to `to_string(value, [])`.

### Returns

* `{:ok, formatted_string}` on success.

* `{:error, exception}` if formatting fails. The exception is a
  struct (e.g. `%Localize.UnknownLocaleError{}`).

# `to_string`

```elixir
@spec to_string(t(), Keyword.t()) :: {:ok, String.t()} | {:error, Exception.t()}
```

Formats `value` as a localized string with the given options.

Each implementation accepts the option set of its underlying
formatter. Every implementation accepts at least `:locale`.

### Arguments

* `value` is any term that has a `Localize.Chars` implementation.

* `options` is a keyword list of options forwarded to the
  underlying formatter.

### Returns

* `{:ok, formatted_string}` on success.

* `{:error, exception}` if formatting fails.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
