Translator engine & progress (design)
This document aligns how translation runs in i18nprune: provider abstraction, shared leaf pipeline (translateLeaf), stderr progress + SIGINT, and how generate (including --resume) behaves with flags, errors, and non-interactive runs.
Live progress behavior and flags are documented in Translation config and CLI verbosity.
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, sogeneratecan 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 CLI verbosity.
4. Data flow: generate
- Argv → global
RunOptions+mergeGenerateOptionsFromEnv. --lang— required if prompts are skipped (canPromptGenerate); else catalog validation.- Loop — preserve / parity / dry-run / translate branches;
session.progress.tickfor each leaf. - Writes —
writeLocaleJsonFromContextSyncfor<target>.jsononly (no locale sidecar files). - Failure —
translateLeafthrows → command exits viareportCliError(ensuresession.fail()on error paths).
5. Data flow: generate --resume
--resume+--target/--all— same catalog rules as fullgenerate; non-interactive runs require an explicit selector (or--allwith--resume).- Eligibility — only review-eligible leaves that still match the source string at the same path are translated (respect
policies.parity,reference, and preserve rules). - Progress — same
createSessionProgresscontract as fullgenerate. - Failure — same as
generate: translate errors → non-zero exit; partial runs may setpartial/resumeHinton thegenerateJSON envelope (see Translation config).
6. See also
- Translation config
- CLI prompts
- CLI verbosity
- JSON output (
--json) - Release notes — CLI/Core release history.