Why Polish, and why now?
The site has a sizeable Polish audience reading the English version (Google Search Console shows pl-PL as the #5 country by impressions), and Polish scientific terminology has well-established academic conventions that machine translation tends to mangle ("Inżynieria Materiałowa", not "Inżynieria Materiałów"; "Dynamika Płynów", not "Dynamika Cieczy"). Doing this as a single infrastructure release rather than rolling translations into weekly waves keeps the locale layer surgically clean.
What "Phase 1" actually covers
Phase 1 is the entry-point layer — every page a Polish reader is likely to land on from organic search:
- /pl/ — homepage with WebSite + BreadcrumbList + FAQPage JSON-LD (8 Polish Q&A, each 100–300 words; eligible for Google FAQ rich snippets).
- /pl/categories/ — alphabetic hub of all 80 categories sorted by their Polish titles (so "Algorytmy" comes before "Astronomia", not somewhere in the K's like in EN).
- /pl/categories/<id>/ — 80 category landings, each with localized hero, CollectionPage JSON-LD listing every member simulation, and an explicit fallback note ("Symulacja w jęz. angielskim") on each sim card so visitors aren't surprised when they click through to an EN/UK page.
- /pl/about/, /pl/contact/, /pl/privacy/, /pl/terms/, /pl/blog/, /pl/sitemap-html/, /pl/knowledge-graph/ — the 7 static pages every locale needs. Privacy and Terms are full Polish text; Blog and Knowledge-Graph are announcement-stubs linking to the EN/UK versions until Phase 2.
Phase 1 deliberately does not translate the 559 simulation pages or 295 articles. Those would be a Phase 2 effort (Q3 2026 plan) because the value-per-page of translating an entry-point ("Fizyka kwantowa") is much higher than translating one specific niche sim ("BB84 protocol").
The translation layer
Translations live in the data, not in templates. Each row in
shared/data/categories.json now has two extra fields next
to its UK counterparts:
{
"id": "fluid-dynamics",
"title": "Fluid Dynamics",
"titleUk": "Гідродинаміка",
"titlePl": "Dynamika Płynów",
"description": "SPH, MPM, Navier-Stokes equations...",
"descriptionUk": "SPH, MPM, рівняння Нав'є-Стокса...",
"descriptionPl": "SPH, MPM, równania Naviera-Stokesa, fale strzemienne, turbulencja."
}
Translations are curated, not machine-generated. A small sample of decisions that mattered:
| Category ID | English | Polish (chosen) | Why not the literal MT |
|---|---|---|---|
materials-science | Materials Science | Inżynieria Materiałowa | Polish academia uses the engineering framing for this field; "Materiałoznawstwo" exists but is dated. |
fluid-dynamics | Fluid Dynamics | Dynamika Płynów | "Płyny" covers both liquids and gases; "Cieczy" would exclude gases. |
chronobiology | Chronobiology | Chronobiologia | Direct loanword; "Biologia rytmów" would obscure the field name. |
networks | Networks | Sieci | Bare "Sieci", not "Sieci komputerowe", because the category covers social/biological/transport networks too. |
perception | Perception | Percepcja | Both "Percepcja" and "Postrzeganie" work; "Percepcja" is the term in cognitive science textbooks. |
game-math | Game Math | Matematyka Gier | Genitive case ("Matematyka Gier") is the natural compound, not "Matematyka Gra". |
3-way navbar switcher
Before v1.1.8 the language switcher in the navbar was a single button that toggled EN ↔ UK based on the current path. Adding a third locale forced a design choice: dropdown, three pills, or cycle? The cycle won — same single button, but now goes EN → UK → PL → EN on each click. Two reasons:
- No layout shift. The existing navbar had pixel-tight spacing on mobile; adding two more chips would have pushed the search and theme toggle off-screen on 360-pixel viewports.
-
Path preservation works the same way. The
getLangHrefs()helper now returns all 3 hrefs for the current page (stripping/uk/or/pl/first, then prepending the target locale), andgetAlternateHref()just picks the "next" entry. Click handlers for SPA-style hash re-computation still work because the function is recomputed at click time.
The cycle is biased towards EN because that's the most-complete locale — if you cycle past your target, one more click brings you to EN, which always has the page.
// shared/components.js
function getLangHrefs() {
const rootUrl = new URL(_base || "./", window.location.href).href;
const noLang = currRelPath.replace(/^(uk|pl)\//, "");
const hrefs = {
en: rootUrl + noLang,
uk: rootUrl + "uk/" + noLang,
pl: rootUrl + "pl/" + noLang,
};
const cycle = { en: "uk", uk: "pl", pl: "en" };
hrefs.next = hrefs[cycle[_lang] || "uk"];
return hrefs;
}
Hreflang surgery on 174 existing pages
Adding a third locale isn't just about creating new files; every
existing EN and UK page that has a PL twin needs a
<link rel="alternate" hreflang="pl"> in its head,
otherwise Google won't connect the cluster. The
add_pl_hreflang.js script:
- Loops over root index, UK index, the categories hub, all 80 EN + 80 UK categories, and 14 static-page pairs.
- Detects existing
hreflang="pl"tags and skips if already correct. - Strips any wrong PL tags (e.g. pointing to the wrong target).
- Inserts the new tag immediately after
hreflang="uk"if present, falling back to afterhreflang="en", falling back to before</head>.
Idempotent by construction: running it twice changes zero files. 174 pages updated, 0 already correct (first run), 4 files missing (those were UK twins that never existed for some static pages).
Sitemap: 2158 → 2247 URLs
scripts/gen_sitemap.js picked up PL automatically because
the existing per-locale loop now checks for pl/<path>/index.html
in addition to the EN and UK variants. Each URL entry has a
<xhtml:link rel="alternate" hreflang="pl"> sibling
when the PL twin exists.
PL URLs are emitted at priority 0.05–0.10 below their EN counterparts (so 0.8 for PL category vs 0.9 EN) as a soft signal that the locale is a Phase 1 stub. The five sub-sitemaps now total 2247 URLs: sitemap_categories.xml (240, +80), sitemap_sims.xml (1118), sitemap_articles.xml (555), sitemap_blog.xml (224), sitemap_static.xml (28, +6).
FAQPage as a search-result accelerator
Polish-language FAQPage rich snippets remain relatively underused on scientific-education queries, which makes them a good early-mover bet for Phase 1. The PL homepage has 8 FAQ entries covering the questions that come up in support email and Discord: "Czy trzeba coś instalować?", "Czy te symulacje są naukowo poprawne?", "Czy mogę używać symulacji w szkole?". Each answer is 100–300 words to clear Google's quality threshold; collectively they're the most-readable single document about what MySimulator is for, in any language.
What's deferred to Phase 2 (Q3 2026)
- Sim pages. Top-100 simulations translated to Polish; the long tail (459 specialised sims) stays EN/UK.
- Articles. Top 50 articles by traffic, translated to Polish.
- Blog. Polish-language devlog series starting with a Phase 2 announcement post.
- Knowledge-graph node labels. Currently the D3 force-graph shows EN/UK names; Phase 2 will switch labels based on locale.
- Search index. Add
titlePl+descriptionPlto the search corpus so a PL-speaker typing "kwantowy" finds the relevant categories. - Tag pages. Translate the 80+ tag landings to Polish.
Order of impact (estimated from EN→UK Phase 1→2 conversion ratios): sim pages give 5–10× the click volume of category pages, articles give 2–3× (longer dwell, fewer bounces), and tag pages fill in the long tail.
Files touched
Five new scripts were enough to drive the entire release; everything else is generated:
scripts/add_pl_translations.js— populatestitlePl+descriptionPlincategories.json.scripts/gen_pl_pages.js— emits all 89 PL HTML files.scripts/add_pl_hreflang.js— patches 174 EN+UK pages.scripts/gen_sitemap.js— extended for 3-locale URL clusters.shared/components.js— extended for 3-way navbar withI18N.pl+getLangHrefs().
Running node scripts/add_pl_translations.js && node
scripts/gen_pl_pages.js && node scripts/add_pl_hreflang.js
&& node scripts/gen_sitemap.js reproduces the entire
release from the data layer alone.
Up next
Wave 62 returns to simulations next week — likely fluid dynamics and statistical mechanics. The PL Phase 2 prep (search-index update, sim translation tooling) will happen quietly in parallel; expect Phase 2 to land around the Q3 2026 boundary.