Setup
Bu sayfada
This guide covers the basic setup for Better i18n with TanStack Start.
Step 1: Create i18n Configuration #
Create a minimal configuration file:
app/i18n.config.ts
export const i18nConfig = { // [!code highlight]
projectId: "your-org/your-project", // [!code highlight]
defaultLocale: "en", // [!code highlight]
} as const // [!code highlight]Step 2: Setup Root Layout #
Load messages on the server and provide them to the client:
app/routes/__root.tsx
import {
createRootRouteWithContext,
Outlet,
HeadContent,
Scripts,
} from "@tanstack/react-router"
import { BetterI18nProvider } from "@better-i18n/use-intl" // [!code ++]
import { getMessages } from "@better-i18n/use-intl/server" // [!code ++]
import { i18nConfig } from "../i18n.config" // [!code ++]
interface RouterContext {
locale: string
}
export const Route = createRootRouteWithContext<RouterContext>()({
staleTime: 0, // re-run loader when locale changes // [!code highlight]
loader: async ({ context }) => {
const locale = context.locale || i18nConfig.defaultLocale
const messages = await getMessages({ // [!code ++]
projectId: i18nConfig.projectId, // [!code ++]
locale, // [!code ++]
}) // [!code ++]
return { messages, locale } // [!code ++]
},
component: RootComponent,
})
function RootComponent() {
const { messages, locale } = Route.useLoaderData()
return (
<html lang={locale}>
<head>
<HeadContent />
</head>
<body>
<BetterI18nProvider // [!code ++]
projectId={i18nConfig.projectId} // [!code ++]
locale={locale} // [!code ++]
messages={messages} // [!code ++]
timeZone="UTC" // [!code highlight]
> // [!code ++]
<Outlet />
</BetterI18nProvider> // [!code ++]
<Scripts />
</body>
</html>
)
}BetterI18nProvider Props #
| Prop | Type | Default | Description |
|---|---|---|---|
project | string | Required | Project identifier (org/project) |
locale | string | Required | Current locale (from router context) |
messages | Messages | — | Pre-loaded SSR messages from getMessages() |
timeZone | string | System tz | IANA timezone — set explicitly to avoid SSR hydration mismatches |
onLocaleChange | (locale: string) => void | — | Locale switch callback |
storage | TranslationStorage | — | Persistent translation cache adapter |
staticData | Record<string, Messages> | — | Offline fallback translations |
fetchTimeout | number | 10000 | CDN fetch timeout in ms |
retryCount | number | 1 | Retry attempts on CDN failure |
Step 3: Use Translations #
Use the useTranslations hook in any component:
app/routes/index.tsx
import { createFileRoute } from "@tanstack/react-router"
import { useTranslations } from "@better-i18n/use-intl" // [!code highlight]
export const Route = createFileRoute("/")({
component: HomePage,
})
function HomePage() {
const t = useTranslations("home") // [!code highlight]
return (
<div>
<h1>{t("title")}</h1>
<p>{t("description")}</p>
</div>
)
}Step 4: Add Language Switcher #
Use the built-in component or build your own:
TSX
import { LanguageSwitcher } from "@better-i18n/use-intl"
function Header() {
return (
<header>
<nav>
<LanguageSwitcher className="locale-select" />
</nav>
</header>
)
}Interpolation #
Pass dynamic values to translations:
TSX
function Greeting({ user }) {
const t = useTranslations("greeting")
return <p>{t("welcome", { name: user.name })}</p>
}Translation file
{
"greeting": {
"welcome": "Welcome back, {name}!"
}
}Date & Number Formatting #
Use the useFormatter hook:
TSX
import { useFormatter } from "@better-i18n/use-intl"
function ProductPrice({ price, date }) {
const format = useFormatter()
return (
<div>
<span>{format.number(price, { style: "currency", currency: "USD" })}</span>
<time>{format.dateTime(date, { dateStyle: "medium" })}</time>
</div>
)
}Next Steps #
- SSR & Hydration — Configure server-side rendering properly.
- Path-Based Routing — Add SEO-friendly locale URLs.
- Middleware — Automatic locale detection.
Better I18N