DEV Community

DropThe
DropThe

Posted on

Building a Multi-Language Site Without WPML: Our Custom Translation Stack

WPML costs $99/year. Polylang adds complexity. We built a custom translation system for two sites and it took less time than configuring either plugin.

Here's how we handle 5 languages across DropThe and Facil.guide, a senior-friendly tech guide site launching in English, Spanish, French, Portuguese, and Italian.

Architecture

Three components. That's it.

1. URL Router

Language detection from URL prefix:

function dt_detect_language() {
    $uri = $_SERVER['REQUEST_URI'];
    $prefixes = ['es', 'fr', 'pt', 'it'];
    foreach ($prefixes as $lang) {
        if (strpos($uri, "/$lang/") === 0) return $lang;
    }
    return 'en'; // default
}
Enter fullscreen mode Exit fullscreen mode

No database lookups. No cookies. No session state. Pure URL-based, which is exactly what Google wants for hreflang.

2. String Translation Function

function __($key, $lang = null) {
    static $strings = [];
    $lang = $lang ?: dt_detect_language();

    if (!isset($strings[$lang])) {
        $file = "/translations/{$lang}.json";
        $strings[$lang] = file_exists($file) 
            ? json_decode(file_get_contents($file), true) 
            : [];
    }

    return $strings[$lang][$key] ?? $key;
}
Enter fullscreen mode Exit fullscreen mode

JSON files per language. Falls back to the key itself (English). No database queries for UI strings.

3. Hreflang Output

function dt_output_hreflang() {
    $path = dt_get_path_without_lang();
    $langs = ['en' => '', 'es' => '/es', 'fr' => '/fr', 'pt' => '/pt', 'it' => '/it'];

    foreach ($langs as $lang => $prefix) {
        $url = home_url($prefix . $path);
        echo "<link rel='alternate' hreflang='{$lang}' href='{$url}' />\n";
    }
    echo "<link rel='alternate' hreflang='x-default' href='" . home_url($path) . "' />\n";
}
Enter fullscreen mode Exit fullscreen mode

Translation Workflow

For content translation, we use Grok API (grok-3-fast) in batch:

def translate_batch(texts, target_lang):
    prompt = f"Translate to {target_lang}. Keep technical terms. Return JSON array."
    response = grok_api(prompt, json.dumps(texts))
    return json.loads(response)
Enter fullscreen mode Exit fullscreen mode

Cost: ~$0.002 per article. We translate 50 articles for under $0.10.

Why This Approach Works for Facil.guide

Facil.guide targets seniors learning technology -- people who might be more comfortable reading in their native language. Spanish, French, Portuguese, and Italian from day one means we serve 2B+ native speakers without the overhead of a translation plugin.

The same system powers DropThe's upcoming Spanish and Portuguese editions.

What WPML Does That We Don't Need

  • String translation UI -- We edit JSON files directly
  • Media duplication -- We use the same images across languages
  • WooCommerce integration -- Not an e-commerce site
  • Translation management dashboard -- Overkill for 5 languages with API translation

Performance

Metric WPML Our System
DB queries per page 15-30 0
Plugin weight 2.8MB 3 functions
Setup time 2 hours 1 hour
Annual cost $99 $0

The JSON files add ~50KB total across all languages. Cached in PHP's static variables, so loaded once per request.


Facil.guide is a senior-friendly tech guide site. DropThe is a data utility network covering movies, companies, crypto, and more. Both built with custom WordPress stacks.

Top comments (0)