Quick start
Install Better Translation, configure local runtime loading, provide messages, and mark your first Message.
This gets Better Translation running in local dev mode. Local mode is the default: the Vite
plugin scans your app, writes generated Runtime bundles, and generates the virtual
better-translation/messages module your app uses at runtime.
1. Install
Add the package to a Vite app running Vite 8 or later.
bun add better-translation2. Configure the Vite plugin
Add the plugin to your Vite config, list your Locales, and choose the Default locale.
import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import { tanstackStart } from "@tanstack/react-start/plugin/vite"
import { betterTranslation } from "better-translation/vite"
export default defineConfig({
plugins: [
betterTranslation({
locales: ["en", "es", "fr"],
defaultLocale: "en",
}),
tanstackStart(),
react(),
],
})Plugin order
Put betterTranslation before the framework plugin. It rewrites Translation markers before React or Svelte transforms see your
code.
3. Load messages and provide them
The plugin config above generates better-translation/messages. Load the active Locale from
that virtual module, then wrap your app with TranslateProvider.
Load messages before rendering your translated app, then pass them to the framework provider.
import { createRootRoute, Outlet } from "@tanstack/react-router"
import { loadMessages } from "better-translation/messages"
import { TranslateProvider } from "better-translation/react"
import { getLocale } from "./-locale"
export const Route = createRootRoute({
beforeLoad: async () => {
const locale = await getLocale()
const messages = await loadMessages(locale)
return { locale, messages }
},
component: RootComponent,
})
function RootComponent() {
const { locale, messages } = Route.useRouteContext()
return (
<TranslateProvider locale={locale} messages={messages}>
<Outlet />
</TranslateProvider>
)
}The runtime target only changes how the generated loadMessages implementation gets the
Runtime bundle.
4. Mark copy with T
Once the provider is in place, wrap UI copy where you author it. The plugin extracts this as a Message and gives it a stable lookup id.
import { T } from "better-translation/react"
export function CheckoutButton() {
return (
<button>
<T>Pay now</T>
</button>
)
}5. Start dev
Run your Vite dev server. The plugin scans your source and writes local Runtime bundles.
bun run devThe default module target writes files under src/lib/bt. The public target writes files
under Vite's public directory, at /bt by default.
Bonus: auto-fill Locale values
Local mode can fill missing non-default Locale values during dev with a translate function.
Use the built-in AI helper, or call your own translation API.
import { defineConfig } from "vite"
import { betterTranslation } from "better-translation/vite"
import { createAiTranslate } from "better-translation/ai"
export default defineConfig({
plugins: [
betterTranslation({
locales: ["en", "es", "fr"],
defaultLocale: "en",
runtime: {
type: "local",
translate: createAiTranslate({
prompt: "Friendly, concise product UI copy.",
}),
},
}),
],
})The plugin writes generated values into your local Runtime bundles and cache. Production local
builds never call translate; they only check that committed Runtime bundles are complete.