Pull docs translations from Crowdin #470
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: 'Pull docs translations from Crowdin' | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| on: | |
| schedule: | |
| - cron: '0 */2 * * *' # Every two hours | |
| workflow_dispatch: | |
| inputs: | |
| force_pull: | |
| description: 'Force pull translations regardless of status' | |
| required: false | |
| type: boolean | |
| default: false | |
| workflow_call: | |
| inputs: | |
| force_pull: | |
| description: 'Force pull translations regardless of status' | |
| required: false | |
| type: boolean | |
| default: false | |
| pull_request: | |
| paths: | |
| - 'packages/twenty-docs/**' | |
| - '.github/crowdin-docs.yml' | |
| - '.github/workflows/docs-i18n-pull.yaml' | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} | |
| jobs: | |
| pull_docs_translations: | |
| name: Pull docs translations | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| token: ${{ github.token }} | |
| repository: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name || github.repository }} | |
| ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} | |
| - name: Install dependencies | |
| uses: ./.github/actions/yarn-install | |
| - name: Setup i18n-docs branch | |
| if: github.event_name != 'pull_request' | |
| run: | | |
| git fetch origin i18n-docs || true | |
| git checkout -B i18n-docs origin/i18n-docs || git checkout -b i18n-docs | |
| - name: Configure git | |
| run: | | |
| git config --global user.name 'github-actions' | |
| git config --global user.email 'github-actions@twenty.com' | |
| - name: Stash any changes before pulling translations | |
| if: github.event_name != 'pull_request' | |
| run: | | |
| git add . | |
| git stash || true | |
| # Install Crowdin CLI for downloading translations | |
| - name: Install Crowdin CLI | |
| if: github.event_name != 'pull_request' && (inputs.force_pull == true || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') | |
| run: npm install -g @crowdin/cli | |
| - name: Pre-translate untranslated strings | |
| if: github.event_name != 'pull_request' && (inputs.force_pull == true || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') | |
| run: | | |
| COMMON="--config .github/crowdin-docs.yml --token $CROWDIN_PERSONAL_TOKEN --base-url https://twenty.api.crowdin.com --translate-untranslated-only --no-progress" | |
| echo "=== TM pre-translate (perfect matches only) ===" | |
| crowdin pre-translate --method tm --translate-with-perfect-match-only $COMMON || echo "::warning::TM pre-translate failed (non-blocking)" | |
| echo "=== MT pre-translate (engine 2: Crowdin Translate) ===" | |
| crowdin pre-translate --method mt --engine-id 2 $COMMON || echo "::warning::MT pre-translate failed (non-blocking)" | |
| echo "=== AI pre-translate (prompt 1; covers ko/tr + markup MT skips) ===" | |
| crowdin pre-translate --method ai --ai-prompt 1 $COMMON || echo "::warning::AI pre-translate failed (non-blocking)" | |
| env: | |
| CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} | |
| # Pull docs translations from Crowdin one language at a time | |
| # This avoids build timeout issues when processing all languages at once | |
| - name: Pull translated docs from Crowdin | |
| id: pull | |
| if: github.event_name != 'pull_request' && (inputs.force_pull == true || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') | |
| run: | | |
| # Languages supported by Mintlify (see packages/twenty-docs/src/shared/supported-languages.ts) | |
| LANGUAGES="fr ar cs de es it ja ko pt ro ru tr zh-CN" | |
| FAILED_LANGUAGES="" | |
| for lang in $LANGUAGES; do | |
| echo "=== Pulling translations for $lang ===" | |
| if ! crowdin download \ | |
| --config .github/crowdin-docs.yml \ | |
| --token "$CROWDIN_PERSONAL_TOKEN" \ | |
| --base-url "https://twenty.api.crowdin.com" \ | |
| --language "$lang" \ | |
| --skip-untranslated-strings=false \ | |
| --skip-untranslated-files=false \ | |
| --export-only-approved=false \ | |
| --verbose; then | |
| echo "::warning::Crowdin download failed for $lang (likely a server-side build failure); its translations will be stale until fixed." | |
| FAILED_LANGUAGES="$FAILED_LANGUAGES $lang" | |
| fi | |
| echo "" | |
| done | |
| echo "failed_languages=${FAILED_LANGUAGES# }" >> "$GITHUB_OUTPUT" | |
| echo "=== Download complete (failed:${FAILED_LANGUAGES:-none}) ===" | |
| env: | |
| CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} | |
| - name: Fix file permissions | |
| if: github.event_name != 'pull_request' | |
| run: sudo chown -R runner:docker . || true | |
| - name: Fix translated documentation links | |
| run: bash packages/twenty-docs/scripts/fix-translated-links.sh | |
| - name: Prune orphan localized translations | |
| if: github.event_name != 'pull_request' && (inputs.force_pull == true || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') | |
| run: yarn docs:prune-orphans --apply | |
| - name: Regenerate navigation template | |
| if: github.event_name == 'pull_request' | |
| run: yarn docs:generate-navigation-template | |
| - name: Regenerate docs.json | |
| run: yarn docs:generate | |
| - name: Regenerate documentation paths constants | |
| run: yarn docs:generate-paths | |
| - name: Commit artifacts to pull request branch | |
| if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository | |
| run: | | |
| git add packages/twenty-docs/docs.json packages/twenty-docs/navigation/navigation.template.json packages/twenty-shared/src/constants/DocumentationPaths.ts | |
| if git diff --staged --quiet --exit-code; then | |
| echo "No navigation/doc changes to commit." | |
| exit 0 | |
| fi | |
| git commit -m "chore: sync docs artifacts" | |
| git push origin "HEAD:$HEAD_REF" | |
| env: | |
| HEAD_REF: ${{ github.head_ref }} | |
| - name: Check for changes and commit | |
| if: github.event_name != 'pull_request' | |
| id: check_changes | |
| run: | | |
| git add . | |
| if ! git diff --staged --quiet --exit-code; then | |
| git commit -m "chore: update docs translations from Crowdin and fix internal links" | |
| echo "changes_detected=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "changes_detected=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Push changes | |
| if: github.event_name != 'pull_request' && steps.check_changes.outputs.changes_detected == 'true' | |
| run: git push origin HEAD:i18n-docs | |
| - name: Create pull request | |
| if: github.event_name != 'pull_request' && steps.check_changes.outputs.changes_detected == 'true' | |
| run: | | |
| if git diff --name-only origin/main..HEAD | grep -q .; then | |
| gh pr create -B main -H i18n-docs --title 'i18n - docs translations' --body 'Created by Github action' || true | |
| else | |
| echo "No file differences between branches, skipping PR creation" | |
| fi | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Mint twenty-infra dispatch token | |
| id: app-token | |
| if: github.event_name != 'pull_request' && steps.check_changes.outputs.changes_detected == 'true' | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | |
| with: | |
| client-id: ${{ vars.TWENTY_WORKFLOW_DISPATCHER_CLIENT_ID }} | |
| private-key: ${{ secrets.TWENTY_WORKFLOW_DISPATCHER_PRIVATE_KEY }} | |
| owner: twentyhq | |
| repositories: twenty-infra | |
| permission-actions: write | |
| - name: Trigger i18n automerge | |
| if: github.event_name != 'pull_request' && steps.check_changes.outputs.changes_detected == 'true' | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| run: | | |
| gh workflow run automerge-i18n.yaml --repo twentyhq/twenty-infra --ref main | |
| - name: Fail run if any language failed to download | |
| if: always() && steps.pull.outputs.failed_languages != '' | |
| run: | | |
| echo "::error::Crowdin download failed for:${{ steps.pull.outputs.failed_languages }}. These languages were skipped and are now stale — fix the source (e.g. build-breaking translations) and re-run." | |
| exit 1 |