Localization
Root.js has built-in localization: locale-aware URLs, translated strings, and translations for CMS content, all managed in one place.
Configuration
Set the site's locales and URL format with the i18n option in root.config.ts:
// @/root.config.ts
import {defineConfig} from '@blinkk/root';
export default defineConfig({
i18n: {
locales: ['en', 'ja'],
defaultLocale: 'en',
urlFormat: '/[locale]/[path]',
},
});
Each route is then served at a URL per locale, e.g. /ja/about. Use fallbacks to set which locales a missing translation falls back to.
Translations
Translated strings live in translations/, one JSON file per locale, mapping each source string to its translation:
// @/translations/fr.json
{
"Hello, world!": "Bonjour le monde !",
"Hello, {name}!": "Bonjour {name} !"
}
useTranslations()
useTranslations() returns a function that translates a string into the current locale. Values in curly braces are replaced with the params you pass.
// @/routes/index.tsx
import {useTranslations} from '@blinkk/root';
export default function Page(props) {
const t = useTranslations();
if (props.name) {
return <h1>{t('Hello, {name}!', {name: props.name})}</h1>;
}
return <h1>{t('Hello, world!')}</h1>;
}
Translating CMS content
Schema fields marked with translate: true are sent for translation. Editors translate them in the CMS, with AI, or by exporting them to a spreadsheet or translation vendor.
Translations are stored in the CMS. createRoute() loads them for each page, and cmsClient.loadTranslationsForLocale() loads them anywhere else. Pass them to ctx.render() to use them with useTranslations(). See Data fetching.
For content that differs by locale but isn't a translation, like a localized image or link, use localized values.
Translations manager v2 (experimental)
The v2 translations manager stores each doc's translations with the doc, per locale, and drafts and publishes them together with the doc. It's still experimental, so it's off by default. To try it, opt in:
cmsPlugin({
experiments: {v2TranslationsManager: true},
});
On the first dev server start or root build after you opt in, the CMS copies your v1 translations into the new format. The v1 data is left in place as a backup. To run the migration yourself, use root-cms translations.migrate, and --status to check whether it has run.
The migration check runs before each build, so with the v2 translations manager, root build needs Firestore access. Make sure your CI has application default credentials, e.g. gcloud auth application-default login or GOOGLE_APPLICATION_CREDENTIALS.