Control UI Locale Refresh #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Control UI Locale Refresh | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - ui/src/i18n/locales/en.ts | |
| - ui/src/i18n/locales/*.ts | |
| - ui/src/i18n/.i18n/* | |
| - ui/src/i18n/lib/types.ts | |
| - ui/src/i18n/lib/registry.ts | |
| - scripts/control-ui-i18n.ts | |
| - .github/workflows/control-ui-locale-refresh.yml | |
| release: | |
| types: | |
| - published | |
| schedule: | |
| - cron: "23 4 * * *" | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: control-ui-locale-refresh | |
| cancel-in-progress: false | |
| jobs: | |
| plan: | |
| if: github.repository == 'openclaw/openclaw' && (github.event_name != 'push' || github.actor != 'github-actions[bot]') | |
| runs-on: ubuntu-latest | |
| outputs: | |
| has_locales: ${{ steps.plan.outputs.has_locales }} | |
| locales_json: ${{ steps.plan.outputs.locales_json }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| submodules: false | |
| - name: Plan locale matrix | |
| id: plan | |
| env: | |
| BEFORE_SHA: ${{ github.event.before }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| run: | | |
| set -euo pipefail | |
| all_locales_json='["zh-CN","zh-TW","pt-BR","de","es","ja-JP","ko","fr","tr","uk","id","pl"]' | |
| if [ "$EVENT_NAME" != "push" ]; then | |
| echo "has_locales=true" >> "$GITHUB_OUTPUT" | |
| echo "locales_json=$all_locales_json" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| before_ref="$BEFORE_SHA" | |
| if [ -z "$before_ref" ] || [ "$before_ref" = "0000000000000000000000000000000000000000" ]; then | |
| before_ref="$(git rev-parse HEAD^)" | |
| fi | |
| changed_files="$(git diff --name-only "$before_ref" HEAD)" | |
| echo "changed files:" | |
| printf '%s\n' "$changed_files" | |
| if printf '%s\n' "$changed_files" | grep -Eq '^(ui/src/i18n/locales/en\.ts|ui/src/i18n/lib/types\.ts|ui/src/i18n/lib/registry\.ts|scripts/control-ui-i18n\.ts|\.github/workflows/control-ui-locale-refresh\.yml)$'; then | |
| echo "has_locales=true" >> "$GITHUB_OUTPUT" | |
| echo "locales_json=$all_locales_json" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| locales_json="$(printf '%s\n' "$changed_files" | node <<'EOF' | |
| const fs = require("node:fs"); | |
| const changed = fs.readFileSync(0, "utf8").split(/\r?\n/).filter(Boolean); | |
| const locales = new Set(); | |
| for (const file of changed) { | |
| let match = file.match(/^ui\/src\/i18n\/locales\/(.+)\.ts$/); | |
| if (match && match[1] !== "en") { | |
| locales.add(match[1]); | |
| continue; | |
| } | |
| match = file.match(/^ui\/src\/i18n\/\.i18n\/(.+)\.(?:meta\.json|tm\.jsonl)$/); | |
| if (match) { | |
| locales.add(match[1]); | |
| } | |
| } | |
| process.stdout.write(JSON.stringify([...locales])); | |
| EOF | |
| )" | |
| if [ "$locales_json" = "[]" ]; then | |
| echo "has_locales=false" >> "$GITHUB_OUTPUT" | |
| echo "locales_json=[]" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "has_locales=true" >> "$GITHUB_OUTPUT" | |
| echo "locales_json=$locales_json" >> "$GITHUB_OUTPUT" | |
| refresh: | |
| needs: plan | |
| if: github.repository == 'openclaw/openclaw' && needs.plan.outputs.has_locales == 'true' | |
| strategy: | |
| fail-fast: false | |
| max-parallel: 4 | |
| matrix: | |
| locale: ${{ fromJson(needs.plan.outputs.locales_json) }} | |
| runs-on: ubuntu-latest | |
| name: Refresh ${{ matrix.locale }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| persist-credentials: true | |
| submodules: false | |
| - name: Setup Node environment | |
| uses: ./.github/actions/setup-node-env | |
| with: | |
| install-bun: "false" | |
| use-sticky-disk: "false" | |
| - name: Ensure translation provider secrets exist | |
| env: | |
| OPENAI_API_KEY: ${{ secrets.OPENCLAW_DOCS_I18N_OPENAI_API_KEY || secrets.OPENAI_API_KEY }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${OPENAI_API_KEY:-}" ] && [ -z "${ANTHROPIC_API_KEY:-}" ]; then | |
| echo "Missing OPENCLAW_DOCS_I18N_OPENAI_API_KEY, OPENAI_API_KEY, or ANTHROPIC_API_KEY secret." | |
| exit 1 | |
| fi | |
| - name: Refresh control UI locale files | |
| env: | |
| OPENAI_API_KEY: ${{ secrets.OPENCLAW_DOCS_I18N_OPENAI_API_KEY || secrets.OPENAI_API_KEY }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| OPENCLAW_CONTROL_UI_I18N_MODEL: gpt-5.4 | |
| OPENCLAW_CONTROL_UI_I18N_THINKING: low | |
| run: node --import tsx scripts/control-ui-i18n.ts sync --locale "${{ matrix.locale }}" --write | |
| - name: Commit and push locale updates | |
| env: | |
| LOCALE: ${{ matrix.locale }} | |
| TARGET_BRANCH: ${{ github.event.repository.default_branch }} | |
| run: | | |
| set -euo pipefail | |
| if git diff --quiet -- ui/src/i18n; then | |
| echo "No control UI locale changes for ${LOCALE}." | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add -A ui/src/i18n | |
| git commit --no-verify -m "chore(ui): refresh ${LOCALE} control ui locale" | |
| for attempt in 1 2 3 4 5; do | |
| git fetch origin "${TARGET_BRANCH}" | |
| git rebase --autostash "origin/${TARGET_BRANCH}" | |
| if git push origin HEAD:"${TARGET_BRANCH}"; then | |
| exit 0 | |
| fi | |
| echo "Push attempt ${attempt} for ${LOCALE} failed; retrying." | |
| sleep $((attempt * 2)) | |
| done | |
| echo "Failed to push ${LOCALE} locale update after retries." | |
| exit 1 |