Translator engine & progress (design)
This document aligns how translation runs in i18nprune: provider abstraction, shared leaf pipeline (translateLeaf), stderr progress + SIGINT, and how generate / fill behave with flags, errors, and non-interactive runs.
Live progress behaviour and flags are documented in Translation progress.
1. Layers (today)
| Layer | Role | Main paths |
|---|---|---|
| Provider | HTTP/API: translate(text, sourceLang, targetLang) → Promise<string> | packages/cli/src/providers/google/, createTranslator() in packages/cli/src/core/translator/init.ts |
| Leaf pipeline | Placeholders: mask → translate → restore → validate; 3 retries with backoff | packages/cli/src/core/translator/index.ts (translateLeaf) |
| Types | Translator, TranslateRequest (request shape for future use) | packages/cli/src/types/core/translator/index.ts |
| Progress | TTY stderr multi-line or single-line tick / done / fail; no-op when JSON / quiet / silent | packages/cli/src/core/progress/index.ts, translation.ts, session.ts |
| Session | SIGINT → exit 130, stdin discard during progress | packages/cli/src/core/progress/session.ts (createSessionProgress) |
| Logger policy | canPrintProgress, canPrintInfo, … | packages/cli/src/utils/logger/policy.ts |
Rule: Anything that draws live progress must respect canPrintProgress(run) (and no live progress on run.json).
2. Target shape (central “engine”)
The codebase already has a single translateLeaf entry point. The next tightening steps (not all done yet):
- Stable errors — Map provider failures (
fetch, 4xx/5xx, malformed JSON) toI18nPruneError(or a smallTranslatorErrorsubclass) withcode/cause, sogenerate/fillcan surface one consistent pattern (retry exhausted → message + optional hint). - Edge cases — Empty string, overlong text, rate limits: classify in one place (provider or thin wrapper), not in each command.
- Optional
@types— Keeppackages/cli/src/types/core/translatoras the single contract; addTranslatorResult/TranslatorFailureonly if we need discriminated unions for--jsonsummaries. - Future providers —
createTranslator()(or env-based factory) chooses implementation; commands never importgoogledirectly.
3. Progress (implementation summary)
- Stream: stderr only, so
stdoutcan emit a single JSON document when--jsonis on. - TTY: Multi-line block (bar + key path + timing) redrawn in place; non-TTY stderr uses a single updating line.
- Hidden when:
--json,-q,-s, or policy says no progress — see progress.
4. Data flow: generate
- Argv → global
RunOptions+mergeGenerateOptionsFromEnv. --lang— required if prompts are skipped (canPromptGenerate); else catalog validation + meta defaults.- Loop — preserve / parity / dry-run / translate branches;
session.progress.tickfor each leaf. - Writes —
writeJsonFiletarget + optional.meta.json. - Failure —
translateLeafthrows → command exits viareportCliError(ensuresession.fail()on error paths).
5. Data flow: fill
--lang— prompt or flag; catalog validation; not the source locale.- Source vs target — only leaves where target value still equals source are re-translated (respect parity policy).
- Progress — same
createSessionProgresscontract asgenerate. - Failure — same as
generate: translate errors → non-zero exit.
6. See also
- Translation progress
- Command behaviors index
- Exit codes & behavior
- JSON mode & long commands
- Roadmap — reporting (
--report-file).