mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user