Localize.Chars protocol (Localize v0.5.0)

Copy Markdown View Source

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

TypeDelegates to
IntegerLocalize.Number.to_string/2
FloatLocalize.Number.to_string/2
DecimalLocalize.Number.to_string/2
DateLocalize.Date.to_string/2
TimeLocalize.Time.to_string/2
DateTimeLocalize.DateTime.to_string/2
NaiveDateTimeLocalize.DateTime.to_string/2
RangeLocalize.Number.to_range_string/2
BitStringidentity (returns the string unchanged)
ListLocalize.List.to_string/2
Localize.UnitLocalize.Unit.to_string/2
Localize.DurationLocalize.Duration.to_string/2
Localize.LanguageTagLocalize.Locale.LocaleDisplay.display_name/2
Localize.CurrencyLocalize.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/1Localize.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.

Summary

Types

t()

All the types that implement this protocol.

Functions

Formats value as a localized string with default options.

Formats value as a localized string with the given options.

Types

t()

@type t() :: term()

All the types that implement this protocol.

Functions

to_string(value)

@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(value, options)

@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.