Files
mai-bot/docs/github-actions-crowdin-workflow-report.md

10 KiB

GitHub-Side Localization Workflow Report

Scope

This document defines the repository-side localization workflow for Option B. It focuses on GitHub Actions, branch conventions, pull requests, and validation gates around Crowdin.

This is intentionally repository-specific operational guidance, not a generic Crowdin or GitHub Actions tutorial.

External prerequisite:

  • the Crowdin project and GitHub Actions secrets must already be configured
  • if Crowdin's native GitHub integration exists on the Crowdin side, it must not be used as a second write-back path for this repository

Repository Policy

  • zh-CN is the only source language of truth in the repository
  • target-locale files committed in the repository are synchronization artifacts and reviewable outputs, not the normal long-term editing surface
  • existing committed target translations must be bootstrapped into Crowdin once before steady-state sync is trusted
  • after bootstrap, Crowdin is the normal editing surface for target translations
  • GitHub Actions is the only allowed GitHub-side synchronization mechanism between this repository and Crowdin
  • translations return through l10n_* pull requests, not direct pushes into main or r-dev

Branch Model

  • source branches covered by the localization workflow:
    • main
    • r-dev
  • Crowdin return branches:
    • l10n_main
    • l10n_r-dev
  • merge strategy:
    • translations do not go directly into main or r-dev
    • translations are reviewed through pull requests before merge

Source of Truth and Trigger Surface

  • source locale for JSON translations: locales/zh-CN/*.json
  • source locale for prompt templates: prompts/zh-CN/**/*.prompt
  • current prompt template extension in the repository: .prompt

Normal push-triggered source uploads remain strictly source-driven. Translated target assets are not part of the steady-state upload trigger set.

Workflows Involved

1. crowdin-bootstrap.yml

Role:

  • provides a manual bootstrap path for existing committed target translations
  • seeds Crowdin from the repository's current target-locale state
  • keeps this exceptional upload path separate from normal source-driven sync

Triggers:

  • manual dispatch only

Visibility requirement:

  • because this workflow uses workflow_dispatch, GitHub only exposes it after the workflow file exists on the repository default branch
  • in this repository, maintainers should merge the workflow file into main before expecting it to appear in the Actions UI or be runnable through gh workflow run

Inputs:

  • base_branch: main or r-dev
  • confirm_bootstrap: explicit confirmation string

Behavior:

  • checks out the selected repository branch
  • uploads sources and committed target translations to Crowdin
  • does not download translations
  • does not create or update l10n_* pull requests

Guardrail:

  • this workflow is intentionally one-time or exceptional
  • maintainers must not treat it as a continuous GitHub-to-Crowdin target-translation sync path

2. crowdin-sync.yml

Role:

  • uploads source-language assets to Crowdin
  • downloads currently available translations from Crowdin when the workflow runs
  • creates or updates localization pull requests back to the matching base branch

Triggers:

  • manual dispatch
  • scheduled sync every 6 hours: 17 */6 * * * UTC
  • push to main or r-dev when one of these paths changes:
    • crowdin.yml
    • locales/zh-CN/*.json
    • prompts/zh-CN/**/*.prompt

Branch behavior:

  • push-triggered runs sync the current Git branch and use a matching localization branch name:
    • main -> l10n_main -> PR into main
    • r-dev -> l10n_r-dev -> PR into r-dev
  • scheduled runs explicitly cover both main and r-dev

Permissions and credentials:

  • contents: write
  • pull-requests: write
  • GITHUB_TOKEN
  • CROWDIN_PROJECT_ID
  • CROWDIN_PERSONAL_TOKEN

Important boundary:

  • the steady-state workflow keeps the PR-based return flow intact
  • normal runs do not upload direct GitHub edits to target-locale files back into Crowdin

3. i18n-validate.yml

Role:

  • runs repository-side localization validation
  • blocks structurally invalid or policy-breaking localization changes

Triggers:

  • pull requests that touch:
    • locales/**/*.json
    • prompts/**/*.prompt
    • scripts/i18n_validate.py
    • src/common/i18n/**/*.py
    • src/common/prompt_i18n.py
    • src/prompt/prompt_manager.py
  • pushes to main or r-dev for the same path set

Validation scope:

  • JSON locale key alignment against zh-CN
  • placeholder consistency
  • plural structure consistency
  • prompt placeholder consistency
  • English locale protection against Chinese source-language leakage
  • rejection of non-zh-CN entries that directly preserve Chinese source text

Prompt behavior note:

  • missing target prompt files currently produce warnings, not hard failures
  • runtime still falls back to zh-CN prompt templates when localized prompt files are absent

4. precheck.yml

Role:

  • checks whether a pull request conflicts with its real target branch
  • preserves the existing conflict-label behavior

