translate init applies the localisation rules — code comments, figure labels, i18n font config. The sync path applies none of them, so every new lecture arriving through the automated production path lands unlocalised, and the divergence from the seeded corpus grows with each upstream merge.
Evidence
applyLocalizationRules is referenced in exactly two places, both CLI:
src/cli/index.ts
src/cli/commands/init.ts
Neither sync-orchestrator.ts nor file-processor.ts calls it. What the sync path does have is a set of translator-prompt rules telling the model to preserve localisation that is already present (translator.ts:276, :369, :636 — the #107 mitigation). For a new file there is nothing to preserve, so it arrives with English code comments, English figure labels, and no font block.
What it looks like in production
QuantEcon/lecture-python.zh-cn#202 is a clean natural experiment: ten new lectures for the same edition in the same PR, nine delivered by sync and one by init -f (retranslated by hand after the sync run failed the parity check on it, #156).
|
# i18n lines |
Plot labels |
| Nine sync-delivered lectures |
0 each |
English |
| One init-delivered lecture |
4 |
14 translated to Chinese |
Edition-wide the init-shaped result is the convention, not the exception: 103 of 122 lectures carry font config, because the corpus was seeded with init.
So a Chinese edition is now accumulating lectures whose figures are labelled in English, silently, through the path that is supposed to be the automated one.
Why it is easy to miss
Nothing fails. The build is green either way — an unlocalised lecture is valid MyST that executes fine, and its figures simply come out in English. There is no warning on the sync run and nothing in the review verdict covers it (the reviewer sees a faithful translation of the prose, which it is). It only becomes visible by diffing a synced lecture against a seeded one, which is not part of any workflow.
It also produces a false positive in review when the two paths meet. On #202 the review flagged the init-delivered lecture's font block as a blocker — "an apparent unauthorized insertion of font-loading code lines into a Python code cell ... out of scope for a translation task" — and dropped the verdict from PASS to WARN. That block is correct and necessary: without it the lecture's 14 Chinese plot labels render as tofu. The reviewer reached the wrong conclusion because the nine siblings in the same PR set the local expectation, and they are the ones that are wrong.
Suggested fix
- Apply the localisation rules on the sync path for newly-created files, using the same
applyLocalizationRules the CLI uses, with the rule set resolved from .translate/config.yml so an edition can opt out.
- Failing that, report it — a sync that creates a file without localising it should say so on the PR, so the gap is visible rather than silent.
Related but distinct: #107 is resync removing existing localisation; this is sync never adding it. #141 is the injection being nondeterministic and the font path hard-coded — which also bit on #202, where init wrote _fonts/SourceHanSerifSC-SemiBold.otf into an edition that keeps the font at lectures/fonts/, so addfont() would have raised FileNotFoundError on the one lecture that needed it.
translate initapplies the localisation rules — code comments, figure labels, i18n font config. The sync path applies none of them, so every new lecture arriving through the automated production path lands unlocalised, and the divergence from the seeded corpus grows with each upstream merge.Evidence
applyLocalizationRulesis referenced in exactly two places, both CLI:Neither
sync-orchestrator.tsnorfile-processor.tscalls it. What the sync path does have is a set of translator-prompt rules telling the model to preserve localisation that is already present (translator.ts:276,:369,:636— the #107 mitigation). For a new file there is nothing to preserve, so it arrives with English code comments, English figure labels, and no font block.What it looks like in production
QuantEcon/lecture-python.zh-cn#202 is a clean natural experiment: ten new lectures for the same edition in the same PR, nine delivered by
syncand one byinit -f(retranslated by hand after the sync run failed the parity check on it, #156).# i18nlinesEdition-wide the init-shaped result is the convention, not the exception: 103 of 122 lectures carry font config, because the corpus was seeded with
init.So a Chinese edition is now accumulating lectures whose figures are labelled in English, silently, through the path that is supposed to be the automated one.
Why it is easy to miss
Nothing fails. The build is green either way — an unlocalised lecture is valid MyST that executes fine, and its figures simply come out in English. There is no warning on the sync run and nothing in the review verdict covers it (the reviewer sees a faithful translation of the prose, which it is). It only becomes visible by diffing a synced lecture against a seeded one, which is not part of any workflow.
It also produces a false positive in review when the two paths meet. On #202 the review flagged the init-delivered lecture's font block as a
blocker— "an apparent unauthorized insertion of font-loading code lines into a Python code cell ... out of scope for a translation task" — and dropped the verdict from PASS to WARN. That block is correct and necessary: without it the lecture's 14 Chinese plot labels render as tofu. The reviewer reached the wrong conclusion because the nine siblings in the same PR set the local expectation, and they are the ones that are wrong.Suggested fix
applyLocalizationRulesthe CLI uses, with the rule set resolved from.translate/config.ymlso an edition can opt out.Related but distinct: #107 is resync removing existing localisation; this is sync never adding it. #141 is the injection being nondeterministic and the font path hard-coded — which also bit on #202, where
initwrote_fonts/SourceHanSerifSC-SemiBold.otfinto an edition that keeps the font atlectures/fonts/, soaddfont()would have raisedFileNotFoundErroron the one lecture that needed it.