# `PhoenixKitCatalogue.Workers.TranslationSweepWorker`
[🔗](https://github.com/BeamLabEU/phoenix_kit_catalogue/blob/v0.44.2/lib/phoenix_kit_catalogue/workers/translation_sweep_worker.ex#L1)

Opt-in, self-rescheduling Oban worker that tops up catalogue AI
translations: on every tick it enqueues one `PhoenixKitAI.TranslateWorker`
job per (resource, target language) pair currently `:missing` or
`:stale` — `:unknown` is never picked up automatically (design source
doc §4.1: an operator decides that pair's fate explicitly).

Settings (`PhoenixKitCatalogue.Web.Settings`, Task 4):

  * `catalogue_translation_sweep_enabled` — off by default; a disabled
    tick still reschedules its successor, it just does no work.
  * `catalogue_translation_sweep_interval_minutes` — gap to the next tick.
  * `catalogue_translation_sweep_langs` — target languages to consider.
  * `catalogue_translation_sweep_max_per_run` — enqueue cap for one tick,
    counted in jobs (across every resource type) not resources.

## Self-rescheduling

The very first action of `perform/1` is scheduling the NEXT tick —
before any of the tick's own work runs — so a crashed or slow tick
never breaks the chain. Uniqueness (`period: :infinity`, `states:
[:available, :scheduled]`) keeps at most one pending tick in the
queue at a time; the explicit state list (rather than Oban's default,
which references `:suspended`) sidesteps the `22P02` landmine
`PhoenixKitAI.Translations` and `PhoenixKitCatalogue.Workers.PdfExtractor`
already document for hosts whose `oban_job_state` enum predates that
value. `max_attempts: 1` — retrying a missed tick is pointless, the
next one is already scheduled.

`ensure_scheduled/0` seeds the very first tick at boot (see
`PhoenixKitCatalogue.children/0`) and is safe to call again any time
(e.g. a future settings save) — the same uniqueness collapses repeat
calls to the one pending job.

# `endpoint_and_prompts`

```elixir
@spec endpoint_and_prompts() ::
  {:ok, String.t(), %{required(String.t()) =&gt; String.t()}} | :unavailable
```

Resolves the AI endpoint and per-resource-type prompt uuids, the same way
every sweep tick does: `Translations.available?/0` ALONE isn't enough (it
doesn't verify the configured default endpoint still exists/is enabled) —
the double check the design source calls for (§4.3 step 2).

Public so `Web.TranslationsLive`'s manual "Translate" / bulk actions share
this exact resolution path with the automatic sweep tick, rather than
re-deriving which prompt belongs to which resource type a second time.

# `ensure_scheduled`

```elixir
@spec ensure_scheduled() :: {:ok, Oban.Job.t()} | {:error, term()}
```

Ensures exactly one sweep tick is available/scheduled. Called from the
boot-time task registered in `children/0`; concurrent callers (boot,
a future settings save, the tick itself) all collapse onto the same
unique row.

# `ensure_scheduled_if_enabled`

```elixir
@spec ensure_scheduled_if_enabled() ::
  {:ok, Oban.Job.t()} | {:error, term()} | :skipped
```

Boot-time gate for `ensure_scheduled/0`: seeds the chain only if the
sweep is already enabled, so a host that has never turned it on gets no
ticking job row. `Web.Settings.update_sweep_enabled/1` calls
`ensure_scheduled/0` unconditionally when it flips the setting to
`true`, so the chain always starts exactly when a host first opts in —
at boot (already enabled from a previous save) or from that save
itself.

# `resource_type_for`

```elixir
@spec resource_type_for(:item | :category | :set_label | :set_value) :: String.t()
```

The `ai_translatables/0` resource_type string for a `TranslationStatus.list/2` type atom.

---

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