{t('home.hero.title')}
{t('home.hero.subtitle')}
{t('home.cta')}---
title: Internationalisation
sort: 130
section-id: guides
keywords: i18n, internationalisation, localisation, translation, locale routing, multilingual
description: Setting up internationalisation in Velox — translation files, locale routing, and pluralisation
language: en
---
# Internationalisation
Velox has built-in internationalisation (i18n) support through `@velox/i18n`. It handles locale detection, URL-based locale routing, typed translation files, and pluralisation.
## Setup
```bash
npm install @velox/i18n
```
Configure in `velox.config.ts`:
```typescript
import { defineConfig } from 'velox';
export default defineConfig({
i18n: {
locales: ['en', 'fr', 'de', 'ja'],
defaultLocale: 'en',
routing: 'prefix-except-default',
// 'prefix': all locales get a prefix (/en, /fr, /de, /ja)
// 'prefix-except-default': default locale has no prefix
// 'domain': different domains per locale
messagesDir: 'messages',
},
});
```
## Translation Files
Create a `messages/` directory at your project root:
```
messages/
├── en.json
├── fr.json
├── de.json
└── ja.json
```
Translation files use a flat or nested key structure:
```json
// messages/en.json
{
"nav.home": "Home",
"nav.blog": "Blog",
"nav.about": "About",
"home.hero.title": "Build faster with Velox",
"home.hero.subtitle": "The TypeScript framework for the modern web",
"home.cta": "Get started",
"post.readMore": "Read more",
"post.publishedOn": "Published on {date}",
"post.comments": "{count, plural, =0{No comments} =1{1 comment} other{# comments}}",
"auth.loginButton": "Log in",
"auth.logoutButton": "Log out",
"errors.notFound": "Page not found",
"errors.serverError": "Something went wrong"
}
```
```json
// messages/fr.json
{
"nav.home": "Accueil",
"nav.blog": "Blog",
"nav.about": "À propos",
"home.hero.title": "Construisez plus vite avec Velox",
"home.hero.subtitle": "Le framework TypeScript pour le web moderne",
"home.cta": "Commencer",
"post.readMore": "Lire la suite",
"post.publishedOn": "Publié le {date}",
"post.comments": "{count, plural, =0{Aucun commentaire} =1{1 commentaire} other{# commentaires}}",
"auth.loginButton": "Se connecter",
"auth.logoutButton": "Se déconnecter",
"errors.notFound": "Page introuvable",
"errors.serverError": "Une erreur est survenue"
}
```
## Using Translations
### In Server Blocks
```tsx
---
import { useTranslations } from 'velox/i18n';
const t = useTranslations();
const locale = useLocale().locale;
---
{t('home.hero.subtitle')}{t('home.hero.title')}
{t('post.publishedOn', { date: publishedDate })}
``` ### Pluralisation Velox uses the ICU message format for pluralisation: ```typescript t('post.comments', { count: 0 }); // "No comments" t('post.comments', { count: 1 }); // "1 comment" t('post.comments', { count: 42 }); // "42 comments" ``` ### In Client Components ```typescript import { useTranslations } from 'velox/i18n/client'; export default function LikeButton({ postId }: { postId: string }) { const t = useTranslations(); const liked = signal(false); return ( ); } ``` ## Locale Routing With `routing: 'prefix-except-default'` and `defaultLocale: 'en'`: | URL | Locale | |-----|--------| | `/` | `en` | | `/about` | `en` | | `/fr` | `fr` | | `/fr/about` | `fr` | | `/de/blog/my-post` | `de` | Velox automatically generates alternate hreflang links for SEO. ## Locale Switcher Component ```tsx import { useLocale, usePathname } from 'velox/i18n/client'; export default function LocaleSwitcher() { const { locale, locales } = useLocale(); const pathname = usePathname(); return (