# `MishkaCmsCore.Runtime.Translation`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/resources/translation.ex#L5)

A word a runtime template asks for with `t/1`, in one language, for one site.

A component, a page or a layout written in the builder is a database row compiled at runtime, so
the build-time Gettext pipeline cannot reach it: `mix gettext.extract` reads files, and a `.po`
only takes effect in a rebuilt backend. This is the runtime counterpart — a row per
`{site, key, language}`, cached in ETS by `MishkaCmsCore.Runtime.States.TranslationsState`, read by
`MishkaCmsCore.Runtime.Translations.translate/2` and edited in the admin with no deploy.

## Stored per language, listed per key

A row is one key in one language, edited with a plain form. `value` is `nil` until somebody writes
it, and `t/1` renders the key itself until then — the key is the source text, so an untranslated
word reads as English, never blank.

The admin list shows one row per key, the way the page list shows one per page: a chip per
language, translated or missing, and a modal with every language to fill in. Searching or
filtering by language still lists a word once — see `Translations.one_row_per_key/2`.

## Multitenant like `MishkaCmsCore.Runtime.Setting`

`global? true`: a row with `site_id` nil is a translation every site gets, a row with a `site_id`
is that site's own and wins over the global one. A site admin reads their own rows and the global
ones, and writes only their own.

## Where rows come from

`MishkaCmsCore.Runtime.Resources.Changes.RegisterTranslationKeys` registers the keys a template
asks for whenever a component, page or layout is saved, one row per language the site publishes
in. `:register` never touches a value, so saving a template again cannot erase a translation.

# `t`

```elixir
@type t() :: %MishkaCmsCore.Runtime.Translation{
  __lateral_join_source__: term(),
  __meta__: term(),
  __metadata__: term(),
  __order__: term(),
  aggregates: term(),
  calculations: term(),
  created_at: term(),
  id: term(),
  key: term(),
  language: term(),
  languages: term(),
  site: term(),
  site_id: term(),
  updated_at: term(),
  used: term(),
  value: term()
}
```

# `__guarded_change__`

Apply the full GuardedStruct pipeline (sanitize → validate → derive →
main_validator) to `attrs` and return either `{:ok, transformed_attrs}`
or `{:error, errors}`. Wire this into an `Ash.Resource.Change` to plug
guardedstruct rules into Ash's changeset pipeline.

The function is named `__guarded_change__` because it does more than
validate — it can also transform values (trim, downcase, slugify,
auto-fill, etc.) before they reach the data layer.

# `__guarded_field_meta__`

O(1) lookup of a field's compile-time metadata by name. Returns
`nil` for unknown fields.

# `__guarded_field_name_set__`

Compile-time-baked `MapSet` of every field name owned by the
`guardedstruct` block. Used by `GuardedStruct.AshResource.Change`
to decide, in O(1), whether a key in `changeset.atomics`
belongs to the pipeline.

# `default_short_name`

# `input`

```elixir
@spec input(values :: map() | Keyword.t()) :: map() | no_return()
```

Validates that the keys in the provided input are valid for at least one action on the resource.

Raises a KeyError error at compile time if not. This exists because generally a struct should only ever
be created by Ash as a result of a successful action. You should not be creating records manually in code,
e.g `%MyResource{value: 1, value: 2}`. Generally that is fine, but often with embedded resources it is nice
to be able to validate the keys that are being provided, e.g

```elixir
Resource
|> Ash.Changeset.for_create(:create, %{embedded: EmbeddedResource.input(foo: 1, bar: 2)})
|> Ash.create()
```

# `input`

```elixir
@spec input(values :: map() | Keyword.t(), action :: atom()) :: map() | no_return()
```

Same as `input/1`, except restricts the keys to values accepted by the action provided.

# `primary_key_matches?`

---

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