FeaturesInternationalization

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:

StyleAppearance
textLanguage name only
rcfCountry flag icon
cfiSVG 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:

  1. Translate locales/<app>/<code>/common.json.
  2. Run pnpm sk:generate.
  3. 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.

Next steps