mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-24 10:40:07 +08:00
try with cc
This commit is contained in:
parent
c7e64b2d55
commit
ecbe8d433a
63
.github/workflows/translator.yml
vendored
63
.github/workflows/translator.yml
vendored
@ -1,57 +1,36 @@
|
||||
name: GitHub Issue Translator
|
||||
|
||||
env:
|
||||
API_KEY: ${{ secrets.TRANSLATE_API_KEY}}
|
||||
MODEL: ${{ vars.MODEL || 'deepseek/deepseek-v3.1'}}
|
||||
BASE_URL: ${{ vars.BASE_URL || 'https://api.ppinfra.com/openai'}}
|
||||
name: Use Claude To Translate
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
discussion:
|
||||
types: [created, edited]
|
||||
discussion_comment:
|
||||
types: [created, edited]
|
||||
pull_request_target:
|
||||
types: [opened, edited]
|
||||
pull_request_review_comment:
|
||||
types: [created, edited]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
translate:
|
||||
claude-response:
|
||||
if: |
|
||||
(github.event_name == 'issue_comment')||(github.event_name == 'issues')
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'CherryHQ/cherry-studio'
|
||||
name: Auto Translate GitHub Content
|
||||
permissions:
|
||||
issues: write
|
||||
discussions: write
|
||||
pull-requests: write
|
||||
contents: read
|
||||
|
||||
pull-requests: read
|
||||
issues: read
|
||||
id-token: write
|
||||
actions: read # Required for Claude to read CI results on PRs
|
||||
steps:
|
||||
- name: 🐈⬛ Checkout
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: 📦 Setting Node.js
|
||||
uses: actions/setup-node@v4
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
node-version: 20
|
||||
fetch-depth: 1
|
||||
|
||||
- name: 📦 Install translation dependencies
|
||||
run: |
|
||||
mkdir -p /tmp/translation-deps
|
||||
cd /tmp/translation-deps
|
||||
echo '{"dependencies": {"openai": "^5.12.2", "@octokit/rest": "^21.0.0"}}' > package.json
|
||||
npm install --no-package-lock
|
||||
echo "NODE_PATH=/tmp/translation-deps/node_modules" >> $GITHUB_ENV
|
||||
- name: Run Claude Code
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
|
||||
- name: 🌐 Run Translation Script
|
||||
run: node scripts/github-translator.js
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
GITHUB_EVENT_NAME: ${{ github.event_name }}
|
||||
GITHUB_CONTEXT: ${{ toJSON(github) }}
|
||||
# This is an optional setting that allows Claude to read CI results on PRs
|
||||
additional_permissions: |
|
||||
actions: read
|
||||
|
||||
prompt: "Replace the issue with its English translation and substitute the original language."
|
||||
|
||||
@ -1,208 +0,0 @@
|
||||
const { Octokit } = require('@octokit/rest')
|
||||
const OpenAI = require('openai')
|
||||
|
||||
class GitHubTranslator {
|
||||
constructor() {
|
||||
this.octokit = new Octokit({
|
||||
auth: process.env.GITHUB_TOKEN
|
||||
})
|
||||
|
||||
this.openai = new OpenAI({
|
||||
apiKey: process.env.API_KEY,
|
||||
baseURL: process.env.BASE_URL
|
||||
})
|
||||
|
||||
this.model = process.env.MODEL || 'deepseek/deepseek-v3.1'
|
||||
this.repo = process.env.GITHUB_REPOSITORY.split('/')
|
||||
this.context = JSON.parse(process.env.GITHUB_CONTEXT)
|
||||
}
|
||||
|
||||
async translateText(text, targetLang = 'English') {
|
||||
if (!text || this.isAlreadyTranslated(text) || this.isPrimarylyEnglish(text)) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.openai.chat.completions.create({
|
||||
model: this.model,
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: `You are a professional translator. Translate the following text to ${targetLang}. Keep the original formatting (markdown, code blocks, links) intact. If the text is already primarily in ${targetLang}, respond with "NO_TRANSLATION_NEEDED".`
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: text
|
||||
}
|
||||
],
|
||||
temperature: 0.3
|
||||
})
|
||||
|
||||
const translation = response.choices[0].message.content.trim()
|
||||
return translation === 'NO_TRANSLATION_NEEDED' ? null : translation
|
||||
} catch (error) {
|
||||
console.error('Translation error:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
isAlreadyTranslated(text) {
|
||||
return (
|
||||
text.includes('**🌐 Translation**') ||
|
||||
text.includes('**English Translation**') ||
|
||||
text.includes('<!-- Translated by GitHub Translator -->')
|
||||
)
|
||||
}
|
||||
|
||||
isPrimarylyEnglish(text) {
|
||||
// Simple heuristic: if text contains mostly English characters
|
||||
const englishChars = text.match(/[a-zA-Z\s]/g) || []
|
||||
const totalChars = text.replace(/\s/g, '')
|
||||
return englishChars.length / totalChars.length > 0.7
|
||||
}
|
||||
|
||||
formatTranslation(originalText, translation) {
|
||||
return `${originalText}
|
||||
|
||||
---
|
||||
|
||||
**🌐 English Translation:**
|
||||
|
||||
${translation}
|
||||
|
||||
<!-- Translated by GitHub Translator -->`
|
||||
}
|
||||
|
||||
async handleIssue() {
|
||||
const issue = this.context.event.issue
|
||||
if (!issue) return
|
||||
|
||||
let updates = {}
|
||||
|
||||
// Translate title
|
||||
if (issue.title) {
|
||||
const translatedTitle = await this.translateText(issue.title)
|
||||
if (translatedTitle) {
|
||||
updates.title = `${issue.title} / ${translatedTitle}`
|
||||
}
|
||||
}
|
||||
|
||||
// Translate body
|
||||
if (issue.body) {
|
||||
const translatedBody = await this.translateText(issue.body)
|
||||
if (translatedBody) {
|
||||
updates.body = this.formatTranslation(issue.body, translatedBody)
|
||||
}
|
||||
}
|
||||
|
||||
// Update issue if we have translations
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await this.octokit.issues.update({
|
||||
owner: this.repo[0],
|
||||
repo: this.repo[1],
|
||||
issue_number: issue.number,
|
||||
...updates
|
||||
})
|
||||
|
||||
console.log(`✅ Translated issue #${issue.number}`)
|
||||
}
|
||||
}
|
||||
|
||||
async handleComment() {
|
||||
const comment = this.context.event.comment
|
||||
if (!comment) return
|
||||
|
||||
const translatedBody = await this.translateText(comment.body)
|
||||
if (translatedBody) {
|
||||
await this.octokit.issues.updateComment({
|
||||
owner: this.repo[0],
|
||||
repo: this.repo[1],
|
||||
comment_id: comment.id,
|
||||
body: this.formatTranslation(comment.body, translatedBody)
|
||||
})
|
||||
|
||||
console.log(`✅ Translated comment #${comment.id}`)
|
||||
}
|
||||
}
|
||||
|
||||
async handlePullRequest() {
|
||||
const pr = this.context.event.pull_request
|
||||
if (!pr) return
|
||||
|
||||
let updates = {}
|
||||
|
||||
// Translate title
|
||||
if (pr.title) {
|
||||
const translatedTitle = await this.translateText(pr.title)
|
||||
if (translatedTitle) {
|
||||
updates.title = `${pr.title} / ${translatedTitle}`
|
||||
}
|
||||
}
|
||||
|
||||
// Translate body
|
||||
if (pr.body) {
|
||||
const translatedBody = await this.translateText(pr.body)
|
||||
if (translatedBody) {
|
||||
updates.body = this.formatTranslation(pr.body, translatedBody)
|
||||
}
|
||||
}
|
||||
|
||||
// Update PR if we have translations
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await this.octokit.pulls.update({
|
||||
owner: this.repo[0],
|
||||
repo: this.repo[1],
|
||||
pull_number: pr.number,
|
||||
...updates
|
||||
})
|
||||
|
||||
console.log(`✅ Translated PR #${pr.number}`)
|
||||
}
|
||||
}
|
||||
|
||||
async handleDiscussion() {
|
||||
// Note: GitHub's GraphQL API would be needed for full discussion support
|
||||
console.log('Discussion translation not implemented yet')
|
||||
}
|
||||
|
||||
async run() {
|
||||
try {
|
||||
const eventName = process.env.GITHUB_EVENT_NAME
|
||||
|
||||
console.log(`🌐 Processing ${eventName} event...`)
|
||||
|
||||
switch (eventName) {
|
||||
case 'issues':
|
||||
await this.handleIssue()
|
||||
break
|
||||
case 'issue_comment':
|
||||
await this.handleComment()
|
||||
break
|
||||
case 'pull_request':
|
||||
case 'pull_request_target':
|
||||
await this.handlePullRequest()
|
||||
break
|
||||
case 'pull_request_review_comment':
|
||||
await this.handleComment()
|
||||
break
|
||||
case 'discussion':
|
||||
case 'discussion_comment':
|
||||
await this.handleDiscussion()
|
||||
break
|
||||
default:
|
||||
console.log(`Event ${eventName} not supported`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Translation workflow error:', error)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run the translator
|
||||
if (require.main === module) {
|
||||
const translator = new GitHubTranslator()
|
||||
translator.run()
|
||||
}
|
||||
|
||||
module.exports = GitHubTranslator
|
||||
Loading…
Reference in New Issue
Block a user