Documentation i18n Sync (EN ⇄ VI)
LumiBase ships docs in two locales: docs/en (canonical) and docs/vi. The
i18n sync tooling under scripts/docs-i18n/ keeps them aligned, detects when a
file is written in the wrong language, translates stale or missing targets, and
stamps every managed file with a version and update timestamp.
Translation is performed by Claude (the Anthropic Messages API) — there are no
third-party machine-translation services. Translation runs in CI where the
ANTHROPIC_API_KEY lives as a secret; locally you can run detection (and the
no-loss preservation step) without any key.
What it does
- Language detection. A heuristic detector (
lang-detect.mjs) classifies each doc asviorenfrom its prose, ignoring code blocks, links and front matter. It is tuned for the vi/en pair only. - Source-of-truth resolution. Per file pair the tooling decides which side
is authored content and which is a translation. A file under
docs/enthat actually contains Vietnamese is treated as Vietnamese-authored. - No-loss preservation. When an
enfile holds Vietnamese and there is nodocs/vicounterpart, that Vietnamese content is copied intodocs/vibefore anything translates over theenfile. When a Vietnameseenfile conflicts with an existing, differentdocs/vifile, the tooling does not overwrite anything — it copies the at-risk content todocs/.i18n/preserved/and flags a conflict for manual review. - Machine translation. Stale or missing targets are translated with markdown-safe placeholder protection (code, links, inline code and HTML are never sent to the translator).
- Versioning. Each written file gets front matter:
version,lastUpdated,sourceLang,translatedFrom,sourceHash,mtEngine, andsyncStatus(machine-translatedorneeds-review). Re-translation only happens when the sourcesourceHashchanges, so output is stable. - Audit trail. Every run appends to
docs/i18n-sync-log.mdand writes a machine-readabledocs/.i18n/last-report.json.
Running it
# Detect only — classify files, write log + report, change no docs.
pnpm docs:i18n:detect
# Preserve at-risk Vietnamese content + stamp versions, but do not translate
# (safe to run without an API key).
pnpm docs:i18n:preserve
# Full sync: preserve + translate with Claude + version. Needs ANTHROPIC_API_KEY.
ANTHROPIC_API_KEY=*** pnpm docs:i18n:sync
Environment variables:
| Variable | Purpose | Default |
|---|---|---|
ANTHROPIC_API_KEY | Anthropic API key (required to translate) | — |
ANTHROPIC_MODEL | Claude model id | claude-sonnet-4-6 |
ANTHROPIC_BASE_URL | API base URL (proxy override) | https://api.anthropic.com |
ANTHROPIC_MAX_TOKENS | Max output tokens per request | 8192 |
DOCS_ROOT | Override docs root (testing/CI) | repo docs/ |
Without an API key, --apply automatically degrades to preservation +
versioning and records why in the log.
CI
.github/workflows/docs-i18n-sync.yml runs on changes under docs/** or
scripts/docs-i18n/**:
- Pull requests: detect-only. Uploads the report as an artifact; writes nothing.
- Push to
main: full sync, then commits the result back.
Configure ANTHROPIC_API_KEY as a repository secret for translation to run.
Front-matter fields
| Field | Meaning |
|---|---|
version | Integer, bumped when content changes |
lastUpdated | ISO-8601 timestamp of the last write |
sourceLang | Authored language of this file |
translatedFrom | Source locale a translated file came from |
sourceHash | Hash of the source body the translation was built from |
contentHash | Hash of an authored file's own body (change detection) |
mtEngine | Engine used (claude) |
syncStatus | machine-translated or needs-review |
The docs viewer reads lastUpdated for the displayed "last modified" date,
falling back to filesystem mtime when the field is absent.
Limitations
- Detection is heuristic, not a statistical language model; borderline files
(mostly English with a few Vietnamese terms) may need a manual
sourceLangstamp. - The default authoring direction for an in-sync pair is
en → vi. Files authored in Vietnamese underdocs/enare detected and handled, but routine pairs assume English is canonical. - Translations are produced by Claude and
needs-reviewoutput should be proofread before release. - Each document is translated in a single request; very long docs may hit the
ANTHROPIC_MAX_TOKENSlimit and need a higher value (the run fails loudly rather than writing a truncated file).