The site was English-only, with a hard deadline to have something French to show at a presentation. The original plan was to hand-build a /fr/ page and a static link between the two versions — workable, but it would mean remembering by hand, forever, which pages had a translation and which didn’t.
What got done
Installed and activated a multilingual plugin (Polylang, free tier) instead of hand-rolling that bookkeeping. It gives every page and post a language, and it ships a native language-switcher block that checks, per page, whether a published translation actually exists before showing a link to it. No per-page configuration required — it just checks.
The switcher went into the site’s header menu and got tested in both directions, live: publish a French version of a page, and an “English” / “Français” link appears automatically on both the English and French versions, no code involved. Unpublish it, and the link disappears again. That was the actual requirement — no dead links, no page silently claiming a translation it doesn’t have.
Also hit and fixed a stale-rewrite-rules problem: right after turning the plugin on, the new French URLs all 404’d. Common gotcha after installing anything that changes URL structure — toggling the plugin off and back on forced WordPress to regenerate its URL rules, no server access needed.
What went wrong
The assumption going in was that everything — installing the plugin, configuring the languages, and pairing a page with its translation — could happen the same way the rest of this site gets built: no browser, pure API calls. That held for installing and activating the plugin. It did not hold for the rest. The plugin’s very first language setup has to run through its own setup wizard in the WordPress dashboard; there’s no API for that one-time step. And pairing a page with its translation over the API turned out to be a paid-tier feature, not something the free version does — found that out by trying it and watching the request report success while quietly changing nothing.
There was also a stretch of a few minutes where a blank, untranslated placeholder page was live and reachable by anyone who found the URL, while the switcher was being tested. Caught and fixed, but it happened, so it’s going in the log rather than getting quietly cleaned up.
Still open
- Pairing a page with its translation still needs a manual step today. Finding a way to remove that step — without paying for API access — is next.
- The French homepage’s URL isn’t clean yet; it’s redirecting to something longer than it should.
- The switcher is built, but the homepage doesn’t have real French copy behind it yet.
- Mobile layout, the privacy policy rewrite, and SEO/GEO prep are all still open from before.
