Back to Portfolio
Mesogeiako — Vocational Training Centre & Custom CMS

Mesogeiako — Vocational Training Centre & Custom CMS

Mesogeiako is a licensed lifelong-learning and vocational training centre in Thessaloniki, and its site had grown up on WordPress and the BeTheme page builder. The rebuild keeps every piece of that content and none of the platform. It is plain PHP 8.5 on MySQL — a front controller, a fifteen-line autoloader, no framework, no Composer and no build step — about five thousand lines of source and two thousand of templates. Greek is the default and English lives under /en/, and parity is structural rather than bolted on: every project, news item and page is an item row with one translation row per language, slugs sit on the translation so Greek pages keep Greek-derived URLs, and a third language would need no schema change. The migration is code, not a one-off export. Twenty-one ordered, repeatable steps rebuild the whole site from the WordPress source — parsing BeTheme's Muffin Builder content, stripping boilerplate, normalising headings, merging duplicate news, pulling embedded data-URI images out into files — and carry 33 projects, 90 news items and 20 pages across in both languages, with 798 redirects so old links land on their new pages. Every image in the library is a WebP under 200 KB: quality steps down from 82 to 40, then the dimensions shrink until it fits, and the original is discarded. The admin gives the centre its projects, news and pages in both languages under TinyMCE, a media library, contact details, and the homepage counters and blocks. Structured data carries the postal address, the business registry number and the opening hours — three facts the old site published in a theme footer widget, where no post and no importer could see them.

Technologies

PHP 8.5 MySQL Vanilla JS GSAP TinyMCE 7 WebP pipeline (GD) i18n (EL/EN) SEO WordPress migration

Challenges

WordPress had hidden some of the facts. The postal address, the business registry number and the opening hours lived in a theme footer widget rather than in any post, so a content export never saw them, and the second phone number disagreed with itself across three places on the old site. Greek URLs also broke the obvious database design: percent-encoded, they routinely run past 191 characters while sharing long prefixes, so a prefix index rejects genuinely different addresses as duplicates. And a bilingual editor has a quiet failure mode — a save that omits one language is indistinguishable from someone deliberately clearing it.

Solutions

Each correction is its own numbered migration step, so a fresh rebuild from WordPress reproduces the client-confirmed phone number and the recovered address instead of re-importing the old mistakes. Redirects are made unique on a SHA-256 hash of the old path rather than a prefix. Each archive segment resolves only in its own language, so an item cannot live at two URLs and split its own ranking. Every translation form posts a presence marker per language — which is what lets a missing language mean 'untouched' rather than 'delete' — and the admin smoke suite carries a regression test for it. Filenames are NFC-normalised before transliteration, because macOS and Linux store the same Greek name as different bytes.