Add i18n sync-hash and repair-fences tooling#1227
Conversation
Manual translation edits can refresh hashes without re-running translate, auto-chunk applies to more pages, and unclosed ``` blocks can be fixed structurally before falling back to full re-translation.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds hash-sync and fence-repair i18n tooling, wires new npm commands, lowers auto-chunk thresholds, and updates docs plus tests for manual-edit and truncation workflows. Changesi18n hash sync and fence repair tooling
Sequence Diagram(s)sequenceDiagram
participant Developer
participant sync-hash-i18n.ts
participant Hashing logic
participant Target MDX file
Developer->>sync-hash-i18n.ts: run pnpm translate:sync-hash
sync-hash-i18n.ts->>Hashing logic: compute expected source hash
Hashing logic-->>sync-hash-i18n.ts: synced content and warnings
sync-hash-i18n.ts->>Target MDX file: compare stored hash and write updates
sequenceDiagram
participant Developer
participant repair-fences-i18n.ts
participant check-translation-truncation.ts
participant Target MDX file
Developer->>repair-fences-i18n.ts: run pnpm translate:repair-fences
repair-fences-i18n.ts->>check-translation-truncation.ts: scan for unclosed_code_fence
check-translation-truncation.ts-->>repair-fences-i18n.ts: repair details
repair-fences-i18n.ts->>Target MDX file: write repaired content or dry-run log
A hash here, a fence there—just enough plumbing to keep the translations in fair repair. 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Align file-arg filtering with translate-i18n so snippets/ paths are not processed as pages, and report only actionable fence repairs during dry-run.
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/scripts/i18n/check-translation-truncation.ts:
- Around line 139-146: The fence-repair path in check-translation-truncation.ts
is inserting an extra blank line between frontmatter and body because
parseFrontmatterAndBody already preserves the trailing newline. Update the
concatenation in the repair flow (around result.repaired / newBody) so the
rebuilt content uses frontmatter plus newBody directly, matching the original
spacing and avoiding the unnecessary diff bloat.
In @.github/scripts/i18n/repair-fences-i18n.ts:
- Around line 42-48: The readFileOr helper is swallowing every read failure and
treating it like a missing file, which hides real errors. Update readFileOr to
distinguish “file not found” from other read problems, and only return the
fallback for the missing-file case; for permissions/I/O/encoding failures,
surface the error or log it with enough context before rethrowing. Keep the
existing callers in repair-fences-i18n.ts unchanged except for handling the
non-missing failure path around readFileOr.
- Around line 122-127: The dry-run summary in the fence repair flow is counting
all fence issues as “repaired,” even though the loop in repair-fences-i18n.ts
increments skipped for empty, already balanced, or repairTargetContent-failed
cases. Update the final console.log in the summary section to compute dry-run
repaired count from only the actually fixable items, using the same issue
classification used in the main loop and its skipped checks, so the displayed
repaired and skipped totals match the real outcomes. Reference the repair loop
and the final summary block around fenceIssues, repaired, and skipped to keep
the counts consistent.
- Around line 88-99: The pre-check in the i18n repair flow is duplicating work
already handled by repairTargetContent. Remove the separate
parseFrontmatterAndBody/hasUnclosedCodeFence check and rely on
repairTargetContent(targetContent) to decide whether anything needs fixing via
its repaired result. Keep the existing logging and skipped handling in the
repair loop, using repairTargetContent and repairUnclosedCodeFence as the main
path.
In @.github/scripts/i18n/sync-hash-i18n.ts:
- Around line 251-298: The same file/lang pair is being read multiple times, so
update the sync flow to reuse preloaded contents instead of re-reading from
disk. Adjust syncOneFile to optionally accept existing enContent and
targetContent (or similar) and use those values when provided, and then pass the
cached reads from the outer processing loop into syncOneFile so the drift check
and processing path share one read per file. Keep the existing behavior in
makeMapping, computeSyncedContent, and collectVerifyWarnings unchanged while
removing the redundant readFileOr calls.
- Around line 354-359: The phase split in sync-hash-i18n.ts is classifying
snippet paths as both pages and snippets because the pageArgs filter is using a
replace-based check that does not exclude normal snippets/ paths. Update the
fileArgs partitioning logic in the phase-building block so pageArgs only
contains non-snippet files via a direct prefix test, and keep snippetArgs
reserved for paths under snippets/. Make sure the phases array is populated from
these mutually exclusive groups so each file is processed only once by the
correct target directory.
- Around line 100-112: The recursive traversal in collectMdx currently descends
into every directory, including noisy repo folders like node_modules, .git, and
dist. Update collectMdx to skip a small exclude list before recursing, and make
the list easy to reuse/configure similarly to loadI18nConfig’s exclude_dirs
contract. Keep the .mdx collection behavior the same, but prevent entering
excluded directories when walking from REPO_ROOT.
- Around line 300-322: collectEnglishFiles() is returning snippet CLI args with
the snippets/ prefix still attached, which later causes makeMapping() to resolve
a duplicated snippets path. Update the snippetsMode branch that handles fileArgs
so it normalizes each snippet argument to a path relative to the snippets root
before returning it, while preserving the existing page-file behavior. Use
collectEnglishFiles() and the snippetsMode/fileArgs handling as the place to
make this adjustment.
- Around line 92-98: readFileOr currently treats every read failure as if the
file were missing, which hides real I/O problems. Update readFileOr to only
return the fallback for the specific ENOENT case and rethrow or surface all
other errors, using the existing readFile helper and its catch block to
distinguish the error by code.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: a9f2c938-e526-445e-afc8-b0d0974bb019
📒 Files selected for processing (8)
.cursor/skills/docs-i18n-translate/SKILL.md.github/scripts/i18n/README.md.github/scripts/i18n/check-translation-truncation.ts.github/scripts/i18n/repair-fences-i18n.ts.github/scripts/i18n/repair-fences.test.ts.github/scripts/i18n/sync-hash-i18n.ts.github/scripts/i18n/translation-config.jsonpackage.json
| const { frontmatter, body } = parseFrontmatterAndBody(targetContent); | ||
| const result = repairUnclosedCodeFence(body); | ||
| if (!result.repaired) { | ||
| return { content: targetContent, repaired: false, detail: "" }; | ||
| } | ||
| const newBody = result.body; | ||
| const raw = frontmatter ? `${frontmatter}\n${newBody}` : newBody; | ||
| const content = raw.endsWith("\n") ? raw : `${raw}\n`; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Extra blank line inserted between frontmatter and body on every fence repair.
parseFrontmatterAndBody already restores the newline consumed by its \n? regex group (frontmatter: ${match[1]}\n``), so plain frontmatter + body concatenation faithfully reproduces the original spacing. Here, `${frontmatter}\n${newBody}` adds one more `\n` on top of that, so every repaired file gains a spurious extra blank line between frontmatter and content (regardless of whether the source had a blank line there or not). Harmless for MDX rendering (consecutive blank lines collapse), but it needlessly bloats the diff/committed file beyond the actual fence fix.
🔧 Proposed fix
const newBody = result.body;
- const raw = frontmatter ? `${frontmatter}\n${newBody}` : newBody;
+ const raw = frontmatter ? `${frontmatter}${newBody}` : newBody;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { frontmatter, body } = parseFrontmatterAndBody(targetContent); | |
| const result = repairUnclosedCodeFence(body); | |
| if (!result.repaired) { | |
| return { content: targetContent, repaired: false, detail: "" }; | |
| } | |
| const newBody = result.body; | |
| const raw = frontmatter ? `${frontmatter}\n${newBody}` : newBody; | |
| const content = raw.endsWith("\n") ? raw : `${raw}\n`; | |
| const { frontmatter, body } = parseFrontmatterAndBody(targetContent); | |
| const result = repairUnclosedCodeFence(body); | |
| if (!result.repaired) { | |
| return { content: targetContent, repaired: false, detail: "" }; | |
| } | |
| const newBody = result.body; | |
| const raw = frontmatter ? `${frontmatter}${newBody}` : newBody; | |
| const content = raw.endsWith("\n") ? raw : `${raw}\n`; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/scripts/i18n/check-translation-truncation.ts around lines 139 - 146,
The fence-repair path in check-translation-truncation.ts is inserting an extra
blank line between frontmatter and body because parseFrontmatterAndBody already
preserves the trailing newline. Update the concatenation in the repair flow
(around result.repaired / newBody) so the rebuilt content uses frontmatter plus
newBody directly, matching the original spacing and avoiding the unnecessary
diff bloat.
| async function readFileOr(path: string, fallback = ""): Promise<string> { | ||
| try { | ||
| return await readFile(path, "utf-8"); | ||
| } catch { | ||
| return fallback; | ||
| } | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔵 Trivial | 💤 Low value
Silent error swallowing in readFileOr may hide real failures.
Any read error (permissions, I/O failure, encoding issue), not just "file missing", falls back to an empty string, which is then silently skipped at line 83-86 as if the target simply didn't exist yet. A truly missing target vs. a broken read are treated identically with no diagnostic output.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/scripts/i18n/repair-fences-i18n.ts around lines 42 - 48, The
readFileOr helper is swallowing every read failure and treating it like a
missing file, which hides real errors. Update readFileOr to distinguish “file
not found” from other read problems, and only return the fallback for the
missing-file case; for permissions/I/O/encoding failures, surface the error or
log it with enough context before rethrowing. Keep the existing callers in
repair-fences-i18n.ts unchanged except for handling the non-missing failure path
around readFileOr.
| const { body } = parseFrontmatterAndBody(targetContent); | ||
| if (!hasUnclosedCodeFence(body)) { | ||
| console.log(` [${issue.lang}] ${issue.enRel}: already balanced, skipping`); | ||
| skipped++; | ||
| continue; | ||
| } | ||
|
|
||
| const result = repairTargetContent(targetContent); | ||
| if (!result.repaired) { | ||
| skipped++; | ||
| continue; | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Redundant fence detection before repair.
hasUnclosedCodeFence(body) (line 89) duplicates work already performed inside repairTargetContent (which internally calls repairUnclosedCodeFence, per the upstream snippet). Since repairTargetContent already returns repaired: false when there's nothing to fix, the earlier manual parse/check is unnecessary and just doubles the frontmatter/body parsing work per file. Not wrong, just gets a fence around the pun-free zone unnecessarily.
♻️ Simplify by relying on repairTargetContent's own result
- const { body } = parseFrontmatterAndBody(targetContent);
- if (!hasUnclosedCodeFence(body)) {
- console.log(` [${issue.lang}] ${issue.enRel}: already balanced, skipping`);
- skipped++;
- continue;
- }
-
const result = repairTargetContent(targetContent);
if (!result.repaired) {
+ console.log(` [${issue.lang}] ${issue.enRel}: already balanced, skipping`);
skipped++;
continue;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { body } = parseFrontmatterAndBody(targetContent); | |
| if (!hasUnclosedCodeFence(body)) { | |
| console.log(` [${issue.lang}] ${issue.enRel}: already balanced, skipping`); | |
| skipped++; | |
| continue; | |
| } | |
| const result = repairTargetContent(targetContent); | |
| if (!result.repaired) { | |
| skipped++; | |
| continue; | |
| } | |
| const result = repairTargetContent(targetContent); | |
| if (!result.repaired) { | |
| console.log(` [${issue.lang}] ${issue.enRel}: already balanced, skipping`); | |
| skipped++; | |
| continue; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/scripts/i18n/repair-fences-i18n.ts around lines 88 - 99, The
pre-check in the i18n repair flow is duplicating work already handled by
repairTargetContent. Remove the separate
parseFrontmatterAndBody/hasUnclosedCodeFence check and rely on
repairTargetContent(targetContent) to decide whether anything needs fixing via
its repaired result. Keep the existing logging and skipped handling in the
repair loop, using repairTargetContent and repairUnclosedCodeFence as the main
path.
| async function readFileOr(path: string, fallback = ""): Promise<string> { | ||
| try { | ||
| return await readFile(path, "utf-8"); | ||
| } catch { | ||
| return fallback; | ||
| } | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
readFileOr swallows all errors, not just missing files.
Any error (permission denied, EMFILE, transient I/O) is treated identically to "file doesn't exist", silently returning the fallback. This can mislead the tool into reporting a real file as "missing target" or "skipped" without surfacing the actual cause.
🛡️ Only swallow ENOENT, surface other errors
async function readFileOr(path: string, fallback = ""): Promise<string> {
try {
return await readFile(path, "utf-8");
- } catch {
+ } catch (err: unknown) {
+ const code = (err as NodeJS.ErrnoException)?.code;
+ if (code !== "ENOENT") {
+ console.error(` ⚠ failed to read ${path}: ${err instanceof Error ? err.message : String(err)}`);
+ }
return fallback;
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async function readFileOr(path: string, fallback = ""): Promise<string> { | |
| try { | |
| return await readFile(path, "utf-8"); | |
| } catch { | |
| return fallback; | |
| } | |
| } | |
| async function readFileOr(path: string, fallback = ""): Promise<string> { | |
| try { | |
| return await readFile(path, "utf-8"); | |
| } catch (err: unknown) { | |
| const code = (err as NodeJS.ErrnoException)?.code; | |
| if (code !== "ENOENT") { | |
| console.error(` ⚠ failed to read ${path}: ${err instanceof Error ? err.message : String(err)}`); | |
| } | |
| return fallback; | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/scripts/i18n/sync-hash-i18n.ts around lines 92 - 98, readFileOr
currently treats every read failure as if the file were missing, which hides
real I/O problems. Update readFileOr to only return the fallback for the
specific ENOENT case and rethrow or surface all other errors, using the existing
readFile helper and its catch block to distinguish the error by code.
| async function collectMdx(dir: string): Promise<string[]> { | ||
| const results: string[] = []; | ||
| const entries = await readdir(dir, { withFileTypes: true }); | ||
| for (const entry of entries) { | ||
| const full = join(dir, entry.name); | ||
| if (entry.isDirectory()) { | ||
| results.push(...(await collectMdx(full))); | ||
| } else if (entry.name.endsWith(".mdx")) { | ||
| results.push(full); | ||
| } | ||
| } | ||
| return results; | ||
| } |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
collectMdx walks the entire tree with no exclusions — node_modules and .git included.
Unlike loadI18nConfig's exclude_dirs contract (built specifically from language dirs + extra_exclude_dirs), this recursive walk has no directory skip-list at all. Called against REPO_ROOT (line 318) it will recurse into node_modules, .git, dist, etc. before filtering by extension, wasting significant I/O on typical docs monorepos.
⏱️ Skip well-known noisy directories during traversal
+const SKIP_DIR_NAMES = new Set(["node_modules", ".git", ".next", "dist", "build"]);
+
async function collectMdx(dir: string): Promise<string[]> {
const results: string[] = [];
const entries = await readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const full = join(dir, entry.name);
- if (entry.isDirectory()) {
+ if (entry.isDirectory() && !SKIP_DIR_NAMES.has(entry.name)) {
results.push(...(await collectMdx(full)));
} else if (entry.name.endsWith(".mdx")) {
results.push(full);
}
}
return results;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async function collectMdx(dir: string): Promise<string[]> { | |
| const results: string[] = []; | |
| const entries = await readdir(dir, { withFileTypes: true }); | |
| for (const entry of entries) { | |
| const full = join(dir, entry.name); | |
| if (entry.isDirectory()) { | |
| results.push(...(await collectMdx(full))); | |
| } else if (entry.name.endsWith(".mdx")) { | |
| results.push(full); | |
| } | |
| } | |
| return results; | |
| } | |
| const SKIP_DIR_NAMES = new Set(["node_modules", ".git", ".next", "dist", "build"]); | |
| async function collectMdx(dir: string): Promise<string[]> { | |
| const results: string[] = []; | |
| const entries = await readdir(dir, { withFileTypes: true }); | |
| for (const entry of entries) { | |
| const full = join(dir, entry.name); | |
| if (entry.isDirectory() && !SKIP_DIR_NAMES.has(entry.name)) { | |
| results.push(...(await collectMdx(full))); | |
| } else if (entry.name.endsWith(".mdx")) { | |
| results.push(full); | |
| } | |
| } | |
| return results; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/scripts/i18n/sync-hash-i18n.ts around lines 100 - 112, The recursive
traversal in collectMdx currently descends into every directory, including noisy
repo folders like node_modules, .git, and dist. Update collectMdx to skip a
small exclude list before recursing, and make the list easy to reuse/configure
similarly to loadI18nConfig’s exclude_dirs contract. Keep the .mdx collection
behavior the same, but prevent entering excluded directories when walking from
REPO_ROOT.
| async function syncOneFile( | ||
| relPath: string, | ||
| lang: LangConfig, | ||
| snippetsMode: boolean, | ||
| dryRun: boolean, | ||
| verify: boolean | ||
| ): Promise<SyncResult> { | ||
| const { enPath, targetPath, enRel, targetRel } = makeMapping(lang, relPath, snippetsMode); | ||
| const enContent = await readFileOr(enPath); | ||
| if (!enContent || enContent.length < 50) { | ||
| return { status: "skipped", warnings: [] }; | ||
| } | ||
|
|
||
| const targetContent = await readFileOr(targetPath); | ||
| if (!targetContent.trim()) { | ||
| console.log(` [${lang.code}] missing target: ${targetRel}`); | ||
| return { status: "missing", warnings: [] }; | ||
| } | ||
|
|
||
| const { output, expectedFileHash } = computeSyncedContent( | ||
| enContent, | ||
| targetContent, | ||
| relPath, | ||
| enRel, | ||
| snippetsMode | ||
| ); | ||
|
|
||
| const existingHash = getExistingHash(targetContent); | ||
| if (existingHash === expectedFileHash && output === targetContent) { | ||
| return { status: "unchanged", warnings: [] }; | ||
| } | ||
|
|
||
| const warnings = verify | ||
| ? collectVerifyWarnings(enContent, targetContent, relPath, lang.code, snippetsMode) | ||
| : []; | ||
|
|
||
| if (dryRun) { | ||
| console.log(` [${lang.code}] would sync hash: ${targetRel}`); | ||
| for (const w of warnings) console.log(` ⚠ ${w}`); | ||
| return { status: "updated", warnings }; | ||
| } | ||
|
|
||
| await mkdir(dirname(targetPath), { recursive: true }); | ||
| await writeFile(targetPath, output); | ||
| console.log(` [${lang.code}] synced hash: ${targetRel}`); | ||
| for (const w of warnings) console.log(` ⚠ ${w}`); | ||
| return { status: "updated", warnings }; | ||
| } |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win
Same file/lang pair is read from disk up to three times.
The outer loop reads enContent/targetContent for drift detection (lines 381-384), reads them again in the processing loop (lines 398-401), and syncOneFile (called at line 407) reads them a third time internally (lines 259, 264). For a full run across many pages × languages this triples I/O for no functional benefit.
♻️ Cache per-language reads once per file
- for (const relPath of files) {
- let fileHasDrift = false;
- for (const lang of selectedLangs) {
- const { enPath, targetPath } = makeMapping(lang, relPath, phase.snippetsMode);
- const enContent = await readFileOr(enPath);
- const targetContent = await readFileOr(targetPath);
- if (!enContent || enContent.length < 50) continue;
- if (!allFiles && fileArgs.length === 0 && !needsHashSync(enContent, targetContent, relPath, phase.snippetsMode)) {
- continue;
- }
- fileHasDrift = true;
- }
+ for (const relPath of files) {
+ const perLang = await Promise.all(
+ selectedLangs.map(async (lang) => {
+ const { enPath, targetPath } = makeMapping(lang, relPath, phase.snippetsMode);
+ const enContent = await readFileOr(enPath);
+ const targetContent = await readFileOr(targetPath);
+ const eligible = !!enContent && enContent.length >= 50;
+ const drift =
+ eligible &&
+ (allFiles || fileArgs.length > 0 || needsHashSync(enContent, targetContent, relPath, phase.snippetsMode));
+ return { lang, eligible, drift };
+ })
+ );
+ const fileHasDrift = perLang.some((p) => p.drift);syncOneFile would still re-read internally; consider optionally accepting preloaded enContent/targetContent to eliminate the last redundant read too.
Also applies to: 378-419
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/scripts/i18n/sync-hash-i18n.ts around lines 251 - 298, The same
file/lang pair is being read multiple times, so update the sync flow to reuse
preloaded contents instead of re-reading from disk. Adjust syncOneFile to
optionally accept existing enContent and targetContent (or similar) and use
those values when provided, and then pass the cached reads from the outer
processing loop into syncOneFile so the drift check and processing path share
one read per file. Keep the existing behavior in makeMapping,
computeSyncedContent, and collectVerifyWarnings unchanged while removing the
redundant readFileOr calls.
| async function collectEnglishFiles(snippetsMode: boolean, fileArgs: string[]): Promise<string[]> { | ||
| if (fileArgs.length > 0) { | ||
| return fileArgs | ||
| .map((f) => stripLangPrefix(f, config.languages)) | ||
| .filter((f) => | ||
| snippetsMode | ||
| ? isEnglishSnippetPath(f.replace(/^snippets\//, ""), pathFilterOpts) | ||
| : isEnglishPagePath(f, pathFilterOpts) | ||
| ); | ||
| } | ||
|
|
||
| if (snippetsMode) { | ||
| const all = await collectMdx(join(REPO_ROOT, "snippets")); | ||
| return all | ||
| .map((f) => relative(join(REPO_ROOT, "snippets"), f)) | ||
| .filter((f) => isEnglishSnippetPath(f, pathFilterOpts)); | ||
| } | ||
|
|
||
| const all = await collectMdx(REPO_ROOT); | ||
| return all | ||
| .map((f) => relative(REPO_ROOT, f)) | ||
| .filter((f) => isEnglishPagePath(f, pathFilterOpts)); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -A 20 'function stripLangPrefix' .github/scripts/i18n/i18n-config.mjs
rg -n -A 20 'function isEnglishSnippetPath' .github/scripts/i18n/i18n-config.mjsRepository: Comfy-Org/docs
Length of output: 1771
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the mapping logic and nearby helpers involved in snippet path handling.
ast-grep outline .github/scripts/i18n/sync-hash-i18n.ts --view expanded
printf '\n--- makeMapping and collectEnglishFiles ---\n'
sed -n '120,190p' .github/scripts/i18n/sync-hash-i18n.ts
printf '\n--- later file-args handling and mapping call sites ---\n'
sed -n '190,360p' .github/scripts/i18n/sync-hash-i18n.ts
printf '\n--- normalizeRelPath and isEnglishMdxPath helpers ---\n'
rg -n -A 20 'function normalizeRelPath|function isEnglishMdxPath|function isEnglishSnippetPath|function stripLangPrefix' .github/scripts/i18n/i18n-config.mjsRepository: Comfy-Org/docs
Length of output: 13349
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show how collectEnglishFiles output is consumed and whether snippets paths are normalized later.
sed -n '360,460p' .github/scripts/i18n/sync-hash-i18n.ts
# Also inspect any remaining references to filterFileArgsForPhase/collectEnglishFiles.
rg -n 'filterFileArgsForPhase|collectEnglishFiles|syncOneFile\(' .github/scripts/i18n/sync-hash-i18n.tsRepository: Comfy-Org/docs
Length of output: 4025
Strip snippets/ from CLI snippet args before returning
.github/scripts/i18n/sync-hash-i18n.ts: filterFileArgsForPhase() keeps snippets/foo.mdx unchanged in snippets mode, so collectEnglishFiles() hands makeMapping() a path that already includes snippets/. That turns into join(REPO_ROOT, "snippets", "snippets/foo.mdx") and the file goes missing in action. Normalize the returned snippet arg to foo.mdx here.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/scripts/i18n/sync-hash-i18n.ts around lines 300 - 322,
collectEnglishFiles() is returning snippet CLI args with the snippets/ prefix
still attached, which later causes makeMapping() to resolve a duplicated
snippets path. Update the snippetsMode branch that handles fileArgs so it
normalizes each snippet argument to a path relative to the snippets root before
returning it, while preserving the existing page-file behavior. Use
collectEnglishFiles() and the snippetsMode/fileArgs handling as the place to
make this adjustment.
Refresh translationSourceHash and add translationBlockHashes from English source without changing translated prose. Intended to pair with the lower auto_chunk thresholds in PR #1227.
Summary
pnpm translate:sync-hashto refreshtranslationSourceHash/translationBlockHashesfrom English after manual zh/ja/ko edits (no API calls).pnpm translate:repair-fencesto append missing closing ``` markers detected by the truncation scan (structural fix, no API).auto_chunkthresholds to 3k chars / 2 sections so more pages use section-level incremental translation.Test plan
bun test ./.github/scripts/i18n/repair-fences.test.tspnpm translate:sync-hash -- --dry-run installation/system_requirements.mdxpnpm translate:repair-fences -- --dry-run(reports unclosed fence candidates)sync-hashon a manually edited pagerepair-fenceson flaggedcustom-nodes/js/translations in a separate PR