Skip to main content

Command Palette

Search for a command to run...

Stub Generation for p5.js Translations

Updated
View as Markdown
A

I am a developer from Nepal who loves playing with tech.

Have you ever translated something, only to find out later that the original text changed while you were working? Or worse — that a whole new page existed and nobody told you?

For that, I built stub file generation. I'm writing this blog today to explain more.

A little context 🤷‍♀️

This summer, I'm working with the Processing Foundation as a Google Summer of Code contributor. My project is called Continued Development of Translation Tracker and my mentor is Divyansh Srivastava, who built the original tracker during GSoC 2025.

Quick background: the p5.js reference documentation lives in five languages — English as the source, plus Spanish (es), Hindi (hi), Korean (ko), and Simplified Chinese (zh-Hans). All of it is maintained by human volunteers.

Divyansh's tracker from last year already solves one problem: when an English page changes, it opens a GitHub issue telling translators what went stale.

But there was a second problem nobody was handling: what about pages that were never translated at all? When a new function is added in p5.js, a new English content page appears and... that's it. No Spanish file. No Hindi file. Simple, no idea and no signal. Translators had to notice missing files by comparing directory trees manually.

So for the last couple of weeks, I built stub file generation. Here's the PR if you want to jump straight to code: #1473

What is a stub? (important part)

A stub is scaffolding, not a translation.

I want to be super clear here because the p5.js community cares deeply about this: the tool does not translate anything. A machine can't decide how "friendly" should sound in Korean, and it shouldn't try. Humans translate. The tool just removes the boring setup work.

When the tracker finds an English reference page with no counterpart in a language folder, it generates a placeholder MDX file that contains:

  • The essential English frontmatter only — title, module, submodule, file, description. Not the full API dump, just enough to orient a translator.

  • A needsTranslation: true flag, so nothing ever mistakes it for finished work

  • A short placeholder body + an HTML comment linking back to the English source file

The stub lands in the exact mirror location — example/en/foo.mdx becomes example/es/foo.mdx and so on.

How it works

The flow looks like this:

  1. Scan the English reference pages (triggered by a push that touches src/content/**/en/**, or manually)

  2. Find missing translationsfindMissingTranslations() checks each language folder for a matching file

  3. Generate stubsgenerateStubFromEnglish() builds the placeholder from the English source (MDX only for now, .yaml files are skipped — more on that below 😅)

  4. Open one PR per language

Why one PR per language instead of one giant PR? Because the Spanish stewards should review the Spanish batch, and the Hindi stewards should review theirs. A single (or should I say a mega) PR touching four languages at once is hard to review.

And nothing auto-merges... like ever. Maintainers and language stewards review every stub PR. The job of my script is to propose; humans decide.

Note: There's also a cap — STUB_MAX_FILES limits stubs to 50 per language per run (per language, not global!). Without this, a full scan of the historical backlog would flood the repo with hundreds of files in one go.

The workflow file

The stub generation runs as its own GitHub Action: .github/workflows/translation-stubs.yml. It runs with GENERATE_STUBS=true, completely separate from the issue-tracking workflow (translation-sync.yml). The two modes are mutually exclusive — when stub mode is on, the issue logic doesn't run at all. We can simply say: different jobs, different outcomes, no interference.

You can also trigger it manually with workflow_dispatch, which supports two inputs: full_scan (scan everything, not just the latest changes) and languages (target specific languages only).

For local development, I added a dry-run command:

npm run test:stubs

This previews everything into a gitignored stub-preview/ folder . It's best if we want to test locally and don't touch src/content/ files locally. I used this frequently to test.

Refactoring the tracker

While building this, the tracker's single index.js was getting way too big. After discussing with Divyansh, I split it into focused modules:

File What it does
index.js Entry point and orchestration
constants.js Supported languages, content types, stub frontmatter keys
utils.js Path helpers, frontmatter parsing, file scanning
github-tracker.js GitHub API client (issues, diffs, stub PR creation)
workflows.js Translation status checks and stub generation logic

Much easier to navigate now, and future features (auto-close, dashboard) have obvious homes.

Challenges I faced 💪

  • I deleted my local repo. Yes, really. Mid-project. Thankfully like 50% of changes were pushed to my fork, so "disaster recovery" was relatively less painful (I didn't crash :)

  • Divyansh's review caught a bug. My generateStubFromEnglish assumed MDX frontmatter everywhere, but the scanners also pick up .yaml files. Fine today (stubs are reference-only), but the moment we extend to tutorials and examples, one YAML file would throw and kill the entire batch because I had no per-file try/catch. The fix was to skip .yaml explicitly for now, and wrap each file's generation so one failure becomes a logged warning instead of a dead run.

What's next 🚀

Per the tracking issue (#1404), coming up:

  • Auto-close tracker issues when a translation PR merges

  • A progress dashboardtranslation-status.json with per-language coverage, rendered as markdown progress bars

  • Expanding stub coverage to tutorials/ and examples/ (this is where that YAML handling becomes essential)

  1. Stub generation PR #1473

  2. Project tracking issue #1404

  3. Week 1 PR — in-issue diffs #1424

  4. The p5.js website repo

Summary

The whole point of this script is simple: remove the friction of finding missing files, creating placeholders, opening PRs. It does keep humans in the loop while doing so.

If you speak Spanish, Hindi, Korean, or Chinese and want to contribute to p5.js translations, the stub PRs must make it easier than ever to jump in.