name: i18n Auto-Translate # Uses GitHub Models inference API (https://models.github.ai/inference) with the # repository's GITHUB_TOKEN. Requires `models: read` permission. Default model is # openai/gpt-5-mini (a fast/cheap GPT-5 in the catalog); override via repository variable # TRANSLATE_MODEL (e.g. openai/gpt-5-nano for cheaper, openai/gpt-5 for flagship). # # Optional: to use Anthropic Claude directly, add a repo secret ANTHROPIC_API_KEY, # then set repository variable TRANSLATE_BACKEND=anthropic and TRANSLATE_MODEL to # a Claude model id (e.g. claude-sonnet-4-6). Claude is NOT in the GitHub Models # catalog as of 2026-05. on: pull_request: paths: - "skills/**/SKILL.zh-CN.md" - "skills/**/SKILL.md" - "manifest.json" - "categories.json" workflow_dispatch: inputs: target_locale: description: "Target locale (default: all from manifest.supportedLocales)" required: false skill: description: "Specific skill path, e.g. skills/web-access (default: all)" required: false permissions: contents: write pull-requests: write models: read concurrency: group: i18n-translate-${{ github.ref }} cancel-in-progress: true jobs: translate: # Don't run on bot's own commits to avoid loops if: github.actor != 'desirecore-bot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') runs-on: ubuntu-latest timeout-minutes: 30 steps: - name: Checkout PR branch uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.ref || github.ref }} token: ${{ secrets.DESIRECORE_BOT_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 - name: Install uv uses: astral-sh/setup-uv@v3 - name: Check for stale translations id: precheck run: | set +e uv run --quiet scripts/i18n/translate.py --check rc=$? if [ $rc -eq 0 ]; then echo "stale=false" >> "$GITHUB_OUTPUT" else echo "stale=true" >> "$GITHUB_OUTPUT" fi exit 0 - name: Translate stale locales if: steps.precheck.outputs.stale == 'true' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} TRANSLATE_BACKEND: ${{ vars.TRANSLATE_BACKEND || 'github' }} TRANSLATE_MODEL: ${{ vars.TRANSLATE_MODEL || 'openai/gpt-5-mini' }} run: | set -e ARGS=() if [ -n "${{ github.event.inputs.skill }}" ]; then ARGS+=("${{ github.event.inputs.skill }}") fi if [ -n "${{ github.event.inputs.target_locale }}" ]; then ARGS+=(--target "${{ github.event.inputs.target_locale }}") fi uv run --quiet scripts/i18n/translate.py "${ARGS[@]}" - name: Validate after translation if: steps.precheck.outputs.stale == 'true' run: uv run --quiet scripts/i18n/validate-i18n.py - name: Detect changes id: diff if: steps.precheck.outputs.stale == 'true' run: | if git diff --quiet; then echo "changed=false" >> "$GITHUB_OUTPUT" else echo "changed=true" >> "$GITHUB_OUTPUT" git diff --stat fi - name: Commit & push translations if: steps.diff.outputs.changed == 'true' run: | git config user.name "desirecore-bot" git config user.email "bot@desirecore.net" git add -A git commit -m "chore(i18n): auto-translate skills [skip ci]" \ -m "Generated by scripts/i18n/translate.py via i18n-translate workflow." \ -m "Backend: ${TRANSLATE_BACKEND:-github} Model: ${TRANSLATE_MODEL:-openai/gpt-5-mini}" git push env: TRANSLATE_BACKEND: ${{ vars.TRANSLATE_BACKEND || 'github' }} TRANSLATE_MODEL: ${{ vars.TRANSLATE_MODEL || 'openai/gpt-5-mini' }} - name: Comment on PR if: steps.diff.outputs.changed == 'true' && github.event_name == 'pull_request' uses: actions/github-script@v7 with: github-token: ${{ secrets.DESIRECORE_BOT_TOKEN || secrets.GITHUB_TOKEN }} script: | const backend = process.env.TRANSLATE_BACKEND || 'github'; const model = process.env.TRANSLATE_MODEL || 'openai/gpt-5-mini'; const body = [ '🌐 **i18n auto-translation pushed**', '', 'New machine-translated content was added to this PR by `scripts/i18n/translate.py`.', '', '**Please review the translated strings** before merging:', '- Check terminology against `scripts/i18n/glossary.json`', '- Verify Markdown structure (headings/tables/code fences) is preserved', '- If you edit a translation, set `metadata.i18n..translated_by: human` to lock it.', '', `Backend: \`${backend}\` Model: \`${model}\``, ].join('\n'); await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body, }); env: TRANSLATE_BACKEND: ${{ vars.TRANSLATE_BACKEND || 'github' }} TRANSLATE_MODEL: ${{ vars.TRANSLATE_MODEL || 'openai/gpt-5-mini' }} - name: Label on translation failure if: failure() && github.event_name == 'pull_request' uses: actions/github-script@v7 with: github-token: ${{ secrets.DESIRECORE_BOT_TOKEN || secrets.GITHUB_TOKEN }} script: | await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, labels: ['i18n-translation-failed'], });