Behavior:

  • checks out the PR head commit
  • fetches the actual PR base branch from github.event.pull_request.base.ref
  • performs a merge simulation against that real base branch
  • marks the PR as conflicted only if the merge simulation produces unmerged files

This means:

  • feature branches into main are checked against main
  • feature branches into r-dev are checked against r-dev
  • l10n_main PRs are checked against main
  • l10n_r-dev PRs are checked against r-dev

5. ruff-pr.yml

Role:

  • runs Ruff lint and format checks for pull requests that are relevant to Python code quality

Effect:

  • translation-only localization pull requests do not run Ruff by default
  • Python or Ruff-related pull requests still run the existing Ruff checks

End-to-End GitHub Flow

A. One-time bootstrap of existing target translations

  1. A maintainer chooses main or r-dev as the branch whose committed target translations should seed Crowdin.
  2. The maintainer manually runs crowdin-bootstrap.yml with explicit confirmation.
  3. The workflow uploads the selected branch's current sources and committed target translations to Crowdin.
  4. No l10n_* pull request is created by this bootstrap workflow.
  5. After bootstrap, target-language maintenance should move to Crowdin as the normal editing surface.

B. Normal source-language update on main or r-dev

  1. A source-language change is pushed to main or r-dev.
  2. crowdin-sync.yml uploads zh-CN source assets to Crowdin.
  3. The same workflow may also download any translations currently available in Crowdin when that workflow run executes.
  4. A localization pull request is opened or updated:
    • l10n_main -> main
    • l10n_r-dev -> r-dev

C. Translation return flow

  1. Translators work in Crowdin.
  2. Repository updates do not appear in main or r-dev immediately at approval time.
  3. Repository write-back happens when crowdin-sync.yml runs.
  4. GitHub updates or creates l10n_${branch} pull requests.
  5. Maintainers review and merge the localization pull request in the normal PR flow.

D. Scheduled sync

  1. Every 6 hours, GitHub Actions runs a scheduled localization sync.
  2. The workflow explicitly processes both main and r-dev.
  3. If Crowdin currently has downloadable translation updates, GitHub updates or creates the corresponding l10n_ pull requests.

How the Setup Avoids Sync Loops

  • source uploads are triggered only from:
    • crowdin.yml
    • locales/zh-CN/*.json
    • prompts/zh-CN/**/*.prompt
  • translated target files do not trigger another steady-state upload cycle
  • the bootstrap path is manual and confirmation-gated
  • translations return through l10n_ branches and PRs instead of direct pushes to base branches
  • translation-only PRs do not trigger Ruff, which reduces unnecessary CI noise without weakening Python quality gates

GitHub-Usable Maintainer Operations

Trigger the bootstrap path

GitHub UI:

  • Actions -> Crowdin Bootstrap Target Translations
  • if it does not appear yet, first make sure the workflow file has already landed on the default branch (main)
  • choose main or r-dev
  • set confirm_bootstrap to yes-bootstrap-current-target-translations

GitHub CLI:

gh workflow run crowdin-bootstrap.yml \
  --ref main \
  -f base_branch=r-dev \
  -f confirm_bootstrap=yes-bootstrap-current-target-translations

Use this only when seeding Crowdin from already-committed target translations, or in another exceptional recovery scenario.

Trigger a normal manual sync

GitHub UI:

  • Actions -> Crowdin Sync
  • if a newly added manual workflow does not appear, confirm that workflow file is already on the default branch
  • run the workflow on main or r-dev

GitHub CLI:

gh workflow run crowdin-sync.yml --ref main

Inspect workflow runs

gh run list --workflow crowdin-sync.yml --limit 5
gh run list --workflow crowdin-bootstrap.yml --limit 5

Inspect resulting localization pull requests

gh pr list --head l10n_main
gh pr list --head l10n_r-dev

Verify that GitHub Actions is the repository write-back path

  • confirm there is a successful crowdin-sync.yml run corresponding to the latest l10n_* PR update
  • confirm translated content returned through l10n_main or l10n_r-dev, not a direct push into main or r-dev
  • do not rely on a separate Crowdin native GitHub integration PR or branch flow for this repository

Guardrails That Remain Intact

  • localization PRs are still checked against their real base branch
  • repository-side localization validation still runs where expected
  • translation-only PRs still avoid unnecessary Ruff noise by default
  • Python-impacting PRs still run Python quality gates where appropriate
  • the steady-state zh-CN source-trigger model remains unchanged

Bottom Line

The GitHub-side localization workflow now supports the intended Option B model:

  • zh-CN remains the only repository source language
  • existing committed target translations can be bootstrapped into Crowdin once through a manual workflow
  • steady-state sync remains source-driven and GitHub Actions-only
  • translated content still returns through l10n_${branch} pull requests
  • existing PR validation and reduced-noise translation PR behavior remain intact