diff --git a/.github/workflows/translate-i18n-claude.yml b/.github/workflows/translate-i18n-claude.yml index 50a03bb7ab8..aaf51aa6064 100644 --- a/.github/workflows/translate-i18n-claude.yml +++ b/.github/workflows/translate-i18n-claude.yml @@ -8,11 +8,11 @@ on: workflow_dispatch: inputs: files: - description: 'Specific files to translate (space-separated, e.g., "app common"). Leave empty for all files.' + description: 'Specific files to translate (space-separated, e.g., "app common"). Required for full mode; leave empty in incremental mode to use en-US files changed since HEAD~1.' required: false type: string languages: - description: 'Specific languages to translate (space-separated, e.g., "zh-Hans ja-JP"). Leave empty for all supported languages.' + description: 'Specific languages to translate (space-separated, e.g., "zh-Hans ja-JP"). Leave empty for all supported target languages except en-US.' required: false type: string mode: @@ -29,14 +29,14 @@ permissions: pull-requests: write concurrency: - group: translate-i18n-${{ github.ref }} - cancel-in-progress: true + group: translate-i18n-${{ github.event_name }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'push' }} jobs: translate: if: github.repository == 'langgenius/dify' runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 120 steps: - name: Checkout repository @@ -57,28 +57,52 @@ jobs: id: context shell: bash run: | + DEFAULT_TARGET_LANGS=$(awk " + /value: '/ { + value=\$2 + gsub(/[',]/, \"\", value) + } + /supported: true/ && value != \"en-US\" { + printf \"%s \", value + } + " web/i18n-config/languages.ts | sed 's/[[:space:]]*$//') + if [ "${{ github.event_name }}" = "push" ]; then BASE_SHA="${{ github.event.before }}" if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then BASE_SHA=$(git rev-parse HEAD~1 2>/dev/null || true) fi HEAD_SHA="${{ github.sha }}" - CHANGED_FILES=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- 'web/i18n/en-US/*.json' 2>/dev/null | sed -n 's@^.*/@@p' | sed 's/\.json$//' | tr '\n' ' ' | sed 's/[[:space:]]*$//') - TARGET_LANGS="" - SYNC_MODE="incremental" - else - HEAD_SHA=$(git rev-parse HEAD) - TARGET_LANGS="${{ github.event.inputs.languages }}" - SYNC_MODE="${{ github.event.inputs.mode || 'incremental' }}" - if [ -n "${{ github.event.inputs.files }}" ]; then - CHANGED_FILES="${{ github.event.inputs.files }}" + if [ -n "$BASE_SHA" ]; then + CHANGED_FILES=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- 'web/i18n/en-US/*.json' 2>/dev/null | sed -n 's@^.*/@@p' | sed 's/\.json$//' | tr '\n' ' ' | sed 's/[[:space:]]*$//') else CHANGED_FILES=$(find web/i18n/en-US -maxdepth 1 -type f -name '*.json' -print | sed -n 's@^.*/@@p' | sed 's/\.json$//' | sort | tr '\n' ' ' | sed 's/[[:space:]]*$//') fi - if [ "$SYNC_MODE" = "incremental" ]; then - BASE_SHA=$(git rev-parse HEAD~1 2>/dev/null || true) + TARGET_LANGS="$DEFAULT_TARGET_LANGS" + SYNC_MODE="incremental" + else + BASE_SHA="" + HEAD_SHA=$(git rev-parse HEAD) + if [ -n "${{ github.event.inputs.languages }}" ]; then + TARGET_LANGS="${{ github.event.inputs.languages }}" else - BASE_SHA="" + TARGET_LANGS="$DEFAULT_TARGET_LANGS" + fi + SYNC_MODE="${{ github.event.inputs.mode || 'incremental' }}" + if [ -n "${{ github.event.inputs.files }}" ]; then + CHANGED_FILES="${{ github.event.inputs.files }}" + elif [ "$SYNC_MODE" = "incremental" ]; then + BASE_SHA=$(git rev-parse HEAD~1 2>/dev/null || true) + if [ -n "$BASE_SHA" ]; then + CHANGED_FILES=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- 'web/i18n/en-US/*.json' 2>/dev/null | sed -n 's@^.*/@@p' | sed 's/\.json$//' | tr '\n' ' ' | sed 's/[[:space:]]*$//') + else + CHANGED_FILES=$(find web/i18n/en-US -maxdepth 1 -type f -name '*.json' -print | sed -n 's@^.*/@@p' | sed 's/\.json$//' | sort | tr '\n' ' ' | sed 's/[[:space:]]*$//') + fi + elif [ "$SYNC_MODE" = "full" ]; then + echo "workflow_dispatch full mode requires the files input to stay within CI limits." >&2 + exit 1 + else + CHANGED_FILES="" fi fi @@ -93,6 +117,7 @@ jobs: fi { + echo "DEFAULT_TARGET_LANGS=$DEFAULT_TARGET_LANGS" echo "BASE_SHA=$BASE_SHA" echo "HEAD_SHA=$HEAD_SHA" echo "CHANGED_FILES=$CHANGED_FILES" @@ -103,7 +128,7 @@ jobs: } >> "$GITHUB_OUTPUT" echo "Files: ${CHANGED_FILES:-}" - echo "Languages: ${TARGET_LANGS:-}" + echo "Languages: ${TARGET_LANGS:-}" echo "Mode: $SYNC_MODE" - name: Run Claude Code for Translation Sync @@ -113,6 +138,7 @@ jobs: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} allowed_bots: 'github-actions[bot]' + show_full_output: ${{ github.event_name == 'workflow_dispatch' }} prompt: | You are the i18n sync agent for the Dify repository. Your job is to keep translations synchronized with the English source files under `${{ github.workspace }}/web/i18n/en-US/`, then open a PR with the result. @@ -139,11 +165,13 @@ jobs: Required execution plan: 1. Resolve target languages. - - If no target languages were provided, read `${{ github.workspace }}/web/i18n-config/languages.ts` and use every language with `supported: true`. + - Use the provided `Target languages` value as the source of truth. + - If it is unexpectedly empty, read `${{ github.workspace }}/web/i18n-config/languages.ts` and use every language with `supported: true` except `en-US`. 2. Stay strictly in scope. - Only process the files listed in `Files in scope`. - - Only process the resolved target languages. + - Only process the resolved target languages, never `en-US`. - Do not touch unrelated i18n files. + - Do not modify `${{ github.workspace }}/web/i18n/en-US/`. 3. Detect English changes per file. - Read the current English JSON file for each file in scope. - If sync mode is `incremental` and `Base SHA` is not empty, run: @@ -160,6 +188,7 @@ jobs: - Use this command as the source of truth for missing and extra keys inside the current scope. 5. Apply translations. - For every target language and scoped file: + - If the locale file does not exist yet, create it with `Write` and then continue with `Edit` as needed. - ADD missing keys. - UPDATE stale translations when the English value changed. - DELETE removed keys. Prefer `pnpm --dir ${{ github.workspace }}/web run i18n:check ${{ steps.context.outputs.FILE_ARGS }} ${{ steps.context.outputs.LANG_ARGS }} --auto-remove` for extra keys so deletions stay in scope. @@ -181,4 +210,4 @@ jobs: 8. If there are no translation changes after verification, do not create a branch, commit, or PR. claude_args: | --max-turns 80 - --allowedTools "Read,Edit,Bash(git *),Bash(git:*),Bash(gh *),Bash(gh:*),Bash(pnpm *),Bash(pnpm:*),Bash(date *),Bash(date:*),Glob,Grep" + --allowedTools "Read,Write,Edit,Bash(git *),Bash(git:*),Bash(gh *),Bash(gh:*),Bash(pnpm *),Bash(pnpm:*),Bash(date *),Bash(date:*),Glob,Grep"