Skip to content

hreflang and switcher targets can 404 with nobody noticing — add a cross-edition reachability check #239

Description

@mmcky

Rewritten 2026-08-03. The original version of this issue proposed that sync maintain a per-language page-coverage manifest for the theme to consume. Measuring the actual latencies afterwards showed that proposal was disproportionate to the problem, so the ask has been narrowed to detection. The original framing is summarised in the comment below.

Context

QuantEcon/lecture-python-programming#490 wires the theme's language switcher into the English source, offering en, fa, fr and zh-cn. The theme emits every switcher link and every hreflang alternate as {language_url}/{pagename}.html, unconditionally, for every configured language. It has no notion of whether a page exists in a given edition, and no way to acquire one.

When the editions diverge, the switcher offers links that 404 and the hreflang alternates point at URLs that do not resolve. The second is the more consequential of the two: a bad alternate is crawled on every page of the site and devalues the annotations generally, not just for the affected page.

What the divergence actually is

This is where the original framing was wrong. Translation review is not the bottleneck. Every sync PR in this series merges within a day, usually within the hour:

Edition Recent sync PRs, opened → merged
.fa 8 min, 12 min, 19 min, 46 min, 4 h, 23 h
.fr 48 min, 23 h
.zh-cn 15 min, 24 min, 48 min, 48 min, 23 h, 2 d

Publishing is the bottleneck. Every repository in this series — the English source included — publishes only when someone pushes a publish* tag:

Repository Last publish Days stale as of 2026-08-03
lecture-python-programming 2026-07-16 18
.fa 2026-06-19 45
.fr 2026-07-17 17
.zh-cn 2026-06-19 45

Publish cadence dominates review lag by roughly 30×. Any measure of "which pages does edition X have" that means published and reachable would therefore be tracking publish-tag timing far more than translation progress.

The case that prompted this

polars was added to the English source on 2026-07-30 (QuantEcon/lecture-python-programming#408). Sync worked exactly as intended: PRs opened, were reviewed, and merged into all three editions the same day — .fa#144, .fr#20, .zh-cn#79, each inside an hour. polars.md and its _toc.yml entry are present in all four repositories now.

All four sites still 404 on polars.html, including English, because no publish tag has been pushed anywhere since it merged. Nothing in the translation pipeline failed here. This is publish cadence, start to finish.

What is worth asking for

Not a coverage manifest. Building a cross-repo data pipeline so the theme can suppress links would, on these numbers, mostly be an elaborate way of tracking publish tags — and it would need to distinguish file present on the default branch from published and reachable, which is the hard part and the part that changes most often.

What is missing is far simpler: nobody finds out when a switcher or hreflang target 404s. The condition is silent. It was found by hand, only because the switcher PR prompted someone to check.

So the proportionate ask is a cross-edition reachability check — build the site, collect the switcher and hreflang URLs the theme actually emitted, request each one, report the failures. Run it on a schedule rather than per-sync, since the state it observes changes on publish, not on merge.

Two things worth knowing for whoever picks this up:

  • Sphinx's linkcheck builder does not cover this. It works from the doctree and checks links in page content; the switcher and hreflang URLs are injected by the theme template and never appear there. This needs its own check against built HTML.
  • The check is only meaningful across editions, so it needs to know the full edition set and each one's published URL. That is the reason this issue is filed here rather than in a single lecture repo — this action already knows the edition topology. If the maintainers judge a scheduled site check out of scope for a translation engine, the alternative home is a shared workflow run from each source repo, and that is a reasonable outcome.

Adjacent: #222 describes sync committing a first-translated lecture left unreachable because nothing adds it to _toc.yml. That is the same class of silent-reachability failure, one layer earlier.

Downstream

If detection later shows the residual window is big enough to be worth suppressing, the consuming change is a small conditional in quantecon-book-theme around the switcher and hreflang emission. That is not worth opening yet — measure first.

Separately, and independent of any of this: quantecon-book-theme ships no 404 template. A language-aware 404 on each edition, offering the English original when a translated page does not exist, fixes the reader-facing half of this with no cross-repo coordination at all, stays correct automatically, and also catches arrivals from search results and shared links that the switcher never touches. That is the better navigation fix and belongs on the theme.

Not blocking

QuantEcon/lecture-python-programming#490 is fine to merge as it stands.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementImprovement to existing content or functionality

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions