Documentation

i18n

Translate pages

Render localized strings with use-intl, declare route namespaces, and load translation bundles.

VitNode uses use-intl for frontend translations. To keep pages light, a page downloads only the translation namespaces its route declares.

Declare the namespace on the route

Plugin routes declare the namespaces a page renders as messages in routes.ts. VitNode loads those strings together with the page's code. This is the site-notes notes page from Plugin page:

plugins/site-notes/src/routes.ts
page('/site-notes/notes', {
  component: lazy(() => import('./pages/notes-page')),
  messages: ['site-notes.notes'], 
}),

Read the strings

Read them with useTranslations from use-intl, scoped to the same namespace:

plugins/site-notes/src/pages/notes-page.tsx
import { useTranslations } from 'use-intl'

const NotesPage = () => {
  const t = useTranslations('site-notes.notes')

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

export default NotesPage

Define the strings

Put them under the plugin's id in src/locales/en.json:

plugins/site-notes/src/locales/en.json
{
  "site-notes": {
    "notes": {
      "title": "Site notes",
      "empty": "No notes yet."
    }
  }
}

If a key renders as its own name, such as site-notes.notes.title, the route does not declare the namespace that holds it.

Keep feature copy in the plugin

Host messages are for the site shell. A product page should declare its plugin namespace as the route's messages and keep its locale JSON beside the route.

For placeholders, plurals and rich text in your strings, see Messages. For a page's <title>, translate in head with t; see Route module.

Global namespaces

core.global is provided by the root shell to every route, supplying shared strings for dialogs, toasts, and buttons.