fix(client): prevent Crowdin translation loss

Use sparse target catalogs and validate Crowdin exports before proposing updates. Reject translation loss unless a maintainer acknowledges each key.
This commit is contained in:
neon
2026-08-05 23:02:58 +00:00
parent 6b0f561c76
commit 8790208858
11 changed files with 749 additions and 67 deletions
+1
View File
@@ -259,6 +259,7 @@ jobs:
- 'pnpm-workspace.yaml'
- '.github/actions/setup-node-pnpm/action.yml'
- '.github/workflows/ci.yml'
- '.github/workflows/crowdin-translations.yml'
- name: Print change scope
shell: bash
+3 -6
View File
@@ -9,11 +9,8 @@ permissions:
jobs:
delete-service-branch:
# Crowdin appends every export to the same service branch and never rebases it.
# A branch that outlives its pull request keeps its original merge base, so each
# later export diverges further from main and eventually conflicts on every
# catalog. Deleting it forces Crowdin to recreate it from the current main.
# This runs on close as well as merge, because Crowdin reuses the branch either way.
# The controlled export workflow reuses this branch for its translation PR.
# Removing it after close guarantees that the next export starts from current main.
if: github.event.pull_request.head.ref == 'l10n_main' && github.event.pull_request.head.repo.full_name == github.repository
name: Delete l10n_main
runs-on: ubuntu-latest
@@ -27,7 +24,7 @@ jobs:
run: |
if gh api "repos/$REPO/git/refs/heads/$BRANCH" >/dev/null 2>&1; then
gh api -X DELETE "repos/$REPO/git/refs/heads/$BRANCH"
echo "Deleted $BRANCH; Crowdin will recreate it from main on the next export."
echo "Deleted $BRANCH; the translation workflow will recreate it from main when needed."
else
echo "$BRANCH is already absent; nothing to delete."
fi
+108
View File
@@ -0,0 +1,108 @@
name: Crowdin Translation Sync
on:
workflow_dispatch:
inputs:
allowed_translation_losses:
description: "Comma-separated locale:message.key acknowledgements for intentional translation removals"
required: false
type: string
schedule:
- cron: "47 5 * * *"
permissions:
contents: read
concurrency:
group: crowdin-translation-sync
cancel-in-progress: false
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
NODE_VERSION: "24"
PNPM_VERSION: "10"
jobs:
synchronize:
name: Validate and propose translations
if: github.repository == 'bookorbit/bookorbit'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout main
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: main
- name: Set up Node.js and pnpm
uses: ./.github/actions/setup-node-pnpm
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
prefetch: "true"
- name: Check synchronization credentials
env:
CROWDIN_TOKEN: ${{ secrets.CROWDIN_TOKEN }}
CROWDIN_PR_TOKEN: ${{ secrets.CROWDIN_PR_TOKEN }}
run: |
if [ -z "$CROWDIN_TOKEN" ]; then
echo "::error::CROWDIN_TOKEN is required."
exit 1
fi
if [ -z "$CROWDIN_PR_TOKEN" ]; then
echo "::error::CROWDIN_PR_TOKEN is required so pull request workflows are triggered."
exit 1
fi
- name: Build sparse translation catalogs
env:
CROWDIN_ALLOWED_TRANSLATION_LOSSES: ${{ inputs.allowed_translation_losses }}
CROWDIN_PROJECT_ID: ${{ vars.CROWDIN_PROJECT_ID }}
CROWDIN_TOKEN: ${{ secrets.CROWDIN_TOKEN }}
run: node client/scripts/sync-crowdin-translations.mjs
- name: Validate translation catalogs
run: pnpm --filter client validate:locales
- name: Test localization runtime and synchronization
run: >-
pnpm --filter client exec vitest run
scripts/locale-catalog-validation.spec.mjs
scripts/sync-crowdin-translations.spec.mjs
src/i18n/icu.spec.ts
- name: Create translation pull request
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ secrets.CROWDIN_PR_TOKEN }}
add-paths: |
client/src/locales/cs.json
client/src/locales/da.json
client/src/locales/de.json
client/src/locales/es.json
client/src/locales/fi.json
client/src/locales/fr.json
client/src/locales/it.json
client/src/locales/nl.json
client/src/locales/pl.json
client/src/locales/pt.json
client/src/locales/ru.json
client/src/locales/sl.json
client/src/locales/sv.json
client/src/locales/uk.json
client/src/locales/zh.json
commit-message: |-
i18n(client): sync translations from Crowdin
Keep sparse translated catalogs synchronized with reviewed Crowdin content.
committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
branch: l10n_main
delete-branch: true
base: main
title: "i18n(client): sync translations from Crowdin"
body: |-
Automated sparse translation export from Crowdin.
Untranslated entries are omitted so Vue I18n falls back to the English catalog.