Skip to content

docs: refresh translations, and translate pages in parallel - #3458

Open
maxisbey wants to merge 5 commits into
mainfrom
docs/refresh-translations-parallel
Open

docs: refresh translations, and translate pages in parallel#3458
maxisbey wants to merge 5 commits into
mainfrom
docs/refresh-translations-parallel

Conversation

@maxisbey

@maxisbey maxisbey commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Re-runs scripts/docs/translations.py translate for all twelve languages to catch up with English changes since #3379, and teaches the tool to do that in one parallel run.

Motivation and Context

Twenty-three pages per language had sections whose English source changed (mostly #3443's move away from presenting the in-memory client as the way to connect, plus the session-limit, redirect and validate_token_resource additions), so translated sites were showing the "translation behind the English page" notice on those. As before, only the changed sections were retranslated and everything else is carried over byte for byte, which is why the diff is section-scoped despite touching 276 generated files. One exception: run/authorization.md in es, ru and uk was retranslated whole with --pages, because the section-scoped run deterministically reproduced a stale phrase from the previous translation ("in-memory client" where the English now says "in-memory test client").

The first commit is the tool change that made the run quick:

  • translate keeps --jobs N pages in flight on a thread pool (default 8). Each page is still its own conversation, so the model sees exactly what it did before; only wall-clock time changes. This refresh (276 pages) took about six minutes at --jobs 32.
  • --lang is optional and repeatable like status/stage, so one invocation covers every language under one concurrency cap and one usage total. Work is ordered page by page across languages so each language's cached system prompt is written by its first request and read by the rest.
  • The run prints its plan before the first request, progress lines carry the language code, Ctrl-C exits at once, a connection dropping mid-reply fails that page rather than the run, and the API client retries rate limits a few more times.

Nothing under docs/, i18n/*/instructions.md or i18n/*/glossary.json changed.

How Has This Been Tested?

  • translations.py status reports 0 missing / 0 outdated / 51 current for every language.
  • scripts/docs/build.sh builds the English site strictly (cross-reference and render-order checks green) and all twelve language sites with 0 warnings.
  • An offline pass over every generated page checked that it is a fixed point under the tool's own structure re-imposition and validation, that every section with unchanged English is byte-identical to main (3120 sections; the three whole-page retranslations excepted), and that no retranslated section came back untranslated, in the wrong script, truncated, or with prompt text leaked into it.
  • Each language's retranslated sections were read against the English source for dropped or inverted content and glossary violations; the handful of terminology inconsistencies found are noted for the per-language glossaries rather than hand-edited here.
  • Tool: new tests for pages in flight together under the --jobs cap, rejected credentials with pages in flight, and a multi-language run; tests/docs passes, pyright and ruff clean.

Breaking Changes

None (the translation tool's CLI is internal docs tooling).

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Generated pages are never hand-edited; if a reviewer spots a wording problem, the fix goes into that language's instructions.md/glossary.json and the page is re-run.

AI Disclaimer

translations.py translate keeps --jobs pages (default 8) in flight at once
on a thread pool; each page is still its own conversation, so the model
sees exactly what it did before and only the wall-clock time changes.
--lang becomes optional and repeatable like the other commands, so one
invocation refreshes every language under a single concurrency cap and
usage total. Work is ordered page by page across languages so each
language's cached system prompt is written by its first request and read
by the rest. The run prints its plan (pages, languages, concurrency,
model) before the first request, progress lines carry the language code,
and Ctrl-C leaves at once instead of waiting for pages in flight.

The API client retries rate limits and overloads a few more times since
many pages now share one limit, a connection dropping mid-reply fails that
page rather than the run, and the shared Markdown renderer used for
heading ids is serialised behind a lock.
Re-runs scripts/docs/translations.py translate for all twelve languages to
catch up with the English changes since the last refresh. Twenty-three
pages per language had sections whose English changed; only those sections
were retranslated and everything else is carried over byte for byte.
run/authorization.md in es, ru and uk was retranslated whole (--pages)
because the section-scoped run kept reproducing a stale phrase from the
previous translation.

Nothing under docs/, the per-language instructions or glossaries changed.
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📚 Documentation preview

Preview https://pr-3458.mcp-python-docs.pages.dev
Deployment https://89db0bfb.mcp-python-docs.pages.dev
Commit fde648c
Triggered by @maxisbey
Updated 2026-09-06 14:07:42 UTC

httpx is only present through the optional translate group (the SDK itself
depends on httpx2), so a top-level import broke every environment that
loads the docs tooling without that group. Import it next to anthropic,
where its errors are caught.
@maxisbey
maxisbey marked this pull request as ready for review September 6, 2026 13:22

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

One verified lower-impact observation (a convention, logging or cleanup point) was not posted.

Comment thread scripts/docs/translations.py
Comment thread tests/docs/test_translations.py Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 279 files

Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.

Re-trigger cubic

Comment thread i18n/fr/pages/advanced/low-level-server.md
Comment thread i18n/ko/pages/get-started/testing.md
Comment thread i18n/ko/pages/client/oauth-clients.md
Comment thread i18n/fr/pages/handlers/progress.md
Comment thread i18n/zh/pages/run/legacy-clients.md
Comment thread i18n/fr/pages/protocol-versions.md
Comment thread i18n/fr/pages/deprecated.md
Comment thread i18n/ru/pages/handlers/multi-round-trip.md
Comment thread i18n/hi/pages/protocol-versions.md
Comment thread i18n/es/pages/handlers/progress.md
When the API rejects the credentials part-way through a parallel run, stop
starting pages but still collect the ones already in flight and write those
that succeed, then report usage and exit 2. Previously a page that had
already finished in the same batch could be dropped depending on set
iteration order. The test fake's call counter is now guarded by a lock.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No new blocking issues. 1 optional suggestion (a nit or a note on pre-existing code) was found and not posted. Nothing in this review needs a push before merging.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread scripts/docs/translations.py Outdated
The test added in the previous commit assumed the rejected page is seen
before its sibling lands; with four pages and two in flight the loop may
legitimately start a third page first, which then waited alone at the
test's barrier. Split it into two order-independent tests: a sibling that
lands beside a rejected page is written (exactly two pages in the run),
and no further page starts once the rejection is seen (both in flight
rejected). The worker now hands a ConfigError back as a value like a
PageError, so that page's usage is counted and the loop needs no except.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed the PR again at the latest commits and found no bugs; the two points from my earlier review are resolved in the code as it now stands, though a human pass is still worthwhile given the volume of regenerated translation content.

What was reviewed: the reworked stop-on-rejected-credentials flow (ConfigError now returned as a value, the done batch fully drained and successful pages written before the run exits 2) and the lock-guarded test-fake counter both check out at HEAD; also checked the window-refill loop for lost or double-submitted jobs (queue/running bookkeeping is consistent), the os._exit(130) interrupt path (usage printed first, no file left half-written since write_text is atomic per page), and that all new write_text calls pass encoding=. The 276 regenerated i18n pages are machine-produced and section-scoped per the tool's provenance hashes — their translation quality is the part no automated review here can vouch for.

Extended reasoning...

Overview

The PR has five commits: the parallel-translation rework of scripts/docs/translations.py (repeatable --lang, --pages, --jobs with a manually refilled ThreadPoolExecutor window, page-major interleaving via zip_longest, a render lock, lazy httpx import with HTTPError mapped to PageError), matching tests in tests/docs/test_translations.py, and ~276 regenerated pages under i18n/*/pages/. The last two commits (b5557cb, fde648c) landed after or alongside my previous review and directly address the two nits I posted: the results loop at scripts/docs/translations.py:997-1027 now records the first ConfigError, keeps draining the wait(...).done set so sibling pages that completed are written, and raises the held error only after the loop; and the RejectedTogether fake in tests/docs/test_translations.py increments its call counter under threading.Lock. Both fixes are verified in the code at HEAD, not taken from the commit messages.

Security risks

None material. The change touches only internal docs tooling, tests, and generated markdown — no src/ code, no auth/crypto/permission paths, no dependency changes. The credentials involved (ANTHROPIC_API_KEY) are read from the environment as before; error messages print the API's rejection text but no key material. os._exit(130) on KeyboardInterrupt is deliberate and commented (non-daemon pool threads), and each page write is a single write_text with explicit encoding, so an interrupt cannot leave a truncated page.

Level of scrutiny

Moderate for the tool code, low for the generated pages. The executor loop is genuinely subtle — it took two follow-up commits to get the stop-path and its tests deterministic — so I re-read the final version rather than trusting the commit messages: the refill condition (queue and stopped is None) or running cannot spin (either something is submitted or wait blocks on a non-empty running), jobs are popped exactly once, and stopped = stopped or result preserves the first failure. The generated i18n pages are the bulk of the diff but are machine-produced under the tool's own provenance-hash discipline; no automated review can meaningfully vouch for translation quality across twelve languages, which is the main reason I defer rather than approve. cubic-dev-ai also left an inline comment at the ConfigError-handling line after my last review; the subsequent commit reworked exactly that code, but I cannot read the comment's text to confirm it is fully addressed — another reason not to approve over it.

Other factors

The bug-hunt run over the final version exited on a dry streak with no findings. The tests are strong for this kind of code: barrier-synchronized fakes proving genuine two-in-flight interleaving, order-independent split tests for the stop path, and snapshot-asserted output; the repo's 100%-branch-coverage gate applies to scripts/ tests too. I chose to speak despite my prior review because the post-review commit fde648c substantively changed the code I had flagged, and confirming those fixes check out is new information the author has not yet received.

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.

1 participant