diff --git a/.github/workflows/translate.yaml b/.github/workflows/translate.yaml new file mode 100644 index 000000000..69a633f05 --- /dev/null +++ b/.github/workflows/translate.yaml @@ -0,0 +1,68 @@ +name: Translate Docs +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} +on: + push: + branches: + - main + paths: + - "docs/*" +permissions: + contents: write + pull-requests: write +jobs: + build: + name: Translate Docs + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + persist-credentials: false + - id: md_files + run: | + FILES=$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" -- 'docs/*.md') + FILES=$(echo "$FILES" | xargs -n1 basename | tr '\n' ' ') + [ -z "$FILES" ] && echo "found=false" >> "$GITHUB_OUTPUT" || echo "found=true" >> "$GITHUB_OUTPUT" + echo "files=$FILES" >> "$GITHUB_OUTPUT" + - name: Set up PHP + if: steps.md_files.outputs.found == 'true' + uses: shivammathur/setup-php@v2 + with: + php-version: '8.5' + - name: run translation script + if: steps.md_files.outputs.found == 'true' + env: + GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}' + MD_FILES: '${{ steps.md_files.outputs.files }}' + run: | + php ./docs/translate.php "$MD_FILES" + - name: Run Linter + if: steps.md_files.outputs.found == 'true' + uses: super-linter/super-linter/slim@v8 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + LINTER_RULES_PATH: / + MARKDOWN_CONFIG_FILE: .markdown-lint.yaml + FIX_NATURAL_LANGUAGE: true + FIX_MARKDOWN: true + - name: Create Pull Request + if: steps.md_files.outputs.found == 'true' + uses: peter-evans/create-pull-request@v8 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + title: "docs: update translations" + commit-message: "docs: update translations" + committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> + author: ${{ github.actor }} <${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com> + branch: translations/${{ github.run_id }} + delete-branch: true + body: | + Translation updates for: ${{ steps.md_files.outputs.files }}. + labels: | + translations + bot + draft: false diff --git a/docs/translate.php b/docs/translate.php new file mode 100644 index 000000000..d491b58e8 --- /dev/null +++ b/docs/translate.php @@ -0,0 +1,126 @@ + 'Chinese', + 'fr' => 'French', + 'ja' => 'Japanese', + 'pt-br' => 'Portuguese (Brazilian)', + 'ru' => 'Russian', + 'tr' => 'Turkish', +]; + +function makeGeminiRequest(string $systemPrompt, string $userPrompt, string $model, string $apiKey, int $reties = 2): string +{ + $url = "https://generativelanguage.googleapis.com/v1beta/models/$model:generateContent"; + $body = json_encode([ + "contents" => [ + ["role" => "model", "parts" => ['text' => $systemPrompt]], + ["role" => "user", "parts" => ['text' => $userPrompt]] + ], + ]); + + $response = @file_get_contents($url, false, stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'header' => "Content-Type: application/json\r\nX-Goog-Api-Key: $apiKey\r\nContent-Length: " . strlen($body) . "\r\n", + 'content' => $body, + 'timeout' => 300, + ] + ])); + $generatedDocs = json_decode($response, true)['candidates'][0]['content']['parts'][0]['text'] ?? ''; + + if (!$response || !$generatedDocs) { + print_r(error_get_last()); + print_r($response); + if ($reties > 0) { + echo "Retrying... ($reties retries left)\n"; + sleep(SLEEP_SECONDS_BETWEEN_REQUESTS); + return makeGeminiRequest($systemPrompt, $userPrompt, $model, $apiKey, $reties - 1); + } + exit(1); + } + + return $generatedDocs; +} + +function createPrompt(string $language, string $englishFile, string $currentTranslation): array +{ + $systemPrompt = << str_ends_with($filename, '.md')); +foreach ($files as $file) { + $englishFile = file_get_contents(__DIR__ . "/$file"); + if ($fileToTranslate && !in_array($file, $fileToTranslate)) { + continue; + } + foreach (LANGUAGES as $language => $languageName) { + echo "Translating $file to $languageName\n"; + $currentTranslation = file_get_contents(__DIR__ . "/$language/$file") ?: ''; + [$systemPrompt, $userPrompt] = createPrompt($language, $englishFile, $currentTranslation); + $markdown = makeGeminiRequest($systemPrompt, $userPrompt, MODEL, $apiKey); + + echo "Writing translated file to $language/$file\n"; + file_put_contents(__DIR__ . "/$language/$file", sanitizeMarkdown($markdown)); + + echo "sleeping to avoid rate limiting...\n"; + sleep(SLEEP_SECONDS_BETWEEN_REQUESTS); + } +}