Repository navigation
Translate only changed segments and protect code from the translation service - #436
Merged
Merged
Conversation
… service When the English of a translated file changes, diff the English the translation was made from (status sourceBlob, verified against sourceHash, else looked up in git history) against the current English and send only what changed: - Markdown: changed blocks (paragraphs, headings, list items, table rows, alerts) and frontmatter title/description; unchanged and moved blocks are kept from the translation; blocks whose only change is inline code, a marker, a link target or the list number are repaired without a request; fenced code comes from English. - _ui-strings.json: changed keys. toc.yml: changed names (matched by name + href). Files are sent whole when the translation does not align with or verify against its English, is the English text, mostly changed (segmentDiff.maxChangedRatio) or when --retry-failed retries a failed segment update. Unchanged blocks are sent as untranslatable context (not billed): segmentDiff.contextSegments (default 2 per side), overridable with the TRANSLATED_CONTEXT_SEGMENTS repository variable. Code placeholders: the service rewrites backticks as quotation marks, which broke code fences and inline code in the first production run (#432). Fenced blocks and inline code spans are now sent as {{fN}}/{{cN}} placeholders and put back on delivery; a missing placeholder rejects the delivery. Losing inline code spans is now a problem instead of a warning. Also: restore_positionally leaves spans the translation reordered alone, --migrate-blobs backfills sourceBlob, the dry-run job checks out full history.
Output of translate-content.py --migrate-blobs: the git blob of the English version each translation was made from (es 421, zh 420 entries).
|
Azure Static Web Apps: Your stage site is ready! Visit it here: https://lemon-wave-075f3af03-436.westeurope.5.azurestaticapps.net |
Metallist1
approved these changes
Oct 21, 2026
- Repair heading-level-only changes from the existing translation, without a request. - A rejected key (401/403) stops the run and records nothing; other failed requests are recorded but planned again, since they were not billed. - Retry 408, honour Retry-After (capped at 60 s) and jitter the backoff. - --keep-rejected saves billed deliveries that fail verification. - --merge-status rebuilds the status files entry by entry after the translation branch is folded in, so older branch entries never overwrite newer ones from main. - Workflow: nightly schedule instead of push; 35-minute step timeout below the job's 45; push and PR steps run on cancel; this run's translations, rejected deliveries and the report are uploaded as an artifact.
… the change - downloads, release-history, user-options (es, zh): version, date and URL updates, "six" -> "seven" and a new table row, built from the existing translated siblings through the segment rebuild and verification; recorded at the current English. - Structurally stale translations repaired and recorded against the English they were made from (Crowdin sync 5c994d1): security-privacy (es, zh: link spacing matched to the English), DR002 (es: duplicated clause removed), release notes 3_0_3, 3_1_2, 3_3_4 (zh: extra or duplicated onboarding sentence removed), personalizing-te3 (es: mistranslated heading rewritten).
|
Azure Static Web Apps: Your stage site is ready! Visit it here: https://blue-island-085db6f03-436.westeurope.6.azurestaticapps.net |
mlonsk
enabled auto-merge (squash)
October 23, 2026 09:09
|
Azure Static Web Apps: Your stage site is ready! Visit it here: https://blue-island-085db6f03-436.westeurope.6.azurestaticapps.net |
Metallist1
approved these changes
Oct 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
`DisableAi`→ «DisableAi»). Pages with code fences were rejected (code fences 4 -> 2), and pages that only lost inline code were written broken, because that was only a warning.What changes
Segment-level updates. When the English of a translated file changes, the run diffs the English the translation was made from against the current English and sends only what changed.
title/description_ui-strings.jsontoc.ymlsourceBlob, the git blob of the English the translation was made from. Its content is checked againstsourceHash; when the blob is missing, the version is looked up bysourceHashin git history.--planand in the run report, when:--synccopy);segmentDiff.maxChangedRatio(0.8) of the text changed;--retry-failedretries a failed segment update.sourceHash/sourceBlob.translatable: falsecontext. Translated confirmed context is not billed. The default is 2 blocks per side (segmentDiff.contextSegments); the repository variableTRANSLATED_CONTEXT_SEGMENTSoverrides it (-1= whole page,0= none).Code placeholders.
{{fN}}/{{cN}}placeholders, in both whole-file and segment orders, and put back on delivery.Repair fix.
restore_positionallyno longer swaps inline code or link targets that the translation reordered. Before, it did; for example, Chinesefeatures/code-actions.mdhadTRUE()/SWITCHand theIN/ORlinks swapped.Paid work is kept, and failures that cost nothing don't block.
TRANSLATED_API_KEYsecret. Other failed requests are recorded with"request": trueand planned again by the next run. Only a billed delivery that fails verification waits for--retry-failed.Retry-Afteris honoured (at most 60 s), and the backoff is jittered.--keep-rejected DIRsaves each billed delivery that fails verification (order, response, problems) for a manual repair.--merge-status OURS THEIRSrebuilds the status files entry by entry after the open translation branch is folded in: an entry comes from the branch only if the branch changed it since the merge base. The-X theirstext merge alone resolved conflicting JSON hunks from the branch, which droppedsourceBlobfor about 60 entries per language in a simulation with New translations (Translated) #432.translations.patch, apply withgit apply), the rejected deliveries and the report are uploaded as an artifact, so a rejected push loses nothing.Trigger. The
pushtrigger is replaced by a nightly schedule (03:00 UTC, production only) plusworkflow_dispatch. One run per night collects the day's English changes, so a paragraph edited in several PRs is billed once.Translation data (third commit; no request, recorded through the same rebuild and verification as a delivery):
downloads,release-history,user-options(es, zh): version, date and URL updates, "six" → "seven" and a new table row, built from the existing translated siblings.untranslatedby the baseline are repaired and recorded against the English they were made from (Crowdin sync 5c994d1):security-privacy(es, zh; link spacing matched to the English; next run sends the 3 table rows changed in Release 27.1.0 #428),kb/DR002(es; duplicated clause removed), release notes3_0_3,3_1_2,3_3_4(zh; extra or duplicated onboarding sentence removed),personalizing-te3(es; mistranslated heading rewritten by hand).Other changes.
--migrate-blobs. The second commit is its output:sourceBlobfor es 421 and zh 420 entries, with 0 not found.dry_runjob checks out full history.translation_segments.pyis added to the workflow path filters and to the ruff/mypy lists.mainis merged in (Release 27.1.0).Verification
translate-content.py --self-test: 239 passed, 0 failed (up from 141). New coverage:--migrate-blobs;--keep-rejected,--merge-status(three-way, including a branch delete and both-sides change);features/advanced-refresh.md, one sentence changed): 110 of 5,702 characters billed, and the translated diff shows only the changed block.--planon this branch, with New translations (Translated) #432 closed: es 38 and zh 38 files, about 114k characters each (229k in total, down from 314k before the translation-data commit). About half isreferences/preferences.md(restructured in Release 27.1.0 #428), sent whole.ruff check,ruff format --check,mypy --strict,actionlint: clean.--merge-statusin the fold step, the patch artifact and thealways()steps. Scheduled runs use the workflow onmain, so the first nightly run after the merge is the first real run of these steps.Notes
security/security-privacy.md, zh release notes3_0_3,3_1_2,3_3_4); the run then deletes thelocalizationbranch and starts frommain.TRANSLATED_ENVisproduction. Merging this PR does not start a run; the next 03:00 UTC run sends the plan above. To control the timing, dispatchtranslate.ymlafter merging, or setTRANSLATED_ENV=sandboxuntil then.applies_tonotes to English, like whole-file translation does. Expect that noise in the first translation PRs.