From 83a943d8c4d35f0c5680bd2c6e7a2ba77317ee3c Mon Sep 17 00:00:00 2001 From: NFish Date: Thu, 15 Jan 2026 11:47:27 +0800 Subject: [PATCH] build: require node 24.13.0 (#30945) (#31027) Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com> --- .github/workflows/style.yml | 2 +- .github/workflows/tool-test-sdks.yaml | 11 +- .github/workflows/translate-i18n-claude.yml | 421 ++++++++++++++++++++ .github/workflows/web-tests.yml | 2 +- web/Dockerfile | 2 +- web/README.md | 4 +- web/package.json | 9 +- 7 files changed, 438 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/translate-i18n-claude.yml diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 8710f422fc..3194215ad2 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -88,7 +88,7 @@ jobs: uses: actions/setup-node@v4 if: steps.changed-files.outputs.any_changed == 'true' with: - node-version: 22 + node-version: 24 cache: pnpm cache-dependency-path: ./web/pnpm-lock.yaml diff --git a/.github/workflows/tool-test-sdks.yaml b/.github/workflows/tool-test-sdks.yaml index b1ccd7417a..dcbc675cff 100644 --- a/.github/workflows/tool-test-sdks.yaml +++ b/.github/workflows/tool-test-sdks.yaml @@ -16,10 +16,6 @@ jobs: name: unit test for Node.js SDK runs-on: ubuntu-latest - strategy: - matrix: - node-version: [16, 18, 20, 22] - defaults: run: working-directory: sdks/nodejs-client @@ -29,10 +25,15 @@ jobs: with: persist-credentials: false +<<<<<<< HEAD - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 +======= + - name: Use Node.js + uses: actions/setup-node@v6 +>>>>>>> 328897f81c (build: require node 24.13.0 (#30945)) with: - node-version: ${{ matrix.node-version }} + node-version: 24 cache: '' cache-dependency-path: 'pnpm-lock.yaml' diff --git a/.github/workflows/translate-i18n-claude.yml b/.github/workflows/translate-i18n-claude.yml new file mode 100644 index 0000000000..8344af9890 --- /dev/null +++ b/.github/workflows/translate-i18n-claude.yml @@ -0,0 +1,421 @@ +name: Translate i18n Files with Claude Code + +# Note: claude-code-action doesn't support push events directly. +# Push events are handled by trigger-i18n-sync.yml which sends repository_dispatch. +# See: https://github.com/langgenius/dify/issues/30743 + +on: + repository_dispatch: + types: [i18n-sync] + workflow_dispatch: + inputs: + files: + description: 'Specific files to translate (space-separated, e.g., "app common"). Leave empty for all files.' + required: false + type: string + languages: + description: 'Specific languages to translate (space-separated, e.g., "zh-Hans ja-JP"). Leave empty for all supported languages.' + required: false + type: string + mode: + description: 'Sync mode: incremental (only changes) or full (re-check all keys)' + required: false + default: 'incremental' + type: choice + options: + - incremental + - full + +permissions: + contents: write + pull-requests: write + +jobs: + translate: + if: github.repository == 'langgenius/dify' + runs-on: ubuntu-latest + timeout-minutes: 60 + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Configure Git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + package_json_file: web/package.json + run_install: false + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + cache: pnpm + cache-dependency-path: ./web/pnpm-lock.yaml + + - name: Detect changed files and generate diff + id: detect_changes + run: | + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + # Manual trigger + if [ -n "${{ github.event.inputs.files }}" ]; then + echo "CHANGED_FILES=${{ github.event.inputs.files }}" >> $GITHUB_OUTPUT + else + # Get all JSON files in en-US directory + files=$(ls web/i18n/en-US/*.json 2>/dev/null | xargs -n1 basename | sed 's/.json$//' | tr '\n' ' ') + echo "CHANGED_FILES=$files" >> $GITHUB_OUTPUT + fi + echo "TARGET_LANGS=${{ github.event.inputs.languages }}" >> $GITHUB_OUTPUT + echo "SYNC_MODE=${{ github.event.inputs.mode || 'incremental' }}" >> $GITHUB_OUTPUT + + # For manual trigger with incremental mode, get diff from last commit + # For full mode, we'll do a complete check anyway + if [ "${{ github.event.inputs.mode }}" == "full" ]; then + echo "Full mode: will check all keys" > /tmp/i18n-diff.txt + echo "DIFF_AVAILABLE=false" >> $GITHUB_OUTPUT + else + git diff HEAD~1..HEAD -- 'web/i18n/en-US/*.json' > /tmp/i18n-diff.txt 2>/dev/null || echo "" > /tmp/i18n-diff.txt + if [ -s /tmp/i18n-diff.txt ]; then + echo "DIFF_AVAILABLE=true" >> $GITHUB_OUTPUT + else + echo "DIFF_AVAILABLE=false" >> $GITHUB_OUTPUT + fi + fi + elif [ "${{ github.event_name }}" == "repository_dispatch" ]; then + # Triggered by push via trigger-i18n-sync.yml workflow + # Validate required payload fields + if [ -z "${{ github.event.client_payload.changed_files }}" ]; then + echo "Error: repository_dispatch payload missing required 'changed_files' field" >&2 + exit 1 + fi + echo "CHANGED_FILES=${{ github.event.client_payload.changed_files }}" >> $GITHUB_OUTPUT + echo "TARGET_LANGS=" >> $GITHUB_OUTPUT + echo "SYNC_MODE=${{ github.event.client_payload.sync_mode || 'incremental' }}" >> $GITHUB_OUTPUT + + # Decode the base64-encoded diff from the trigger workflow + if [ -n "${{ github.event.client_payload.diff_base64 }}" ]; then + if ! echo "${{ github.event.client_payload.diff_base64 }}" | base64 -d > /tmp/i18n-diff.txt 2>&1; then + echo "Warning: Failed to decode base64 diff payload" >&2 + echo "" > /tmp/i18n-diff.txt + echo "DIFF_AVAILABLE=false" >> $GITHUB_OUTPUT + elif [ -s /tmp/i18n-diff.txt ]; then + echo "DIFF_AVAILABLE=true" >> $GITHUB_OUTPUT + else + echo "DIFF_AVAILABLE=false" >> $GITHUB_OUTPUT + fi + else + echo "" > /tmp/i18n-diff.txt + echo "DIFF_AVAILABLE=false" >> $GITHUB_OUTPUT + fi + else + echo "Unsupported event type: ${{ github.event_name }}" + exit 1 + fi + + # Truncate diff if too large (keep first 50KB) + if [ -f /tmp/i18n-diff.txt ]; then + head -c 50000 /tmp/i18n-diff.txt > /tmp/i18n-diff-truncated.txt + mv /tmp/i18n-diff-truncated.txt /tmp/i18n-diff.txt + fi + + echo "Detected files: $(cat $GITHUB_OUTPUT | grep CHANGED_FILES || echo 'none')" + + - name: Run Claude Code for Translation Sync + if: steps.detect_changes.outputs.CHANGED_FILES != '' + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} + prompt: | + You are a professional i18n synchronization engineer for the Dify project. + Your task is to keep all language translations in sync with the English source (en-US). + + ## CRITICAL TOOL RESTRICTIONS + - Use **Read** tool to read files (NOT cat or bash) + - Use **Edit** tool to modify JSON files (NOT node, jq, or bash scripts) + - Use **Bash** ONLY for: git commands, gh commands, pnpm commands + - Run bash commands ONE BY ONE, never combine with && or || + - NEVER use `$()` command substitution - it's not supported. Split into separate commands instead. + + ## WORKING DIRECTORY & ABSOLUTE PATHS + Claude Code sandbox working directory may vary. Always use absolute paths: + - For pnpm: `pnpm --dir ${{ github.workspace }}/web ` + - For git: `git -C ${{ github.workspace }} ` + - For gh: `gh --repo ${{ github.repository }} ` + - For file paths: `${{ github.workspace }}/web/i18n/` + + ## EFFICIENCY RULES + - **ONE Edit per language file** - batch all key additions into a single Edit + - Insert new keys at the beginning of JSON (after `{`), lint:fix will sort them + - Translate ALL keys for a language mentally first, then do ONE Edit + + ## Context + - Changed/target files: ${{ steps.detect_changes.outputs.CHANGED_FILES }} + - Target languages (empty means all supported): ${{ steps.detect_changes.outputs.TARGET_LANGS }} + - Sync mode: ${{ steps.detect_changes.outputs.SYNC_MODE }} + - Translation files are located in: ${{ github.workspace }}/web/i18n/{locale}/{filename}.json + - Language configuration is in: ${{ github.workspace }}/web/i18n-config/languages.ts + - Git diff is available: ${{ steps.detect_changes.outputs.DIFF_AVAILABLE }} + + ## CRITICAL DESIGN: Verify First, Then Sync + + You MUST follow this three-phase approach: + + ═══════════════════════════════════════════════════════════════ + ║ PHASE 1: VERIFY - Analyze and Generate Change Report ║ + ═══════════════════════════════════════════════════════════════ + + ### Step 1.1: Analyze Git Diff (for incremental mode) + Use the Read tool to read `/tmp/i18n-diff.txt` to see the git diff. + + Parse the diff to categorize changes: + - Lines with `+` (not `+++`): Added or modified values + - Lines with `-` (not `---`): Removed or old values + - Identify specific keys for each category: + * ADD: Keys that appear only in `+` lines (new keys) + * UPDATE: Keys that appear in both `-` and `+` lines (value changed) + * DELETE: Keys that appear only in `-` lines (removed keys) + + ### Step 1.2: Read Language Configuration + Use the Read tool to read `${{ github.workspace }}/web/i18n-config/languages.ts`. + Extract all languages with `supported: true`. + + ### Step 1.3: Run i18n:check for Each Language + ```bash + pnpm --dir ${{ github.workspace }}/web install --frozen-lockfile + ``` + ```bash + pnpm --dir ${{ github.workspace }}/web run i18n:check + ``` + + This will report: + - Missing keys (need to ADD) + - Extra keys (need to DELETE) + + ### Step 1.4: Generate Change Report + + Create a structured report identifying: + ``` + ╔══════════════════════════════════════════════════════════════╗ + ║ I18N SYNC CHANGE REPORT ║ + ╠══════════════════════════════════════════════════════════════╣ + ║ Files to process: [list] ║ + ║ Languages to sync: [list] ║ + ╠══════════════════════════════════════════════════════════════╣ + ║ ADD (New Keys): ║ + ║ - [filename].[key]: "English value" ║ + ║ ... ║ + ╠══════════════════════════════════════════════════════════════╣ + ║ UPDATE (Modified Keys - MUST re-translate): ║ + ║ - [filename].[key]: "Old value" → "New value" ║ + ║ ... ║ + ╠══════════════════════════════════════════════════════════════╣ + ║ DELETE (Extra Keys): ║ + ║ - [language]/[filename].[key] ║ + ║ ... ║ + ╚══════════════════════════════════════════════════════════════╝ + ``` + + **IMPORTANT**: For UPDATE detection, compare git diff to find keys where + the English value changed. These MUST be re-translated even if target + language already has a translation (it's now stale!). + + ═══════════════════════════════════════════════════════════════ + ║ PHASE 2: SYNC - Execute Changes Based on Report ║ + ═══════════════════════════════════════════════════════════════ + + ### Step 2.1: Process ADD Operations (BATCH per language file) + + **CRITICAL WORKFLOW for efficiency:** + 1. First, translate ALL new keys for ALL languages mentally + 2. Then, for EACH language file, do ONE Edit operation: + - Read the file once + - Insert ALL new keys at the beginning (right after the opening `{`) + - Don't worry about alphabetical order - lint:fix will sort them later + + Example Edit (adding 3 keys to zh-Hans/app.json): + ``` + old_string: '{\n "accessControl"' + new_string: '{\n "newKey1": "translation1",\n "newKey2": "translation2",\n "newKey3": "translation3",\n "accessControl"' + ``` + + **IMPORTANT**: + - ONE Edit per language file (not one Edit per key!) + - Always use the Edit tool. NEVER use bash scripts, node, or jq. + + ### Step 2.2: Process UPDATE Operations + + **IMPORTANT: Special handling for zh-Hans and ja-JP** + If zh-Hans or ja-JP files were ALSO modified in the same push: + - Run: `git -C ${{ github.workspace }} diff HEAD~1 --name-only` and check for zh-Hans or ja-JP files + - If found, it means someone manually translated them. Apply these rules: + + 1. **Missing keys**: Still ADD them (completeness required) + 2. **Existing translations**: Compare with the NEW English value: + - If translation is **completely wrong** or **unrelated** → Update it + - If translation is **roughly correct** (captures the meaning) → Keep it, respect manual work + - When in doubt, **keep the manual translation** + + Example: + - English changed: "Save" → "Save Changes" + - Manual translation: "保存更改" → Keep it (correct meaning) + - Manual translation: "删除" → Update it (completely wrong) + + For other languages: + Use Edit tool to replace the old value with the new translation. + You can batch multiple updates in one Edit if they are adjacent. + + ### Step 2.3: Process DELETE Operations + For extra keys reported by i18n:check: + - Run: `pnpm --dir ${{ github.workspace }}/web run i18n:check --auto-remove` + - Or manually remove from target language JSON files + + ## Translation Guidelines + + - PRESERVE all placeholders exactly as-is: + - `{{variable}}` - Mustache interpolation + - `${variable}` - Template literal + - `content` - HTML tags + - `_one`, `_other` - Pluralization suffixes (these are KEY suffixes, not values) + - Use appropriate language register (formal/informal) based on existing translations + - Match existing translation style in each language + - Technical terms: check existing conventions per language + - For CJK languages: no spaces between characters unless necessary + - For RTL languages (ar-TN, fa-IR): ensure proper text handling + + ## Output Format Requirements + - Alphabetical key ordering (if original file uses it) + - 2-space indentation + - Trailing newline at end of file + - Valid JSON (use proper escaping for special characters) + + ═══════════════════════════════════════════════════════════════ + ║ PHASE 3: RE-VERIFY - Confirm All Issues Resolved ║ + ═══════════════════════════════════════════════════════════════ + + ### Step 3.1: Run Lint Fix (IMPORTANT!) + ```bash + pnpm --dir ${{ github.workspace }}/web lint:fix --quiet -- 'i18n/**/*.json' + ``` + This ensures: + - JSON keys are sorted alphabetically (jsonc/sort-keys rule) + - Valid i18n keys (dify-i18n/valid-i18n-keys rule) + - No extra keys (dify-i18n/no-extra-keys rule) + + ### Step 3.2: Run Final i18n Check + ```bash + pnpm --dir ${{ github.workspace }}/web run i18n:check + ``` + + ### Step 3.3: Fix Any Remaining Issues + If check reports issues: + - Go back to PHASE 2 for unresolved items + - Repeat until check passes + + ### Step 3.4: Generate Final Summary + ``` + ╔══════════════════════════════════════════════════════════════╗ + ║ SYNC COMPLETED SUMMARY ║ + ╠══════════════════════════════════════════════════════════════╣ + ║ Language │ Added │ Updated │ Deleted │ Status ║ + ╠══════════════════════════════════════════════════════════════╣ + ║ zh-Hans │ 5 │ 2 │ 1 │ ✓ Complete ║ + ║ ja-JP │ 5 │ 2 │ 1 │ ✓ Complete ║ + ║ ... │ ... │ ... │ ... │ ... ║ + ╠══════════════════════════════════════════════════════════════╣ + ║ i18n:check │ PASSED - All keys in sync ║ + ╚══════════════════════════════════════════════════════════════╝ + ``` + + ## Mode-Specific Behavior + + **SYNC_MODE = "incremental"** (default): + - Focus on keys identified from git diff + - Also check i18n:check output for any missing/extra keys + - Efficient for small changes + + **SYNC_MODE = "full"**: + - Compare ALL keys between en-US and each language + - Run i18n:check to identify all discrepancies + - Use for first-time sync or fixing historical issues + + ## Important Notes + + 1. Always run i18n:check BEFORE and AFTER making changes + 2. The check script is the source of truth for missing/extra keys + 3. For UPDATE scenario: git diff is the source of truth for changed values + 4. Create a single commit with all translation changes + 5. If any translation fails, continue with others and report failures + + ═══════════════════════════════════════════════════════════════ + ║ PHASE 4: COMMIT AND CREATE PR ║ + ═══════════════════════════════════════════════════════════════ + + After all translations are complete and verified: + + ### Step 4.1: Check for changes + ```bash + git -C ${{ github.workspace }} status --porcelain + ``` + + If there are changes: + + ### Step 4.2: Create a new branch and commit + Run these git commands ONE BY ONE (not combined with &&). + **IMPORTANT**: Do NOT use `$()` command substitution. Use two separate commands: + + 1. First, get the timestamp: + ```bash + date +%Y%m%d-%H%M%S + ``` + (Note the output, e.g., "20260115-143052") + + 2. Then create branch using the timestamp value: + ```bash + git -C ${{ github.workspace }} checkout -b chore/i18n-sync-20260115-143052 + ``` + (Replace "20260115-143052" with the actual timestamp from step 1) + + 3. Stage changes: + ```bash + git -C ${{ github.workspace }} add web/i18n/ + ``` + + 4. Commit: + ```bash + git -C ${{ github.workspace }} commit -m "chore(i18n): sync translations with en-US - Mode: ${{ steps.detect_changes.outputs.SYNC_MODE }}" + ``` + + 5. Push: + ```bash + git -C ${{ github.workspace }} push origin HEAD + ``` + + ### Step 4.3: Create Pull Request + ```bash + gh pr create --repo ${{ github.repository }} --title "chore(i18n): sync translations with en-US" --body "## Summary + + This PR was automatically generated to sync i18n translation files. + + ### Changes + - Mode: ${{ steps.detect_changes.outputs.SYNC_MODE }} + - Files processed: ${{ steps.detect_changes.outputs.CHANGED_FILES }} + + ### Verification + - [x] \`i18n:check\` passed + - [x] \`lint:fix\` applied + + 🤖 Generated with Claude Code GitHub Action" --base main + ``` + + claude_args: | + --max-turns 150 + --allowedTools "Read,Write,Edit,Bash(git *),Bash(git:*),Bash(gh *),Bash(gh:*),Bash(pnpm *),Bash(pnpm:*),Bash(date *),Bash(date:*),Glob,Grep" diff --git a/.github/workflows/web-tests.yml b/.github/workflows/web-tests.yml index 1a8925e38d..eb17cf3018 100644 --- a/.github/workflows/web-tests.yml +++ b/.github/workflows/web-tests.yml @@ -31,7 +31,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: pnpm cache-dependency-path: ./web/pnpm-lock.yaml diff --git a/web/Dockerfile b/web/Dockerfile index 20aa43c901..dce7af8d02 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,5 +1,5 @@ # base image -FROM node:22.21.1-alpine3.23 AS base +FROM node:24-alpine AS base LABEL maintainer="takatost@gmail.com" # if you located in China, you can use aliyun mirror to speed up diff --git a/web/README.md b/web/README.md index 7f5740a471..3210cfd219 100644 --- a/web/README.md +++ b/web/README.md @@ -8,8 +8,8 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next Before starting the web frontend service, please make sure the following environment is ready. -- [Node.js](https://nodejs.org) >= v22.11.x -- [pnpm](https://pnpm.io) v10.x +- [Node.js](https://nodejs.org) +- [pnpm](https://pnpm.io) First, install the dependencies: diff --git a/web/package.json b/web/package.json index 3af928aa7b..1a6fee8b00 100644 --- a/web/package.json +++ b/web/package.json @@ -3,9 +3,12 @@ "type": "module", "version": "1.11.2", "private": true, - "packageManager": "pnpm@10.26.1+sha512.664074abc367d2c9324fdc18037097ce0a8f126034160f709928e9e9f95d98714347044e5c3164d65bd5da6c59c6be362b107546292a8eecb7999196e5ce58fa", - "engines": { - "node": ">=v22.11.0" + "packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a", + "imports": { + "#i18n": { + "react-server": "./i18n-config/lib.server.ts", + "default": "./i18n-config/lib.client.ts" + } }, "browserslist": [ "last 1 Chrome version",