name: Crowdin
on:
workflow_dispatch:
schedule:
- cron: "17 3 * * *"
push:
branches:
- master
paths:
- crates/openlogi-ui/locales/en.toml
- .config/crowdin.yml
- .github/workflows/crowdin.yml
- .github/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@v7
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@70062d7a876d3eb6334754fa26efd2fbd90c32f2
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 .github/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-ui/locales/*.toml /tmp/openlogi-locales-before/
- name: Synchronize with Crowdin
uses: crowdin/github-action@8f01d54f70f1713ee3f09d82c2bbb2daeac28689
with:
config: .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 .github/scripts/i18n/merge_crowdin_download.py \
--before /tmp/openlogi-locales-before \
--locales crates/openlogi-ui/locales \
--en crates/openlogi-ui/locales/en.toml
- 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-ui/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-ui/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.toml` is the English source of truth; feature PRs keep semantic-key parity across all `locales/*.toml`.
- 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 /
EOF
)"
body="$(printf '%s\n' "$body" | sed 's/^ //')"
gh pr create \
--base master \
--head crowdin/i18n \
--title "chore(i18n): sync Crowdin translations" \
--body "$body"
fi