Skip to content

Add continuous translations (Faster Releases, Phase 2)#25675

Draft
jkmassel wants to merge 5 commits into
trunkfrom
jkmassel/daily-translation-sync
Draft

Add continuous translations (Faster Releases, Phase 2)#25675
jkmassel wants to merge 5 commits into
trunkfrom
jkmassel/daily-translation-sync

Conversation

@jkmassel

Copy link
Copy Markdown
Contributor

Description

Phase 2 of the Faster Releases RFC: keep translations continuously in sync instead of only at code freeze. Two halves, both in Buildkite, additive to the existing release process.

Upload on each trunk merge

Regenerate the English Localizable.strings from code, run a placeholder guardrail, and push to trunk so GlotPress imports new strings promptly.

  • upload_strings_for_translation lane + a trunk-gated 🌐 Localization step.
  • Guardrail (StringPlaceholders, fastlane/helpers/string_placeholders.rb): an existing key may not change its placeholder shape — count, position, or argument type — without getting a new key, since that would silently break every existing translation. New and removed keys are fine.

Download daily

Download the latest human translations, AI-backfill anything still untranslated so the app never ships untranslated copy, and open/update a single PR (dependabot-style, branch bot/translation-sync).

  • sync_translations lane + .buildkite/translation-sync.yml (the daily schedule itself is configured in Buildkite).
  • backfill_missing_translations: translates missing keys via claude-sonnet-4-6, drops any result whose placeholders don't match the source, and appends under a /* Machine-translated */ marker. The AI output is a stopgap — replaced by human translations on the next sync, and never uploaded to GlotPress.

Testing instructions

This PR runs a 🌐 Upload strings (dry run) step: it regenerates the strings and runs the guardrail on this branch, but does not commit or push (dry_run:true). Watch that job to confirm regeneration + the guardrail work end-to-end in CI.

  • ✅ green — regeneration and the guardrail pass; the job log lists the strings that would be uploaded.
  • ❌ red on the guardrail — a key changed its placeholders incompatibly (a real finding).

⚠️ Before merge

  • Bot must be able to push to trunk. The real upload step does a direct CI push (source use-bot-for-git). Confirm wpmobilebot isn't blocked by trunk branch protection — otherwise we switch to an auto-merge PR.
  • ANTHROPIC_API_KEY must be present in the CI environment for the backfill.
  • The daily backfill re-translates all still-missing strings each run (no caching yet) — recurring cost worth a conscious sign-off.
  • Decide whether the PR dry-run stays as a permanent pre-merge guardrail (an extra genstrings + SPM job per PR) or gets scoped to localization-affecting changes.

Related

jkmassel added 4 commits June 19, 2026 09:24
Part of "Faster Releases" Phase 2 (continuous translations). A Fastlane helper
(`StringPlaceholders`) that flags any localization key present in both the old
and the new English source whose placeholder shape — count, position, or
argument type — changed. New and removed keys are ignored, since copy that needs
a fresh translation is expected to land under a new key.

Exposed as the `validate_string_placeholders` lane, and reused to validate that
AI-backfilled translations preserve their source's placeholders. Enforces the
invariant the continuous-translation model relies on: never reuse a key for
placeholder-incompatible copy.
Part of "Faster Releases" Phase 2 (continuous translations). The daily sync runs
this so the app never ships an untranslated string.

- `backfill_missing_translations` lane: for each locale, translate any key that
  exists in the English base but is missing from the locale, validate that each
  result preserves its placeholders, and append it to the locale's
  `Localizable.strings` under a marker comment.
- `AITranslator` helper wraps the Anthropic API (sonnet-4-6, batched, JSON
  responses) and drops any translation whose placeholders don't match the source.
- Adds the `anthropic` gem.

Human translations from GlotPress overwrite these on the next sync; the AI
output is never uploaded to GlotPress.
Phase 2 of the "Faster Releases" RFC. Both halves run in Buildkite.

Upload (on each trunk merge): regenerate the English `Localizable.strings` from
code, run the placeholder guardrail, and push to trunk so GlotPress imports new
strings promptly. Trunk-gated, and guarded against re-triggering itself.

Download (daily): `sync_translations` downloads the latest translations, runs the
AI backfill, and opens/updates a single PR to trunk (dependabot-style). Scheduled
via `.buildkite/translation-sync.yml`.

- `sync_translations` + `upload_strings_for_translation` lanes
- `.buildkite/commands/{sync-translations,upload-strings-for-translation}.sh`
- trunk-gated upload step in pipeline.yml (mac queue); daily pipeline yaml

The daily schedule itself is configured in Buildkite, pointing at the new pipeline.
Lets the upload-on-merge flow be exercised from a PR without touching trunk:
`dry_run:true` regenerates the English strings and runs the placeholder guardrail
but skips the commit and push. Adds a PR-only "Upload strings (dry run)" step;
the real step stays trunk-gated.
@dangermattic

Copy link
Copy Markdown
Collaborator
1 Warning
⚠️ This PR is larger than 500 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.
1 Message
📖 This PR is still a Draft: some checks will be skipped.

Generated by 🚫 Danger

The `fastlane/helpers/` path isn't covered by the lane-file exclusions in
.rubocop.yml, so the Metrics cops apply.

- Single-quote the model symbol (Style/QuotedSymbols).
- Extract translation validation out of `translate` into
  `validated_translations` (AbcSize / CyclomaticComplexity / RedundantEach).
- Collapse `signature` to one uniform position-keyed path and extract
  `specifiers` (AbcSize / CyclomaticComplexity / PerceivedComplexity). The
  signature string is internal — only compared within a run — so the change is
  behavior-preserving.
@wpmobilebot

Copy link
Copy Markdown
Contributor
App Icon📲 You can test the changes from this Pull Request in WordPress by scanning the QR code below to install the corresponding build.
App NameWordPress
ConfigurationRelease-Alpha
Build Number32722
VersionPR #25675
Bundle IDorg.wordpress.alpha
Commit28313d6
Installation URL6h958nrtk3388
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot

Copy link
Copy Markdown
Contributor
App Icon📲 You can test the changes from this Pull Request in Jetpack by scanning the QR code below to install the corresponding build.
App NameJetpack
ConfigurationRelease-Alpha
Build Number32722
VersionPR #25675
Bundle IDcom.jetpack.alpha
Commit28313d6
Installation URL5pcc8qi7citq8
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants