Internationalization (i18n)
Adonis EOS provides comprehensive multi-language support for global content delivery.
Locale Configuration
Configure available locales in .env:
LOCALES=en,es,fr,de
DEFAULT_LOCALE=en
Content Translation
Post-Level Translation
Each post has a locale:
{
"title": "Welcome",
"slug": "welcome",
"locale": "en",
"type": "page"
}
Create translations by creating new posts with:
Same slug
Different locale
Linked via slug matching
Module Translation
Mark props as translatable in module schema:
propsSchema: {
title: {
type: 'string',
translatable: true, // This content varies by language
},
maxItems: {
type: 'number',
translatable: false, // This stays the same
}
}
URL Structure
Localized URLs follow this pattern:
/welcome → Default locale (en)
/es/bienvenido → Spanish version
/fr/bienvenue → French version
API Usage
Fetch content by locale:
GET /api/posts?locale=es&type=page
Menu Translation
Create separate menus per locale:
GET /api/menus/primary?locale=en → English menu
GET /api/menus/primary?locale=es → Spanish menu
Each menu can have locale-specific:
Item labels
URLs
Structure
Translation Workflow
1. Create Source Content
Create the original content in the default locale (usually en).
2. Duplicate for Translation
In the admin:
Open the source post
Click "Duplicate"
Change locale to target language
Update slug (e.g.,
welcome→bienvenido)Save as draft
3. Translate Content
Editors with appropriate permissions can:
Edit translatable module props
Update title, slug, and metadata
Keep non-translatable settings unchanged
4. Publish Translation
Once reviewed, publish the translated version.
Language Switching
Frontend Implementation
// Get current locale from URL
const locale = window.location.pathname.split('/')[1] || 'en'
// Switch language
const switchLocale = (newLocale: string) => {
const currentSlug = getCurrentSlug()
window.location.href = `/${newLocale}/${currentSlug}`
}
// Language switcher component
;<select value=en onChange={(e) => switchLocale(e.target.value)}>
<option value="en">English</option>
<option value="es">Español</option>
<option value="fr">Français</option>
</select>
Finding Translations
To find all translations of a post:
GET /api/posts?slug=welcome&allLocales=true
Returns all locale versions of posts with that slug.
Best Practices
Content Guidelines
Keep slugs consistent - Use translated versions
Maintain structure - Same modules in same order
Cultural adaptation - Don't just translate, localize
Test thoroughly - Check all locales before launch
Technical Guidelines
Use locale-aware routes - Always include locale in URLs
Default fallbacks - Fall back to default locale if translation missing
SEO optimization - Use
hreflangtagsDate formatting - Respect locale-specific formats
RTL support - Consider right-to-left languages
Role-Based Translation
The Translator role has permissions to:
View all content
Edit translatable fields
Save drafts for review
Cannot publish or delete
Perfect for translation teams!
Forms and i18n
Forms can have locale-specific versions:
{
"slug": "contact",
"locale": "en",
"fields": [
{ "label": "Name", "name": "name" }
]
}
Create separate form definitions per locale.
Common Patterns
Loading Translations
// Get posts for current locale
const posts = await fetch(`/api/posts?locale=${currentLocale}`)
// Get post with fallback
const post = await fetch(`/api/posts/$internationalization?locale=$en`).catch(() =>
fetch(`/api/posts/$internationalization?locale=en`)
)
Locale Detection
// From URL
const urlLocale = window.location.pathname.split('/')[1]
// From browser
const browserLocale = navigator.language.split('-')[0]
// From cookie
const savedLocale = cookies.get('locale')
// Priority: URL > Cookie > Browser > Default
const locale = urlLocale || savedLocale || browserLocale || 'en'
Troubleshooting
Issue: Content not showing in new locale
Solution: Ensure post exists with correct locale value
Issue: Menu showing wrong language
Solution: Create locale-specific menu or verify API call includes
?locale=
Issue: Editor can't edit translations
Solution: Check role has appropriate permissions in role configuration