mirror of
https://github.com/langgenius/dify-docs.git
synced 2026-03-27 13:28:32 +07:00
401 lines
16 KiB
YAML
401 lines
16 KiB
YAML
name: Update Translation PR
|
||
|
||
on:
|
||
pull_request:
|
||
types: [synchronize]
|
||
paths:
|
||
- 'docs.json'
|
||
- 'en/**/*.md'
|
||
- 'en/**/*.mdx'
|
||
|
||
permissions:
|
||
contents: write
|
||
pull-requests: write
|
||
actions: read
|
||
|
||
jobs:
|
||
update-translation:
|
||
runs-on: ubuntu-latest
|
||
# Only run if this is an English-only PR update
|
||
if: github.event.pull_request.draft == false
|
||
steps:
|
||
- name: Checkout PR
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0
|
||
token: ${{ secrets.GITHUB_TOKEN }}
|
||
|
||
- name: Set up Python
|
||
uses: actions/setup-python@v4
|
||
with:
|
||
python-version: '3.9'
|
||
|
||
- name: Check if PR is English-only
|
||
id: check-pr-type
|
||
run: |
|
||
echo "Checking if this PR contains only English changes..."
|
||
|
||
BASE_SHA="${{ github.event.pull_request.base.sha }}"
|
||
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
|
||
|
||
# Use PR analyzer to check PR type
|
||
cd tools/translate
|
||
python pr_analyzer.py "$BASE_SHA" "$HEAD_SHA" > /tmp/pr_analysis_output.txt 2>&1
|
||
|
||
if [ $? -eq 0 ]; then
|
||
# Parse analyzer output
|
||
source /tmp/pr_analysis_output.txt
|
||
echo "PR Type: $pr_type"
|
||
echo "pr_type=$pr_type" >> $GITHUB_OUTPUT
|
||
|
||
if [ "$pr_type" = "english" ]; then
|
||
echo "✅ English-only PR detected - proceeding with translation update"
|
||
echo "should_update=true" >> $GITHUB_OUTPUT
|
||
else
|
||
echo "ℹ️ Not an English-only PR (type: $pr_type) - skipping translation update"
|
||
echo "should_update=false" >> $GITHUB_OUTPUT
|
||
fi
|
||
else
|
||
echo "❌ PR analysis failed - likely mixed content, skipping translation update"
|
||
echo "should_update=false" >> $GITHUB_OUTPUT
|
||
echo "pr_type=unknown" >> $GITHUB_OUTPUT
|
||
fi
|
||
|
||
- name: Find associated translation PR
|
||
if: steps.check-pr-type.outputs.should_update == 'true'
|
||
id: find-translation-pr
|
||
run: |
|
||
PR_NUMBER=${{ github.event.pull_request.number }}
|
||
echo "Looking for translation PR associated with PR #${PR_NUMBER}..."
|
||
|
||
# Search for translation PR by branch name pattern
|
||
TRANSLATION_PR_DATA=$(gh pr list \
|
||
--search "head:docs-sync-pr-${PR_NUMBER}" \
|
||
--json number,title,url,state \
|
||
--jq '.[0] // empty' 2>/dev/null || echo "")
|
||
|
||
if [ -n "$TRANSLATION_PR_DATA" ] && [ "$TRANSLATION_PR_DATA" != "null" ]; then
|
||
TRANSLATION_PR_NUMBER=$(echo "$TRANSLATION_PR_DATA" | jq -r '.number')
|
||
TRANSLATION_PR_STATE=$(echo "$TRANSLATION_PR_DATA" | jq -r '.state')
|
||
TRANSLATION_PR_URL=$(echo "$TRANSLATION_PR_DATA" | jq -r '.url')
|
||
|
||
if [ "$TRANSLATION_PR_STATE" = "OPEN" ]; then
|
||
echo "✅ Found active translation PR #${TRANSLATION_PR_NUMBER}"
|
||
echo "translation_pr_number=$TRANSLATION_PR_NUMBER" >> $GITHUB_OUTPUT
|
||
echo "translation_pr_url=$TRANSLATION_PR_URL" >> $GITHUB_OUTPUT
|
||
echo "found_translation_pr=true" >> $GITHUB_OUTPUT
|
||
else
|
||
echo "ℹ️ Found translation PR #${TRANSLATION_PR_NUMBER} but it's ${TRANSLATION_PR_STATE} - skipping update"
|
||
echo "found_translation_pr=false" >> $GITHUB_OUTPUT
|
||
fi
|
||
else
|
||
echo "ℹ️ No translation PR found for PR #${PR_NUMBER} - this might be the first update"
|
||
echo "found_translation_pr=false" >> $GITHUB_OUTPUT
|
||
fi
|
||
|
||
- name: Install dependencies
|
||
if: steps.find-translation-pr.outputs.found_translation_pr == 'true'
|
||
run: |
|
||
cd tools/translate
|
||
pip install httpx aiofiles python-dotenv
|
||
|
||
- name: Update translations
|
||
if: steps.find-translation-pr.outputs.found_translation_pr == 'true'
|
||
id: update-translations
|
||
env:
|
||
DIFY_API_KEY: ${{ secrets.DIFY_API_KEY }}
|
||
run: |
|
||
echo "Updating translations for PR #${{ github.event.pull_request.number }}..."
|
||
|
||
PR_NUMBER=${{ github.event.pull_request.number }}
|
||
SYNC_BRANCH="docs-sync-pr-${PR_NUMBER}"
|
||
BASE_SHA="${{ github.event.pull_request.base.sha }}"
|
||
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
|
||
|
||
# Switch to translation branch
|
||
git fetch origin "$SYNC_BRANCH:$SYNC_BRANCH" || {
|
||
echo "❌ Could not fetch translation branch $SYNC_BRANCH"
|
||
echo "update_successful=false" >> $GITHUB_OUTPUT
|
||
exit 0
|
||
}
|
||
|
||
git checkout "$SYNC_BRANCH"
|
||
|
||
# Reset translation branch to latest main to get fresh translations
|
||
git reset --hard origin/main
|
||
|
||
# Re-run translation analysis and generation
|
||
cd tools/translate
|
||
|
||
# Create updated sync script
|
||
cat > update_translations.py <<'EOF'
|
||
import json
|
||
import sys
|
||
import os
|
||
import asyncio
|
||
from pathlib import Path
|
||
|
||
# Add parent directory to path
|
||
sys.path.append(os.path.dirname(__file__))
|
||
from sync_and_translate import DocsSynchronizer
|
||
from pr_analyzer import PRAnalyzer
|
||
|
||
async def update_translations():
|
||
base_sha = sys.argv[1]
|
||
head_sha = sys.argv[2]
|
||
|
||
# Analyze changes
|
||
analyzer = PRAnalyzer(base_sha, head_sha)
|
||
result = analyzer.categorize_pr()
|
||
|
||
if result['type'] != 'english':
|
||
print(f"PR type is {result['type']}, not english - skipping")
|
||
return False
|
||
|
||
# Initialize synchronizer
|
||
api_key = os.environ.get("DIFY_API_KEY")
|
||
if not api_key:
|
||
print("Error: DIFY_API_KEY not set")
|
||
return False
|
||
|
||
synchronizer = DocsSynchronizer(api_key)
|
||
|
||
# Get English files that need translation
|
||
file_categories = result['files']
|
||
english_files = file_categories['english']
|
||
|
||
results = {
|
||
"translated": [],
|
||
"failed": [],
|
||
"skipped": [],
|
||
"updated": True
|
||
}
|
||
|
||
# Translate English files
|
||
for file_path in english_files[:10]: # Limit to 10 files for safety
|
||
print(f"Updating translations for: {file_path}")
|
||
|
||
try:
|
||
for target_lang in ["zh-hans", "ja-jp"]:
|
||
target_path = file_path.replace("en/", f"{target_lang}/")
|
||
success = await synchronizer.translate_file_with_notice(
|
||
file_path,
|
||
target_path,
|
||
target_lang
|
||
)
|
||
if success:
|
||
results["translated"].append(target_path)
|
||
else:
|
||
results["failed"].append(target_path)
|
||
except Exception as e:
|
||
print(f"Error processing {file_path}: {e}")
|
||
results["failed"].append(file_path)
|
||
|
||
# Handle docs.json structure sync if needed
|
||
docs_changes = result['docs_json_changes']
|
||
if docs_changes['any_docs_json_changes']:
|
||
print("Updating docs.json structure...")
|
||
try:
|
||
sync_log = synchronizer.sync_docs_json_structure()
|
||
print("\n".join(sync_log))
|
||
except Exception as e:
|
||
print(f"Error syncing docs.json structure: {e}")
|
||
|
||
# Save results
|
||
with open("/tmp/update_results.json", "w") as f:
|
||
json.dump(results, f, indent=2)
|
||
|
||
return len(results["failed"]) == 0
|
||
|
||
if __name__ == "__main__":
|
||
success = asyncio.run(update_translations())
|
||
sys.exit(0 if success else 1)
|
||
EOF
|
||
|
||
# Run the update
|
||
python update_translations.py "$BASE_SHA" "$HEAD_SHA"
|
||
UPDATE_EXIT_CODE=$?
|
||
|
||
echo "update_exit_code=$UPDATE_EXIT_CODE" >> $GITHUB_OUTPUT
|
||
|
||
# Check for changes
|
||
if [[ -n $(git status --porcelain) ]]; then
|
||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||
echo "✅ Translation updates detected"
|
||
else
|
||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||
echo "ℹ️ No translation updates needed"
|
||
fi
|
||
|
||
- name: Commit and push translation updates
|
||
if: steps.update-translations.outputs.has_changes == 'true'
|
||
id: commit-updates
|
||
run: |
|
||
PR_NUMBER=${{ github.event.pull_request.number }}
|
||
SYNC_BRANCH="docs-sync-pr-${PR_NUMBER}"
|
||
|
||
git config user.name 'github-actions[bot]'
|
||
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
||
|
||
git add .
|
||
git commit -m "🔄 Update translations for PR #${PR_NUMBER}
|
||
|
||
Updated translations following changes in PR #${PR_NUMBER}.
|
||
|
||
Auto-updated by translation workflow.
|
||
🤖 Generated with GitHub Actions"
|
||
|
||
# Push updates to translation branch
|
||
git push origin "$SYNC_BRANCH" --force
|
||
|
||
echo "commit_successful=true" >> $GITHUB_OUTPUT
|
||
echo "✅ Translation updates committed and pushed"
|
||
|
||
- name: Comment on original PR about update
|
||
if: steps.update-translations.outputs.has_changes == 'true' && steps.commit-updates.outputs.commit_successful == 'true'
|
||
uses: actions/github-script@v7
|
||
continue-on-error: true
|
||
with:
|
||
script: |
|
||
const fs = require('fs');
|
||
const prNumber = ${{ github.event.pull_request.number }};
|
||
const translationPrNumber = '${{ steps.find-translation-pr.outputs.translation_pr_number }}';
|
||
const translationPrUrl = '${{ steps.find-translation-pr.outputs.translation_pr_url }}';
|
||
|
||
// Load update results
|
||
let results = { translated: [], failed: [], skipped: [] };
|
||
try {
|
||
results = JSON.parse(fs.readFileSync('/tmp/update_results.json', 'utf8'));
|
||
} catch (e) {
|
||
console.log('Could not load update results');
|
||
}
|
||
|
||
let comment = `## 🔄 Translation PR Updated
|
||
|
||
Your English documentation changes have been automatically translated and the translation PR has been updated.
|
||
|
||
### 🔗 Updated Translation PR: [#${translationPrNumber}](${translationPrUrl})
|
||
|
||
`;
|
||
|
||
if (results.translated && results.translated.length > 0) {
|
||
comment += `### ✅ Updated Translations (${results.translated.length} files):\n`;
|
||
results.translated.slice(0, 6).forEach(file => {
|
||
comment += `- \`${file}\`\n`;
|
||
});
|
||
if (results.translated.length > 6) {
|
||
comment += `- ... and ${results.translated.length - 6} more files\n`;
|
||
}
|
||
comment += '\n';
|
||
}
|
||
|
||
if (results.failed && results.failed.length > 0) {
|
||
comment += `### ⚠️ Update Issues (${results.failed.length}):\n`;
|
||
results.failed.slice(0, 3).forEach(file => {
|
||
comment += `- \`${file}\`\n`;
|
||
});
|
||
comment += '\n';
|
||
}
|
||
|
||
comment += `### 🔄 What's Updated:
|
||
- **Translation Files**: All corresponding zh-hans and ja-jp files
|
||
- **Navigation Structure**: Updated docs.json if needed
|
||
- **Automatic**: This update happened automatically when you updated your PR
|
||
|
||
---
|
||
🤖 _Automatic update from the translation workflow._`;
|
||
|
||
try {
|
||
await github.rest.issues.createComment({
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo,
|
||
issue_number: prNumber,
|
||
body: comment
|
||
});
|
||
} catch (error) {
|
||
console.log('Could not comment on original PR:', error.message);
|
||
}
|
||
|
||
- name: Comment on translation PR about update
|
||
if: steps.update-translations.outputs.has_changes == 'true' && steps.commit-updates.outputs.commit_successful == 'true'
|
||
uses: actions/github-script@v7
|
||
continue-on-error: true
|
||
with:
|
||
script: |
|
||
const fs = require('fs');
|
||
const prNumber = ${{ github.event.pull_request.number }};
|
||
const translationPrNumber = '${{ steps.find-translation-pr.outputs.translation_pr_number }}';
|
||
|
||
// Load update results
|
||
let results = { translated: [], failed: [], skipped: [] };
|
||
try {
|
||
results = JSON.parse(fs.readFileSync('/tmp/update_results.json', 'utf8'));
|
||
} catch (e) {
|
||
console.log('Could not load update results');
|
||
}
|
||
|
||
const updateComment = `## 🔄 Automatic Translation Update
|
||
|
||
This translation PR has been automatically updated following changes in the original PR #${prNumber}.
|
||
|
||
### 📝 What Was Updated:
|
||
- **Source**: Changes from PR #${prNumber}
|
||
- **Updated Files**: ${results.translated ? results.translated.length : 0} translation files
|
||
- **Languages**: Chinese (zh-hans) and Japanese (ja-jp)
|
||
|
||
### ✅ Translation Status:
|
||
${results.translated && results.translated.length > 0 ?
|
||
`**Successfully Updated (${results.translated.length} files):**\n` +
|
||
results.translated.slice(0, 5).map(f => `- \`${f}\``).join('\n') +
|
||
(results.translated.length > 5 ? `\n- ... and ${results.translated.length - 5} more` : '') :
|
||
'- All translations are up to date'}
|
||
|
||
${results.failed && results.failed.length > 0 ?
|
||
`\n### ⚠️ Update Issues:\n${results.failed.slice(0, 3).map(f => `- \`${f}\``).join('\n')}` : ''}
|
||
|
||
### 🔄 Review Process:
|
||
1. **Automatic Update**: This PR was updated automatically
|
||
2. **Review Needed**: Please review the updated translations
|
||
3. **Independent Merge**: This PR can still be merged independently
|
||
|
||
---
|
||
🤖 _This update was triggered automatically by changes to PR #${prNumber}._`;
|
||
|
||
try {
|
||
await github.rest.issues.createComment({
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo,
|
||
issue_number: translationPrNumber,
|
||
body: updateComment
|
||
});
|
||
} catch (error) {
|
||
console.log('Could not comment on translation PR:', error.message);
|
||
}
|
||
|
||
- name: Handle no updates needed
|
||
if: steps.find-translation-pr.outputs.found_translation_pr == 'true' && steps.update-translations.outputs.has_changes != 'true'
|
||
uses: actions/github-script@v7
|
||
continue-on-error: true
|
||
with:
|
||
script: |
|
||
const prNumber = ${{ github.event.pull_request.number }};
|
||
const translationPrNumber = '${{ steps.find-translation-pr.outputs.translation_pr_number }}';
|
||
|
||
const comment = `## ✅ Translation PR Already Up to Date
|
||
|
||
Your changes to PR #${prNumber} did not require translation updates.
|
||
|
||
The translation PR [#${translationPrNumber}](https://github.com/${{ github.repository }}/pull/${translationPrNumber}) remains current.
|
||
|
||
🤖 _Automatic check from the translation workflow._`;
|
||
|
||
try {
|
||
await github.rest.issues.createComment({
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo,
|
||
issue_number: prNumber,
|
||
body: comment
|
||
});
|
||
} catch (error) {
|
||
console.log('Could not comment on original PR:', error.message);
|
||
} |