Validation & quality

# Validators

What lingo validate checks — each validator is config-gated and maps to a violation type.

## Config-gated checks

`validate` runs the validators you enable under `validators:` and reports their violations. Each violation has a *type*; the `strict` tiers decide which types make the command exit non-zero (see the [Configuration reference](https://locallingo.zoolutions.llc/docs/configuration-reference)).

| Validator | Type | Default |
| --- | --- | --- |
| Missing | `missing` | on |
| Outdated | `outdated` | on |
| Duplicate values | `duplicate_value` | off |
| Manual edits | `manual_edit` | off |

## Missing

Reports keys present in the source locale but absent from a target locale — the untranslated ones. The fix is a `lingo translate`.

## Outdated

Reports target keys whose recorded source hash no longer matches the current English value — i.e. the source text changed after the translation was made, so the translation may no longer be accurate. See [Drift & state](https://locallingo.zoolutions.llc/docs/drift-state) for how this is tracked. The fix is `lingo translate` (or `--force-key` for a single key).

## Duplicate values

Flags a source-locale key whose value matches an `activerecord.attributes.*` value — a sign you're re-declaring a label Rails already provides. The AR key wins; the offending key should reuse it. Runs on the source locale only, to avoid false positives from grammatical differences in other locales. Off by default; enable it and add `duplicate_value` to your `strict_all` tier for a stricter CI gate.

## Manual edits

When enabled, locallingo records a `target_hash` alongside each key's source hash. If a target value's current hash no longer matches — someone hand-edited it — this validator surfaces it so the next `translate` doesn't silently overwrite the edit. Confirm the edit with `lingo accept-edits` to protect it. See [Drift & state](https://locallingo.zoolutions.llc/docs/drift-state).