Skip to content

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

LanguageCodeStatus
EnglishenComplete
SlovakskComplete

How It Works

Locale Detection

The current locale is determined in the following order:

  1. Cookie -- A NEXT_LOCALE cookie stores the user's language preference.
  2. Browser preference -- If no cookie is set, the browser's Accept-Language header is used.
  3. Default -- Falls back to en if 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:

json
{
  "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/:

bash
cp -r messages/en messages/cs

Translate all values in the copied files.

2. Update next-intl Configuration

Add the new locale to the i18n.ts configuration:

typescript
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:

typescript
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:

typescript
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

tsx
import { useTranslations } from 'next-intl';

export default function ScorecardPage() {
  const t = useTranslations('scorecard');

  return <h1>{t('title')}</h1>;
}

In Client Components

tsx
'use client';

import { useTranslations } from 'next-intl';

export default function AddButton() {
  const t = useTranslations('scorecard');

  return <button>{t('addMeasurable')}</button>;
}

With Variables

tsx
// Translation: "weekOf": "Week of {date}"
t('weekOf', { date: '2026-01-05' });
// Output: "Week of 2026-01-05"

With Plurals

json
{
  "rockCount": "You have {count, plural, one {# rock} other {# rocks}}"
}
tsx
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 addMeasurable over btn1.
  • 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

Built with VitePress