A lightweight, plugin-free multilingual architecture for PHP sites — one central dictionary, a single lookup function, and no CMS translation plugin required.
AI-Ready Blueprints, Tools

A Plugin-Free Multilingual Structure for PHP Sites

A lightweight, plugin-free multilingual architecture for PHP sites — one central dictionary, a single lookup function, and no CMS translation plugin required.
One Dictionary File, No WPML, No Polylang

Multilingual plugins solve a bigger problem than most small sites actually have. WPML and Polylang manage per-post translation relationships, hreflang tagging, and an admin workflow for translators — real value for a large editorial site with multiple contributors. For a five-page hospitality or business site that just needs its content in two or three languages, that's a lot of machinery for a small job, and it assumes WordPress in the first place.

This is the alternative: a single dictionary array, one lookup function, and a querystring-based language switch. No CMS dependency, no plugin license, no database tables for translation relationships.

Why this trade-off makes sense — and when it doesn't

The whole system trades flexibility for simplicity. Every translatable string lives in one array, in one file, pulled through a single function call everywhere it's used. Adding a language means adding one key to every dictionary entry — not installing anything, not learning a new admin screen.

This is the wrong choice if the site has dozens of frequently-changing pages, multiple non-technical editors who need a translation UI, or strict SEO requirements around per-language URL paths (/en/page instead of ?lang=en) — this approach doesn't produce those out of the box.

The dictionary and language detection

One file, included first on every page, validates the requested language against a whitelist rather than trusting it directly.

$valid_langs = ['it', 'en', 'de'];
$lang = (isset($_GET['lang']) && in_array($_GET['lang'], $valid_langs))
        ? $_GET['lang'] : 'it';

$t = [];
$t['nav_about'] = ['it' => 'Chi siamo', 'en' => 'About us', 'de' => 'Über uns'];

The whitelist matters more than it looks. Without it, an unrecognized ?lang= value either throws warnings deep in template code or, worse, gets used unsanitized somewhere it shouldn't. Validating once at the entry point means every file downstream can trust $lang without re-checking it.

The lookup function

A single helper retrieves a string in the current language, with a fallback if a key is missing entirely.

function T(array $t, string $key, string $lang, string $fallback_lang = 'it'): string {
    if (isset($t[$key][$lang])) {
        return $t[$key][$lang];
    }
    if (isset($t[$key][$fallback_lang])) {
        return $t[$key][$fallback_lang];
    }
    return '';
}

The fallback parameter earns its place even on a two-language site. It's what stops a newly-added section — written in the primary language before its translation is ready — from breaking the page for visitors in the other language. It just shows the untranslated text instead of nothing.

Longer text doesn't belong in the array

Short UI strings fit the dictionary comfortably. Full legal text — privacy policy, cookie policy — usually doesn't, once it's long enough that burying it in array values makes it unreadable to review. For those pages, a direct conditional block is the better pattern, deliberately outside the $t system:

if ($lang === 'it') {
    // full Italian legal text, as readable HTML
} elseif ($lang === 'de') {
    // full German legal text
} else {
    // full English legal text
}

This isn't an inconsistency — legal text needs to be reviewed in context by whoever handles compliance, and that's easier as a readable block than as escaped array values.

The most common thing that gets missed

When a new language gets added, the dictionary itself is rarely where translations go missing — it's the handful of strings that were never routed through $t in the first place. Meta descriptions, og:title, og:locale, and other tags in <head> are the usual culprits, because they're easy to write directly during initial development and easy to forget once the multilingual system is fully wired in elsewhere.

Getting a working copy of this

The full technical specification — including the language switcher pattern, section-specific mini-dictionaries, and the complete checklist for adding a language later — is available as a blueprint document.

Download the Blueprint (.md)

How to use the blueprint, step by step

1. Download the file. It's a plain .md text file, nothing to install.

2. Open a new conversation with an AI coding assistant. Google AI Studio, Claude Projects, Qwen Chat, or any comparable tool that reads attached files and generates PHP will work.

3. Attach the file and describe your site. State which languages you need, whether the site is plain PHP or runs on something else, roughly how many pages exist, and flag anything unusual — a menu with dozens of items, long legal pages, or SEO requirements around per-language URLs.

4. Review before deploying. Check that every page's <head> tags were actually routed through the dictionary and not left hardcoded, and have any legal text reviewed by someone fluent in each language before publishing — this blueprint builds the structure, not the translations themselves.

Need help with this solution or looking for custom development?

Visit my Stay In Touch page to connect and discuss your project. Discover a range of web development services and specialized WordPress solutions tailored to your needs. Let's work together to enhance your digital presence.