The sync carries .md and nothing else — sync-orchestrator.ts:269, and again at :277, :292 and :426, all filter on endsWith('.md'). That is a deliberate and sensible scope for prose. But translation repos also carry hand-localised non-.md assets under lectures/_static/, and those assets are outside the sync in every direction: they cannot be created by it, updated by it, or repaired by it.
One instance of this is already known and documented — src/bibliography.ts:9 notes that _static/quant-econ.bib falls outside the sync because of the .md filter. What I don't think has been looked at is the class, which is considerably bigger than the .bib.
What is actually out there
Measured 2026-08-11, lecture-python-intro against lecture-intro.zh-cn:
|
intro |
zh-cn |
non-.md files under lectures/_static |
47 |
49 |
.ipynb figure/data builders that differ |
|
5 of 6 |
.png assets that differ |
|
12 of 26 |
| files present only in zh-cn |
|
2 |
And lecture-python.zh-cn is larger again — 118 non-.md _static files, including 18 .py, 7 .csv, 6 .pdf and 4 .gv.
These are localisations, not drift
The differing files are not stale copies. They are deliberate translations that someone did by hand. From _static/lecture_specific/markov_chains_I/figures.ipynb, which builds a graphviz diagram:
| intro |
zh-cn |
dot.node("Growth") |
dot.node("增长") |
dot.node("Stagnation") |
dot.node("停滞") |
dot.node("Collapse") |
dot.node("崩溃") |
The same pattern appears in lake_model/figures.ipynb ('New entrants', 'Unemployed', 'Employed') and networks/figures.ipynb ("poor", "middle class", "rich"). The 12 differing PNGs are the rendered outputs of exactly those notebooks — so the images are localised too.
So the work has been done. The question is how it stays correct.
Three failure directions, none currently covered
1. Upstream changes are invisible. If lecture-python-intro edits markov_chains_I/figures.ipynb — adds a state, renames one, changes the layout — zh-cn's localised copy silently keeps the old structure and its committed PNG keeps showing the old diagram. Nothing compares them, so nothing reports it. The translated site quietly diverges from the English one in its figures while every .md stays perfectly in sync.
2. Mechanical edits to the English source cannot be carried. This is what surfaced it for us. In QuantEcon/workspace-lectures#23 we are repointing dataset URLs across the lecture family, and _static/lecture_specific/inequality/data.ipynb reads a dataset by URL in all four consuming repos. Every .md read could be handled by repointing English and letting the sync follow. The notebook read could not — zh-cn's copy can never receive a sync PR for it, however many times the English source is repointed, so it had to be hand-edited in QuantEcon/lecture-intro.zh-cn#292. It is also a served artifact: it returns 200 on the Pages site with the URL in its body, so leaving it stale serves a dead link to anyone who downloads it.
3. New localisation has no path. If a new English lecture arrives with a figure builder, the sync brings the prose and the translator must notice, by hand, that a notebook also needs translating.
What might be worth doing
Ordered by how much I think each buys relative to cost. Genuinely open questions rather than a proposal — the team knows the constraints here better than I do.
A detector, reporting only. For each non-.md file under _static/, compare the translation's copy against the English one and report three states: identical, deliberately-localised-and-current, deliberately-localised-but-the-English-changed-since. The third is the one that matters, and it is the one nobody can see today. This needs a record of "which English commit was this localised against", which does not exist yet — a sidecar or a note in the translation repo would do. Cheap, and it converts a silent failure into a visible one without automating anything risky.
A Claude skill for the fix. Once a stale localised asset is flagged, updating it is exactly the kind of small bounded task a skill suits: read the English version, read the current localisation, carry the English change across while preserving the translated strings, re-render if the asset is generated. Notably safer than folding these into the automated sync, because a human is invoking it on a named file rather than a pipeline rewriting notebooks unattended.
Widening the sync itself — probably not, or not first. A .ipynb is JSON with embedded outputs, and an LLM resync of one is a much riskier operation than a prose section. .png and .pdf cannot be handled at all. The .md-only filter looks like the right default; the gap is that nothing else covers what it excludes.
Happy to help with whichever direction is preferred. Related: QuantEcon/workspace-lectures#26 covers the adjacent problem inside .md files, where the sync replaces a whole code cell and drops a localisation sitting in it.
The sync carries
.mdand nothing else —sync-orchestrator.ts:269, and again at:277,:292and:426, all filter onendsWith('.md'). That is a deliberate and sensible scope for prose. But translation repos also carry hand-localised non-.mdassets underlectures/_static/, and those assets are outside the sync in every direction: they cannot be created by it, updated by it, or repaired by it.One instance of this is already known and documented —
src/bibliography.ts:9notes that_static/quant-econ.bibfalls outside the sync because of the.mdfilter. What I don't think has been looked at is the class, which is considerably bigger than the.bib.What is actually out there
Measured 2026-08-11,
lecture-python-introagainstlecture-intro.zh-cn:.mdfiles underlectures/_static.ipynbfigure/data builders that differ.pngassets that differAnd
lecture-python.zh-cnis larger again — 118 non-.md_staticfiles, including 18.py, 7.csv, 6.pdfand 4.gv.These are localisations, not drift
The differing files are not stale copies. They are deliberate translations that someone did by hand. From
_static/lecture_specific/markov_chains_I/figures.ipynb, which builds a graphviz diagram:dot.node("Growth")dot.node("增长")dot.node("Stagnation")dot.node("停滞")dot.node("Collapse")dot.node("崩溃")The same pattern appears in
lake_model/figures.ipynb('New entrants','Unemployed','Employed') andnetworks/figures.ipynb("poor","middle class","rich"). The 12 differing PNGs are the rendered outputs of exactly those notebooks — so the images are localised too.So the work has been done. The question is how it stays correct.
Three failure directions, none currently covered
1. Upstream changes are invisible. If
lecture-python-introeditsmarkov_chains_I/figures.ipynb— adds a state, renames one, changes the layout — zh-cn's localised copy silently keeps the old structure and its committed PNG keeps showing the old diagram. Nothing compares them, so nothing reports it. The translated site quietly diverges from the English one in its figures while every.mdstays perfectly in sync.2. Mechanical edits to the English source cannot be carried. This is what surfaced it for us. In QuantEcon/workspace-lectures#23 we are repointing dataset URLs across the lecture family, and
_static/lecture_specific/inequality/data.ipynbreads a dataset by URL in all four consuming repos. Every.mdread could be handled by repointing English and letting the sync follow. The notebook read could not — zh-cn's copy can never receive a sync PR for it, however many times the English source is repointed, so it had to be hand-edited in QuantEcon/lecture-intro.zh-cn#292. It is also a served artifact: it returns 200 on the Pages site with the URL in its body, so leaving it stale serves a dead link to anyone who downloads it.3. New localisation has no path. If a new English lecture arrives with a figure builder, the sync brings the prose and the translator must notice, by hand, that a notebook also needs translating.
What might be worth doing
Ordered by how much I think each buys relative to cost. Genuinely open questions rather than a proposal — the team knows the constraints here better than I do.
A detector, reporting only. For each non-
.mdfile under_static/, compare the translation's copy against the English one and report three states: identical, deliberately-localised-and-current, deliberately-localised-but-the-English-changed-since. The third is the one that matters, and it is the one nobody can see today. This needs a record of "which English commit was this localised against", which does not exist yet — a sidecar or a note in the translation repo would do. Cheap, and it converts a silent failure into a visible one without automating anything risky.A Claude skill for the fix. Once a stale localised asset is flagged, updating it is exactly the kind of small bounded task a skill suits: read the English version, read the current localisation, carry the English change across while preserving the translated strings, re-render if the asset is generated. Notably safer than folding these into the automated sync, because a human is invoking it on a named file rather than a pipeline rewriting notebooks unattended.
Widening the sync itself — probably not, or not first. A
.ipynbis JSON with embedded outputs, and an LLM resync of one is a much riskier operation than a prose section..pngand.pdfcannot be handled at all. The.md-only filter looks like the right default; the gap is that nothing else covers what it excludes.Happy to help with whichever direction is preferred. Related: QuantEcon/workspace-lectures#26 covers the adjacent problem inside
.mdfiles, where the sync replaces a whole code cell and drops a localisation sitting in it.