Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Tutorial: Connect an Existing Target

Add action-translation to a repo that was already translated

This tutorial covers connecting action-translation to a project where the target repository already exists and contains translations — but was translated manually or with a different tool, before action-translation was in place.

By the end, you’ll have:

Time: ~30 minutes (includes sync verification) Cost: Free for status/heading-maps. ~0.03/fileforsynccheck. 0.03/file for sync check. ~0.16/file if resync needed.

When to use this tutorial

Use this when you have:

If you’re starting from scratch with no translations, see Tutorial: Fresh Setup instead.

Overview

Step 1: Assess the current state          (translate status)
Step 2: Check sync before bootstrapping    (translate status --check-sync)
Step 3: Catch up stale translations        (translate forward, if needed)
Step 4: Bootstrap .translate/ metadata     (translate status --write-state)
Step 5: Fix missing heading-maps           (translate headingmap)
Step 6: Configure workflows and secrets    (manual GitHub setup)
Step 7: Verify the connection              (translate doctor)

Step 1: Assess the current state

Before making changes, understand what you’re working with. The status command gives a free, instant diagnostic:

npx translate status \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn

Example output:

Sync Status: lecture-python-intro ↔ lecture-python-intro.zh-cn (zh-cn)

  File                              Status
  ────────────────────────────────  ────────────────────
  intro.md                          ✅ ALIGNED
  cobweb.md                         📋 MISSING_HEADINGMAP
  solow.md                          📋 MISSING_HEADINGMAP
  cagan_adaptive.md                 ⏳ OUTDATED
  pv.md                             ⚠️ SOURCE_AHEAD
  new_lecture.md                    ➕ SOURCE_ONLY
  old_removed.md                    🔸 TARGET_ONLY

What to look for:

StatusWhat it meansAction needed
ALIGNEDSections match and file is up to dateNone
MISSING_HEADINGMAPNo heading-map in target frontmatterAdd heading-map (Step 3)
OUTDATEDSource has newer commitsResync later (see Resync Tutorial)
SOURCE_AHEADSource has more sections than targetResync later
SOURCE_ONLYFile exists only in sourceTranslate new file later
TARGET_ONLYFile exists only in targetInvestigate (orphaned?)

At this stage, you’re gathering information. Pay attention to OUTDATED and SOURCE_AHEAD statuses — these indicate the translations may have fallen behind the source. We’ll address those in Step 2 before bootstrapping.

For a more comprehensive assessment including workflow and state file verification, run:

npx translate doctor \
  -t ~/repos/lecture-python-intro.zh-cn

This reports configuration health across all dimensions. All items should be ✅ by the end of Step 5.


Step 2: Check sync before bootstrapping

Run the sync check to compare source and target content:

npx translate status \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn \
  --check-sync

This uses a lightweight LLM comparison to classify each file:

ResultMeaningAction
IDENTICALTranslation matches current sourceSafe to bootstrap
I18N_ONLYDifferences are localization-only (fonts, RTL, etc.)Safe to bootstrap
CONTENT_CHANGESSource has content changes not in translationResync first (Step 3)
TARGET_HAS_ADDITIONSTarget has extra content not in sourceRun backward to capture, then resync

If all files show IDENTICAL or I18N_ONLY, skip to Step 4. If any show CONTENT_CHANGES, proceed to Step 3. If any show TARGET_HAS_ADDITIONS, consider running translate backward first to capture improvements before resyncing.


Step 3: Catch up stale translations

For files flagged as CONTENT_STALE, use the forward command to bring them up to date. The forward command preserves existing localization (RTL adaptations, font configuration, # i18n code, locale-specific links) while updating the translation to match the current source content.

# Dry run first — see what would change
npx translate forward \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn \
  --dry-run

# Resync all stale files
npx translate forward \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn

For a single file:

npx translate forward \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn \
  -f solow.md

Review and commit the changes:

cd ~/repos/lecture-python-intro.zh-cn
git diff lectures/  # Review all changes
git add lectures/
git commit -m "Resync translations to current source via forward"
git push

Step 4: Bootstrap .translate/ metadata

Now that translations are current, bootstrap the state tracking. The .translate/ folder records which source commit each translation is synced to:

npx translate status \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn \
  -l zh-cn \
  --write-state

This creates:

.translate/
├── config.yml              # Project settings
└── state/
    ├── intro.md.yml        # Per-file metadata
    ├── cobweb.md.yml
    └── solow.md.yml

Each state file records:

source-sha: abc1234f       # Current HEAD SHA for the source file
synced-at: "2026-03-16"    # Target file's last commit date
model: unknown             # Not recoverable from history
mode: RESYNC               # Marked as RESYNC (not a fresh translation)
section-count: 5           # Current source section count

Commit the metadata:

cd ~/repos/lecture-python-intro.zh-cn
git add .translate/
git commit -m "Bootstrap .translate/ metadata via action-translation"
git push origin main

Step 5: Fix missing heading-maps

Heading-maps are essential for reliable section matching. If Step 1 showed MISSING_HEADINGMAP for some files, you need to add them.

The headingmap command generates heading-maps by matching source and target headings by position — no LLM calls needed, and no changes to translations:

# Generate heading-map for a single file
npx translate headingmap \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn \
  -f cobweb.md

This:

  1. Parses both files to extract section headings

  2. Matches them by position (1st source → 1st target, etc.)

  3. Injects the heading-map into the target file’s YAML frontmatter

  4. Makes no changes to the translation content

For all files missing heading-maps:

npx translate headingmap \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn

Preview first with --dry-run:

npx translate headingmap \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn \
  --dry-run

Option B: Use forward to regenerate (also adds heading-maps)

If you already ran forward in Step 3, heading-maps were generated as part of that process. Check if any files still need them:

npx translate headingmap \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn \
  --dry-run

The diff should show the heading-map added to the frontmatter. The translation content should be largely preserved (the RESYNC mode is designed to maintain existing style).

For multiple files:

# Resync all files that need attention
npx translate forward \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn

Option C: Add heading-maps manually

If you don’t want to re-translate files (e.g., they’ve been carefully reviewed by human translators), you can add heading-maps by hand.

For each translated file:

  1. Parse the source file to find the English section headings and their IDs

  2. Find the corresponding translated headings in the target file

  3. Add the heading-map to the target file’s YAML frontmatter

Example:

English source (cobweb.md):

## Overview
## The Cobweb Model
### Equilibrium
## Exercises

Chinese target (cobweb.md):

## 概述
## 蛛网模型
### 均衡
## 练习

Add this to the target’s frontmatter:

---
title: 蛛网模型
translation:
  title: 蛛网模型
  headings:
    Overview: "概述"
    The Cobweb Model: "蛛网模型"
    The Cobweb Model::Equilibrium: "均衡"
    Exercises: "练习"
---

Heading keys use the full heading text with :: nesting for subsections. See Heading Maps for complete format rules.

Commit the heading-maps

cd ~/repos/lecture-python-intro.zh-cn
git add lectures/
git commit -m "Add heading-maps to translated files"
git push origin main

Step 6: Configure workflows and secrets

Now set up the automated pipeline. You need a workflow in the source repo and one in the target repo.

6a: Source repo — Sync workflow

Create .github/workflows/sync-translations.yml in the source repository:

name: Sync Translations

on:
  pull_request:
    types: [closed]
    paths:
      - 'lectures/**/*.md'
      - 'lectures/_toc.yml'
  issue_comment:
    types: [created]

jobs:
  sync:
    if: >
      (github.event_name == 'pull_request' && github.event.pull_request.merged == true) ||
      (github.event_name == 'issue_comment' && contains(github.event.comment.body, '\translate-resync'))
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 2

      - uses: QuantEcon/action-translation@v0
        with:
          mode: sync
          target-repo: 'QuantEcon/lecture-python-intro.zh-cn'
          source-language: 'en'
          target-language: 'zh-cn'
          docs-folder: 'lectures'
          anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
          github-token: ${{ secrets.TRANSLATION_PAT }}

6b: Source repo — Secrets

SecretValuePurpose
ANTHROPIC_API_KEYYour Anthropic API keyPays for Claude translations
TRANSLATION_PATGitHub PAT with repo scopeCreates PRs in the target repo

6c: Target repo — Review workflow

Create .github/workflows/review-translations.yml in the target repository:

name: Review Translations

on:
  pull_request:
    types: [opened, synchronize, labeled, reopened]

jobs:
  review:
    # Ignore `labeled` events for every other label: a sync applies its labels one call at a
    # time, and each one would otherwise start a full (billed) review of the same diff.
    if: >
      contains(github.event.pull_request.labels.*.name, 'action-translation') &&
      (github.event.action != 'labeled' || github.event.label.name == 'action-translation')
    runs-on: ubuntu-latest

    permissions:
      contents: read
      pull-requests: write

    # One review per PR — supersede an in-flight review instead of running both
    concurrency:
      group: review-translations-${{ github.event.pull_request.number }}
      cancel-in-progress: true

    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 2

      - uses: QuantEcon/action-translation@v0
        with:
          mode: review
          source-repo: 'QuantEcon/lecture-python-intro'
          source-language: 'en'
          target-language: 'zh-cn'
          docs-folder: 'lectures'
          anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
          github-token: ${{ secrets.GITHUB_TOKEN }}

6d: Target repo — Secret

SecretValue
ANTHROPIC_API_KEYYour Anthropic API key

6e: Merge order

Merge the target repo changes first (.translate/, heading-maps, review workflow), then the source repo sync workflow. This ensures the target is ready to receive translation PRs when the sync workflow activates.


Step 7: Verify the connection

Run status again to confirm everything is properly connected:

npx translate status \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn

What “ready” looks like:

Run a final health check:

npx translate doctor \
  -t ~/repos/lecture-python-intro.zh-cn \
  -s ~/repos/lecture-python-intro

All checks should pass ✅. If some files show OUTDATED or SOURCE_AHEAD, address them with forward before considering the connection complete — otherwise those files will be silently out of date.


Common issues when connecting

Many files show MISSING_HEADINGMAP

This is expected for repos translated before action-translation. Use headingmap to fix without changing translations:

npx translate headingmap \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn

Or use forward if you also want to update the translations:

npx translate forward \
  -s ~/repos/lecture-python-intro \
  -t ~/repos/lecture-python-intro.zh-cn

Review changes, commit, and push.

Section counts don’t match (SOURCE_AHEAD / TARGET_AHEAD)

This means the source and target have different numbers of ## sections. Common causes:

Files exist in only one repo

Next steps