name: Crowdin
on:
workflow_dispatch:
schedule:
- cron: "17 3 * * *"
push:
branches:
- master
paths:
- crates/openlogi-gui/locales/en.yml
- crowdin.yml
- .github/workflows/crowdin.yml
- scripts/i18n/**
- .github/actions/github-app-token-from-1password/**
permissions:
contents: read
concurrency:
group: crowdin-${{ github.ref }}
cancel-in-progress: true
jobs:
synchronize:
name: Synchronize translations
runs-on: ubuntu-latest
if: github.repository == 'AprilNEA/OpenLogi'
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Mint GitHub App token
id: github_app
uses: ./.github/actions/github-app-token-from-1password
with:
op-service-account-token: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
op-github-app-item: ${{ secrets.OP_GITHUB_APP_ITEM }}
- name: Configure git for App token
env:
GH_TOKEN: ${{ steps.github_app.outputs.token }}
run: |
set -euo pipefail
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
- name: Load Crowdin credentials from 1Password
id: crowdin_config
uses: 1password/load-secrets-action@eb2efd0703da22a93c467f2d1ffbb6826c11e19c with:
export-env: false
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.OP_CROWDIN_SECRET_ITEM }}/CROWDIN_PROJECT_ID
CROWDIN_PERSONAL_TOKEN: ${{ secrets.OP_CROWDIN_SECRET_ITEM }}/CROWDIN_PERSONAL_TOKEN
- name: Self-test locale merge script
run: python3 scripts/i18n/merge_crowdin_download.py --self-test
- name: Snapshot locale catalogs
run: |
set -euo pipefail
mkdir -p /tmp/openlogi-locales-before
cp crates/openlogi-gui/locales/*.yml /tmp/openlogi-locales-before/
- name: Synchronize with Crowdin
uses: crowdin/github-action@c7af9bc98b01694653031fef2a0dc6c7888ce9bc with:
config: crowdin.yml
upload_sources: true
upload_translations: true
import_eq_suggestions: false
download_translations: true
skip_untranslated_strings: true
push_translations: false
create_pull_request: false
env:
GITHUB_TOKEN: ${{ steps.github_app.outputs.token }}
CROWDIN_PROJECT_ID: ${{ steps.crowdin_config.outputs.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ steps.crowdin_config.outputs.CROWDIN_PERSONAL_TOKEN }}
- name: Merge Crowdin export into complete catalogs
run: |
set -euo pipefail
python3 scripts/i18n/merge_crowdin_download.py \
--before /tmp/openlogi-locales-before \
--locales crates/openlogi-gui/locales \
--en crates/openlogi-gui/locales/en.yml
- name: Push translation branch and open PR
env:
GH_TOKEN: ${{ steps.github_app.outputs.token }}
run: |
set -euo pipefail
if git diff --quiet -- crates/openlogi-gui/locales/; then
echo "No real Crowdin translation updates"
exit 0
fi
git config user.name "Crowdin Bot"
git config user.email "support+bot@crowdin.com"
git checkout -B crowdin/i18n
git add crates/openlogi-gui/locales/
git commit -m "chore(i18n): sync Crowdin translations"
git push -f origin crowdin/i18n
if gh pr view crowdin/i18n --json number --jq .number >/dev/null 2>&1; then
echo "Translation PR already open for crowdin/i18n"
else
body="$(cat <<'EOF'
Automated per-language translation sync from Crowdin.
- `en.yml` is the English source of truth; feature PRs keep key parity across all `locales/*.yml`.
- Existing non-English catalogs are uploaded first so Crowdin is seeded with real work.
- Download is merged into complete catalogs: only values that differ from English are applied.
- Untranslated keys are never replaced with English fill-in and never deleted (#549 / #552).
EOF
)"
# Strip the common indent from the workflow heredoc.
body="$(printf '%s\n' "$body" | sed 's/^ //')"
gh pr create \
--base master \
--head crowdin/i18n \
--title "chore(i18n): sync Crowdin translations" \
--body "$body"
fi