--- name: docs-i18n-translate description: >- Translate ComfyUI Mintlify docs from English MDX to ja/zh/ko using translate-i18n.ts. Incremental hash sync, chunked long pages, changelog update_blocks, glossary terms. Use when translating docs, updating zh/ja/ko changelog or pages, running pnpm translate, translationSourceHash, glossary sync, docs.json i18n, or fixing truncated translations. --- # Docs i18n Translation Translate **Mintlify docs** (not CMS). English is source of truth; ja / zh / ko are generated under `{lang}/` and `snippets/{lang}/`. **Separate from CMS:** `pnpm cms:prepare` writes gitignored `.github/scripts/cms/staging/` for Strapi. See skill `cms-changelog-sync`. ## Architecture ``` index.mdx, changelog/index.mdx, … ← English (edit here) │ ▼ pnpm translate ← MDX only (does NOT touch docs.json) {ja,zh,ko}/… ← translated MDX (commit to git) snippets/{ja,zh,ko}/… │ ▼ only if EN nav changed pnpm translate:sync-docs-json ← mirror nav paths in docs.json (opt-in) ``` Incremental: each file stores `translationSourceHash` in frontmatter. Unchanged English → skip. ### Code and comments inside fenced blocks Code lines (identifiers, keywords, string literals, numeric values, indentation, blank lines, the language tag, the closing fence) stay byte-for-byte identical to the English source. The **comment text** inside a fenced block is translated: it is documentation prose the reader is meant to understand, so whole-line comments and trailing comments after code are localized, on the same line and position as in English. Python docstrings (a standalone triple-quoted string that opens a `def`, `class` or module) count as documentation, so their text is translated too; a triple-quoted string used as a value inside code stays code. Boundary rules: Python-style `#` and `//` open a comment outside a string or regex literal, so `value=1# note` counts as a comment; shell-style `#` and `--` need a word boundary, so a CLI flag such as `--deployment` stays code. C-style block comments are tracked across lines, so a generator method starting with `*` stays code. Comment markers inside quoted strings or JavaScript regex literals and multiline template literals stay code. A docstring is only a standalone triple-quoted string that opens a suite, not a triple-quoted value inside an expression or conditional. A line with other executable code stays byte-identical. Opening and closing fence lines stay byte-identical too. - Never translate a shebang (`#!...`), a string literal used as a value, a variable name or any code token. - `validateTranslatedBlock` compares code via `codeBlocksMatch()`, which strips comments per the fence's language tag. A translated comment passes; a changed, dropped or commented-out code line still fails and the block is retried. - When editing a translation by hand, translate its comments and docstrings too. ### Values, headings and punctuation Values the caller sends are not prose. In code blocks **and in prose labels** they stay byte-for-byte identical to the English source: - booleans `true` / `false`, enums such as `auto`, `disabled`, `standard`, `fast`, `mp4`, `mov`, JSON keys, model ids, endpoint paths - the label punctuation and its own line: `true:` stays `true:`, `standard =` stays `standard =`, and every labelled item keeps its own line (`mp4:` must not be glued to the sentence above it) - only the explanation after the label is translated: `true: Returns the last frame` becomes `true: 最終フレームを返します`, never `真:…` Headings: translate the heading text the way the target language's pages do (ja スキーマ / 入力 / 出力, ko 스키마 / 입력 / 출력, zh 输入 / 输出), and keep any `{#anchor}` exactly as the English source has it. Chinese model pages conventionally keep `## Schema` in English, so leave that heading alone for zh. Other rules that the reviews keep flagging: - Chinese prose uses full-width punctuation (,。:;()), not ASCII commas or colons. - Terminology follows the glossary (`glossary.mjs` and the per-language overrides) and stays consistent inside a file; no invented words (fixed is 固定, not 顶固). Keep senses apart: an English `link` pointing at a URL or a document is a 链接, a link between nodes in a graph (`LLink`, node connections, canvas wiring) is a 连线. - Never reverse the polarity of a sentence: `so it applies here` must not become `so it does not apply here`, and a limit that "never adjudicates a real prompt" is not an instruction to configure it. ### Title / description frontmatter (localized pages) `title` and `description` frontmatter carry localized meaning, not word-for-word translation. Localized titles keep the official product name untranslated; descriptions convey the same scope as EN within 40-160 chars. When an EN page's title/description changes in this repo, the zh/ja/ko values are updated in the same commit. Rules and examples: [.cursor/rules/docs-frontmatter.mdc](../../rules/docs-frontmatter.mdc). ## Environment (`.env.local`) | Variable | Purpose | |----------|---------| | `TRANSLATE_API_KEY` | Primary API key | | `TRANSLATE_API_BASE_URL` | OpenAI-compatible endpoint | | `TRANSLATE_API_MODEL` | e.g. `deepseek-v4-pro`, `qwen-mt-plus` | | `TRANSLATE_CONCURRENCY` | Parallel requests (default 5) | | `FRONTEND_LOCALES_URL` | Optional; override remote locale URL for glossary sync | | `FRONTEND_LOCALES_PATH` | Optional; use a local frontend checkout instead of remote | Requires **Bun**. ## Commands | Command | Action | |---------|--------| | `pnpm translate` | Pending pages + snippets, all languages | | `pnpm translate:dry-run` | Preview pending work | | `pnpm translate:force` | Re-translate everything | | `pnpm translate -- --lang zh,ja` | Specific languages | | `pnpm translate -- path/to/page.mdx` | Specific file(s) | | `pnpm translate:snippets` | Snippets only | | `pnpm translate -- --pages-only` | Skip snippets | | `pnpm translate:check-truncation` | Scan for truncated output | | `pnpm translate:repair-fences` | Append missing closing ``` (no API) | | `pnpm translate:repair-truncated -- --lang ko` | Re-translate flagged files | | `pnpm translate:sync-hash` | Refresh hashes after manual zh/ja/ko edits (no API) | | `pnpm translate -- --with-docs-json` | Translate then sync `docs.json` nav (opt-in) | | `pnpm translate:sync-docs-json` | Sync `docs.json` nav paths only (labels preserved) | | `pnpm translate:sync-docs-json -- --translate-nav-labels` | Also translate new EN nav labels | | `pnpm glossary:sync` | Rebuild glossary from ComfyUI frontend | | `pnpm glossary:sync:dry-run` | Preview glossary sync | Logs (gitignored): `.github/i18n-logs/translate/` ## Standard workflow ### After editing English MDX ```bash pnpm translate:dry-run # see pending pnpm translate -- changelog/index.mdx # or specific paths pnpm translate:check-truncation # if long page / changelog ``` ### Small English edits (manual translation) When only a line or paragraph changed: ```bash # 1. Edit English + update zh/ja/ko by hand (or ask Cursor to patch matching sections) # 2. Sync hashes so translate skips the file pnpm translate:sync-hash -- path/to/page.mdx pnpm translate:sync-hash -- --verify path/to/page.mdx # optional sanity check ``` For larger or new sections, use `pnpm translate -- path/to/page.mdx` (chunked pages only re-translate changed `##` sections when `auto_chunk` applies). ### Changelog (`changelog/index.mdx`) - Strategy: `update_blocks` (configured in `translation-config.json`) - Only **new or changed** `` blocks are translated (by label + `translationBlockHashes`) - Dates in `description` are localized automatically (ja/zh/ko formats) - Block hashes stored in `translationBlockHashes` frontmatter **Omit from English changelog** when triaging ComfyUI commits — these are **[ComfyUI-WIKI](https://github.com/Comfy-Org/ComfyUI-WIKI)** sync PRs, not core product features: | Skip | Typical pattern | |------|-----------------| | Embedded docs | `update embedded docs to v…`, `comfyui-embedded-docs` bump | | Workflow templates | `update workflow templates to v…`, `comfyui-workflow-templates` bump | | Model blueprints | `Add new model blueprints`, template-library starter workflows | Do not add bullets for dependency-only version bumps. See also **`cms-changelog-sync`** for CMS popup rules. **Docs changelog bullet URLs** (same as local CMS): matching [blog.comfy.org](https://blog.comfy.org/) post first, then the GitHub PR, then the ComfyUI repo commit/tag/compare. Do not use Cloud `?template=` links on the docs changelog. Cloud popup URLs are a separate rule in **cms-changelog-sync**. ```bash pnpm translate -- changelog/index.mdx pnpm translate -- changelog/index.mdx --lang zh ``` ### Long pages (truncation risk) | Strategy | When | Config | |----------|------|--------| | `heading_sections` | Long reference pages | `chunked_files` or `auto_chunk` (≥3k chars, ≥2 `##`) | | `update_blocks` | Changelog | `chunked_files` entry for `changelog/index.mdx` | Oversized individual `##` blocks (e.g. many Mintlify Tabs) are sub-chunked when they exceed `auto_chunk.max_block_chars` (default 6000): Tabs → `###` → fence-safe size splits. Invalid/truncated blocks stay pending (hash not updated). Checkpoints per block — safe to resume after interrupt. ```bash pnpm translate -- tutorials/partner-nodes/pricing.mdx --lang ko pnpm translate:check-truncation -- --lang ko pnpm translate:repair-truncated -- --lang ko ``` ## Terminology (glossary) Three layers — see `.github/scripts/i18n/README.md` for detail: | Layer | File / config | Effect | |-------|---------------|--------| | `preserve_terms` | `translation-config.json` | Keep English (LoRA, checkpoint, …) | | `glossary/frontend/{lang}.json` | Machine-synced | Mirror ComfyUI frontend | | `glossary/overrides/{lang}.json` | Hand-edited | Corrections; wins over frontend | ```bash pnpm glossary:sync # after frontend locale updates # Edit overrides/{lang}.json for term decisions # Edit preserve_terms for English-only terms ``` **Never hand-edit** `glossary/frontend/` — run `glossary:sync`. ## Skipped paths `translation-config.json` → `skip_paths`: e.g. `built-in-nodes` (not auto-translated). ## Agent checklist When user updates English docs and needs translations: - [ ] Identify changed files (or run `pnpm translate:dry-run`) - [ ] For small edits: hand-update translations, then `pnpm translate:sync-hash -- ` - [ ] For larger edits: run `pnpm translate` for affected paths — **not** `cms:prepare` unless CMS/Strapi - [ ] For changelog, translate **docs** `zh/changelog/` etc., not CMS staging - [ ] After long pages, run `pnpm translate:check-truncation` - [ ] Commit translated MDX + updated `translationSourceHash` / `translationBlockHashes` - [ ] Do not commit `.github/i18n-logs/` - [ ] Do **not** expect `pnpm translate` to edit `docs.json`; if EN nav structure changed, run `pnpm translate:sync-docs-json` (or `--with-docs-json`) separately - [ ] Optional quality pass: skill `docs-i18n-review` ## Key files | Path | Role | |------|------| | `.github/scripts/i18n/translate-i18n.ts` | Entry point | | `.github/scripts/i18n/chunked-translate.ts` | Block splitting/reassembly | | `.github/scripts/i18n/sync-hash-i18n.ts` | Hash-only sync after manual edits | | `.github/scripts/i18n/translation-config.json` | Languages, skip paths, chunked files | | `.github/scripts/i18n/glossary.mjs` | Term injection | | `.github/scripts/i18n/README.md` | Full reference | | `.github/workflows/i18n-sync-check.yml` | PR reminder for missing translations | ## Troubleshooting | Issue | Fix | |-------|-----| | File skipped | English hash unchanged — use `pnpm translate:force` or edit EN source | | Manual translation done | `pnpm translate:sync-hash -- ` to refresh hashes | | Truncated translation | `translate:repair-truncated` or add to `chunked_files` | | Missing closing ``` only | `translate:repair-fences` (structural); re-translate if code inside block was cut | | Wrong term | `glossary/overrides/{lang}.json` or `preserve_terms` | | PR i18n comment | Run `pnpm translate` for listed files | | Changelog date still English | Re-run translate for that block; dates derived from EN | ## Docs vs CMS translation | | Docs (`pnpm translate`) | CMS (`pnpm cms:prepare`) | |--|-------------------------|---------------------------| | Output | `{lang}/changelog/index.mdx` | `staging/{lang}/…` (gitignored) | | English source | Full docs changelog | LLM-simplified staging EN | | Purpose | Mintlify site | Strapi in-app popup | | Commit | Yes | No (staging gitignored) |