11 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-CNis 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 intomainorr-dev
Branch Model
- source branches covered by the localization workflow:
mainr-dev
- Crowdin return branches:
l10n_mainl10n_r-dev
- merge strategy:
- translations do not go directly into
mainorr-dev - translations are reviewed through pull requests before merge
- translations do not go directly into
Source of Truth and Trigger Surface
- source locale for JSON translations:
locales/zh-CN/*.json - source locale for prompt templates:
prompts/zh-CN/**/*.prompt - source locale for dashboard WebUI translations:
dashboard/src/i18n/locales/zh.json - current prompt template extension in the repository:
.prompt - dashboard WebUI keeps short runtime locale filenames (
zh,en,ja,ko) in Git, butdashboard/src/i18n/locales/zh.jsonis still the repository-sidezh-CNsource asset for that file group
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
mainbefore expecting it to appear in the Actions UI or be runnable throughgh workflow run
Inputs:
base_branch:mainorr-devconfirm_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
mainorr-devwhen one of these paths changes:crowdin.ymllocales/zh-CN/*.jsonprompts/zh-CN/**/*.promptdashboard/src/i18n/locales/zh.json
Branch behavior:
- push-triggered runs sync the current Git branch and use a matching localization branch name:
main -> l10n_main -> PR into mainr-dev -> l10n_r-dev -> PR into r-dev
- scheduled runs explicitly cover both
mainandr-dev
Permissions and credentials:
contents: writepull-requests: writeGITHUB_TOKENCROWDIN_PROJECT_IDCROWDIN_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/**/*.jsonprompts/**/*.promptdashboard/src/i18n/index.tsdashboard/src/i18n/locales/*.jsonscripts/i18n_validate.pysrc/common/i18n/**/*.pysrc/common/prompt_i18n.pysrc/prompt/prompt_manager.py
- pushes to
mainorr-devfor the same path set
Validation scope:
- JSON locale key alignment against
zh-CN - dashboard nested JSON locale key alignment against
dashboard/src/i18n/locales/zh.json - placeholder consistency
- dashboard i18next interpolation placeholder consistency
- plural structure consistency
- prompt placeholder consistency
- English locale protection against Chinese source-language leakage
- rejection of non-
zh-CNentries 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-CNprompt 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
mainare checked againstmain - feature branches into
r-devare checked againstr-dev l10n_mainPRs are checked againstmainl10n_r-devPRs are checked againstr-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
- A maintainer chooses
mainorr-devas the branch whose committed target translations should seed Crowdin. - The maintainer manually runs
crowdin-bootstrap.ymlwith explicit confirmation. - The workflow uploads the selected branch's current sources and committed target translations to Crowdin.
- No
l10n_*pull request is created by this bootstrap workflow. - After bootstrap, target-language maintenance should move to Crowdin as the normal editing surface.
B. Normal source-language update on main or r-dev
- A source-language change is pushed to
mainorr-dev. crowdin-sync.ymluploads source-language assets to Crowdin, including the dashboard WebUI source filedashboard/src/i18n/locales/zh.json.- The same workflow may also download any translations currently available in Crowdin when that workflow run executes.
- A localization pull request is opened or updated:
l10n_main -> mainl10n_r-dev -> r-dev
C. Translation return flow
- Translators work in Crowdin.
- Repository updates do not appear in
mainorr-devimmediately at approval time. - Repository write-back happens when
crowdin-sync.ymlruns. - GitHub updates or creates
l10n_${branch}pull requests. - Maintainers review and merge the localization pull request in the normal PR flow.
D. Scheduled sync
- Every 6 hours, GitHub Actions runs a scheduled localization sync.
- The workflow explicitly processes both
mainandr-dev. - 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.ymllocales/zh-CN/*.jsonprompts/zh-CN/**/*.promptdashboard/src/i18n/locales/zh.json
- 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
mainorr-dev - set
confirm_bootstraptoyes-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
mainorr-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.ymlrun corresponding to the latestl10n_*PR update - confirm translated content returned through
l10n_mainorl10n_r-dev, not a direct push intomainorr-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-CNsource-trigger model remains unchanged
Bottom Line
The GitHub-side localization workflow now supports the intended Option B model:
zh-CNremains 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