# `PhoenixKit.Utils.RecipientLocale`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v2.21.1/lib/phoenix_kit/utils/recipient_locale.ex#L1)

Resolves which locale a message should be rendered in for a given recipient.

Outbound messages (email, and the external notification channels) render on
a background or request process that has no meaningful Gettext locale of its
own — the sender's locale is not the recipient's. This module is the single
answer to "whose language is this message in".

## Where the preference lives

A user's dialect preference is stored full ("en-GB", "pt-BR") in
`custom_fields["preferred_locale"]`, written by
`PhoenixKit.Users.Auth.update_user_locale_preference/2` from the language
switcher. Absent means the user never chose one.

## Two consumers, two shapes

- `for_rendering/1` — for **template lookup**. Returns the full dialect and
  never `nil`, falling back to the site's content language and then `"en"`.
  Template resolution does its own dialect→base narrowing ("en-GB" is tried
  before "en"), so handing it the dialect strictly beats pre-truncating.
- `base/1` — for **Gettext**, which keys on base codes and treats `nil` as
  "leave the current locale alone".

## Installing it

`in_locale/2` runs a function with a locale installed on the process, which is
how a Gettext-backed default reaches the right language: these render on a
background worker or on behalf of another user, so the locale arrives as a
value rather than being ambient. `nil` means "leave the current locale alone"
— what a screen rendering for its own viewer wants.

## Failure

`for_rendering/1` is total. The site default is read through
`PhoenixKit.Settings`, which can raise on an unowned checkout *or exit* on a
dead pool; both are caught and answered with `"en"`. An unreachable database
must degrade a message to English, never fail the send — this is the path
`PhoenixKit.Users.LoginAlerts` calls during sign-in.

# `base`

```elixir
@spec base(term()) :: String.t() | nil
```

The recipient's preference as a base language code, or `nil`.

For Gettext, which keys on base codes; `nil` means "no preference, leave the
current locale alone".

    iex> alias PhoenixKit.Utils.RecipientLocale
    iex> RecipientLocale.base(%{custom_fields: %{"preferred_locale" => "pt-BR"}})
    "pt"

# `for_rendering`

```elixir
@spec for_rendering(term()) :: String.t()
```

The locale to render a template in for `recipient`. Never `nil`.

Preference → the site's content language → `"en"`.

    iex> alias PhoenixKit.Utils.RecipientLocale
    iex> RecipientLocale.for_rendering(%{custom_fields: %{"preferred_locale" => "uk"}})
    "uk"

# `in_locale`

```elixir
@spec in_locale(String.t() | nil, (-&gt; result)) :: result when result: term()
```

Runs `fun` with `locale` installed on the process, restoring it afterwards.

A `nil` locale runs `fun` untouched, so a caller with no recipient preference
keeps whatever locale is already in force.

    iex> alias PhoenixKit.Utils.RecipientLocale
    iex> RecipientLocale.in_locale(nil, fn -> :ran end)
    :ran

# `preferred`

```elixir
@spec preferred(term()) :: String.t() | nil
```

The recipient's stored dialect preference, or `nil` when they have none.

Accepts anything: a `%User{}`, a plain map, or a bare email string (used by
the magic-link registration path, where no account exists yet).

    iex> alias PhoenixKit.Utils.RecipientLocale
    iex> RecipientLocale.preferred(%{custom_fields: %{"preferred_locale" => "en-GB"}})
    "en-GB"
    iex> RecipientLocale.preferred("someone@example.com")
    nil

---

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