Internationalization
EOS Hub supports multiple languages through next-intl. The application ships with English and Slovak, and can be extended with additional languages.
Supported Languages
| Language | Code | Status |
|---|---|---|
| English | en | Complete |
| Slovak | sk | Complete |
How It Works
Locale Detection
The current locale is determined in the following order:
- Cookie -- A
NEXT_LOCALEcookie stores the user's language preference. - Browser preference -- If no cookie is set, the browser's
Accept-Languageheader is used. - Default -- Falls back to
enif no match is found.
Switching Languages
Users can switch languages using the language selector in the application header. This sets the NEXT_LOCALE cookie and reloads the page with the new locale.
Translation File Structure
Translation files are organized by locale and namespace in the messages/ directory:
messages/
├── en/
│ ├── common.json
│ ├── auth.json
│ ├── dashboard.json
│ ├── meetings.json
│ ├── scorecard.json
│ ├── rocks.json
│ ├── issues.json
│ ├── todos.json
│ ├── vto.json
│ ├── accountability.json
│ ├── people-analyzer.json
│ ├── admin.json
│ └── ...
└── sk/
├── common.json
├── auth.json
├── dashboard.json
└── ...Each JSON file contains a flat or nested object of translation keys and values:
{
"title": "Scorecard",
"addMeasurable": "Add Measurable",
"weekOf": "Week of {date}",
"goal": {
"above": "Above {value}",
"below": "Below {value}",
"equal": "Equal to {value}"
}
}Adding a New Language
To add a new language (for example, Czech):
1. Create Translation Files
Create a new directory messages/cs/ and copy all files from messages/en/:
cp -r messages/en messages/csTranslate all values in the copied files.
2. Update next-intl Configuration
Add the new locale to the i18n.ts configuration:
export const locales = ['en', 'sk', 'cs'] as const;
export const defaultLocale = 'en';3. Update the Middleware
Ensure the middleware includes the new locale in its matcher:
export default createMiddleware({
locales: ['en', 'sk', 'cs'],
defaultLocale: 'en',
});4. Add to VitePress Documentation (Optional)
If you want documentation in the new language, add a new locale entry in .vitepress/config.ts:
locales: {
en: { label: 'English', lang: 'en', link: '/en/' },
sk: { label: 'Slovencina', lang: 'sk', link: '/sk/' },
cs: { label: 'Cestina', lang: 'cs', link: '/cs/' },
},Using Translations in Code
In Server Components
import { useTranslations } from 'next-intl';
export default function ScorecardPage() {
const t = useTranslations('scorecard');
return <h1>{t('title')}</h1>;
}In Client Components
'use client';
import { useTranslations } from 'next-intl';
export default function AddButton() {
const t = useTranslations('scorecard');
return <button>{t('addMeasurable')}</button>;
}With Variables
// Translation: "weekOf": "Week of {date}"
t('weekOf', { date: '2026-01-05' });
// Output: "Week of 2026-01-05"With Plurals
{
"rockCount": "You have {count, plural, one {# rock} other {# rocks}}"
}t('rockCount', { count: 3 });
// Output: "You have 3 rocks"TIP
Always use translation keys instead of hardcoded strings, even if you are only supporting one language. This makes future localization much easier.
Best Practices
- Namespace by feature -- Keep translations organized by feature area (scorecard, rocks, meetings).
- Use descriptive keys -- Prefer
addMeasurableoverbtn1. - Include context -- When a string might be ambiguous, add a comment or use a more specific key name.
- Handle plurals -- Use ICU message format for quantities.
- Test all locales -- After adding translations, verify the layout does not break with longer text (common with German or Slovak).
Next Steps
- Theming -- Customize the visual appearance
- Configuration -- Full configuration reference