CLI
Use @better-translate/cli when you want Better Translate to build and update locale files for you.
You do not need the CLI to use the runtime packages. It is optional.
1. Install the package
npm install -D @better-translate/cliThe CLI stays provider-agnostic. Your app installs and configures the AI SDK provider package directly.
2. Create a source locale file
Create src/messages/en.json:
1{2 "home": {3 "title": "Hello",4 "description": "Welcome to the app"5 }6}You can also start with an empty {} and let bt extract populate it.
3. Create the CLI config
Create better-translate.config.ts:
1import { createOllama } from "ollama-ai-provider-v2";2import { defineConfig } from "@better-translate/cli/config";34const ollama = createOllama({5 baseURL: process.env.OLLAMA_BASE_URL ?? "http://localhost:11434/api",6});78export default defineConfig({9 sourceLocale: "en",10 locales: ["es", "fr"],11 model: ollama("qwen3:4b"),12 providerOptions: {13 ollama: {14 think: true,15 },16 },17 messages: {18 entry: "./src/messages/en.json",19 },20});If you use Ollama, install ollama-ai-provider-v2. If you use a hosted provider, install that provider package instead, such as @ai-sdk/openai, @ai-sdk/anthropic, or @ai-sdk/moonshotai.
The default Ollama API URL is local: http://localhost:11434/api.
This works the same with hosted providers such as OpenAI, Anthropic, and Moonshot AI. The CLI does not bundle provider helpers anymore.
4. Mark strings in your code
Instead of naming translation keys by hand, write the source text directly and add { bt: true }:
1import { t } from "@better-translate/core";23export function navLabel() {4 return t("Home", { bt: true });5}At runtime, { bt: true } returns the string unchanged. The CLI will replace these calls with proper keys on the next extract.
You can also pass other options like params — they are preserved after extraction:
1// You write:2t("Hello world", { bt: true })3t("Hello {name}", { bt: true, params: { name: "" } })45// After bt extract rewrites the file:6t("components.nav.helloWorld")7t("components.nav.helloName", { params: { name: "" } })The key namespace comes from the source file path (components/nav.tsx → components.nav). bt: true is always removed on rewrite.
5. Extract source keys
npx bt extractThis scans for t(..., { bt: true }) calls, adds the missing keys to your source locale file, and rewrites the calls to plain strict keys.
The CLI automatically finds better-translate.config.ts in your project root. The --config flag is only needed if your config file is in a different location.
6. Run the generator
npx bt generateThis creates the target locale files next to your source file.
If markdown.rootDir is enabled and the run would create or overwrite translated .md or .mdx files, the CLI asks for confirmation before making changes. Use --yes or -y to skip the prompt:
npx bt generate --yesNon-interactive runs that need to write translated markdown files must pass --yes.
7. Use the generated files in your app
After the files exist, import them into your @better-translate/core config just like any hand-written locale file.
Markdown
If you also want localized markdown generation, add the markdown.rootDir option:
1import { createOllama } from "ollama-ai-provider-v2";2import { defineConfig } from "@better-translate/cli/config";34const ollama = createOllama({5 baseURL: process.env.OLLAMA_BASE_URL ?? "http://localhost:11434/api",6});78export default defineConfig({9 sourceLocale: "en",10 locales: ["es", "fr"],11 model: ollama("qwen3:4b"),12 messages: {13 entry: "./src/messages/en.json",14 },15 markdown: {16 rootDir: "./content/docs",17 },18});Examples
Full working examples are in the GitHub repo: