Skip to main content

Translations

Krios tracks translation lifecycle separately from the editorial workflow. Both axes operate per-(entry, locale): one tracks "where in the editorial review is this locale?", the other tracks "where in the translation pipeline is this locale?".

TranslationStatus model

model TranslationStatus {
entryId String
locale String
status String // not-started | in-progress | in-review | approved | published
assigneeId String? // who's translating
sourceLocale String? // which locale this was translated from
sourceVersionNum Int? // the source's version when translation started
isStale Boolean // source has been updated since
notes String?
completedAt DateTime?
}

Distinct from ContentLocaleState.workflowState which handles editorial review.

Stale detection

When the site's default (source) locale is saved, every other locale's TranslationStatus is checked: if its recorded sourceVersionNum is older than the new source version, isStale = true is set. The dashboard surfaces a red badge so the translation team sees the divergence the next time they look.

Status transitions

PUT /api/v1/projects/demo/translations/{entryId}/{locale}
{
"status": "in-progress",
"assigneeId": "ckl_user_…",
"notes": "starting translation"
}

Translators clear the stale flag via:

PUT /api/v1/projects/demo/translations/{entryId}/{locale}
{ "acknowledgeStale": true }

This bumps sourceVersionNum to the current entry version, signaling "I reviewed the source diff and my work is still good".

Dashboard

/admin/projects/{slug}/translations renders a matrix:

  • One row per entry
  • One column per locale (union of every site's supportedLocales)
  • Cell shows the locale's status as a colored badge; the source locale is labeled ·src
  • Click a cell → dialog to set status / assignee / notes; stale rows show an "Acknowledge stale" button

Filters: title search, site, type, status, stale-only checkbox.

Stat cards across the top: entries + per-status counts.

Copy-from-locale

Seed a translation from another locale:

POST /api/v1/projects/demo/entries/{id}/copy-locale
{ "sourceLocale": "en-US", "targetLocale": "fr-CA" }

Copies localizable field values from source to target on the same entry. Sets the target's TranslationStatus.status to in-progress with sourceLocale and sourceVersionNum recorded so stale detection works.

The entry editor shows a "No content for {locale}. Copy from {source}?" callout when the current locale has zero localizable values and the site's default locale has values.

Per-locale workflow independence

If the content type has a workflow assigned, each locale progresses through the workflow independently — English can be Published while French is In Review. Translation status and workflow state are independent axes.

Manually mark stale

POST /api/v1/projects/demo/translations/{entryId}/{locale}/mark-stale

Useful when the translator finds a problem with the existing translation that wasn't triggered by a source change.

API

GET /api/v1/projects/{slug}/translations # dashboard list
GET /api/v1/projects/{slug}/translations/{entryId} # single-entry detail
PUT /api/v1/projects/{slug}/translations/{entryId}/{locale}
POST /api/v1/projects/{slug}/translations/{entryId}/{locale}/mark-stale
GET /api/v1/projects/{slug}/translations/stats # aggregate counts

Audit: translation.status_updated and translation.marked_stale rows on every change.