Internationalization (i18n)
Wordless handles multiple languages through a clean, directory-based approach. Each language lives in its own folder, and content automatically inherits language metadata.
Directory Structure
content/
en/
index.php → /en
about.php → /en/about
blog/
index.php → /en/blog
post.php → /en/blog/post
es/
index.php → /es
about.php → /es/about
blog/
index.php → /es/blog
Language Inheritance
Each language folder's index.php declares metadata inherited by all child pages:
<?php $meta = [
'lang' => 'es',
'locale' => 'es-ES',
'dir' => 'ltr',
]; ?>
<h1>Español</h1>
Every page under /es/ automatically inherits lang: 'es',
but can override other metadata:
<?php $meta = [
'title' => 'Acerca de',
'date' => '2026-05-05',
]; ?>
<h1>Acerca de Wordless</h1>
Using Locale Data in Templates
Access language information in your templates:
<html lang="<?= e($content->get('locale', 'en-US')) ?>">
<?php if ($content->get('language') === 'ar'): ?>
<!-- Right-to-left styles for Arabic -->
<?php endif; ?>
Querying Language-Specific Content
Fetch all content in a specific language:
$repo = $container->get(ContentRepositoryInterface::class);
// All Spanish pages
$spanish = $repo->all('es');
// All English blog posts
$englishBlog = $repo->all('en/blog');
foreach ($spanish as $page) {
echo $page->get('lang'); // 'es'
}
Language Switching Navigation
Build language switchers by deriving alternate URLs:
<?php
// Peers are resolved at runtime from content structure
// and passed into $pageMeta['peers'] by ContentController
foreach ($pageMeta['peers'] as $lang => $path):
$isActive = $lang === $pageMeta['lang'];
?>
<a href="<?= e($path) ?>" class="<?= $isActive ? 'active' : '' ?>">
<?= e(strtoupper($lang)) ?>
</a>
<?php endforeach; ?>
SEO Considerations
Language Meta Tag
Always set the lang attribute on the <html> element:
<html lang="<?= e($content->get('locale')) ?>">
Alternate Links
Help search engines understand language variations with hreflang links:
<link rel="alternate" hreflang="es" href="/es/acerca" />
<link rel="alternate" hreflang="en" href="/en/about" />
<link rel="alternate" hreflang="x-default" href="/en/about" />
Sitemaps
The automatic sitemap includes all language variants, helping search engines discover them all.
Scaling to More Languages
Adding a new language is as simple as creating a new folder:
mkdir content/fr
echo '<?php $meta = ["lang" => "fr", "locale" => "fr-FR"]; ?>' > content/fr/index.php
All French content now inherits the language metadata automatically.
Best Practices
- Use BCP 47 locale codes:
en-US,es-ES,fr-FR - Organize by language first: Makes permissions and deployment easier
- Translate slugs naturally:
/en/aboutpairs with/es/acerca— use thepeerskey in$metato declare the link explicitly - Translate all navigation: Link switchers and menus for every language
- Maintain parity: Keep content across languages reasonably synchronized