* Initial plan * Initial analysis: understanding locale system issues Co-authored-by: denshooter <44590296+denshooter@users.noreply.github.com> * Fix translation types to match actual component usage Co-authored-by: denshooter <44590296+denshooter@users.noreply.github.com> * Add comprehensive locale system documentation and fix API route types Co-authored-by: denshooter <44590296+denshooter@users.noreply.github.com> * Address code review feedback: improve readability and translate comments to English Co-authored-by: denshooter <44590296+denshooter@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: denshooter <44590296+denshooter@users.noreply.github.com>
4.9 KiB
Directus Integration - Migration Guide
🎯 Overview
This portfolio now has a hybrid i18n system:
- ✅ JSON Files (Primary) → All translations work from
messages/*.jsonfiles - ✅ Directus CMS (Optional) → Can override translations dynamically without rebuilds
Important: Directus is optional. The app works perfectly fine without it using JSON fallbacks.
📁 New File Structure
Core Infrastructure
lib/directus.ts- REST Client for Directus (usesde-DE,en-USlocale codes)lib/i18n-loader.ts- Loads texts with Fallback Chainlib/translations-loader.ts- Batch loader for all sections (cleaned up to match actual usage)types/translations.ts- TypeScript types for all translation objects (fixed to match components)
Components
All component wrappers properly load and pass translations to client components.
🔄 How It Works
Without Directus (Default)
Component → useTranslations("nav") → JSON File (messages/en.json)
With Directus (Optional)
Server Component → getNavTranslations(locale)
→ Try Directus API (de-DE/en-US)
→ If not found: JSON File (de/en)
→ Props to Client Component
🗄️ Directus Setup (Optional)
Only set this up if you want to edit translations through a CMS without rebuilding the app.
1. Environment Variables
Add to .env.local:
DIRECTUS_URL=https://cms.example.com
DIRECTUS_STATIC_TOKEN=your_token_here
If these are not set, the system will skip Directus and use JSON files only.
2. Collection: messages
Create a messages collection in Directus with these fields:
key(String, required) - e.g., "nav.home"translations(Translations) - Directus native translations feature- Configure languages:
en-USandde-DE
Note: Keys use dot notation (nav.home) but locales use dashes (en-US, de-DE).
3. Permissions
Grant Public role read access to messages collection.
📝 Translation Keys
See docs/LOCALE_SYSTEM.md for the complete list of translation keys and their structure.
All keys are organized hierarchically:
nav.*- Navigation itemshome.hero.*- Hero sectionhome.about.*- About sectionhome.projects.*- Projects sectionhome.contact.*- Contact form and infofooter.*- Footer contentconsent.*- Privacy consent banner
🎨 Rich Text Content
For longer content that needs formatting (bold, italic, lists), use the content_pages collection:
Collection: content_pages (Optional)
Fields:
slug(String, unique) - e.g., "home-hero"locale(String) -enordetitle(String)content(Rich Text or Long Text)
Examples:
home-hero- Hero section descriptionhome-about- About section contenthome-contact- Contact intro text
Components fetch these via /api/content/page and render using RichTextClient.
🔍 Fallback Chain
For every translation key, the system searches in this order:
- Directus (if configured) in requested locale (e.g.,
de-DE) - Directus in English fallback (e.g.,
en-US) - JSON file in requested locale (e.g.,
messages/de.json) - JSON file in English (e.g.,
messages/en.json) - Key itself as last resort (e.g., returns
"nav.home")
✅ What Was Fixed
Previous issues that have been resolved:
- ✅ Type mismatches - All translation types now match actual component usage
- ✅ Unused fields - Removed translation keys that were never used (like
hero.greeting,hero.name) - ✅ Wrong structure - Fixed
AboutTranslationsstructure (removed fakeinterestsnesting) - ✅ Missing keys - Aligned loaders with JSON files and actual component requirements
- ✅ Confusing comments - Removed misleading comments in
translations-loader.ts
🎯 Best Practices
- Always maintain JSON files - Even if using Directus, keep JSON files as fallback
- Use types - TypeScript types ensure correct usage
- Test without Directus - App should work perfectly without CMS configured
- Rich text for formatting - Use
content_pagesfor content that needs bold/italic/lists - JSON for UI labels - Use JSON/messages for short UI strings like buttons and labels
🐛 Troubleshooting
Directus not configured
This is normal! The app works fine. All translations come from JSON files.
Want to use Directus?
- Set up
DIRECTUS_URLandDIRECTUS_STATIC_TOKEN - Create
messagescollection - Add your translations
- They will override JSON values
Translation not showing?
Check in this order:
- Does key exist in
messages/en.json? - Is the key spelled correctly?
- Is component using correct namespace?
📚 Further Reading
- Complete locale documentation:
docs/LOCALE_SYSTEM.md - Directus setup checklist:
DIRECTUS_CHECKLIST.md - Operations guide:
docs/OPERATIONS.md