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.
EOS Terms (Glossary)
EOS terms such as Rocks, L10, IDS or V/TO are never written directly in the message files. They are placeholders that are filled from a glossary with two packs, EOS and neutral, chosen per organization:
"addRock": "Add [[Rock]]",
"noRocks": "No [[rock.other]] for this quarter",
"title": "[[meeting.titles]]"[[key]]uses the term's default form;[[key.form]]a named form (plural, Slovak cases such as[[rock.gen]]).- A capital first letter (
[[Rock]]) capitalizes the result. [[product]]is the product name (PRODUCT_NAME).- The terms and their forms live in
src/i18n/glossary/en.tsandsk.ts; both packs of a language must define the same forms. Tests fail on an unknown placeholder or a term written outside a placeholder.
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