Internationalization
Locale-prefixed routing, per-app translations, browser detection, and language switchers powered by next-intl.
Overview
SiteKnock has first-class internationalization built on next-intl. Every route is locale-prefixed, translations are organized per app and per locale, and you get ready-made language switchers.
Configure languages
"i18n": {
"enabled": true,
"detectBrowser": true,
"langSwitcher": {
"enabled": true,
"when": "hover", // "click" | "hover"
"iconType": "rcf" // "text" | "rcf" | "cfi"
},
"languages": [
{ "code": "en", "text": "English" },
{ "code": "es", "text": "Español" }
]
}
Locale-prefixed routes
Every route is prefixed with the active locale, and the root path redirects to the default language:
/en/dashboard/es/dashboard
Language switchers
Pick a switcher style with langSwitcher.iconType:
| Style | Appearance |
|---|---|
text | Language name only |
rcf | Country flag icon |
cfi | SVG country flags |
Translation files
Translations are JSON, organized per app and locale, with support for nested page bundles:
locales/
└── my-app/
├── en/
│ ├── common.json
│ └── pages/
│ └── legal-terms.json
└── es/
└── common.json
English is always the source language; other locales inherit English for any missing keys, so your app never shows a blank string.
Add a locale
pnpm sk:scaffold locale fr
Or use Studio → Languages, which provides a key-value editor for translations. After adding a locale:
- Translate
locales/<app>/<code>/common.json. - Run
pnpm sk:generate. - Run the app and switch languages to verify.
Using translations in code
import { useTranslations, useLocale } from "@/generated/i18n"
const t = useTranslations()
const locale = useLocale()
All user-facing text should use translation keys — never hardcode strings. This keeps your app fully localizable from day one.